summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoryum <yum.food.vr@gmail.com>2026-01-14 18:35:09 -0800
committeryum <yum.food.vr@gmail.com>2026-01-14 18:35:09 -0800
commite2bdfd38fa2e2371202d115dd60e33427a3b4597 (patch)
treefef91b8697ab035b39bf85a875e9a9c9b898057d
parentdcae38bf23fb8c10902d90958d26b230c326b3d4 (diff)
Impostors: add ray-sphere intersection test
-rw-r--r--impostor.cginc145
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