yum-mirror/slang

Making it easier to work with shaders

git clone https://git.yummers.dev/yum-mirror/slang

Yong HeFix type checking on generic extensions. (#5316)c97166aed

master
8.8 KiB300 linesraw
1implementing fcpw;
2__include ray;
3__include math_constants;
4
5public struct BoundingSphere
6{
7    public float3 c; // sphere center
8    public float r2; // sphere squared radius
9
10    // constructor
11    public __init(float3 c_, float r2_)
12    {
13        c = c_;
14        r2 = r2_;
15    }
16};
17
18public struct BoundingBox
19{
20    public float3 pMin; // aabb min position
21    public float3 pMax; // aabb max position
22
23    // constructor
24    public __init(float3 pMin_, float3 pMax_)
25    {
26        pMin = pMin_;
27        pMax = pMax_;
28    }
29
30    // checks whether box is valid
31    public bool isValid()
32    {
33        return all(pMin <= pMax);
34    }
35
36    // returns box centroid
37    public float3 getCentroid()
38    {
39        return 0.5 * (pMin + pMax);
40    }
41
42    // checks for overlap with sphere
43    public bool overlap(BoundingSphere s, out float d2Min, out float d2Max)
44    {
45        float3 u = pMin - s.c;
46        float3 v = s.c - pMax;
47        float3 a = max(max(u, v), float3(0.0, 0.0, 0.0));
48        float3 b = min(u, v);
49        d2Min = dot(a, a);
50        d2Max = dot(b, b);
51
52        return d2Min <= s.r2;
53    }
54
55    // checks for overlap with sphere
56    public bool overlap(BoundingSphere s, out float d2Min)
57    {
58        float3 u = pMin - s.c;
59        float3 v = s.c - pMax;
60        float3 a = max(max(u, v), float3(0.0, 0.0, 0.0));
61        d2Min = dot(a, a);
62
63        return d2Min <= s.r2;
64    }
65
66    // intersects box with ray
67    public bool intersect(Ray r, out float tMin, out float tMax)
68    {
69        // slab test for ray box intersection
70        // source: http://www.jcgt.org/published/0007/03/04/paper-lowres.pdf
71        float3 t0 = (pMin - r.o) * r.dInv;
72        float3 t1 = (pMax - r.o) * r.dInv;
73        float3 tNear = min(t0, t1);
74        float3 tFar = max(t0, t1);
75
76        float tNearMax = max(0.0, max(tNear.x, max(tNear.y, tNear.z)));
77        float tFarMin = min(r.tMax, min(tFar.x, min(tFar.y, tFar.z)));
78        if (tNearMax > tFarMin)
79        {
80            tMin = FLT_MAX;
81            tMax = FLT_MAX;
82            return false;
83        }
84
85        tMin = tNearMax;
86        tMax = tFarMin;
87        return true;
88    }
89};
90
91public BoundingBox mergeBoundingBoxes(BoundingBox boxA, BoundingBox boxB)
92{
93    BoundingBox box;
94    box.pMin = min(boxA.pMin, boxB.pMin);
95    box.pMax = max(boxA.pMax, boxB.pMax);
96
97    return box;
98}
99
100public bool inRange(float val, float low, float high)
101{
102    return val >= low && val <= high;
103}
104
105public void computeOrthonormalBasis(float3 n, out float3 b1, out float3 b2)
106{
107    // source: https://graphics.pixar.com/library/OrthonormalB/paper.pdf
108    float sign = n.z >= 0.0 ? 1.0 : -1.0;
109    float a = -1.0 / (sign + n.z);
110    float b = n.x * n.y * a;
111
112    b1 = float3(1.0 + sign * n.x * n.x * a, sign * b, -sign * n.x);
113    b2 = float3(b, sign + n.y * n.y * a, -n.y);
114}
115
116public float projectToPlane(float3 n, float3 e)
117{
118    // compute orthonormal basis
119    float3 b1, b2;
120    computeOrthonormalBasis(n, b1, b2);
121
122    // compute maximal projection radius
123    float r1 = dot(e, abs(b1));
124    float r2 = dot(e, abs(b2));
125    return sqrt(r1 * r1 + r2 * r2);
126}
127
128public struct BoundingCone
129{
130    public float3 axis;     // cone axis
131    public float halfAngle; // cone half angle
132    public float radius;    // cone radius
133
134    // constructors
135    public __init()
136    {
137        axis = float3(0.0, 0.0, 0.0);
138        halfAngle = M_PI;
139        radius = 0.0;
140    }
141    public __init(float3 axis_, float halfAngle_, float radius_)
142    {
143        axis = axis_;
144        halfAngle = halfAngle_;
145        radius = radius_;
146    }
147
148    // checks whether cone is valid
149    public bool isValid()
150    {
151        return halfAngle >= 0.0;
152    }
153
154    // check for overlap between this cone and the "view" cone defined by the given
155    // point o and bounding box b; the two cones overlap when there exist two vectors,
156    // one in each cone, that are orthogonal to each other.
157    public bool overlap(float3 o, BoundingBox b, float distToBox,
158                        out float minAngleRange, out float maxAngleRange)
159    {
160        // initialize angle bounds
161        minAngleRange = 0.0f;
162        maxAngleRange = M_PI_2;
163
164        // there's overlap if this cone's halfAngle is greater than 90 degrees, or
165        // if the box contains the view cone origin (since the view cone is invalid)
166        if (halfAngle >= M_PI_2 || distToBox < FLT_EPSILON)
167        {
168            return true;
169        }
170
171        // compute the view cone axis
172        float3 c = b.getCentroid();
173        float3 viewConeAxis = c - o;
174        float l = length(viewConeAxis);
175        viewConeAxis /= l;
176
177        // check for overlap between the view cone axis and this cone
178        float dAxisAngle = acos(max(-1.0, min(1.0, dot(axis, viewConeAxis)))); // [0, 180]
179        if (inRange(M_PI_2, dAxisAngle - halfAngle, dAxisAngle + halfAngle))
180        {
181            return true;
182        }
183
184        // check if the view cone origin lies outside this cone's bounding sphere;
185        // if it does, compute the view cone halfAngle and check for overlap
186        if (l > radius)
187        {
188            float viewConeHalfAngle = asin(radius / l);
189            float halfAngleSum = halfAngle + viewConeHalfAngle;
190            minAngleRange = dAxisAngle - halfAngleSum;
191            maxAngleRange = dAxisAngle + halfAngleSum;
192            return halfAngleSum >= M_PI_2 ? true : inRange(M_PI_2, minAngleRange, maxAngleRange);
193        }
194
195        // the view cone origin lies inside the box's bounding sphere, so check if
196        // the plane defined by the view cone axis intersects the box; if it does, then
197        // there's overlap since the view cone has a halfAngle greater than 90 degrees
198        float3 e = b.pMax - c;
199        float d = dot(e, abs(viewConeAxis)); // max projection length onto axis
200        float s = l - d;
201        if (s <= 0.0)
202        {
203            return true;
204        }
205
206        // compute the view cone halfAngle by projecting the max extents of the box
207        // onto the plane, and check for overlap
208        d = projectToPlane(viewConeAxis, e);
209        float viewConeHalfAngle = atan2(d, s);
210        float halfAngleSum = halfAngle + viewConeHalfAngle;
211        minAngleRange = dAxisAngle - halfAngleSum;
212        maxAngleRange = dAxisAngle + halfAngleSum;
213        return halfAngleSum >= M_PI_2 ? true : inRange(M_PI_2, minAngleRange, maxAngleRange);
214    }
215};
216
217public float3 rotate(float3 u, float3 v, float theta)
218{
219    float cosTheta = cos(theta);
220    float sinTheta = sin(theta);
221    float3 w = length(cross(u, v));
222    float3 oneMinusCosThetaW = (1.0 - cosTheta) * w;
223
224    float3x3 R;
225    R[0][0] = cosTheta + oneMinusCosThetaW[0] * w[0];
226    R[0][1] = oneMinusCosThetaW[1] * w[0] - sinTheta * w[2];
227    R[0][2] = oneMinusCosThetaW[2] * w[0] + sinTheta * w[1];
228    R[1][0] = oneMinusCosThetaW[0] * w[1] + sinTheta * w[2];
229    R[1][1] = cosTheta + oneMinusCosThetaW[1] * w[1];
230    R[1][2] = oneMinusCosThetaW[2] * w[1] - sinTheta * w[0];
231    R[2][0] = oneMinusCosThetaW[0] * w[2] - sinTheta * w[1];
232    R[2][1] = oneMinusCosThetaW[1] * w[2] + sinTheta * w[0];
233    R[2][2] = cosTheta + oneMinusCosThetaW[2] * w[2];
234
235    return mul(R, u);
236}
237
238public BoundingCone mergeBoundingCones(BoundingCone coneA, BoundingCone coneB,
239                                       float3 originA, float3 originB,
240                                       float3 newOrigin)
241{
242    BoundingCone cone;
243    if (coneA.isValid() && coneB.isValid())
244    {
245        float3 axisA = coneA.axis;
246        float3 axisB = coneB.axis;
247        float halfAngleA = coneA.halfAngle;
248        float halfAngleB = coneB.halfAngle;
249        float3 dOriginA = newOrigin - originA;
250        float3 dOriginB = newOrigin - originB;
251        cone.radius = sqrt(max(coneA.radius * coneA.radius + dot(dOriginA, dOriginA),
252                               coneB.radius * coneB.radius + dot(dOriginB, dOriginB)));
253
254        if (halfAngleB > halfAngleA)
255        {
256            float3 tmpAxis = axisA;
257            axisA = axisB;
258            axisB = tmpAxis;
259
260            float tmpHalfAngle = halfAngleA;
261            halfAngleA = halfAngleB;
262            halfAngleB = tmpHalfAngle;
263        }
264
265        float theta = acos(max(-1.0, min(1.0, dot(axisA, axisB))));
266        if (min(theta + halfAngleB, M_PI) <= halfAngleA)
267        {
268            // right cone is completely inside left cone
269            cone.axis = axisA;
270            cone.halfAngle = halfAngleA;
271            return cone;
272        }
273
274        // merge cones by first computing the spread angle of the cone to cover both cones
275        float oTheta = (halfAngleA + theta + halfAngleB) / 2.0;
276        if (oTheta >= M_PI)
277        {
278            cone.axis = axisA;
279            return cone;
280        }
281
282        float rTheta = oTheta - halfAngleA;
283        cone.axis = rotate(axisA, axisB, rTheta);
284        cone.halfAngle = oTheta;
285    }
286    else if (coneA.isValid())
287    {
288        cone = coneA;
289    }
290    else if (coneB.isValid())
291    {
292        cone = coneB;
293    }
294    else
295    {
296        cone.halfAngle = -M_PI;
297    }
298
299    return cone;
300}