JSFiddle - React, Tailwind, and code Playground
by tfoller
HTML
<div id="cont">
<canvas id="main_canvas" width="600" height="400"></canvas>
<label id="ovr"></label>
<label id="lft"></label>
<label id="rgt"></label>
</div>
<div id="ui">
<button id="pl" style="width: 50px;">....</button>
<button id="sl"><</button>
<label>move split</label>
<button id="sr">></button>
<label style="margin-left:20px;">lights:</label>
<label class="chk">
<input type="checkbox" id="amb" class="chk" checked="">
<span>ambient</span>
</label>
<label class="chk">
<input type="checkbox" id="dir" class="chk" checked="">
<span>directional</span>
</label>
<label class="chk">
<input type="checkbox" id="pnt" class="chk" checked="">
<span>point</span>
</label>
<label class="chk">
<input type="checkbox" id="att" class="chk">
<span>point attenuation</span>
</label>
</div>
<script type="x-shader/x-vertex" id="vs_lamb">
// interpolated world positions and normals
varying mat2x3 vpn;
void main() {
vec4 pos = vec4(position, 1.0);
vec4 nrm = vec4(normal, 0.0);
gl_Position = projectionMatrix * modelViewMatrix * pos;
vpn = mat2x3(modelMatrix * mat2x4(pos, nrm));
vpn[1] = normalize(vpn[1]);
}
</script>
<script type="x-shader/x-vertex" id="fs_lamb">
struct ambientLight {
vec3 color;
float intensity;
};
struct directLight {
vec3 position;
vec3 color;
float intensity;
};
struct pointLight {
vec3 position;
vec3 color;
float intensity;
float power;
float range;
float decay;
};
uniform vec3 dif_color;
uniform ambientLight amb_light;
uniform directLight dir_light;
uniform pointLight pnt_light;
varying mat2x3 vpn;
void main() {
// ambient light
vec3 light = amb_light.color * amb_light.intensity;
// direct light
vec3 dl_pos = dir_light.position;
vec3 dl_dir = normalize(dl_pos);
float dl_dot = dot(dl_dir, vpn[1]);
light += clamp(dl_dot,...
CSS
* {
padding: 0;
margin: 0;
box-sizing: border-box;
-webkit-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}
*:focus {
outline: none;
}
body {
background: #112233;
width: 100vw;
height: 100vh;
}
button {
padding: 3px;
margin-left: 10px;
color: #333;
cursor: pointer;
}
button, input[type="checkbox"] {
filter: invert(100%);
}
label {
color: #aaa;
font-family: Arial, Helvetica, sans-serif;
font-size: 13px;
margin-left: 10px;
}
canvas {
display: inline-block;
border: solid 1px grey;
box-sizing: content-box;
margin: 10px;
}
.chk {
cursor: pointer;
}
.opa {
opacity: 0.3;
}
#cont {
display: inline-block;
position: relative;
}
#ovr, #lft, #rgt {
position: absolute;
opacity: 0.5;
}
#ovr {
bottom: 18px;
right: 18px;
}
#lft {
left: 8px;
top: 15px;
}
#rgt {
right: 18px;
top: 15px;
}
JavaScript
import * as THREE from 'https://unpkg.com/three/build/three.module.js'
import * as CONTROLS from 'https://alikim.com/jsm/controls.module.js'
const get = id => document.getElementById(id);
// 3D
const getShader = id => {
let src = get(id).textContent;
return src;
}
const canvas = get('main_canvas');
const [w, h] = [canvas.width, canvas.height];
const renderer = new THREE.WebGLRenderer({
antialias: true,
canvas: canvas,
});
renderer.setPixelRatio(window.devicePixelRatio);
renderer.setSize(w, h);
const [near, far] = [1, 600];
const mid = -0.5 * (near + far);
const camera = new THREE.PerspectiveCamera(45, w / h, near, far);
camera.position.set(200, 100, 0);
camera.lookAt(0, 0, mid);
// scene for built-in shaders
const scene = new THREE.Scene();
scene.background = new THREE.Color(0, 0, 0.3);
// scene for custom
const scene2 = new THREE.Scene();
scene2.background = new THREE.Color(0, 0, 0.3);
// lights
const intensity = {amb: 0.2, dir: 0.8, pnt: 1.5};
const amb_light = new THREE.AmbientLight(0xfa0000, intensity.amb);
const dir_light = new THREE.DirectionalLight(0x008f00, intensity.dir);
const pl = { power: 1, decay: 0, };
const pnt_light = new THREE.PointLight(0xffffff, intensity.pnt, 1000, pl.decay);
dir_light.position.set(1, 0.5, 0.7);
pnt_light.position.set(0, 0, mid);
// point light helper
const hlp = new THREE.Mesh(
new THREE.SphereGeometry(3, 16, 8),
new THREE.MeshBasicMaterial( { color: pnt_light.color } )
);
hlp.position.copy(pnt_light.position);
const hlp2 = hlp.clone();
// standard cube
const cube = new THREE.Mesh(
new THREE.BoxGeometry(1, 1, 1),
new THREE.MeshLambertMaterial({ color: 0x808000 }),
);
// custom shader cube
const amb_uni = {
color: Object.values(amb_light.color),
get intensity() { return amb_light.intensity },
};
const dir_uni = {
position: Object.values(dir_light.position),
color: Object.values(dir_light.color),
get intensity() { return dir_light.intensity },
};
const pnt_uni = {
position:...