diff options
| -rw-r--r-- | 3ner.cginc | 35 | ||||
| -rw-r--r-- | Scripts/Impostors.cs | 3 | ||||
| -rw-r--r-- | impostor.cginc | 73 | ||||
| -rw-r--r-- | pbr.cginc | 4 |
4 files changed, 84 insertions, 31 deletions
@@ -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; } @@ -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; |
