summaryrefslogtreecommitdiffstats
path: root/instancing.cginc
diff options
context:
space:
mode:
Diffstat (limited to 'instancing.cginc')
-rwxr-xr-xinstancing.cginc150
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);
}