#ifndef __INSTANCING_INC #define __INSTANCING_INC #include "globals.cginc" #include "interpolators.cginc" #if defined(_INSTANCE_TEXTURE_OFFSET) // Hash function for deterministic pseudo-random values // Returns 5 pseudo-random floats in [0,1) from a single hash computation void Hash5(int x, int z, out float r0, out float r1, out float r2, out float r3, out float r4) { int h = (x * 73856093) ^ (z * 83492791); h = h ^ (h >> 13); h = h * 1274126177; r0 = (h & 0x7FFFFFFF) / float(0x7FFFFFFF); h = h ^ (h >> 13); h = h * 1274126177; r1 = (h & 0x7FFFFFFF) / float(0x7FFFFFFF); h = h ^ (h >> 13); h = h * 1274126177; r2 = (h & 0x7FFFFFFF) / float(0x7FFFFFFF); h = h ^ (h >> 13); h = h * 1274126177; r3 = (h & 0x7FFFFFFF) / float(0x7FFFFFFF); h = h ^ (h >> 13); h = h * 1274126177; r4 = (h & 0x7FFFFFFF) / float(0x7FFFFFFF); } // Quaternion multiplication float4 qmul(float4 q1, float4 q2) { return float4( q1.w * q2.x + q1.x * q2.w + q1.y * q2.z - q1.z * q2.y, q1.w * q2.y - q1.x * q2.z + q1.y * q2.w + q1.z * q2.x, q1.w * q2.z + q1.x * q2.y - q1.y * q2.x + q1.z * q2.w, q1.w * q2.w - q1.x * q2.x - q1.y * q2.y - q1.z * q2.z ); } // Rotate vector by quaternion float3 qrotate(float4 q, float3 v) { float3 t = 2 * cross(q.xyz, v); return v + q.w * t + cross(q.xyz, t); } // Quaternion conjugate (inverse for unit quaternions) float4 qconj(float4 q) { return float4(-q.xyz, q.w); } // Create quaternion from Euler angles (in degrees) float4 euler_to_quat(float3 euler) { float3 rad = euler * 0.0174533; // deg to rad float3 c = cos(rad * 0.5); float3 s = sin(rad * 0.5); return float4( s.x * c.y * c.z - c.x * s.y * s.z, c.x * s.y * c.z + s.x * c.y * s.z, c.x * c.y * s.z - s.x * s.y * c.z, c.x * c.y * c.z + s.x * s.y * s.z ); } // Compute rotation quaternion from hash float4 compute_rotation(int x, int z) { int h = (x * 73856093) ^ (z * 83492791); float r0, r1, r2; h = h ^ (h >> 13); h = h * 1274126177; r0 = (h & 0x7FFFFFFF) / float(0x7FFFFFFF); h = h ^ (h >> 13); h = h * 1274126177; r1 = (h & 0x7FFFFFFF) / float(0x7FFFFFFF); h = h ^ (h >> 13); h = h * 1274126177; r2 = (h & 0x7FFFFFFF) / float(0x7FFFFFFF); float3 euler = float3( _Instance_Texture_Offset_Angle_Randomization.x * (r0 * 2.0 - 1.0), _Instance_Texture_Offset_Angle_Randomization.y * (r1 * 2.0 - 1.0), _Instance_Texture_Offset_Angle_Randomization.z * (r2 * 2.0 - 1.0) ); return euler_to_quat(euler); } bool get_instance_transform(out float3 center, out float4 rotation, out float3 scale); bool get_instance_center(out float3 center) { #if defined(_INSTANCE_TEXTURE_OFFSET) && defined(UNITY_INSTANCING_ENABLED) float4 rotation; float3 scale; return get_instance_transform(center, rotation, scale); #else center = 0; return false; #endif // _INSTANCE_TEXTURE_OFFSET && UNITY_INSTANCING_ENABLED } bool get_instance_transform(out float3 center, out float4 rotation, out float3 scale) { #if defined(_INSTANCE_TEXTURE_OFFSET) && defined(UNITY_INSTANCING_ENABLED) uint instanceID = (uint)UNITY_ACCESS_INSTANCED_PROP(InstanceProps, _Instance_ID); float2 texDimensions = _Instance_Texture_Offset_Data_Tex_TexelSize.zw; int2 texCoord = int2(instanceID % (uint)texDimensions.x, instanceID / (uint)texDimensions.x); float2 uv = (float2(texCoord) + 0.5) / texDimensions; float4 instanceData = _Instance_Texture_Offset_Data_Tex.SampleLevel(point_repeat_s, uv, 0); if (instanceData.w < 0) { center = 0; rotation = float4(0, 0, 0, 1); scale = 1.0; return false; } int3 gridCoord = int3(instanceData.xyz); float h0, h1, h2, h3, h4; Hash5(gridCoord.x, gridCoord.z, h0, h1, h2, h3, h4); float3 basePosition = float3( gridCoord.x * _Instance_Texture_Offset_Cell_Dimensions.x, gridCoord.y * _Instance_Texture_Offset_Cell_Dimensions.y, gridCoord.z * _Instance_Texture_Offset_Cell_Dimensions.z ); float3 positionOffset = float3( _Instance_Texture_Offset_Cell_Dimensions.x * h0, _Instance_Texture_Offset_Cell_Dimensions.y * h1, _Instance_Texture_Offset_Cell_Dimensions.z * h2 ); center = basePosition + positionOffset; float4 randomRotation = compute_rotation(gridCoord.x, gridCoord.z); rotation = qmul(randomRotation, _Instance_Texture_Offset_Base_Rotation); scale = _Instance_Texture_Offset_Base_Scale * (1.0 + h4 * _Instance_Texture_Offset_Scale_Randomization); return true; #else center = 0; rotation = float4(0, 0, 0, 1); scale = 1.0; return false; #endif // _INSTANCE_TEXTURE_OFFSET && UNITY_INSTANCING_ENABLED } #endif // _INSTANCE_TEXTURE_OFFSET bool instance_distance_culling() { #if defined(_INSTANCE_DISTANCE_CULLING) float3 instance_pos; #if defined(_INSTANCE_TEXTURE_OFFSET) && defined(UNITY_INSTANCING_ENABLED) if (!get_instance_center(instance_pos)) { // Invalid instance, discard return true; } #else instance_pos = mul(unity_ObjectToWorld, float4(0, 0, 0, 1)).xyz; #endif float distance = length(_WorldSpaceCameraPos - instance_pos); if (distance < _Instance_Distance_Culling_Min_Distance || distance > _Instance_Distance_Culling_Max_Distance) { return true; } #endif // _INSTANCE_DISTANCE_CULLING return false; } void instancing_vert(inout appdata v) { #if defined(_INSTANCE_TEXTURE_OFFSET) && defined(UNITY_INSTANCING_ENABLED) float3 center; float4 finalRotation; float3 scale; if (!get_instance_transform(center, finalRotation, scale)) { // Invalid instance - collapse to degenerate triangle far away v.vertex.xyz = float3(0, -100000, 0); return; } // Apply transform to vertex in object space float3 scaledVertex = v.vertex.xyz * scale; float3 rotatedVertex = qrotate(finalRotation, scaledVertex); float3 transformedVertex = rotatedVertex + center; // Update vertex position v.vertex.xyz = transformedVertex; // Transform normal and tangent v.normal = qrotate(finalRotation, v.normal); v.tangent.xyz = qrotate(finalRotation, v.tangent.xyz); #endif // _INSTANCE_TEXTURE_OFFSET } #endif // __INSTANCING_INC