diff options
| -rw-r--r-- | impostor.cginc | 98 |
1 files changed, 49 insertions, 49 deletions
diff --git a/impostor.cginc b/impostor.cginc index 3a479c8..8cf306f 100644 --- a/impostor.cginc +++ b/impostor.cginc @@ -56,6 +56,7 @@ float2 VirtualPlaneUV(float3 frameDir, float3 pivotToCam, float3 vertexToCam) { float projPivot = dot(planeN, pivotToCam); float projVertex = dot(planeN, vertexToCam); + projVertex = (abs(projVertex) < 1e-4) ? (projVertex < 0 ? -1e-4 : 1e-4) : projVertex; float ratio = projPivot / projVertex; float3 offset = vertexToCam * ratio - pivotToCam; @@ -64,14 +65,6 @@ float2 VirtualPlaneUV(float3 frameDir, float3 pivotToCam, float3 vertexToCam) { return uv * -1.0 + 0.5; } -// General purpose ray-sphere intersection -bool RaySphereIntersect(float3 ro, float3 rayDir, float3 origin, float radius) { - float3 originToRo = ro - origin; - float b = dot(originToRo, rayDir); - float c = dot(originToRo, originToRo) - radius * radius; - return (b * b - c) >= 0.0; -} - #if defined(_IMPOSTORS) float2 ClampUvInCell(float2 uv) { @@ -102,6 +95,15 @@ float SampleImpostorDepthCell(float2 cell, float2 uvInCell, float gridRes) { return _Impostors_Depth_Atlas.SampleGrad(bilinear_clamp_s, atlasUv, gradX, gradY).r; } +float SampleImpostorAlphaCell(float2 cell, float2 uvInCell, float gridRes) { + uvInCell = ClampUvInCell(uvInCell); + float invGridRes = rcp(gridRes); + float2 atlasUv = (cell + uvInCell) * invGridRes; + float2 gradX = ddx(uvInCell) * invGridRes; + float2 gradY = ddy(uvInCell) * invGridRes; + return _Impostors_Atlas.SampleGrad(bilinear_clamp_s, atlasUv, gradX, gradY).a; +} + ImpostorSample SampleImpostorCell(float2 cell, float2 uvInCell, float gridRes) { uvInCell = ClampUvInCell(uvInCell); float invGridRes = rcp(gridRes); @@ -132,32 +134,6 @@ float2 ImpostorParallaxOffsetForFrame(float3 frameDir, float3 pivotToCamOS, floa return (planeCoord - camXY) * (zSurface / camZ); } -ImpostorSample SampleImpostorCellParallaxSafe( - float2 cell, - float3 frameDir, - float3 pivotToCamOS, - float3 vertexToCamOS, - float gridRes) -{ - float2 uvBase = ClampUvInCell(VirtualPlaneUV(frameDir, pivotToCamOS, vertexToCamOS)); - ImpostorSample baseS = SampleImpostorCell(cell, uvBase, gridRes); - - float baseAlpha = baseS.albedo.a; - float parallaxStrength = _Impostors_Parallax * smoothstep(_Impostors_Cutoff, 1.0, baseAlpha); - if (parallaxStrength <= 0.001) return baseS; - - float depth01 = SampleImpostorDepthCell(cell, uvBase, gridRes); - float2 uvParallax = uvBase + ImpostorParallaxOffsetForFrame(frameDir, pivotToCamOS, uvBase, depth01) * parallaxStrength; - ImpostorSample parS = SampleImpostorCell(cell, uvParallax, gridRes); - - float denom = max(baseAlpha - _Impostors_Cutoff, 1e-4); - float t = saturate((parS.albedo.a - _Impostors_Cutoff) / denom); - baseS.albedo = lerp(baseS.albedo, parS.albedo, t); - baseS.normal = lerp(baseS.normal, parS.normal, t); - baseS.metallicGloss = lerp(baseS.metallicGloss, parS.metallicGloss, t); - return baseS; -} - ImpostorSample BlendImpostorSamples(ImpostorSample s0, ImpostorSample s1, ImpostorSample s2, float3 bw) { ImpostorSample result; float3 alpha = float3(s0.albedo.a, s1.albedo.a, s2.albedo.a); @@ -171,7 +147,7 @@ ImpostorSample BlendImpostorSamples(ImpostorSample s0, ImpostorSample s1, Impost // Weight normal/metallicGloss by alpha to avoid blending with transparent (zero) pixels float3 alphaBw = alpha * bw; - alphaBw /= max(dot(alphaBw, 1.0), 0.001); + alphaBw /= max(alphaBw.x + alphaBw.y + alphaBw.z, 0.001); result.normal = s0.normal * alphaBw.x + s1.normal * alphaBw.y + s2.normal * alphaBw.z; result.metallicGloss = s0.metallicGloss * alphaBw.x + s1.metallicGloss * alphaBw.y + s2.metallicGloss * alphaBw.z; @@ -221,8 +197,10 @@ ImpostorResult impostor_frag(float3 worldPos) { length(unity_ObjectToWorld._m02_m12_m22)); float radiusWS = _Impostors_Sphere_Radius * max(scale.x, max(scale.y, scale.z)); - bool didIntersect = RaySphereIntersect(camPos, viewDir, center, radiusWS); - clip(didIntersect - 0.5); + float3 originToRo = camPos - center; + float b = dot(originToRo, viewDir); + float c = dot(originToRo, originToRo) - radiusWS * radiusWS; + clip(b * b - c); // For lattice lookup, use the camera-to-impostor-center direction (matches billboard orientation). float3x3 worldToObject = (float3x3)unity_WorldToObject; @@ -252,23 +230,45 @@ ImpostorResult impostor_frag(float3 worldPos) { float3 frameDir1 = DirFromCell(cell1, gridRes); float3 frameDir2 = DirFromCell(cell2, gridRes); + float2 uvBase0 = ClampUvInCell(VirtualPlaneUV(frameDir0, pivotToCamOS, vertexToCamOS)); + float2 uvBase1 = ClampUvInCell(VirtualPlaneUV(frameDir1, pivotToCamOS, vertexToCamOS)); + float2 uvBase2 = ClampUvInCell(VirtualPlaneUV(frameDir2, pivotToCamOS, vertexToCamOS)); + + float baseAlpha0 = SampleImpostorAlphaCell(cell0, uvBase0, gridRes); + float baseAlpha1 = SampleImpostorAlphaCell(cell1, uvBase1, gridRes); + float baseAlpha2 = SampleImpostorAlphaCell(cell2, uvBase2, gridRes); + float baseAlphaBlended = baseAlpha0 * bw.x + baseAlpha1 * bw.y + baseAlpha2 * bw.z; + float parallaxStrength = _Impostors_Parallax * smoothstep(_Impostors_Cutoff, 1.0, baseAlphaBlended); + + float depth0 = SampleImpostorDepthCell(cell0, uvBase0, gridRes); + float depth1 = SampleImpostorDepthCell(cell1, uvBase1, gridRes); + float depth2 = SampleImpostorDepthCell(cell2, uvBase2, gridRes); + float depthBlended = depth0 * bw.x + depth1 * bw.y + depth2 * bw.z; + if (_Impostors_Debug_Depth > 0.5) { - float2 uvBase0 = ClampUvInCell(VirtualPlaneUV(frameDir0, pivotToCamOS, vertexToCamOS)); - float2 uvBase1 = ClampUvInCell(VirtualPlaneUV(frameDir1, pivotToCamOS, vertexToCamOS)); - float2 uvBase2 = ClampUvInCell(VirtualPlaneUV(frameDir2, pivotToCamOS, vertexToCamOS)); - - float depth0 = SampleImpostorDepthCell(cell0, uvBase0, gridRes); - float depth1 = SampleImpostorDepthCell(cell1, uvBase1, gridRes); - float depth2 = SampleImpostorDepthCell(cell2, uvBase2, gridRes); - - float depthBlended = depth0 * bw.x + depth1 * bw.y + depth2 * bw.z; result.albedo = float4(depthBlended.xxx, 1); return result; } - ImpostorSample s0 = SampleImpostorCellParallaxSafe(cell0, frameDir0, pivotToCamOS, vertexToCamOS, gridRes); - ImpostorSample s1 = SampleImpostorCellParallaxSafe(cell1, frameDir1, pivotToCamOS, vertexToCamOS, gridRes); - ImpostorSample s2 = SampleImpostorCellParallaxSafe(cell2, frameDir2, pivotToCamOS, vertexToCamOS, gridRes); + float2 uv0 = uvBase0; + float2 uv1 = uvBase1; + float2 uv2 = uvBase2; + if (parallaxStrength > 0.001) { + uv0 = uvBase0 + ImpostorParallaxOffsetForFrame(frameDir0, pivotToCamOS, uvBase0, depthBlended) * parallaxStrength; + uv1 = uvBase1 + ImpostorParallaxOffsetForFrame(frameDir1, pivotToCamOS, uvBase1, depthBlended) * parallaxStrength; + 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); + + // 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); + } // Blend 3 samples ImpostorSample blended = BlendImpostorSamples(s0, s1, s2, bw); |
