JSFiddle - React, Tailwind, and code Playground

by tfoller

HTML

<div id="cont">
  <canvas id="main_canvas" width="680" 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">&lt;</button>
  <label>move split</label>
  <button id="sr">&gt;</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>
  <label class="chk">
    <input type="checkbox" id="rfl" class="chk" checked="">
    <span>reflection</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;

  // specular
  uniform bool spec;
  uniform float spec_falloff;
  uniform float spec_strength;
  uniform vec3 cam_pos;
  
  varying mat2x3 vpn;

  vec3 mirror(vec3 n, vec3 a)...

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:...