UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

366 lines (289 loc) • 10.3 kB
import { AddEquation, CustomBlending, GLSL3, OneFactor, OneMinusSrcAlphaFactor, RawShaderMaterial, Vector3 } from "three"; /* * * For ray projection using projection matrix : https://encreative.blogspot.com/2019/05/computing-ray-origin-and-direction-from.html */ const shader_vx = ` in vec2 uv; in vec3 position; in vec3 normal; out vec2 vUv; out vec4 plane0; out vec4 plane1; out vec4 plane2; out vec3 local_ray_near; out vec3 local_ray_far; uniform mat4 modelViewMatrix; uniform mat4 projectionMatrix; uniform mat3 normalMatrix; uniform vec3 uOffset; uniform float uRadius; uniform float uFrames; void main() { vUv = uv; vec2 framesMinusOne = uFrames - vec2(1.0); mat4 m4 = modelViewMatrix; m4[0][0] = 1.0; m4[0][1] = 0.0; m4[0][2] = 0.0; m4[1][0] = 0.0; m4[1][1] = 1.0; m4[1][2] = 0.0; m4[2][0] = 0.0; m4[2][1] = 0.0; m4[2][2] = 1.0; vec3 object_scale = vec3( length(modelViewMatrix[0].xyz), length(modelViewMatrix[1].xyz), length(modelViewMatrix[2].xyz) ); // scale by object's baking bounding sphere's radius float card_diameter = uRadius*2.0; object_scale *= card_diameter; vec3 transformedNormal = normalize(normalMatrix * normal); vec4 mvPosition = m4 * vec4( object_scale*(position+uOffset/card_diameter), 1.0 ); gl_Position = projectionMatrix * mvPosition; mat4 inverse_matrix = inverse(projectionMatrix * modelViewMatrix); // //get 2D projection of this vertex in normalized device coordinates vec2 pos = gl_Position.xy/gl_Position.w; //compute ray's start and end as inversion of this coordinates //in near and far clip planes vec4 near_4 = inverse_matrix * (vec4(pos, -1.0, 1.0)); vec4 far_4 = inverse_matrix * (vec4(pos, 1.0, 1.0)); local_ray_near = near_4.xyz / near_4.w; local_ray_far = far_4.xyz/ far_4.w; } `; const shader_fg = ` precision highp float; precision highp int; const float depth_scale = 0.5; in vec2 vUv; in vec4 plane0; in vec4 plane1; in vec4 plane2; in vec3 vViewPosition; in vec3 vFacingDirection; out vec4 color_out; uniform sampler2D tBase; uniform sampler2D tGeometry; uniform float uFrames; uniform float uDepthScale; uniform bool uIsFullSphere; in vec3 local_ray_near; in vec3 local_ray_far; struct Material{ vec3 diffuse; vec3 normal; float depth; float occlusion; float roughness; float metalness; }; vec2 VecToSphereOct(vec3 pivotToCamera) { vec3 octant = sign(pivotToCamera); // |x| + |y| + |z| = 1 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; } //for hemisphere vec2 VecToHemiSphereOct(vec3 vec) { vec.y = max(vec.y, 0.001); vec = normalize(vec); vec3 octant = sign(vec); // |x| + |y| + |z| = 1 float sum = dot(vec, octant); vec3 octahedron = vec / sum; return vec2( octahedron.x + octahedron.z, octahedron.z - octahedron.x ); } vec2 VectorToGrid(vec3 vec) { if (uIsFullSphere) { return VecToSphereOct(vec); } else { return VecToHemiSphereOct(vec); } } vec4 TriangleInterpolate(vec2 uv){ uv = fract(uv); vec2 omuv = vec2(1.0, 1.0) - uv.xy; vec4 res = vec4(0, 0, 0, 0); //frame 0 res.x = min(omuv.x, omuv.y); //frame 1 res.y = abs(dot(uv, vec2(1.0, -1.0))); //frame 2 res.z = min(uv.x, uv.y); //mask res.w = clamp(ceil(uv.x-uv.y),0.0, 1.0); return res; } vec4 ImposterBlendWeights(sampler2D tex, vec2 frame0, vec2 frame1, vec2 frame2, vec4 weights, vec4 ddxy) { vec4 samp0 = textureGrad(tex, frame0, ddxy.xy, ddxy.zw); vec4 samp1 = textureGrad(tex, frame1, ddxy.xy, ddxy.zw); vec4 samp2 = textureGrad(tex, frame2, ddxy.xy, ddxy.zw); vec4 result = samp0*weights.x + samp1*weights.y + samp2*weights.z; return result; } vec4 ImposterBlendWeightsNearest(sampler2D tex, vec2 frame0, vec2 frame1, vec2 frame2, vec4 weights, vec4 ddxy) { vec4 samp0 = textureGrad(tex, frame0, ddxy.xy, ddxy.zw); vec4 samp1 = textureGrad(tex, frame1, ddxy.xy, ddxy.zw); vec4 samp2 = textureGrad(tex, frame2, ddxy.xy, ddxy.zw); vec4 result; if(weights.x > weights.y && weights.x > weights.z){ result = samp0; }if(weights.y > weights.z){ result = samp1; }else{ result = samp2; } return result; } void calcuateXYbasis(vec3 plane_normal, out vec3 plane_x, out vec3 plane_y) { vec3 up = vec3(0,1,0); //cross product doesnt work if we look directly from bottom if (abs(plane_normal.y) > 0.999f) { up = vec3(0,0,1); } plane_x = normalize(cross(plane_normal, up)); plane_y = normalize(cross(plane_x, plane_normal)); } vec3 projectOnPlaneBasis(vec3 ray, vec3 plane_normal, vec3 plane_x, vec3 plane_y) { //reproject plane normal onto planeXY basos return normalize(vec3( dot(plane_x,ray), dot(plane_y,ray), dot(plane_normal,ray) )); } vec2 recompute_uv(vec2 frame, vec2 frame_uv, sampler2D gBuffer){ vec2 frame_size = vec2(1.0/ uFrames); vec2 source_uv = (frame + frame_uv)*frame_size; float n_depth = texture(gBuffer, source_uv).a; vec2 offset = clamp(length(frame_uv*2.0 - 1.0) * vec2(0.5-n_depth ) * depth_scale,0.0, 1.0); vec2 uv_f = clamp(frame_uv+offset, 0.0, 1.0); uv_f = ( frame + uv_f)*frame_size; return clamp(uv_f,0.0, 1.0); // return source_uv; } void main(){ vec3 view_direction = normalize(local_ray_near-local_ray_far); vec2 octahedral_uv = clamp(VectorToGrid(view_direction)*0.5 + 0.5, 0.0, 1.0); vec2 grid = octahedral_uv * vec2(uFrames - 1.0); vec2 gridFrac = fract(grid); vec2 gridFloor = floor(grid); vec4 weights = TriangleInterpolate( gridFrac ); vec2 frame_uv = vUv; //3 nearest frames vec2 frame0 = gridFloor; vec2 frame1 = gridFloor + mix(vec2(0,1),vec2(1,0),weights.w); vec2 frame2 = gridFloor + vec2(1.0,1.0); vec2 uv0 = recompute_uv(frame0, frame_uv, tGeometry); vec2 uv1 = recompute_uv(frame1, frame_uv, tGeometry); vec2 uv2 = recompute_uv(frame2, frame_uv, tGeometry); vec4 ddxy = vec4( dFdx(vUv.xy), dFdy(vUv.xy) ); vec2 frame_size = vec2(1.0/ uFrames); vec4 texel_color = ImposterBlendWeights( // vec4 texel_color = ImposterBlendWeightsNearest( tBase, uv0, uv1, uv2, weights, ddxy ); // texel_color = vec4(texel_color.aaa, 1.0); if(texel_color.a <= 0.5){ texel_color.r = 1.0; discard; } color_out = texel_color; // color_out = vec4( snapped_oct_uv, 1.0, 1.0); } `; export class ImpostorShaderV0 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; } }