diff options
Diffstat (limited to 'instancing.cginc')
| -rwxr-xr-x | instancing.cginc | 109 |
1 files changed, 69 insertions, 40 deletions
diff --git a/instancing.cginc b/instancing.cginc index 6672db4..5ad3b23 100755 --- a/instancing.cginc +++ b/instancing.cginc @@ -48,6 +48,11 @@ float3 qrotate(float4 q, float3 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 @@ -83,90 +88,114 @@ float4 compute_rotation(int x, int z) { return euler_to_quat(euler); } -#endif // _INSTANCE_TEXTURE_OFFSET +bool get_instance_transform(out float3 center, out float4 rotation, out float3 scale); -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. - float3 instance_pos = mul(unity_ObjectToWorld, float4(0, 0, 0, 1)).xyz; - float distance = length(_WorldSpaceCameraPos - instance_pos); - if (distance > _Instance_Distance_Culling_Distance) { - discard; - } -#endif // _INSTANCE_DISTANCE_CULLING +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 } -void instancing_vert(inout appdata v) { +bool get_instance_transform(out float3 center, out float4 rotation, out float3 scale) { #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; + uint instanceID = (uint)UNITY_ACCESS_INSTANCED_PROP(InstanceProps, _Instance_ID); - // 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; + center = 0; + rotation = float4(0, 0, 0, 1); + scale = 1.0; + return false; } 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) + 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 +} - // Combine with base rotation: apply base rotation first, then world-space randomization - float4 finalRotation = qmul(randomRotation, _Instance_Texture_Offset_Base_Rotation); +#endif // _INSTANCE_TEXTURE_OFFSET - // Compute scale - float3 scale = _Instance_Texture_Offset_Base_Scale * (1.0 + h4 * _Instance_Texture_Offset_Scale_Randomization); +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 + 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; + float3 transformedVertex = rotatedVertex + center; // Update vertex position v.vertex.xyz = transformedVertex; - // Transform normal and tangent (only rotation, no scale - they're direction vectors) + // Transform normal and tangent 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); -} - #endif // __INSTANCING_INC |
