diff options
| author | yum <yum.food.vr@gmail.com> | 2026-01-14 23:00:42 -0800 |
|---|---|---|
| committer | yum <yum.food.vr@gmail.com> | 2026-01-14 23:00:42 -0800 |
| commit | d1eb208ecbeba1ab7bf894d9f14d2652739bc63e (patch) | |
| tree | f80778fef335ea69fb1cb8892e908a56be0fef1b /impostor.cginc | |
| parent | f0d753b1cfa07079886abe0c3c5fc7ee75b426fd (diff) | |
Impostors: integrate into 3ner
Diffstat (limited to 'impostor.cginc')
| -rw-r--r-- | impostor.cginc | 151 |
1 files changed, 49 insertions, 102 deletions
diff --git a/impostor.cginc b/impostor.cginc index 4236778..1d5c9b5 100644 --- a/impostor.cginc +++ b/impostor.cginc @@ -2,19 +2,10 @@ #define __IMPOSTOR_INC #include "UnityCG.cginc" +#include "globals.cginc" #include "vertex_deformation.hlsl" -SamplerState bilinear_clamp_s; -Texture2D _ImpostorAtlas; -float4 _ImpostorAtlas_TexelSize; -int _GridResolution; -float _Cutoff; -float _SphereRadius; -float3 _ImpostorMainCameraPos; - -float4 _Color; -float _DebugMode; - +// Utility functions for hemispherical octahedral mapping 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); @@ -40,27 +31,7 @@ float3 DirFromCell(float2 cell, float gridRes) { return HemiOctDecode(uv); } -float2 ClampUvInCell(float2 uv) { - uv = saturate(uv); - float2 halfTexelInCell = 0.5 * _ImpostorAtlas_TexelSize.xy * (float)_GridResolution; - return clamp(uv, halfTexelInCell, 1.0 - halfTexelInCell); -} - -float4 SampleAtlasGrad(float2 uv, float2 cell, float2 uvGradX, float2 uvGradY) { - uv = ClampUvInCell(uv); - - float invGridRes = rcp((float)_GridResolution); - float2 atlasUv = (cell + uv) * invGridRes; - - // Important: gradients must be computed in-cell; do not let integer cell offsets affect mip selection. - return _ImpostorAtlas.SampleGrad(bilinear_clamp_s, atlasUv, uvGradX * invGridRes, uvGradY * invGridRes); -} - -// Branchless barycentric weights in a unit square split into two triangles along the (0,0)-(1,1) diagonal. -// Returns weights for the 4 square corners: -// .x = (0,0), .y = (0,1), .z = (1,0), .w = (1,1) -// Only three weights are non-zero for any point (true barycentric within one triangle), -// but sampling both possible "middle" vertices avoids triangle-selection discontinuities. +// Branchless barycentric weights in a unit square split into two triangles float4 GridCellBarycentric4(float2 p) { float w00 = 1.0 - max(p.x, p.y); float w11 = min(p.x, p.y); @@ -86,40 +57,43 @@ float2 VirtualPlaneUV(float3 frameDir, float3 pivotToCam, float3 vertexToCam) { return uv * -1.0 + 0.5; } -struct appdata { - float4 vertex : POSITION; - UNITY_VERTEX_INPUT_INSTANCE_ID -#ifdef IMPOSTOR_SHADOW_PASS - float3 normal : NORMAL; -#endif -}; +// 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; +} -struct v2f { -#ifdef IMPOSTOR_SHADOW_PASS - V2F_SHADOW_CASTER; -#else - float4 pos : SV_POSITION; - UNITY_FOG_COORDS(7) -#endif - float3 worldPos : TEXCOORD1; - float3 centerPos : TEXCOORD2; - UNITY_VERTEX_OUTPUT_STEREO -}; +#if defined(_IMPOSTORS) + +float2 ClampUvInCell(float2 uv) { + uv = saturate(uv); + float2 halfTexelInCell = 0.5 * _Impostors_Atlas_TexelSize.xy * (float)_Impostors_Grid_Resolution; + return clamp(uv, halfTexelInCell, 1.0 - halfTexelInCell); +} + +float4 SampleAtlasGrad(float2 uv, float2 cell, float2 uvGradX, float2 uvGradY) { + uv = ClampUvInCell(uv); -v2f vert(appdata v) { - v2f o; - UNITY_SETUP_INSTANCE_ID(v); - UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + float invGridRes = rcp((float)_Impostors_Grid_Resolution); + float2 atlasUv = (cell + uv) * invGridRes; + + // Important: gradients must be computed in-cell; do not let integer cell offsets affect mip selection. + return _Impostors_Atlas.SampleGrad(bilinear_clamp_s, atlasUv, uvGradX * invGridRes, uvGradY * invGridRes); +} +// Billboard vertex transformation for impostors +void impostor_vert(float4 vertexOS, inout float3 worldPos) { float3 center = mul(unity_ObjectToWorld, float4(0,0,0,1)).xyz; - o.centerPos = center; + float3 scale = float3( length(unity_ObjectToWorld._m00_m10_m20), length(unity_ObjectToWorld._m01_m11_m21), length(unity_ObjectToWorld._m02_m12_m22)); -#ifdef IMPOSTOR_SHADOW_PASS - float3 camPos = _ImpostorMainCameraPos; +#ifdef SHADOW_CASTER_PASS + float3 camPos = _Impostors_Main_Camera_Pos; #else float3 camPos = _WorldSpaceCameraPos; #endif @@ -128,56 +102,35 @@ v2f vert(appdata v) { float3 viewWS = normalize(camPos - center); float3 right, up; BillboardBasis(viewWS, right, up); - float3 worldPos = center + v.vertex.x * right * scale.x + v.vertex.y * up * scale.y; - o.worldPos = worldPos; - -#ifdef IMPOSTOR_SHADOW_PASS - v.vertex = mul(unity_WorldToObject, float4(worldPos, 1)); - v.normal = -viewWS; - TRANSFER_SHADOW_CASTER_NORMALOFFSET(o) -#else - o.pos = mul(UNITY_MATRIX_VP, float4(worldPos, 1)); - UNITY_TRANSFER_FOG(o, o.pos); -#endif - - return o; + worldPos = center + vertexOS.x * right * scale.x + vertexOS.y * up * scale.y; } -// 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; -} +// Sample impostor atlas with view-dependent blending +float4 impostor_frag(float3 worldPos) { + // Calculate center in fragment shader to avoid extra interpolator + float3 center = mul(unity_ObjectToWorld, float4(0,0,0,1)).xyz; -float4 frag(v2f i) : SV_Target { // Sphere culling first - float3 viewDir = normalize(i.worldPos - _WorldSpaceCameraPos); - bool didIntersect = RaySphereIntersect(_WorldSpaceCameraPos, viewDir, i.centerPos, _SphereRadius); - clip(didIntersect - 0.5); - - // Camera position for grid computation (matches billboard orientation) -#ifdef IMPOSTOR_SHADOW_PASS - float3 camPos = _ImpostorMainCameraPos; +#ifdef SHADOW_CASTER_PASS + float3 camPos = _Impostors_Main_Camera_Pos; #else float3 camPos = _WorldSpaceCameraPos; #endif - float3 center = i.centerPos; + float3 viewDir = normalize(worldPos - camPos); + bool didIntersect = RaySphereIntersect(camPos, viewDir, center, _Impostors_Sphere_Radius); + clip(didIntersect - 0.5); // For lattice lookup, use the camera-to-impostor-center direction (matches billboard orientation). - // Using the per-fragment view ray (camera→quad point) causes the selected lattice points/weights - // to vary across the billboard, which reads as "not smooth" while moving around the impostor. float3x3 worldToObject = (float3x3)unity_WorldToObject; float3 viewOS = normalize(mul(worldToObject, normalize(camPos - center))); // Get continuous grid position and find the 4 frames - float gridRes = (float)_GridResolution; + float gridRes = (float)_Impostors_Grid_Resolution; float2 grid = GridFromDir(viewOS, gridRes); float2 gridFloor = floor(grid); float2 gridFrac = frac(grid); - // Branchless barycentric blend weights (avoids discontinuities along triangle/cell boundaries). + // Branchless barycentric blend weights float4 bw = GridCellBarycentric4(gridFrac); // Frame cells (square corners) @@ -194,7 +147,7 @@ float4 frag(v2f i) : SV_Target { // Compute virtual plane UVs for all 4 frames float3 pivotToCamOS = mul(worldToObject, camPos - center); - float3 vertexPosOS = mul(worldToObject, i.worldPos - center); + float3 vertexPosOS = mul(worldToObject, worldPos - center); float3 vertexToCamOS = pivotToCamOS - vertexPosOS; float2 uv00 = VirtualPlaneUV(dir00, pivotToCamOS, vertexToCamOS); @@ -209,19 +162,13 @@ float4 frag(v2f i) : SV_Target { float4 s11 = SampleAtlasGrad(uv11, cell11, ddx(uv11), ddy(uv11)); float4 col = s00 * bw.x + s01 * bw.y + s10 * bw.z + s11 * bw.w; -#ifndef IMPOSTOR_SHADOW_PASS - if (_DebugMode > 0.5) + if (_Impostors_Debug_Mode > 0.5) return float4(bw.yz, 0, 1); -#endif - col *= _Color; - clip(col.a - _Cutoff); -#ifdef IMPOSTOR_SHADOW_PASS - SHADOW_CASTER_FRAGMENT(i) -#else - UNITY_APPLY_FOG(i.fogCoord, col); + clip(col.a - _Impostors_Cutoff); return col; -#endif } -#endif +#endif // _IMPOSTORS + +#endif // __IMPOSTOR_INC |
