summaryrefslogtreecommitdiffstats
path: root/impostor.cginc
diff options
context:
space:
mode:
authoryum <yum.food.vr@gmail.com>2026-01-13 19:52:38 -0800
committeryum <yum.food.vr@gmail.com>2026-01-13 19:57:02 -0800
commit6df5a9a34d81d1200231b974fcc85f89e80eb63e (patch)
tree66d6388a3f94acd6929bd23858c3fc01fe8430d6 /impostor.cginc
parent8c3a05445f529c10ebbf5bfdc0eb220fe95c558c (diff)
Impostors: implement simple grid snapped impostor particle
Diffstat (limited to 'impostor.cginc')
-rw-r--r--impostor.cginc129
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