diff options
| author | yum <yum.food.vr@gmail.com> | 2026-02-16 16:32:00 -0800 |
|---|---|---|
| committer | yum <yum.food.vr@gmail.com> | 2026-02-16 16:36:24 -0800 |
| commit | 864c2ba12dc864d9cb55cb797ba8919bee5b5913 (patch) | |
| tree | aa6cd98a71e4ef05d23f762127d3759a4a3e3e21 /impostor.cginc | |
| parent | 6504b2c4631bab477838548167b88c1052eac263 (diff) | |
Add instancing distance culling, scale deformation
* GPU instance distance culling now takes a min/max range
* Fold recovers ops from material, allowing state to persist across
editor restarts
* Add scale node to vertex deformation framework
* Remove fold presets - dumb LLM idea, unused
* Drop more "undeform" code; unused, was for ray marching, which does
not work well
* Fix reflection energy compensation; was using cloth math, which makes
things too bright
Diffstat (limited to 'impostor.cginc')
| -rwxr-xr-x | impostor.cginc | 108 |
1 files changed, 77 insertions, 31 deletions
diff --git a/impostor.cginc b/impostor.cginc index 14fee5b..5674c02 100755 --- a/impostor.cginc +++ b/impostor.cginc @@ -5,6 +5,15 @@ #include "globals.cginc" #include "vertex_deformation.hlsl" +struct ImpostorResult { + float4 albedo; + float3 normal; + float metallic; + float smoothness; + float3 objPos; +}; + +#if defined(_IMPOSTORS) float2 HemiOctEncode(float3 N) { N.y = max(N.y, 1e-4); float3 p = hemi_octahedron_to_plane(normalize(N), 0, float3(1,0,0), float3(0,1,0), 1); @@ -54,8 +63,6 @@ float2 VirtualPlaneUV(float3 planeX, float3 planeY, float3 planeN, float3 pivotT return 0.5 - float2(dot(planeX, offset), dot(planeY, offset)); } -#if defined(_IMPOSTORS) - float2 ClampUvInCell(float2 uv, float2 halfTexelInCell) { return clamp(saturate(uv), halfTexelInCell, 1.0 - halfTexelInCell); } @@ -67,14 +74,6 @@ struct ImpostorSample { float depth; }; -struct ImpostorResult { - float4 albedo; - float3 normal; - float metallic; - float smoothness; - float3 objPos; -}; - ImpostorSample SampleImpostorCell(float2 cell, float2 uvInCell, float invGridRes) { float2 atlasUv = (cell + uvInCell) * invGridRes; float2 gradX = ddx(uvInCell) * invGridRes; @@ -89,22 +88,24 @@ ImpostorSample SampleImpostorCell(float2 cell, float2 uvInCell, float invGridRes return s; } -float2 ImpostorParallaxOffset(float3 planeX, float3 planeY, float3 planeN, float3 pivotToCamOS, float encodedDepth) { +float2 ImpostorParallaxOffset(float3 planeX, float3 planeY, float3 planeN, float3 pivotToCamOS, float encodedDepth, + float impostorNear, float impostorFar, float impostorRadius) { float2 camXY = float2(dot(pivotToCamOS, planeX), dot(pivotToCamOS, planeY)); float camZ = dot(pivotToCamOS, planeN); camZ = (abs(camZ) < 1e-4) ? (camZ < 0 ? -1e-4 : 1e-4) : camZ; - float worldSpaceDepth = lerp(_Impostors_Near_Clip, _Impostors_Far_Clip, encodedDepth); - float depth01 = (worldSpaceDepth - _Impostors_Near_Clip) / (2.0 * _Impostors_Sphere_Radius); + float worldSpaceDepth = lerp(impostorNear, impostorFar, encodedDepth); + float depth01 = (worldSpaceDepth - impostorNear) / (2.0 * impostorRadius); float height = 0.5 - depth01; return (camXY / camZ) * height; } -float3 ReconstructObjectOffset(float3 planeX, float3 planeY, float3 planeN, float2 uv, float encodedDepth) { - float2 offsetXY = (0.5 - uv) * (2.0 * _Impostors_Sphere_Radius); - float worldSpaceDepth = lerp(_Impostors_Near_Clip, _Impostors_Far_Clip, encodedDepth); - float offsetZ = (_Impostors_Sphere_Radius + _Impostors_Near_Clip) - worldSpaceDepth; +float3 ReconstructObjectOffset(float3 planeX, float3 planeY, float3 planeN, float2 uv, float encodedDepth, + float impostorNear, float impostorFar, float impostorRadius) { + float2 offsetXY = (0.5 - uv) * (2.0 * impostorRadius); + float worldSpaceDepth = lerp(impostorNear, impostorFar, encodedDepth); + float offsetZ = (impostorRadius + impostorNear) - worldSpaceDepth; return offsetXY.x * planeX + offsetXY.y * planeY + offsetZ * planeN; } @@ -120,15 +121,26 @@ ImpostorSample BlendImpostorSamples(ImpostorSample s0, ImpostorSample s1, Impost result.metallicGloss = s0.metallicGloss * alphaBw.x + s1.metallicGloss * alphaBw.y + s2.metallicGloss * alphaBw.z; return result; } +#endif // _IMPOSTORS void impostor_vert(inout float3 vertexOS) { +#if defined(_IMPOSTORS) float3 center = mul(unity_ObjectToWorld, float4(0,0,0,1)).xyz; +#if defined(_INSTANCE_TEXTURE_OFFSET) && defined(UNITY_INSTANCING_ENABLED) + float3 instanceCenter; + float4 instanceRotation; + float3 instanceScale; + if (get_instance_transform(instanceCenter, instanceRotation, instanceScale)) { + center = mul(unity_ObjectToWorld, float4(instanceCenter, 1.0)).xyz; + } +#endif // _INSTANCE_TEXTURE_OFFSET && UNITY_INSTANCING_ENABLED + #ifdef SHADOW_CASTER_PASS float3 camPos = _Impostors_Main_Camera_Pos; #else float3 camPos = _WorldSpaceCameraPos; -#endif +#endif // SHADOW_CASTER_PASS float3 viewWS = normalize(camPos - center); float3 right, up; @@ -136,12 +148,27 @@ void impostor_vert(inout float3 vertexOS) { float radiusScale = _Impostors_Sphere_Radius * 2.0; float3 worldPos = center + (vertexOS.x * right + vertexOS.y * up) * radiusScale; vertexOS = mul(unity_WorldToObject, float4(worldPos, 1.0)).xyz; +#endif // _IMPOSTORS } ImpostorResult impostor_frag(float3 worldPos) { ImpostorResult result = (ImpostorResult)0; +#if defined(_IMPOSTORS) float3 center = mul(unity_ObjectToWorld, float4(0,0,0,1)).xyz; + float4 instanceRotation = float4(0, 0, 0, 1); + float instanceScale = 1.0; + bool hasInstanceTransform = false; + +#if defined(_INSTANCE_TEXTURE_OFFSET) && defined(UNITY_INSTANCING_ENABLED) + float3 instanceCenter; + float3 instanceScale3; + if (get_instance_transform(instanceCenter, instanceRotation, instanceScale3)) { + center = mul(unity_ObjectToWorld, float4(instanceCenter, 1.0)).xyz; + instanceScale = max(instanceScale3.x, max(instanceScale3.y, instanceScale3.z)); + hasInstanceTransform = true; + } +#endif // _INSTANCE_TEXTURE_OFFSET && UNITY_INSTANCING_ENABLED #ifdef SHADOW_CASTER_PASS float3 camPos = _Impostors_Main_Camera_Pos; @@ -151,13 +178,28 @@ ImpostorResult impostor_frag(float3 worldPos) { float3 camToCenter = camPos - center; float3 viewDir = normalize(worldPos - camPos); + float impostorRadius = _Impostors_Sphere_Radius * instanceScale; + float impostorNear = _Impostors_Near_Clip * instanceScale; + float impostorFar = _Impostors_Far_Clip * instanceScale; + float b = dot(camToCenter, viewDir); - float c = dot(camToCenter, camToCenter) - _Impostors_Sphere_Radius * _Impostors_Sphere_Radius; + float c = dot(camToCenter, camToCenter) - impostorRadius * impostorRadius; clip(b * b - c); float3x3 worldToObject = (float3x3)unity_WorldToObject; float3 viewOS = mul(worldToObject, normalize(camToCenter)); + float3 pivotToCamOS = mul(worldToObject, camToCenter); + float3 vertexPosOS = mul(worldToObject, worldPos - center); + + if (hasInstanceTransform) { + float4 instanceRotationInv = qconj(instanceRotation); + viewOS = qrotate(instanceRotationInv, viewOS); + pivotToCamOS = qrotate(instanceRotationInv, pivotToCamOS); + vertexPosOS = qrotate(instanceRotationInv, vertexPosOS); + } + float3 vertexToCamOS = pivotToCamOS - vertexPosOS; + float gridRes = (float)_Impostors_Grid_Resolution; float invGridRes = rcp(gridRes); float2 halfTexelInCell = 0.5 * _Impostors_Atlas_TexelSize.xy * gridRes; @@ -173,10 +215,6 @@ ImpostorResult impostor_frag(float3 worldPos) { float2 cell1 = clamp(gridFloor + (isBottomRight ? float2(1,0) : float2(0,1)), 0, gridRes - 1); float2 cell2 = clamp(gridFloor + float2(1,1), 0, gridRes - 1); - float3 pivotToCamOS = mul(worldToObject, camToCenter); - float3 vertexPosOS = mul(worldToObject, worldPos - center); - float3 vertexToCamOS = pivotToCamOS - vertexPosOS; - float3 frameDir0 = DirFromCell(cell0, gridRes); float3 frameDir1 = DirFromCell(cell1, gridRes); float3 frameDir2 = DirFromCell(cell2, gridRes); @@ -215,9 +253,12 @@ ImpostorResult impostor_frag(float3 worldPos) { [branch] if (parallaxStrength > 0.001) { - parallaxOffset0 = ImpostorParallaxOffset(planeX0, planeY0, planeN0, pivotToCamOS, depthBlended) * parallaxStrength; - parallaxOffset1 = ImpostorParallaxOffset(planeX1, planeY1, planeN1, pivotToCamOS, depthBlended) * parallaxStrength; - parallaxOffset2 = ImpostorParallaxOffset(planeX2, planeY2, planeN2, pivotToCamOS, depthBlended) * parallaxStrength; + parallaxOffset0 = ImpostorParallaxOffset(planeX0, planeY0, planeN0, pivotToCamOS, depthBlended, + impostorNear, impostorFar, impostorRadius) * parallaxStrength; + parallaxOffset1 = ImpostorParallaxOffset(planeX1, planeY1, planeN1, pivotToCamOS, depthBlended, + impostorNear, impostorFar, impostorRadius) * parallaxStrength; + parallaxOffset2 = ImpostorParallaxOffset(planeX2, planeY2, planeN2, pivotToCamOS, depthBlended, + impostorNear, impostorFar, impostorRadius) * parallaxStrength; float maxOffsetSq = max(max(dot(parallaxOffset0, parallaxOffset0), dot(parallaxOffset1, parallaxOffset1)), dot(parallaxOffset2, parallaxOffset2)); needsParallaxResample = maxOffsetSq > 0.00005; @@ -261,20 +302,25 @@ ImpostorResult impostor_frag(float3 worldPos) { result.albedo = blended.albedo; float3 normalOS = blended.normal.xyz * 2.0 - 1.0; + if (hasInstanceTransform) { + normalOS = qrotate(instanceRotation, normalOS); + } result.normal = normalize(mul((float3x3)unity_ObjectToWorld, normalOS)); result.metallic = blended.metallicGloss.r; result.smoothness = blended.metallicGloss.g; #if defined(_IMPOSTORS_DEPTH) - float3 offset0 = ReconstructObjectOffset(planeX0, planeY0, planeN0, finalUv0, depth0); - float3 offset1 = ReconstructObjectOffset(planeX1, planeY1, planeN1, finalUv1, depth1); - float3 offset2 = ReconstructObjectOffset(planeX2, planeY2, planeN2, finalUv2, depth2); + float3 offset0 = ReconstructObjectOffset(planeX0, planeY0, planeN0, finalUv0, depth0, + impostorNear, impostorFar, impostorRadius); + float3 offset1 = ReconstructObjectOffset(planeX1, planeY1, planeN1, finalUv1, depth1, + impostorNear, impostorFar, impostorRadius); + float3 offset2 = ReconstructObjectOffset(planeX2, planeY2, planeN2, finalUv2, depth2, + impostorNear, impostorFar, impostorRadius); result.objPos = offset0 * bw.x + offset1 * bw.y + offset2 * bw.z; #endif return result; -} - #endif // _IMPOSTORS +} #endif // __IMPOSTOR_INC |
