summaryrefslogtreecommitdiffstats
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
parent5731b075b2ffa40b4f059a01ec0c105e681bf43e (diff)
Impostors: fix bounding sphere scale
-rw-r--r--3ner.cginc35
-rw-r--r--Scripts/Impostors.cs3
-rw-r--r--impostor.cginc73
-rw-r--r--pbr.cginc4
4 files changed, 84 insertions, 31 deletions
diff --git a/3ner.cginc b/3ner.cginc
index a6900d3..1788116 100644
--- a/3ner.cginc
+++ b/3ner.cginc
@@ -30,9 +30,7 @@ v2f vert(appdata v) {
UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o);
#if defined(_IMPOSTORS)
- float3 impostorWorldPos = mul(unity_ObjectToWorld, v.vertex);
- impostor_vert(v.vertex, impostorWorldPos);
- v.vertex = mul(unity_WorldToObject, float4(impostorWorldPos, 1));
+ impostor_vert(v.vertex.xyz);
#endif
#if defined(_TESSELLATION)
@@ -253,10 +251,19 @@ void geom(triangle v2f tri_in[3],
}
//endex
-float4 frag(v2f i, uint facing : SV_IsFrontFace) : SV_Target {
+struct FragOut {
+ float4 color;
+#if defined(_IMPOSTORS)
+ float depth : SV_DepthLessEqual;
+#endif
+};
+
+FragOut frag(v2f i, uint facing : SV_IsFrontFace) : SV_Target {
+ FragOut result = (FragOut) 0;
+
UNITY_SETUP_INSTANCE_ID(i);
#if defined(SHADOW_CASTER_PASS)
- return 0;
+ return result;
#endif
#if defined(_RAY_MARCHING)
@@ -276,14 +283,20 @@ float4 frag(v2f i, uint facing : SV_IsFrontFace) : SV_Target {
#if defined(_IMPOSTORS)
i.normal = pbr.normal;
+ i.objPos = pbr.objPos;
+ propagateObjPos(i);
+ result.depth = i.pos.z;
#endif
#if defined(_DEBUG_VIEW_UNLIT)
- return pbr.albedo;
+ result.color = pbr.albedo;
+ return result;
#elif defined(_DEBUG_VIEW_WORLD_SPACE_NORMALS)
- return float4((pbr.normal + 1.0f) * 0.5f, 1);
+ result.color = float4((pbr.normal + 1.0f) * 0.5f, 1);
+ return result;
#elif defined(_DEBUG_VIEW_METALLIC_GLOSS)
- return float4(pbr.metallic, pbr.smoothness, 0, 1);
+ result.color = float4(pbr.metallic, pbr.smoothness, 0, 1);
+ return result;
#endif
LightData light_data;
@@ -291,9 +304,11 @@ float4 frag(v2f i, uint facing : SV_IsFrontFace) : SV_Target {
#if 0
float c = light_data.common.NoV;
- return float4(c,c,c,1);
+ result.color = float4(c,c,c,1);
+ return result;
#endif
- return brdf(pbr, light_data);
+ result.color = brdf(pbr, light_data);
+ return result;
}
#endif // __3NER_INC
diff --git a/Scripts/Impostors.cs b/Scripts/Impostors.cs
index a2c205c..e52452b 100644
--- a/Scripts/Impostors.cs
+++ b/Scripts/Impostors.cs
@@ -492,7 +492,7 @@ public class Impostors : MonoBehaviour
impostorObject = GameObject.CreatePrimitive(PrimitiveType.Quad);
impostorObject.name = "Impostor";
impostorObject.transform.SetParent(transform, false);
- impostorObject.transform.localScale = Vector3.one * sphere_radius_ * 2f;
+ impostorObject.transform.localScale = Vector3.one;
DestroyImmediate(impostorObject.GetComponent<Collider>());
impostorObject.GetComponent<MeshRenderer>().sharedMaterial = impostorMaterial;
@@ -551,4 +551,3 @@ public class ImpostorsEditor : Editor
}
}
#endif // UNITY_EDITOR
-
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;
}
diff --git a/pbr.cginc b/pbr.cginc
index 6a14655..2aa6be7 100644
--- a/pbr.cginc
+++ b/pbr.cginc
@@ -24,6 +24,9 @@ struct Pbr {
float cl_strength;
float3 cl_color;
#endif
+#if defined(_IMPOSTORS)
+ float3 objPos;
+#endif
};
#define MIN_PERCEPTUAL_ROUGHNESS 5e-2f
@@ -166,6 +169,7 @@ Pbr getPbr(v2f i) {
pbr.normal = imp.normal;
pbr.smoothness = imp.smoothness;
pbr.metallic = imp.metallic;
+ pbr.objPos = imp.objPos;
#else
pbr.albedo = _MainTex.Sample(aniso16_trilinear_repeat_s, uv_parallax * _MainTex_ST.xy + _MainTex_ST.zw);
pbr.albedo *= _Color;