yum/3ner

A toon shader for Unity's BIRP.

git clone https://git.yummers.dev/yum/3ner

yumAdd instancing distance culling, scale deformation864c2ba

master
6.0 KiB201 linesraw
1#ifndef __INSTANCING_INC
2#define __INSTANCING_INC
3
4#include "globals.cginc"
5#include "interpolators.cginc"
6
7#if defined(_INSTANCE_TEXTURE_OFFSET)
8
9// Hash function for deterministic pseudo-random values
10// Returns 5 pseudo-random floats in [0,1) from a single hash computation
11void Hash5(int x, int z, out float r0, out float r1, out float r2, out float r3, out float r4) {
12  int h = (x * 73856093) ^ (z * 83492791);
13
14  h = h ^ (h >> 13);
15  h = h * 1274126177;
16  r0 = (h & 0x7FFFFFFF) / float(0x7FFFFFFF);
17
18  h = h ^ (h >> 13);
19  h = h * 1274126177;
20  r1 = (h & 0x7FFFFFFF) / float(0x7FFFFFFF);
21
22  h = h ^ (h >> 13);
23  h = h * 1274126177;
24  r2 = (h & 0x7FFFFFFF) / float(0x7FFFFFFF);
25
26  h = h ^ (h >> 13);
27  h = h * 1274126177;
28  r3 = (h & 0x7FFFFFFF) / float(0x7FFFFFFF);
29
30  h = h ^ (h >> 13);
31  h = h * 1274126177;
32  r4 = (h & 0x7FFFFFFF) / float(0x7FFFFFFF);
33}
34
35// Quaternion multiplication
36float4 qmul(float4 q1, float4 q2) {
37  return float4(
38    q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y,
39    q1.w * q2.y - q1.x * q2.z + q1.y * q2.w + q1.z * q2.x,
40    q1.w * q2.z + q1.x * q2.y - q1.y * q2.x + q1.z * q2.w,
41    q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z
42  );
43}
44
45// Rotate vector by quaternion
46float3 qrotate(float4 q, float3 v) {
47  float3 t = 2 * cross(q.xyz, v);
48  return v + q.w * t + cross(q.xyz, t);
49}
50
51// Quaternion conjugate (inverse for unit quaternions)
52float4 qconj(float4 q) {
53  return float4(-q.xyz, q.w);
54}
55
56// Create quaternion from Euler angles (in degrees)
57float4 euler_to_quat(float3 euler) {
58  float3 rad = euler * 0.0174533; // deg to rad
59  float3 c = cos(rad * 0.5);
60  float3 s = sin(rad * 0.5);
61
62  return float4(
63    s.x * c.y * c.z - c.x * s.y * s.z,
64    c.x * s.y * c.z + s.x * c.y * s.z,
65    c.x * c.y * s.z - s.x * s.y * c.z,
66    c.x * c.y * c.z + s.x * s.y * s.z
67  );
68}
69
70// Compute rotation quaternion from hash
71float4 compute_rotation(int x, int z) {
72  int h = (x * 73856093) ^ (z * 83492791);
73
74  float r0, r1, r2;
75  h = h ^ (h >> 13); h = h * 1274126177;
76  r0 = (h & 0x7FFFFFFF) / float(0x7FFFFFFF);
77  h = h ^ (h >> 13); h = h * 1274126177;
78  r1 = (h & 0x7FFFFFFF) / float(0x7FFFFFFF);
79  h = h ^ (h >> 13); h = h * 1274126177;
80  r2 = (h & 0x7FFFFFFF) / float(0x7FFFFFFF);
81
82  float3 euler = float3(
83    _Instance_Texture_Offset_Angle_Randomization.x * (r0 * 2.0 - 1.0),
84    _Instance_Texture_Offset_Angle_Randomization.y * (r1 * 2.0 - 1.0),
85    _Instance_Texture_Offset_Angle_Randomization.z * (r2 * 2.0 - 1.0)
86  );
87
88  return euler_to_quat(euler);
89}
90
91bool get_instance_transform(out float3 center, out float4 rotation, out float3 scale);
92
93bool get_instance_center(out float3 center) {
94#if defined(_INSTANCE_TEXTURE_OFFSET) && defined(UNITY_INSTANCING_ENABLED)
95  float4 rotation;
96  float3 scale;
97  return get_instance_transform(center, rotation, scale);
98#else
99  center = 0;
100  return false;
101#endif  // _INSTANCE_TEXTURE_OFFSET && UNITY_INSTANCING_ENABLED
102}
103
104bool get_instance_transform(out float3 center, out float4 rotation, out float3 scale) {
105#if defined(_INSTANCE_TEXTURE_OFFSET) && defined(UNITY_INSTANCING_ENABLED)
106  uint instanceID = (uint)UNITY_ACCESS_INSTANCED_PROP(InstanceProps, _Instance_ID);
107
108  float2 texDimensions = _Instance_Texture_Offset_Data_Tex_TexelSize.zw;
109  int2 texCoord = int2(instanceID % (uint)texDimensions.x,
110                       instanceID / (uint)texDimensions.x);
111  float2 uv = (float2(texCoord) + 0.5) / texDimensions;
112  float4 instanceData = _Instance_Texture_Offset_Data_Tex.SampleLevel(point_repeat_s, uv, 0);
113
114  if (instanceData.w < 0) {
115    center = 0;
116    rotation = float4(0, 0, 0, 1);
117    scale = 1.0;
118    return false;
119  }
120
121  int3 gridCoord = int3(instanceData.xyz);
122  float h0, h1, h2, h3, h4;
123  Hash5(gridCoord.x, gridCoord.z, h0, h1, h2, h3, h4);
124
125  float3 basePosition = float3(
126    gridCoord.x * _Instance_Texture_Offset_Cell_Dimensions.x,
127    gridCoord.y * _Instance_Texture_Offset_Cell_Dimensions.y,
128    gridCoord.z * _Instance_Texture_Offset_Cell_Dimensions.z
129  );
130
131  float3 positionOffset = float3(
132    _Instance_Texture_Offset_Cell_Dimensions.x * h0,
133    _Instance_Texture_Offset_Cell_Dimensions.y * h1,
134    _Instance_Texture_Offset_Cell_Dimensions.z * h2
135  );
136
137  center = basePosition + positionOffset;
138
139  float4 randomRotation = compute_rotation(gridCoord.x, gridCoord.z);
140  rotation = qmul(randomRotation, _Instance_Texture_Offset_Base_Rotation);
141
142  scale = _Instance_Texture_Offset_Base_Scale * (1.0 + h4 * _Instance_Texture_Offset_Scale_Randomization);
143  return true;
144#else
145  center = 0;
146  rotation = float4(0, 0, 0, 1);
147  scale = 1.0;
148  return false;
149#endif  // _INSTANCE_TEXTURE_OFFSET && UNITY_INSTANCING_ENABLED
150}
151
152#endif  // _INSTANCE_TEXTURE_OFFSET
153
154bool instance_distance_culling() {
155#if defined(_INSTANCE_DISTANCE_CULLING)
156  float3 instance_pos;
157
158#if defined(_INSTANCE_TEXTURE_OFFSET) && defined(UNITY_INSTANCING_ENABLED)
159  if (!get_instance_center(instance_pos)) {
160    // Invalid instance, discard
161    return true;
162  }
163#else
164  instance_pos = mul(unity_ObjectToWorld, float4(0, 0, 0, 1)).xyz;
165#endif
166
167  float distance = length(_WorldSpaceCameraPos - instance_pos);
168  if (distance < _Instance_Distance_Culling_Min_Distance ||
169      distance > _Instance_Distance_Culling_Max_Distance) {
170    return true;
171  }
172#endif  // _INSTANCE_DISTANCE_CULLING
173  return false;
174}
175
176void instancing_vert(inout appdata v) {
177#if defined(_INSTANCE_TEXTURE_OFFSET) && defined(UNITY_INSTANCING_ENABLED)
178  float3 center;
179  float4 finalRotation;
180  float3 scale;
181  if (!get_instance_transform(center, finalRotation, scale)) {
182    // Invalid instance - collapse to degenerate triangle far away
183    v.vertex.xyz = float3(0, -100000, 0);
184    return;
185  }
186
187  // Apply transform to vertex in object space
188  float3 scaledVertex = v.vertex.xyz * scale;
189  float3 rotatedVertex = qrotate(finalRotation, scaledVertex);
190  float3 transformedVertex = rotatedVertex + center;
191
192  // Update vertex position
193  v.vertex.xyz = transformedVertex;
194
195  // Transform normal and tangent
196  v.normal = qrotate(finalRotation, v.normal);
197  v.tangent.xyz = qrotate(finalRotation, v.tangent.xyz);
198#endif  // _INSTANCE_TEXTURE_OFFSET
199}
200
201#endif  // __INSTANCING_INC