UNPKG

@woosh/meep-engine

Version:

Pure JavaScript game engine. Fully featured and production ready.

307 lines (245 loc) • 11.5 kB
import { GLSL3, NoBlending, RawShaderMaterial, Vector3 } from "three"; import chunk_preamble from './glsl/common.glsl'; const shader_vx = ` in vec2 uv; in vec3 position; out vec2 vUv; out vec4 plane0; out vec4 plane1; out vec4 plane2; ${chunk_preamble} void ImposterVertex( inout ImposterData imp ) { //incoming vertex, object space vec4 vertex = imp.vertex; //camera in object space vec3 objectSpaceCameraPos = mul( unity_WorldToObject, float4(_WorldSpaceCameraPos.xyz,1) ).xyz; vec2 texcoord = imp.uv; float4x4 objectToWorld = unity_ObjectToWorld; float4x4 worldToObject = unity_WorldToObject; vec3 imposterPivotOffset = _ImposterOffset.xyz; half framesMinusOne = _ImposterFrames-1; float3 objectScale = float3(length(float3(objectToWorld[0].x, objectToWorld[1].x, objectToWorld[2].x)), length(float3(objectToWorld[0].y, objectToWorld[1].y, objectToWorld[2].y)), length(float3(objectToWorld[0].z, objectToWorld[1].z, objectToWorld[2].z))); //pivot to camera ray float3 pivotToCameraRay = normalize(objectSpaceCameraPos.xyz-imposterPivotOffset.xyz); //scale uv to single frame texcoord = vec2(texcoord.x,texcoord.y)*(1.0/_ImposterFrames.x); //radius * 2 * unity scaling vec2 size = _ImposterSize.xx * 2.0; // * objectScale.xx; //unity_BillboardSize.xy vec3 projected = SpriteProjection( pivotToCameraRay, _ImposterFrames, size, texcoord.xy ); //this creates the proper offset for vertices to camera facing billboard vec3 vertexOffset = projected + imposterPivotOffset; //subtract from camera pos vertexOffset = normalize(objectSpaceCameraPos-vertexOffset); //then add the original projected world vertexOffset += projected; //remove position of vertex vertexOffset -= vertex.xyz; //add pivot vertexOffset += imposterPivotOffset; //camera to projection vector vec3 rayDirectionLocal = (imposterPivotOffset + projected) - objectSpaceCameraPos; //projected position to camera ray vec3 projInterpolated = normalize( objectSpaceCameraPos - (projected + imposterPivotOffset) ); Ray rayLocal; rayLocal.Origin = objectSpaceCameraPos-imposterPivotOffset; rayLocal.Direction = rayDirectionLocal; vec2 grid = VectorToGrid( pivotToCameraRay ); vec2 gridRaw = grid; grid = saturate((grid+1.0)*0.5); //bias and scale to 0 to 1 grid *= framesMinusOne; vec2 gridFrac = frac(grid); vec2 gridFloor = floor(grid); vec4 weights = TriangleInterpolate( gridFrac ); //3 nearest frames vec2 frame0 = gridFloor; vec2 frame1 = gridFloor + lerp(vec2(0,1),vec2(1,0),weights.w); vec2 frame2 = gridFloor + vec2(1,1); //convert frame coordinate to octahedron direction vec3 frame0ray = FrameXYToRay(frame0, framesMinusOne.xx); vec3 frame1ray = FrameXYToRay(frame1, framesMinusOne.xx); vec3 frame2ray = FrameXYToRay(frame2, framesMinusOne.xx); vec3 planeCenter = vec3(0,0,0); vec3 plane0x; vec3 plane0normal = frame0ray; vec3 plane0z; vec3 frame0local = FrameTransform( projInterpolated, frame0ray, plane0x, plane0z ); frame0local.xz = frame0local.xz/_ImposterFrames.xx; //for displacement //virtual plane UV coordinates vec2 vUv0 = VirtualPlaneUV( plane0normal, plane0x, plane0z, planeCenter, size, rayLocal ); vUv0 /= _ImposterFrames.xx; vec3 plane1x; vec3 plane1normal = frame1ray; vec3 plane1z; vec3 frame1local = FrameTransform( projInterpolated, frame1ray, plane1x, plane1z); frame1local.xz = frame1local.xz/_ImposterFrames.xx; //for displacement //virtual plane UV coordinates vec2 vUv1 = VirtualPlaneUV( plane1normal, plane1x, plane1z, planeCenter, size, rayLocal ); vUv1 /= _ImposterFrames.xx; vec3 plane2x; vec3 plane2normal = frame2ray; vec3 plane2z; vec3 frame2local = FrameTransform( projInterpolated, frame2ray, plane2x, plane2z ); frame2local.xz = frame2local.xz/_ImposterFrames.xx; //for displacement //virtual plane UV coordinates vec2 vUv2 = VirtualPlaneUV( plane2normal, plane2x, plane2z, planeCenter, size, rayLocal ); vUv2 /= _ImposterFrames.xx; //add offset here imp.vertex.xyz += vertexOffset; //overwrite others imp.uv = texcoord; imp.grid = grid; imp.frame0 = vec4(vUv0.xy,frame0local.xz); imp.frame1 = vec4(vUv1.xy,frame1local.xz); imp.frame2 = vec4(vUv2.xy,frame2local.xz); } void main() { vUv = uv; ImposterData imp; imp.vertex = vec4(position, 1.0); imp.uv = uv; ImposterVertex(imp); gl_Position = imp.vertex; float3 normalWorld = UnityObjectToWorldDir(v.normal.xyz); float3 tangentWorld = UnityObjectToWorldDir(v.tangent.xyz); float3x3 tangentToWorld = CreateTangentToWorldPerVertex(normalWorld, tangentWorld, v.tangent.w); o.tangentWorld = tangentToWorld[0]; o.bitangentWorld = tangentToWorld[1]; o.normalWorld = tangentToWorld[2]; //surface o.texCoord.xy = imp.uv; o.texCoord.zw = imp.grid; o.plane0 = imp.frame0; o.plane1 = imp.frame1; o.plane2 = imp.frame2; } `; const shader_fg = ` in vec2 vUv; in vec4 plane0; in vec4 plane1; in vec4 plane2; struct Material{ vec3 diffuse; vec3 normal; float depth; float occlusion; float roughness; float metalness; }; void ImposterSample( in ImposterData imp, out vec4 baseTex, out vec4 worldNormal )//, out half depth ) { vec2 fracGrid = frac(imp.grid); vec4 weights = TriangleInterpolate( fracGrid ); vec2 gridSnap = floor(imp.grid) / _ImposterFrames.xx; vec2 frame0 = gridSnap; vec2 frame1 = gridSnap + (lerp(vec2(0,1),vec2(1,0),weights.w)/_ImposterFrames.xx); vec2 frame2 = gridSnap + (vec2(1,1)/_ImposterFrames.xx); vec2 vp0uv = frame0 + imp.frame0.xy; vec2 vp1uv = frame1 + imp.frame1.xy; vec2 vp2uv = frame2 + imp.frame2.xy; //resolution of atlas (Square) float textureDims = _ImposterBaseTex_TexelSize.z; //fractional frame size, ex 2048/12 = 170.6 float frameSize = textureDims/_ImposterFrames; //actual atlas resolution used, ex 170*12 = 2040 float actualDims = floor(frameSize) * _ImposterFrames; //the scale factor to apply to UV coordinate, ex 2048/2040 = 0.99609375 float scaleFactor = actualDims / textureDims; vp0uv *= scaleFactor; vp1uv *= scaleFactor; vp2uv *= scaleFactor; //clamp out neighboring frames TODO maybe discard instead? vec2 gridSize = 1.0/_ImposterFrames.xx; gridSize *= _ImposterBaseTex_TexelSize.zw; gridSize *= _ImposterBaseTex_TexelSize.xy; float2 border = _ImposterBaseTex_TexelSize.xy*_ImposterBorderClamp; //vp0uv = clamp(vp0uv,frame0+border,frame0+gridSize-border); //vp1uv = clamp(vp1uv,frame1+border,frame1+gridSize-border); //vp2uv = clamp(vp2uv,frame2+border,frame2+gridSize-border); //for parallax modify vec4 n0 = tex2Dlod( _ImposterWorldNormalDepthTex, vec4(vp0uv, 0, 1 ) ); vec4 n1 = tex2Dlod( _ImposterWorldNormalDepthTex, vec4(vp1uv, 0, 1 ) ); vec4 n2 = tex2Dlod( _ImposterWorldNormalDepthTex, vec4(vp2uv, 0, 1 ) ); half n0s = 0.5-n0.a; half n1s = 0.5-n1.a; half n2s = 0.5-n2.a; vec2 n0p = imp.frame0.zw * n0s; vec2 n1p = imp.frame1.zw * n1s; vec2 n2p = imp.frame2.zw * n2s; //add parallax shift vp0uv += n0p; vp1uv += n1p; vp2uv += n2p; //clamp out neighboring frames TODO maybe discard instead? vp0uv = clamp(vp0uv,frame0+border,frame0+gridSize-border); vp1uv = clamp(vp1uv,frame1+border,frame1+gridSize-border); vp2uv = clamp(vp2uv,frame2+border,frame2+gridSize-border); vec2 ddxy = vec2( ddx(imp.uv.x), ddy(imp.uv.y) ); worldNormal = ImposterBlendWeights( _ImposterWorldNormalDepthTex, imp.uv, vp0uv, vp1uv, vp2uv, weights, ddxy ); baseTex = ImposterBlendWeights( _ImposterBaseTex, imp.uv, vp0uv, vp1uv, vp2uv, weights, ddxy ); //pixel depth offset //half pdo = 1-baseTex.a; //float3 objectScale = float3(length(float3(unity_ObjectToWorld[0].x, unity_ObjectToWorld[1].x, unity_ObjectToWorld[2].x)), // length(float3(unity_ObjectToWorld[0].y, unity_ObjectToWorld[1].y, unity_ObjectToWorld[2].y)), // length(float3(unity_ObjectToWorld[0].z, unity_ObjectToWorld[1].z, unity_ObjectToWorld[2].z))); //vec2 size = _ImposterSize.xx * 2.0;// * objectScale.xx; //vec3 viewWorld = mul( UNITY_MATRIX_VP, float4(0,0,1,0) ).xyz; //pdo *= size * abs(dot(normalize(imp.viewDirWorld.xyz),viewWorld)); //depth = pdo; } void main(){ } `; export class ImpostorShaderStandard 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) } }, glslVersion: GLSL3 }); // Save some effort by disabling blending this.blending = NoBlending; } }