yum/3ner
A toon shader for Unity's BIRP.
git clone https://git.yummers.dev/yum/3ner
864c2ba
master
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