UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

220 lines (193 loc) • 6.8 kB
import { AddEquation, CustomBlending, GLSL3, OneFactor, OneMinusSrcAlphaFactor, RawShaderMaterial, Vector3 } from "three"; // The wireframe must trace the SAME card geometry that ImpostorShaderV0 // produces, otherwise the red outline lies about where the impostor surface // actually is. So this vertex shader is the geometry half of the main V0 // vertex shader, minus everything the fragment shader doesn't read (no // vUv, vViewPos, vTangent/vBinormal/vNormal, no per-frame xforms, no // atlas lookup). const shader_vx = ` in vec3 position; uniform mat4 modelViewMatrix; uniform mat4 projectionMatrix; uniform vec3 uOffset; uniform float uRadius; uniform float uFrames; uniform bool uIsFullSphere; // ----- direction -> octahedral grid coord (range -1..+1) ----- vec2 VecToSphereOct(vec3 pivotToCamera) { vec3 octant = sign(pivotToCamera); float sum = dot(pivotToCamera, octant); vec3 octahedron = pivotToCamera / sum; if (octahedron.y < 0.0) { vec3 absolute = abs(octahedron); octahedron.xz = octant.xz * vec2(1.0 - absolute.z, 1.0 - absolute.x); } return octahedron.xz; } vec2 VecToHemiSphereOct(vec3 v) { v.y = max(v.y, 0.001); v = normalize(v); vec3 octant = sign(v); float sum = dot(v, octant); vec3 octahedron = v / sum; return vec2( octahedron.x + octahedron.z, octahedron.z - octahedron.x ); } vec2 VectorToGrid(vec3 v) { return uIsFullSphere ? VecToSphereOct(v) : VecToHemiSphereOct(v); } // ----- octahedral grid coord (range 0..1) -> direction ----- vec3 OctaSphereDec(vec2 coord) { coord = (coord - 0.5) * 2.0; vec3 p = vec3(coord.x, 0.0, coord.y); vec2 a = abs(p.xz); p.y = 1.0 - a.x - a.y; if (p.y < 0.0) { p.xz = sign(p.xz) * vec2(1.0 - a.y, 1.0 - a.x); } return p; } vec3 OctaHemiSphereDec(vec2 coord) { vec3 p = vec3(coord.x - coord.y, 0.0, -1.0 + coord.x + coord.y); vec2 a = abs(p.xz); p.y = 1.0 - a.x - a.y; return p; } vec3 GridToVector(vec2 coord) { return uIsFullSphere ? OctaSphereDec(coord) : OctaHemiSphereDec(coord); } vec3 FrameToRay(vec2 frame, vec2 framesMinusOne) { vec2 f = clamp(frame / framesMinusOne, 0.0, 1.0); return normalize(GridToVector(f)); } vec4 BilinearWeights(vec2 frac_uv) { vec2 omuv = vec2(1.0) - frac_uv; return vec4( omuv.x * omuv.y, frac_uv.x * omuv.y, omuv.x * frac_uv.y, frac_uv.x * frac_uv.y ); } void main() { // Pivot-to-camera direction in object-local space; same as V0. vec3 cameraPos_OS = (inverse(modelViewMatrix) * vec4(0.0, 0.0, 0.0, 1.0)).xyz; vec3 pivotToCameraRay = normalize(cameraPos_OS); // Pick the cell and its 4-corner bilinear weights, then blend the // four corner rays into the "effective" bake direction the card is // showing. Mirrors V0. vec2 framesMinusOne = vec2(uFrames - 1.0); vec2 octahedral_uv = clamp(VectorToGrid(pivotToCameraRay) * 0.5 + 0.5, 0.0, 1.0); vec2 grid = octahedral_uv * framesMinusOne; vec2 gridFloor = min(floor(grid), framesMinusOne - 1.0); vec4 weights = BilinearWeights(grid - gridFloor); vec3 ray00 = FrameToRay(gridFloor + vec2(0.0, 0.0), framesMinusOne); vec3 ray10 = FrameToRay(gridFloor + vec2(1.0, 0.0), framesMinusOne); vec3 ray01 = FrameToRay(gridFloor + vec2(0.0, 1.0), framesMinusOne); vec3 ray11 = FrameToRay(gridFloor + vec2(1.0, 1.0), framesMinusOne); vec3 projectedRay = normalize( ray00 * weights.x + ray10 * weights.y + ray01 * weights.z + ray11 * weights.w ); // TBN construction identical to V0 so the card spans the same plane. vec3 normal_OS = projectedRay; vec3 up_OS = abs(normal_OS.y) > 0.999 ? vec3(0.0, 0.0, -1.0) : vec3(0.0, 1.0, 0.0); vec3 tangent_OS = normalize(cross(up_OS, normal_OS)); vec3 binormal_OS = cross(normal_OS, tangent_OS); float card_diameter = uRadius * 2.0; vec3 pos_OS = uOffset + position.x * card_diameter * tangent_OS + position.y * card_diameter * binormal_OS; gl_Position = projectionMatrix * modelViewMatrix * vec4(pos_OS, 1.0); } `; const shader_fg = ` precision highp float; precision highp int; out vec4 color_out; void main(){ color_out = vec4(1.0, .0, .0, 1.0); } `; export class ImpostorShaderWireframeV0 extends RawShaderMaterial { constructor() { super({ fragmentShader: shader_fg, vertexShader: shader_vx, uniforms: { /** * RGB + Alpha */ tBase: { value: null }, /** * Normal+Depth */ tGeometry: { value: null }, /** * Material properties: Occlusion, Roughness, Metalness * Alpha unused */ tMaterial: { value: null }, /** * Number of frames */ uFrames: { value: 0 }, /** * Radius of bounding sphere of the impostor */ uRadius: { value: 0 }, /** * Impostor offset */ uOffset: { value: new Vector3(0, 0, 0) }, uIsFullSphere: { value: false }, uDepthScale: { // value should be in range between 0 and 1 value: 1 } }, glslVersion: GLSL3 }); // Save some effort by disabling blending this.blending = CustomBlending; this.blendEquation = AddEquation; this.blendSrc = OneFactor; this.blendDst = OneMinusSrcAlphaFactor; } }