yum-mirror/slang
Making it easier to work with shaders
git clone https://git.yummers.dev/yum-mirror/slang
c97166aed
master
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}