summaryrefslogtreecommitdiffstats
path: root/impostor.cginc
diff options
context:
space:
mode:
authoryum <yum.food.vr@gmail.com>2026-02-16 16:32:00 -0800
committeryum <yum.food.vr@gmail.com>2026-02-16 16:36:24 -0800
commit864c2ba12dc864d9cb55cb797ba8919bee5b5913 (patch)
treeaa6cd98a71e4ef05d23f762127d3759a4a3e3e21 /impostor.cginc
parent6504b2c4631bab477838548167b88c1052eac263 (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-ximpostor.cginc108
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