summaryrefslogtreecommitdiffstats
path: root/instancing.cginc
blob: 5ad3b23c3a88e25fb2ae2a5fcfa8a91b9891e4d9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
#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