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__include interaction; 5__include bounding_volumes; 6 7public interface IPrimitive 8{ 9 // returns the bounding box of the primitive 10 BoundingBox getBoundingBox(); 11 12 // returns the centroid of the primitive 13 float3 getCentroid(); 14 15 // returns the normal of the primitive 16 float3 getNormal(); 17 18 // returns the surface area of the primitive 19 float getSurfaceArea(); 20 21 // intersects primitive with ray 22 bool intersect(Ray r, bool checkForOcclusion, inout Interaction i); 23 24 // intersects primitive with sphere 25 bool intersect(BoundingSphere s, inout Interaction i); 26 27 // finds closest point on primitive from sphere center 28 bool findClosestPoint(BoundingSphere s, inout Interaction i); 29 30 // samples point on primitive and returns sampling pdf 31 float samplePoint(float2 randNums, out float2 uv, out float3 p, out float3 n); 32 33 // returns the index of the primitive 34 uint getIndex(); 35}; 36 37public bool intersectLineSegment(float3 pa, float3 pb, 38 float3 ro, float3 rd, float rtMax, bool checkForOcclusion, 39 inout float3 p, inout float3 n, inout float2 uv, inout float d) 40{ 41 float3 u = pa - ro; 42 float3 v = pb - pa; 43 44 // return if line segment and ray are parallel 45 float dv = cross(rd, v)[2]; 46 if (abs(dv) <= FLT_EPSILON) 47 { 48 return false; 49 } 50 51 // solve ro + t*rd = pa + s*(pb - pa) for t >= 0 && 0 <= s <= 1 52 // s = (u x rd)/(rd x v) 53 float ud = cross(u, rd)[2]; 54 float s = ud / dv; 55 56 if (s >= 0.0 && s <= 1.0) 57 { 58 // t = (u x v)/(rd x v) 59 float t = cross(u, v)[2] / dv; 60 61 if (t >= 0.0 && t <= rtMax) 62 { 63 if (checkForOcclusion) 64 { 65 return true; 66 } 67 68 p = pa + s * v; 69 n = normalize(float3(v[1], -v[0], 0.0)); 70 uv = float2(s, 0.0); 71 d = t; 72 return true; 73 } 74 } 75 76 return false; 77} 78 79public float findClosestPointLineSegment(float3 pa, float3 pb, float3 x, out float3 p, out float t) 80{ 81 float3 u = pb - pa; 82 float3 v = x - pa; 83 84 float c1 = dot(u, v); 85 if (c1 <= 0.0) 86 { 87 t = 0.0; 88 p = pa; 89 return length(x - p); 90 } 91 92 float c2 = dot(u, u); 93 if (c2 <= c1) 94 { 95 t = 1.0; 96 p = pb; 97 return length(x - p); 98 } 99 100 t = c1 / c2; 101 p = pa + u * t; 102 return length(x - p); 103} 104 105public struct LineSegment : IPrimitive 106{ 107 public float3 pa; 108 public float3 pb; 109 public uint index; 110 111 // returns the bounding box of the primitive 112 public BoundingBox getBoundingBox() 113 { 114 float3 epsilon = float3(FLT_EPSILON, FLT_EPSILON, 0.0); 115 return BoundingBox(min(pa, pb) - epsilon, max(pa, pb) + epsilon); 116 } 117 118 // returns the centroid of the primitive 119 public float3 getCentroid() 120 { 121 return 0.5 * (pa + pb); 122 } 123 124 // returns the normal of the primitive 125 public float3 getNormal() 126 { 127 float3 s = pb - pa; 128 float3 n = float3(s.y, -s.x, 0.0); 129 130 return normalize(n); 131 } 132 133 // returns the surface area of the primitive 134 public float getSurfaceArea() 135 { 136 return length(pb - pa); 137 } 138 139 // intersects primitive with ray 140 // NOTE: specialized to 2D (z coordinate == 0) 141 public bool intersect(Ray r, bool checkForOcclusion, inout Interaction i) 142 { 143 bool didIntersect = intersectLineSegment(pa, pb, r.o, r.d, r.tMax, checkForOcclusion, i.p, i.n, i.uv, i.d); 144 if (didIntersect) 145 { 146 i.index = index; 147 return true; 148 } 149 150 return false; 151 } 152 153 // intersects primitive with sphere 154 public bool intersect(BoundingSphere s, inout Interaction i) 155 { 156 float d = findClosestPointLineSegment(pa, pb, s.c, i.p, i.uv[0]); 157 if (d * d <= s.r2) 158 { 159 i.d = getSurfaceArea(); 160 i.index = index; 161 return true; 162 } 163 164 return false; 165 } 166 167 // finds closest point on primitive from sphere center 168 public bool findClosestPoint(BoundingSphere s, inout Interaction i) 169 { 170 float d = findClosestPointLineSegment(pa, pb, s.c, i.p, i.uv[0]); 171 if (d * d <= s.r2) 172 { 173 i.uv[1] = 0.0; 174 i.d = d; 175 i.index = index; 176 return true; 177 } 178 179 return false; 180 } 181 182 // samples point on primitive and returns sampling pdf 183 public float samplePoint(float2 randNums, out float2 uv, out float3 p, out float3 n) 184 { 185 float3 s = pb - pa; 186 float area = length(s); 187 float u = randNums[0]; 188 uv = float2(u, 0.0); 189 p = pa + u * s; 190 n = float3(s[1], -s[0], 0.0) / area; 191 192 return 1.0 / area; 193 } 194 195 // returns the index of the primitive 196 public uint getIndex() 197 { 198 return index; 199 } 200}; 201 202public bool intersectTriangle(float3 pa, float3 pb, float3 pc, 203 float3 ro, float3 rd, float rtMax, bool checkForOcclusion, 204 inout float3 p, inout float3 n, inout float2 uv, inout float d) 205{ 206 // Möller–Trumbore intersection algorithm 207 float3 v1 = pb - pa; 208 float3 v2 = pc - pa; 209 float3 q = cross(rd, v2); 210 float det = dot(v1, q); 211 212 // ray and triangle are parallel if det is close to 0 213 if (abs(det) <= FLT_EPSILON) 214 { 215 return false; 216 } 217 float invDet = 1.0 / det; 218 219 float3 r = ro - pa; 220 float v = dot(r, q) * invDet; 221 if (v < 0.0 || v > 1.0) 222 { 223 return false; 224 } 225 226 float3 s = cross(r, v1); 227 float w = dot(rd, s) * invDet; 228 if (w < 0.0 || v + w > 1.0) 229 { 230 return false; 231 } 232 233 float t = dot(v2, s) * invDet; 234 if (t >= 0.0 && t <= rtMax) 235 { 236 if (checkForOcclusion) 237 { 238 return true; 239 } 240 241 p = pa + v1 * v + v2 * w; 242 n = normalize(cross(v1, v2)); 243 uv = float2(1.0 - v - w, v); 244 d = t; 245 return true; 246 } 247 248 return false; 249} 250 251public float findClosestPointTriangle(float3 pa, float3 pb, float3 pc, float3 x, out float3 p, out float2 t) 252{ 253 // source: real time collision detection 254 // check if x in vertex region outside pa 255 float3 ab = pb - pa; 256 float3 ac = pc - pa; 257 float3 ax = x - pa; 258 float d1 = dot(ab, ax); 259 float d2 = dot(ac, ax); 260 if (d1 <= 0.0 && d2 <= 0.0) 261 { 262 // barycentric coordinates (1, 0, 0) 263 t = float2(1.0, 0.0); 264 p = pa; 265 return length(x - p); 266 } 267 268 // check if x in vertex region outside pb 269 float3 bx = x - pb; 270 float d3 = dot(ab, bx); 271 float d4 = dot(ac, bx); 272 if (d3 >= 0.0 && d4 <= d3) 273 { 274 // barycentric coordinates (0, 1, 0) 275 t = float2(0.0, 1.0); 276 p = pb; 277 return length(x - p); 278 } 279 280 // check if x in vertex region outside pc 281 float3 cx = x - pc; 282 float d5 = dot(ab, cx); 283 float d6 = dot(ac, cx); 284 if (d6 >= 0.0 && d5 <= d6) 285 { 286 // barycentric coordinates (0, 0, 1) 287 t = float2(0.0, 0.0); 288 p = pc; 289 return length(x - p); 290 } 291 292 // check if x in edge region of ab, if so return projection of x onto ab 293 float vc = d1 * d4 - d3 * d2; 294 if (vc <= 0.0 && d1 >= 0.0 && d3 <= 0.0) 295 { 296 // barycentric coordinates (1 - v, v, 0) 297 float v = d1 / (d1 - d3); 298 t = float2(1.0 - v, v); 299 p = pa + ab * v; 300 return length(x - p); 301 } 302 303 // check if x in edge region of ac, if so return projection of x onto ac 304 float vb = d5 * d2 - d1 * d6; 305 if (vb <= 0.0 && d2 >= 0.0 && d6 <= 0.0) 306 { 307 // barycentric coordinates (1 - w, 0, w) 308 float w = d2 / (d2 - d6); 309 t = float2(1.0 - w, 0.0); 310 p = pa + ac * w; 311 return length(x - p); 312 } 313 314 // check if x in edge region of bc, if so return projection of x onto bc 315 float va = d3 * d6 - d5 * d4; 316 if (va <= 0.0 && (d4 - d3) >= 0.0 && (d5 - d6) >= 0.0) 317 { 318 // barycentric coordinates (0, 1 - w, w) 319 float w = (d4 - d3) / ((d4 - d3) + (d5 - d6)); 320 t = float2(0.0, 1.0 - w); 321 p = pb + (pc - pb) * w; 322 return length(x - p); 323 } 324 325 // x inside face region. Compute p through its barycentric coordinates (u, v, w) 326 float denom = 1.0 / (va + vb + vc); 327 float v = vb * denom; 328 float w = vc * denom; 329 t = float2(1.0 - v - w, v); 330 p = pa + ab * v + ac * w; //= u*a + v*b + w*c, u = va*denom = 1.0f - v - w 331 return length(x - p); 332} 333 334public struct Triangle : IPrimitive 335{ 336 public float3 pa; 337 public float3 pb; 338 public float3 pc; 339 public uint index; 340 341 // returns the bounding box of the primitive 342 public BoundingBox getBoundingBox() 343 { 344 float3 epsilon = float3(FLT_EPSILON, FLT_EPSILON, FLT_EPSILON); 345 return BoundingBox(min(min(pa, pb), pc) - epsilon, max(max(pa, pb), pc) + epsilon); 346 } 347 348 // returns the centroid of the primitive 349 public float3 getCentroid() 350 { 351 return (pa + pb + pc) / 3.0; 352 } 353 354 // returns the surface area of the primitive 355 public float getSurfaceArea() 356 { 357 return 0.5 * length(cross(pb - pa, pc - pa)); 358 } 359 360 // returns the normal of the primitive 361 public float3 getNormal() 362 { 363 float3 n = cross(pb - pa, pc - pa); 364 365 return normalize(n); 366 } 367 368 // intersects primitive with ray 369 public bool intersect(Ray r, bool checkForOcclusion, inout Interaction i) 370 { 371 bool didIntersect = intersectTriangle(pa, pb, pc, r.o, r.d, r.tMax, checkForOcclusion, i.p, i.n, i.uv, i.d); 372 if (didIntersect) 373 { 374 i.index = index; 375 return true; 376 } 377 378 return false; 379 } 380 381 // intersects primitive with sphere 382 public bool intersect(BoundingSphere s, inout Interaction i) 383 { 384 float d = findClosestPointTriangle(pa, pb, pc, s.c, i.p, i.uv); 385 if (d * d <= s.r2) 386 { 387 i.d = getSurfaceArea(); 388 i.index = index; 389 return true; 390 } 391 392 return false; 393 } 394 395 // finds closest point on primitive from sphere center 396 public bool findClosestPoint(BoundingSphere s, inout Interaction i) 397 { 398 float d = findClosestPointTriangle(pa, pb, pc, s.c, i.p, i.uv); 399 if (d * d <= s.r2) 400 { 401 i.d = d; 402 i.index = index; 403 return true; 404 } 405 406 return false; 407 } 408 409 // samples point on primitive and returns sampling pdf 410 public float samplePoint(float2 randNums, out float2 uv, out float3 p, out float3 n) 411 { 412 n = cross(pb - pa, pc - pa); 413 float area = length(n); 414 float u1 = sqrt(randNums[0]); 415 float u2 = randNums[1]; 416 float u = 1.0 - u1; 417 float v = u2 * u1; 418 float w = 1.0 - u - v; 419 uv = float2(u, v); 420 p = pa * u + pb * v + pc * w; 421 n /= area; 422 423 return 2.0 / area; 424 } 425 426 // returns the index of the primitive 427 public uint getIndex() 428 { 429 return index; 430 } 431}; 432 433public interface ISilhouette 434{ 435 // returns the centroid of the silhouette 436 float3 getCentroid(); 437 438 // returns whether silhouette has two adjacent faces 439 bool hasTwoAdjacentFaces(); 440 441 // returns normal of adjacent face 442 float3 getNormal(uint fIndex); 443 444 // finds closest silhouette point on primitive from sphere center 445 bool findClosestSilhouettePoint(BoundingSphere s, bool flipNormalOrientation, 446 float squaredMinRadius, float precision, 447 inout Interaction i); 448 449 // returns the index of the silhouette 450 uint getIndex(); 451}; 452 453public struct NoSilhouette : ISilhouette 454{ 455 public uint index; 456 457 // returns the centroid of the silhouette 458 public float3 getCentroid() 459 { 460 return float3(0.0, 0.0, 0.0); 461 } 462 463 // returns whether silhouette has two adjacent faces 464 public bool hasTwoAdjacentFaces() 465 { 466 return false; 467 } 468 469 // returns normal of adjacent face 470 public float3 getNormal(uint fIndex) 471 { 472 return float3(0.0, 0.0, 0.0); 473 } 474 475 // finds closest silhouette point on primitive from sphere center 476 public bool findClosestSilhouettePoint(BoundingSphere s, bool flipNormalOrientation, 477 float squaredMinRadius, float precision, 478 inout Interaction i) 479 { 480 return false; 481 } 482 483 // returns the index of the silhouette 484 public uint getIndex() 485 { 486 return UINT_MAX; 487 } 488}; 489 490public bool isSilhouetteVertex(float3 n0, float3 n1, float3 viewDir, float d, bool flipNormalOrientation, float precision) 491{ 492 float sign = flipNormalOrientation ? 1.0 : -1.0; 493 494 // vertex is a silhouette point if it is concave and the query point lies on the vertex 495 if (d <= precision) 496 { 497 float det = n0.x * n1.y - n1.x * n0.y; 498 return sign * det > precision; 499 } 500 501 // vertex is a silhouette point if the query point lies on the halfplane 502 // defined by an adjacent line segment and the other segment is backfacing 503 float3 viewDirUnit = viewDir / d; 504 float dot0 = dot(viewDirUnit, n0); 505 float dot1 = dot(viewDirUnit, n1); 506 507 bool isZeroDot0 = abs(dot0) <= precision; 508 if (isZeroDot0) 509 { 510 return sign * dot1 > precision; 511 } 512 513 bool isZeroDot1 = abs(dot1) <= precision; 514 if (isZeroDot1) 515 { 516 return sign * dot0 > precision; 517 } 518 519 // vertex is a silhouette point if an adjacent line segment is frontfacing 520 // w.r.t. the query point and the other segment is backfacing 521 return dot0 * dot1 < 0.0; 522} 523 524public struct Vertex : ISilhouette 525{ 526 public float3 p; 527 public float3 n0; 528 public float3 n1; 529 public uint index; 530 public uint hasOneAdjacentFace; 531 532 // returns the centroid of the silhouette 533 public float3 getCentroid() 534 { 535 return p; 536 } 537 538 // returns whether silhouette has two adjacent faces 539 public bool hasTwoAdjacentFaces() 540 { 541 return hasOneAdjacentFace == 0; 542 } 543 544 // returns normal of adjacent face 545 public float3 getNormal(uint fIndex) 546 { 547 if (fIndex == 0) 548 { 549 return n0; 550 } 551 552 return n1; 553 } 554 555 // finds closest silhouette point on primitive from sphere center 556 public bool findClosestSilhouettePoint(BoundingSphere s, bool flipNormalOrientation, 557 float squaredMinRadius, float precision, 558 inout Interaction i) 559 { 560 if (squaredMinRadius >= s.r2) 561 { 562 return false; 563 } 564 565 // compute view direction 566 float3 viewDir = s.c - p; 567 float d = length(viewDir); 568 if (d * d > s.r2) 569 { 570 return false; 571 } 572 573 // check if vertex is a silhouette point from view direction 574 bool process = hasOneAdjacentFace == 1 ? true : false; 575 if (!process) 576 { 577 process = isSilhouetteVertex(n0, n1, viewDir, d, flipNormalOrientation, precision); 578 } 579 580 if (process && d * d <= s.r2) 581 { 582 i.p = p; 583 i.uv = float2(0.0, 0.0); 584 i.d = d; 585 i.index = index; 586 return true; 587 } 588 589 return false; 590 } 591 592 // returns the index of the silhouette 593 public uint getIndex() 594 { 595 return index; 596 } 597}; 598 599public bool isSilhouetteEdge(float3 pa, float3 pb, float3 n0, float3 n1, float3 viewDir, 600 float d, bool flipNormalOrientation, float precision) 601{ 602 float sign = flipNormalOrientation ? 1.0 : -1.0; 603 604 // edge is a silhouette if it is concave and the query point lies on the edge 605 if (d <= precision) 606 { 607 float3 edgeDir = normalize(pb - pa); 608 float signedDihedralAngle = atan2(dot(edgeDir, cross(n0, n1)), dot(n0, n1)); 609 return sign * signedDihedralAngle > precision; 610 } 611 612 // edge is a silhouette if the query point lies on the halfplane defined 613 // by an adjacent triangle and the other triangle is backfacing 614 float3 viewDirUnit = viewDir / d; 615 float dot0 = dot(viewDirUnit, n0); 616 float dot1 = dot(viewDirUnit, n1); 617 618 bool isZeroDot0 = abs(dot0) <= precision; 619 if (isZeroDot0) 620 { 621 return sign * dot1 > precision; 622 } 623 624 bool isZeroDot1 = abs(dot1) <= precision; 625 if (isZeroDot1) 626 { 627 return sign * dot0 > precision; 628 } 629 630 // edge is a silhouette if an adjacent triangle is frontfacing w.r.t. the 631 // query point and the other triangle is backfacing 632 return dot0 * dot1 < 0.0; 633} 634 635public struct Edge : ISilhouette 636{ 637 public float3 pa; 638 public float3 pb; 639 public float3 n0; 640 public float3 n1; 641 public uint index; 642 public uint hasOneAdjacentFace; 643 644 // returns the centroid of the silhouette 645 public float3 getCentroid() 646 { 647 return 0.5 * (pa + pb); 648 } 649 650 // returns whether silhouette has two adjacent faces 651 public bool hasTwoAdjacentFaces() 652 { 653 return hasOneAdjacentFace == 0; 654 } 655 656 // returns normal of adjacent face 657 public float3 getNormal(uint fIndex) 658 { 659 if (fIndex == 0) 660 { 661 return n0; 662 } 663 664 return n1; 665 } 666 667 // finds closest silhouette point on primitive from sphere center 668 public bool findClosestSilhouettePoint(BoundingSphere s, bool flipNormalOrientation, 669 float squaredMinRadius, float precision, 670 inout Interaction i) 671 { 672 if (squaredMinRadius >= s.r2) 673 { 674 return false; 675 } 676 677 // compute view direction 678 float d = findClosestPointLineSegment(pa, pb, s.c, i.p, i.uv[0]); 679 if (d * d > s.r2) 680 { 681 return false; 682 } 683 684 // check if edge is a silhouette from view direction 685 bool process = hasOneAdjacentFace == 1 ? true : false; 686 if (!process) 687 { 688 float3 viewDir = s.c - i.p; 689 process = isSilhouetteEdge(pa, pb, n0, n1, viewDir, d, flipNormalOrientation, precision); 690 } 691 692 if (process && d * d <= s.r2) 693 { 694 i.uv[1] = 0.0; 695 i.d = d; 696 i.index = index; 697 return true; 698 } 699 700 return false; 701 } 702 703 // returns the index of the silhouette 704 public uint getIndex() 705 { 706 return index; 707 } 708};