summaryrefslogtreecommitdiffstats
path: root/impostor.cginc
diff options
context:
space:
mode:
authoryum <yum.food.vr@gmail.com>2026-01-16 15:35:54 -0800
committeryum <yum.food.vr@gmail.com>2026-01-16 15:35:54 -0800
commit02c32fcc30e753da96f0aa072ed1d74ca300979c (patch)
treea580f5ec83ea23ec3b6b1c5e9c94266e531bb9e6 /impostor.cginc
parent5731b075b2ffa40b4f059a01ec0c105e681bf43e (diff)
Impostors: fix bounding sphere scale
Diffstat (limited to 'impostor.cginc')
-rw-r--r--impostor.cginc73
1 files changed, 54 insertions, 19 deletions
diff --git a/impostor.cginc b/impostor.cginc
index 8cf306f..b49dfce 100644
--- a/impostor.cginc
+++ b/impostor.cginc
@@ -84,6 +84,7 @@ struct ImpostorResult {
float3 normal;
float metallic;
float smoothness;
+ float3 objPos;
};
float SampleImpostorDepthCell(float2 cell, float2 uvInCell, float gridRes) {
@@ -134,6 +135,23 @@ float2 ImpostorParallaxOffsetForFrame(float3 frameDir, float3 pivotToCamOS, floa
return (planeCoord - camXY) * (zSurface / camZ);
}
+// Reconstruct object space position from frame UV and depth.
+// UV encodes X,Y on the virtual plane (0.5 = center), depth encodes Z (0.5 = center).
+// The baked orthographic view spans [-Radius, Radius] in each axis.
+float3 ReconstructObjPosFromFrame(float3 frameDir, float2 uv, float depth) {
+ float3 planeX, planeY, planeN;
+ FrameBasis(frameDir, planeX, planeY, planeN);
+
+ // Invert VirtualPlaneUV: offset = 0.5 - uv, scale by diameter
+ float2 offsetXY = (0.5 - uv) * 2.0 * _Impostors_Sphere_Radius;
+
+ // Depth 0 = front (toward camera), 0.5 = center, 1 = back (away from camera)
+ // planeN points toward camera, so positive Z = toward camera
+ float offsetZ = (0.5 - depth) * 2.0 * _Impostors_Sphere_Radius;
+
+ return offsetXY.x * planeX + offsetXY.y * planeY + offsetZ * planeN;
+}
+
ImpostorSample BlendImpostorSamples(ImpostorSample s0, ImpostorSample s1, ImpostorSample s2, float3 bw) {
ImpostorSample result;
float3 alpha = float3(s0.albedo.a, s1.albedo.a, s2.albedo.a);
@@ -155,25 +173,22 @@ ImpostorSample BlendImpostorSamples(ImpostorSample s0, ImpostorSample s1, Impost
}
// Billboard vertex transformation for impostors
-void impostor_vert(float4 vertexOS, inout float3 worldPos) {
+void impostor_vert(inout float3 vertexOS) {
float3 center = mul(unity_ObjectToWorld, float4(0,0,0,1)).xyz;
- float3 scale = float3(
- length(unity_ObjectToWorld._m00_m10_m20),
- length(unity_ObjectToWorld._m01_m11_m21),
- length(unity_ObjectToWorld._m02_m12_m22));
-
#ifdef SHADOW_CASTER_PASS
float3 camPos = _Impostors_Main_Camera_Pos;
#else
float3 camPos = _WorldSpaceCameraPos;
#endif
- // Billboard facing the camera direction
+ // Billboard facing the camera direction (world space, then convert back to object space).
float3 viewWS = normalize(camPos - center);
float3 right, up;
BillboardBasis(viewWS, right, up);
- worldPos = center + vertexOS.x * right * scale.x + vertexOS.y * up * scale.y;
+ float radiusScale = _Impostors_Sphere_Radius * 2.0;
+ float3 worldPos = center + vertexOS.x * right * radiusScale + vertexOS.y * up * radiusScale;
+ vertexOS = mul(unity_WorldToObject, float4(worldPos, 1)).xyz;
}
// Sample impostor atlas with view-dependent blending
@@ -191,11 +206,7 @@ ImpostorResult impostor_frag(float3 worldPos) {
#endif
float3 viewDir = normalize(worldPos - camPos);
- float3 scale = float3(
- length(unity_ObjectToWorld._m00_m10_m20),
- length(unity_ObjectToWorld._m01_m11_m21),
- length(unity_ObjectToWorld._m02_m12_m22));
- float radiusWS = _Impostors_Sphere_Radius * max(scale.x, max(scale.y, scale.z));
+ float radiusWS = _Impostors_Sphere_Radius;
float3 originToRo = camPos - center;
float b = dot(originToRo, viewDir);
@@ -259,15 +270,28 @@ ImpostorResult impostor_frag(float3 worldPos) {
uv2 = uvBase2 + ImpostorParallaxOffsetForFrame(frameDir2, pivotToCamOS, uvBase2, depthBlended) * parallaxStrength;
}
- ImpostorSample s0 = SampleImpostorCell(cell0, uv0, gridRes);
- ImpostorSample s1 = SampleImpostorCell(cell1, uv1, gridRes);
- ImpostorSample s2 = SampleImpostorCell(cell2, uv2, gridRes);
+ float2 uv0Final = uv0;
+ float2 uv1Final = uv1;
+ float2 uv2Final = uv2;
+
+ ImpostorSample s0 = SampleImpostorCell(cell0, uv0Final, gridRes);
+ ImpostorSample s1 = SampleImpostorCell(cell1, uv1Final, gridRes);
+ ImpostorSample s2 = SampleImpostorCell(cell2, uv2Final, gridRes);
// Parallax can push UVs into transparent pixels at silhouettes; fall back to unshifted UVs when that happens.
if (parallaxStrength > 0.001) {
- if (s0.albedo.a < _Impostors_Cutoff && baseAlpha0 > _Impostors_Cutoff) s0 = SampleImpostorCell(cell0, uvBase0, gridRes);
- if (s1.albedo.a < _Impostors_Cutoff && baseAlpha1 > _Impostors_Cutoff) s1 = SampleImpostorCell(cell1, uvBase1, gridRes);
- if (s2.albedo.a < _Impostors_Cutoff && baseAlpha2 > _Impostors_Cutoff) s2 = SampleImpostorCell(cell2, uvBase2, gridRes);
+ if (s0.albedo.a < _Impostors_Cutoff && baseAlpha0 > _Impostors_Cutoff) {
+ uv0Final = uvBase0;
+ s0 = SampleImpostorCell(cell0, uv0Final, gridRes);
+ }
+ if (s1.albedo.a < _Impostors_Cutoff && baseAlpha1 > _Impostors_Cutoff) {
+ uv1Final = uvBase1;
+ s1 = SampleImpostorCell(cell1, uv1Final, gridRes);
+ }
+ if (s2.albedo.a < _Impostors_Cutoff && baseAlpha2 > _Impostors_Cutoff) {
+ uv2Final = uvBase2;
+ s2 = SampleImpostorCell(cell2, uv2Final, gridRes);
+ }
}
// Blend 3 samples
@@ -286,6 +310,17 @@ ImpostorResult impostor_frag(float3 worldPos) {
result.metallic = blended.metallicGloss.r;
result.smoothness = blended.metallicGloss.a;
+ // Reconstruct object space position from each frame's base UV and depth, then blend.
+ // Using base UVs and base depths ensures geometric consistency (same texel location).
+ float depth0Pos = SampleImpostorDepthCell(cell0, uv0Final, gridRes);
+ float depth1Pos = SampleImpostorDepthCell(cell1, uv1Final, gridRes);
+ float depth2Pos = SampleImpostorDepthCell(cell2, uv2Final, gridRes);
+
+ float3 objPos0 = ReconstructObjPosFromFrame(frameDir0, uv0Final, depth0Pos);
+ float3 objPos1 = ReconstructObjPosFromFrame(frameDir1, uv1Final, depth1Pos);
+ float3 objPos2 = ReconstructObjPosFromFrame(frameDir2, uv2Final, depth2Pos);
+ result.objPos = objPos0 * bw.x + objPos1 * bw.y + objPos2 * bw.z;
+
return result;
}