diff options
| author | yum <yum.food.vr@gmail.com> | 2026-01-14 18:35:09 -0800 |
|---|---|---|
| committer | yum <yum.food.vr@gmail.com> | 2026-01-14 18:35:09 -0800 |
| commit | e2bdfd38fa2e2371202d115dd60e33427a3b4597 (patch) | |
| tree | fef91b8697ab035b39bf85a875e9a9c9b898057d | |
| parent | dcae38bf23fb8c10902d90958d26b230c326b3d4 (diff) | |
Impostors: add ray-sphere intersection test
| -rw-r--r-- | impostor.cginc | 145 |
1 files changed, 88 insertions, 57 deletions
diff --git a/impostor.cginc b/impostor.cginc index a60c9ca..bb4c38f 100644 --- a/impostor.cginc +++ b/impostor.cginc @@ -8,6 +8,7 @@ SamplerState bilinear_clamp_s; Texture2D _ImpostorAtlas; int _GridResolution; float _Cutoff; +float _SphereRadius; float3 _ImpostorMainCameraPos; #ifndef IMPOSTOR_SHADOW_PASS @@ -97,13 +98,8 @@ struct v2f { float4 pos : SV_POSITION; UNITY_FOG_COORDS(7) #endif - float2 uv1 : TEXCOORD1; - float2 uv2 : TEXCOORD2; - float2 uv3 : TEXCOORD3; - nointerpolation float2 cell1 : TEXCOORD4; - nointerpolation float2 cell2 : TEXCOORD5; - nointerpolation float2 cell3 : TEXCOORD6; - nointerpolation float3 blendWeights : TEXCOORD8; + float3 worldPos : TEXCOORD1; + float3 centerPos : TEXCOORD2; UNITY_VERTEX_OUTPUT_STEREO }; @@ -113,6 +109,7 @@ v2f vert(appdata v) { UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); float3 center = mul(unity_ObjectToWorld, float4(0,0,0,1)).xyz; + o.centerPos = center; float3 scale = float3( length(unity_ObjectToWorld._m00_m10_m20), length(unity_ObjectToWorld._m01_m11_m21), @@ -124,38 +121,12 @@ v2f vert(appdata v) { float3 camPos = _WorldSpaceCameraPos; #endif - // View direction in object space - float3 viewOS = normalize(mul((float3x3)unity_WorldToObject, normalize(camPos - center))); - - // Get continuous grid position and find the 3 frames - float gridRes = (float)_GridResolution; - float2 grid = GridFromDir(viewOS, gridRes); - float2 gridFloor = floor(grid); - float2 gridFrac = frac(grid); - - // Compute barycentric weights and determine which triangle - float4 bary = GridCellBarycentric(gridFrac); - o.blendWeights = bary.xyz; - - // Frame 1: (0,0) corner of cell - o.cell1 = gridFloor; - - // Frame 2: (0,1) or (1,0) corner depending on which triangle - o.cell2 = clamp(gridFloor + lerp(float2(0,1), float2(1,0), bary.w), 0, gridRes - 1); - - // Frame 3: (1,1) corner of cell - o.cell3 = clamp(gridFloor + float2(1,1), 0, gridRes - 1); - - // Get directions for each frame - float3 dir1 = DirFromCell(o.cell1, gridRes); - float3 dir2 = DirFromCell(o.cell2, gridRes); - float3 dir3 = DirFromCell(o.cell3, gridRes); - // Billboard facing the camera direction float3 viewWS = normalize(camPos - center); float3 right, up; BillboardBasis(viewWS, right, up); float3 worldPos = center + v.vertex.x * right * scale.x + v.vertex.y * up * scale.y; + o.worldPos = worldPos; #ifdef IMPOSTOR_SHADOW_PASS v.vertex = mul(unity_WorldToObject, float4(worldPos, 1)); @@ -166,43 +137,103 @@ v2f vert(appdata v) { UNITY_TRANSFER_FOG(o, o.pos); #endif - // Compute virtual plane UVs for all 3 frames - float3 pivotToCamOS = mul((float3x3)unity_WorldToObject, camPos - center) * 100.0; - float3 vertexPosOS = mul((float3x3)unity_WorldToObject, worldPos - center); - float3 vertexToCamOS = pivotToCamOS - vertexPosOS; - - o.uv1 = VirtualPlaneUV(dir1, pivotToCamOS, vertexToCamOS, 0.5); - o.uv2 = VirtualPlaneUV(dir2, pivotToCamOS, vertexToCamOS, 0.5); - o.uv3 = VirtualPlaneUV(dir3, pivotToCamOS, vertexToCamOS, 0.5); - return o; } -float4 BlendFrames(v2f i) { - float4 s1 = SampleAtlas(i.uv1, i.cell1); - float4 s2 = SampleAtlas(i.uv2, i.cell2); - float4 s3 = SampleAtlas(i.uv3, i.cell3); - return s1 * i.blendWeights.x + s2 * i.blendWeights.y + s3 * i.blendWeights.z; +// General purpose ray-sphere intersection. +bool raySphereIntersect( + float3 ro, float3 rayDir, + float3 origin, float radius, + out float3 posNear, out float3 posFar) { + float3 originToRo = ro - origin; + + float b = dot(originToRo, rayDir); + float c = dot(originToRo, originToRo) - radius * radius; + float disc = b * b - c; + + if (disc < 0) { + return false; + } + + float t_near = -b - sqrt(disc); + float t_far = -b + sqrt(disc); + + posNear = ro + rayDir * t_near; + posFar = ro + rayDir * t_far; + + return true; } -#ifdef IMPOSTOR_SHADOW_PASS float4 frag(v2f i) : SV_Target { - float4 col = BlendFrames(i); - clip(col.a - _Cutoff); - SHADOW_CASTER_FRAGMENT(i) -} + // Sphere culling first + float3 eyeVec = i.worldPos - _WorldSpaceCameraPos; + float3 viewDir = normalize(eyeVec); + float3 posNear, posFar; + bool didIntersect = raySphereIntersect( + _WorldSpaceCameraPos, viewDir, + i.centerPos, _SphereRadius, + posNear, posFar); + clip(didIntersect - 0.5); + + // Camera position for grid computation (matches billboard orientation) +#ifdef IMPOSTOR_SHADOW_PASS + float3 camPos = _ImpostorMainCameraPos; #else -float4 frag(v2f i) : SV_Target { - float4 col = BlendFrames(i); + float3 camPos = _WorldSpaceCameraPos; +#endif + float3 center = i.centerPos; + + // View direction in object space + float3 viewOS = normalize(mul((float3x3)unity_WorldToObject, normalize(camPos - center))); + + // Get continuous grid position and find the 3 frames + float gridRes = (float)_GridResolution; + float2 grid = GridFromDir(viewOS, gridRes); + float2 gridFloor = floor(grid); + float2 gridFrac = frac(grid); + + // Compute barycentric weights and determine which triangle + float4 bary = GridCellBarycentric(gridFrac); + float3 blendWeights = bary.xyz; + // Frame cells + float2 cell1 = gridFloor; + float2 cell2 = clamp(gridFloor + lerp(float2(0,1), float2(1,0), bary.w), 0, gridRes - 1); + float2 cell3 = clamp(gridFloor + float2(1,1), 0, gridRes - 1); + + // Get directions for each frame + float3 dir1 = DirFromCell(cell1, gridRes); + float3 dir2 = DirFromCell(cell2, gridRes); + float3 dir3 = DirFromCell(cell3, gridRes); + + // Compute virtual plane UVs for all 3 frames + float3 pivotToCamOS = mul((float3x3)unity_WorldToObject, camPos - center) * 100.0; + float3 vertexPosOS = mul((float3x3)unity_WorldToObject, i.worldPos - center); + float3 vertexToCamOS = pivotToCamOS - vertexPosOS; + + float2 uv1 = VirtualPlaneUV(dir1, pivotToCamOS, vertexToCamOS, 0.5); + float2 uv2 = VirtualPlaneUV(dir2, pivotToCamOS, vertexToCamOS, 0.5); + float2 uv3 = VirtualPlaneUV(dir3, pivotToCamOS, vertexToCamOS, 0.5); + + // Sample and blend frames + float4 s1 = SampleAtlas(uv1, cell1); + float4 s2 = SampleAtlas(uv2, cell2); + float4 s3 = SampleAtlas(uv3, cell3); + float4 col = s1 * blendWeights.x + s2 * blendWeights.y + s3 * blendWeights.z; + +#ifndef IMPOSTOR_SHADOW_PASS if (_DebugMode > 0.5) - return float4(i.blendWeights.xy, 0, 1); + return float4(blendWeights.xy, 0, 1); +#endif col *= _Color; clip(col.a - _Cutoff); +#ifdef IMPOSTOR_SHADOW_PASS + SHADOW_CASTER_FRAGMENT(i) +#else UNITY_APPLY_FOG(i.fogCoord, col); return col; -} #endif +} #endif |
