diff options
| author | yum <yum.food.vr@gmail.com> | 2026-01-18 14:59:59 -0800 |
|---|---|---|
| committer | yum <yum.food.vr@gmail.com> | 2026-01-18 14:59:59 -0800 |
| commit | 6504b2c4631bab477838548167b88c1052eac263 (patch) | |
| tree | e7d3aa4d19a21e624927a6771e2aeb4815af9393 /instancing.cginc | |
| parent | fbe7ed126883b0c4a1d5115e5c953bc244bc0214 (diff) | |
Grass: add crude instancing code
Diffstat (limited to 'instancing.cginc')
| -rwxr-xr-x | instancing.cginc | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/instancing.cginc b/instancing.cginc index eb8e25b..6672db4 100755 --- a/instancing.cginc +++ b/instancing.cginc @@ -4,6 +4,87 @@ #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); +} + +// 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); +} + +#endif // _INSTANCE_TEXTURE_OFFSET + void instance_distance_culling(inout v2f i) { #if defined(_INSTANCE_DISTANCE_CULLING) // We want to measure the distance from the instance's transform to the camera. @@ -15,6 +96,75 @@ void instance_distance_culling(inout v2f i) { #endif // _INSTANCE_DISTANCE_CULLING } +void instancing_vert(inout appdata v) { +#if defined(_INSTANCE_TEXTURE_OFFSET) && defined(UNITY_INSTANCING_ENABLED) + // Extract instance ID from the matrix translation component + // We encoded it in the X translation (m03) on the CPU side + float encodedID = unity_ObjectToWorld[0][3]; + uint instanceID = (uint)encodedID; + + // Read grid coordinates from instance data texture + // _TexelSize.zw contains (width, height) + 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); + + // Check if this is a valid instance (alpha >= 0) + if (instanceData.w < 0) { + // Invalid instance - collapse to degenerate triangle far away + v.vertex.xyz = float3(0, -100000, 0); + return; + } + + int3 gridCoord = int3(instanceData.xyz); + + // Compute hash values for randomization + float h0, h1, h2, h3, h4; + Hash5(gridCoord.x, gridCoord.z, h0, h1, h2, h3, h4); + + // Base grid position + 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 + ); + + // Add randomized offset within cell + float3 positionOffset = float3( + _Instance_Texture_Offset_Cell_Dimensions.x * h0, + _Instance_Texture_Offset_Cell_Dimensions.y * h1, + _Instance_Texture_Offset_Cell_Dimensions.z * h2 + ); + + // Compute random rotation from hash (in world space) + float4 randomRotation = compute_rotation(gridCoord.x, gridCoord.z); + + // Combine with base rotation: apply base rotation first, then world-space randomization + float4 finalRotation = qmul(randomRotation, _Instance_Texture_Offset_Base_Rotation); + + // Compute scale + float3 scale = _Instance_Texture_Offset_Base_Scale * (1.0 + h4 * _Instance_Texture_Offset_Scale_Randomization); + + // Apply transform to vertex in object space + float3 scaledVertex = v.vertex.xyz * scale; + float3 rotatedVertex = qrotate(finalRotation, scaledVertex); + float3 transformedVertex = rotatedVertex + basePosition + positionOffset; + + // Compensate for the encoded offset that Unity will apply via the matrix + // Unity will add encodedID to X, so we subtract it here + transformedVertex.x -= encodedID; + + // Update vertex position + v.vertex.xyz = transformedVertex; + + // Transform normal and tangent (only rotation, no scale - they're direction vectors) + v.normal = qrotate(finalRotation, v.normal); + v.tangent.xyz = qrotate(finalRotation, v.tangent.xyz); +#endif // _INSTANCE_TEXTURE_OFFSET +} + void instancing_frag(v2f i) { instance_distance_culling(i); } |
