diff --git a/scripts/3d_model_viewer.js b/scripts/3d_model_viewer.js index d36998d..a2fc4a5 100644 --- a/scripts/3d_model_viewer.js +++ b/scripts/3d_model_viewer.js @@ -13,6 +13,41 @@ var STEP_PARAMS = { linearDeflection: 0.4, angularDeflection: 0.2 }; + // Default render mode is the electron-microscope look; append ?render=normal to disable. + var RENDER_MODE = new URLSearchParams(window.location.search).get("render") || "em"; + var EM_VERT = [ + "varying vec3 vNormal;", + "varying vec3 vViewPos;", + "void main() {", + " vNormal = normalize(normalMatrix * normal);", + " vec4 mvPosition = modelViewMatrix * vec4(position, 1.0);", + " vViewPos = mvPosition.xyz;", + " gl_Position = projectionMatrix * mvPosition;", + "}" + ].join("\n"); + var EM_FRAG = [ + "uniform vec3 uColor;", + "uniform vec3 uLightDir;", + "varying vec3 vNormal;", + "varying vec3 vViewPos;", + "float emHash(vec2 p) { return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453); }", + "void main() {", + " vec3 N = normalize(vNormal);", + " vec3 L = normalize(uLightDir);", + " float diff = max(dot(N, L), 0.0);", + " float back = max(dot(N, -L), 0.0) * 0.2;", + " float lum = 0.25 + diff * 0.75 + back;", + " vec3 dN = fwidth(vNormal) * 2.0;", + " vec3 dP = fwidth(vViewPos);", + " float edge = clamp(length(dN) * 6.0 + length(dP) * 0.4, 0.0, 1.0);", + " float line = smoothstep(0.0, 0.4, mod(gl_FragCoord.y, 4.0) - 1.2);", + " float scan = 0.82 + 0.18 * line;", + " vec3 col = uColor * (lum * scan + edge * 0.6);", + " col += vec3(0.04) * (emHash(gl_FragCoord.xy) - 0.5);", + " gl_FragColor = vec4(col, 1.0);", + "}" + ].join("\n"); + var WORKER_SRC = [ 'import { OcctKernel } from "' + OCCT_JS_ABS + '";', "let kernel = null; let initP = null;", @@ -234,6 +269,20 @@ renderer.setSize(initSize.w, initSize.h); var cs = getComputedStyle(document.documentElement); function cssVar(n, fb) { var v = (cs.getPropertyValue(n) || "").trim(); return v || fb; } + function buildMaterial() { + if (RENDER_MODE === "em") { + return new THREE.ShaderMaterial({ + uniforms: { + uColor: { value: new THREE.Color(0xd6d6d6) }, + uLightDir: { value: new THREE.Vector3(0.6, 0.9, 0.4).normalize() } + }, + vertexShader: EM_VERT, + fragmentShader: EM_FRAG, + side: THREE.DoubleSide + }); + } + return new THREE.MeshStandardMaterial({ color: cssVar("--model-viewer-color", cssVar("--background-active", "#e0a15c")), metalness: 0.2, roughness: 0.6, side: THREE.DoubleSide, flatShading: false }); + } renderer.setClearColor(cssVar("--background-primary", "#1d150f"), 1); var scene = new THREE.Scene(); @@ -310,7 +359,7 @@ geo.setAttribute("position", new THREE.Float32BufferAttribute(e.data.positions, 3)); if (e.data.indices) geo.setIndex(new THREE.BufferAttribute(e.data.indices, 1)); fixWinding(geo); - var mat = new THREE.MeshStandardMaterial({ color: cssVar("--model-viewer-color", cssVar("--background-active", "#e0a15c")), metalness: 0.2, roughness: 0.6, side: THREE.DoubleSide, flatShading: false }); + var mat = buildMaterial(); chunkGeometry(geo, group, mat); var tris = (e.data.indices && e.data.indices.length) ? e.data.indices.length / 3 : e.data.positions.length / 9; fit(); @@ -332,7 +381,7 @@ } else { var geo = new THREE.STLLoader().parse(data.buffer); geo.computeVertexNormals(); - var mat = new THREE.MeshStandardMaterial({ color: cssVar("--model-viewer-color", cssVar("--background-active", "#e0a15c")), metalness: 0.25, roughness: 0.55, flatShading: true }); + var mat = buildMaterial(); group.add(new THREE.Mesh(geo, mat)); fit(); showCanvas();