diff options
| author | yum <yum.food.vr@gmail.com> | 2026-01-13 19:52:38 -0800 |
|---|---|---|
| committer | yum <yum.food.vr@gmail.com> | 2026-01-13 19:57:02 -0800 |
| commit | 6df5a9a34d81d1200231b974fcc85f89e80eb63e (patch) | |
| tree | 66d6388a3f94acd6929bd23858c3fc01fe8430d6 /impostor.cginc | |
| parent | 8c3a05445f529c10ebbf5bfdc0eb220fe95c558c (diff) | |
Impostors: implement simple grid snapped impostor particle
Diffstat (limited to 'impostor.cginc')
| -rw-r--r-- | impostor.cginc | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/impostor.cginc b/impostor.cginc new file mode 100644 index 0000000..e265438 --- /dev/null +++ b/impostor.cginc @@ -0,0 +1,129 @@ +#ifndef __IMPOSTOR_INC +#define __IMPOSTOR_INC + +#include "UnityCG.cginc" +#include "vertex_deformation.hlsl" + +SamplerState bilinear_clamp_s; +Texture2D _ImpostorAtlas; +int _GridResolution; +float _Cutoff; +float3 _ImpostorMainCameraPos; + +#ifndef IMPOSTOR_SHADOW_PASS +float4 _Color; +float _DebugMode; +#endif + +float2 HemiOctEncode(float3 N) { + N.y = max(N.y, 1e-4); + N = normalize(N); + float3 p = hemi_octahedron_to_plane(N, 0, float3(1,0,0), float3(0,1,0), 1); + return float2(p.x, p.z); +} + +float3 HemiOctDecode(float2 uv) { + return normalize(plane_to_hemi_octahedron(float3(uv.x, 0, uv.y), 0, float3(1,0,0), float3(0,1,0), 1)); +} + +void BillboardBasis(float3 fwd, out float3 right, out float3 up) { + right = abs(fwd.y) > 0.999 ? float3(-1,0,0) : normalize(cross(float3(0,1,0), fwd)); + up = cross(fwd, right); +} + +int2 GetCell(float3 viewDir) { + float2 uv = HemiOctEncode(viewDir) * 0.5 + 0.5; + return clamp(int2(round(uv * (_GridResolution - 1))), 0, _GridResolution - 1); +} + +float3 CellDir(int2 cell) { + float2 uv = float2(cell) / max(1.0, _GridResolution - 1) * 2.0 - 1.0; + return HemiOctDecode(uv); +} + +float4 SampleAtlas(float2 uv, float2 cell) { + return _ImpostorAtlas.Sample(bilinear_clamp_s, (cell + uv) / _GridResolution); +} + +// ============================================================================ +// Vertex/Fragment +// ============================================================================ + +struct appdata { + float4 vertex : POSITION; + UNITY_VERTEX_INPUT_INSTANCE_ID +#ifdef IMPOSTOR_SHADOW_PASS + float3 normal : NORMAL; +#endif +}; + +struct v2f { +#ifdef IMPOSTOR_SHADOW_PASS + V2F_SHADOW_CASTER; +#else + float4 pos : SV_POSITION; + UNITY_FOG_COORDS(3) +#endif + float2 uv : TEXCOORD1; + float2 cell : TEXCOORD2; + UNITY_VERTEX_OUTPUT_STEREO +}; + +v2f vert(appdata v) { + v2f o; + UNITY_SETUP_INSTANCE_ID(v); + UNITY_INITIALIZE_VERTEX_OUTPUT_STEREO(o); + + 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 IMPOSTOR_SHADOW_PASS + float3 camPos = _ImpostorMainCameraPos; +#else + float3 camPos = _WorldSpaceCameraPos; +#endif + + float3 viewOS = normalize(mul((float3x3)unity_WorldToObject, normalize(camPos - center))); + int2 cell = GetCell(viewOS); + float3 snapOS = CellDir(cell); + float3 snapWS = normalize(mul((float3x3)unity_ObjectToWorld, snapOS)); + + float3 right, up; + BillboardBasis(snapWS, right, up); + float3 worldPos = center + v.vertex.x * right * scale.x + v.vertex.y * up * scale.y; + +#ifdef IMPOSTOR_SHADOW_PASS + v.vertex = mul(unity_WorldToObject, float4(worldPos, 1)); + v.normal = -snapWS; + TRANSFER_SHADOW_CASTER_NORMALOFFSET(o) +#else + o.pos = mul(UNITY_MATRIX_VP, float4(worldPos, 1)); + UNITY_TRANSFER_FOG(o, o.pos); +#endif + + float3 localOff = mul((float3x3)unity_WorldToObject, worldPos - center); + BillboardBasis(snapOS, right, up); + o.uv = float2(1 - (dot(localOff, right) + 0.5), dot(localOff, up) + 0.5); + o.cell = float2(cell); + return o; +} + +float4 frag(v2f i) : SV_Target { + float4 col = SampleAtlas(i.uv, i.cell); +#ifndef IMPOSTOR_SHADOW_PASS + if (_DebugMode > 0.5) return float4(1, 0, 0, 1); + col *= _Color; +#endif + clip(col.a - _Cutoff); +#ifdef IMPOSTOR_SHADOW_PASS + SHADOW_CASTER_FRAGMENT(i) +#else + UNITY_APPLY_FOG(i.fogCoord, col); + return col; +#endif +} + +#endif |
