poppygl 0.0.31 → 0.0.32
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +1 -0
- package/dist/index.js +213 -79
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -177,6 +177,7 @@ declare class StudioLighting {
|
|
|
177
177
|
private occlusion;
|
|
178
178
|
private eye;
|
|
179
179
|
private shadowDirections;
|
|
180
|
+
private diffuseCache;
|
|
180
181
|
constructor(drawCalls: DrawCall[], camera: Camera);
|
|
181
182
|
private bounceRadiance;
|
|
182
183
|
shade(position: V3, n: V3, base: V3, material: Material, sampleIndex?: number): V3;
|
package/dist/index.js
CHANGED
|
@@ -1466,8 +1466,19 @@ var studio_environment_default = {
|
|
|
1466
1466
|
// lib/render/RayOcclusion.ts
|
|
1467
1467
|
import { vec3 as vec33 } from "gl-matrix";
|
|
1468
1468
|
var RayOcclusion = class {
|
|
1469
|
-
|
|
1469
|
+
bounds = new Float64Array(0);
|
|
1470
|
+
escapes = new Uint32Array(0);
|
|
1471
|
+
starts = new Uint32Array(0);
|
|
1472
|
+
counts = new Uint8Array(0);
|
|
1473
|
+
triangleData = new Float64Array(0);
|
|
1474
|
+
triangles = [];
|
|
1470
1475
|
bias;
|
|
1476
|
+
inverse = [0, 0, 0];
|
|
1477
|
+
parallelAxes = 0;
|
|
1478
|
+
closest = {
|
|
1479
|
+
hit: null,
|
|
1480
|
+
limit: Infinity
|
|
1481
|
+
};
|
|
1471
1482
|
constructor(drawCalls) {
|
|
1472
1483
|
const triangles = [];
|
|
1473
1484
|
for (const dc of drawCalls) {
|
|
@@ -1491,10 +1502,19 @@ var RayOcclusion = class {
|
|
|
1491
1502
|
const max = [0, 1, 2].map(
|
|
1492
1503
|
(a) => Math.max(p[a], q[a], r[a])
|
|
1493
1504
|
);
|
|
1505
|
+
const e1 = q.map((v, a) => v - p[a]), e2 = r.map((v, a) => v - p[a]);
|
|
1506
|
+
const normal = [
|
|
1507
|
+
e1[1] * e2[2] - e1[2] * e2[1],
|
|
1508
|
+
e1[2] * e2[0] - e1[0] * e2[2],
|
|
1509
|
+
e1[0] * e2[1] - e1[1] * e2[0]
|
|
1510
|
+
];
|
|
1511
|
+
const length = Math.hypot(...normal) || 1;
|
|
1512
|
+
for (let a = 0; a < 3; a++) normal[a] /= length;
|
|
1494
1513
|
triangles.push({
|
|
1495
1514
|
p,
|
|
1496
|
-
e1
|
|
1497
|
-
e2
|
|
1515
|
+
e1,
|
|
1516
|
+
e2,
|
|
1517
|
+
normal,
|
|
1498
1518
|
min,
|
|
1499
1519
|
max,
|
|
1500
1520
|
center: min.map((v, a) => (v + max[a]) / 2),
|
|
@@ -1502,10 +1522,52 @@ var RayOcclusion = class {
|
|
|
1502
1522
|
});
|
|
1503
1523
|
}
|
|
1504
1524
|
}
|
|
1505
|
-
|
|
1506
|
-
|
|
1525
|
+
const root = triangles.length ? this.build(triangles) : null;
|
|
1526
|
+
if (root) this.flatten(root, triangles.length);
|
|
1527
|
+
const extent = root ? Math.hypot(...root.max.map((v, a) => v - root.min[a])) : 1;
|
|
1507
1528
|
this.bias = Math.max(1e-7, extent * 1e-5);
|
|
1508
1529
|
}
|
|
1530
|
+
/** Preorder bounds with subtree escape offsets retain the existing SAH
|
|
1531
|
+
* primitive order, while avoiding recursive calls and pointer-heavy nodes
|
|
1532
|
+
* during millions of ray queries. Float64 retains the original precision.
|
|
1533
|
+
*/
|
|
1534
|
+
flatten(root, triangleCount) {
|
|
1535
|
+
const nodes = [], escapes = [];
|
|
1536
|
+
const visit = (node) => {
|
|
1537
|
+
const index = nodes.length;
|
|
1538
|
+
nodes.push(node);
|
|
1539
|
+
escapes.push(0);
|
|
1540
|
+
if (!node.triangles) {
|
|
1541
|
+
visit(node.left);
|
|
1542
|
+
visit(node.right);
|
|
1543
|
+
}
|
|
1544
|
+
escapes[index] = nodes.length;
|
|
1545
|
+
};
|
|
1546
|
+
visit(root);
|
|
1547
|
+
this.bounds = new Float64Array(nodes.length * 6);
|
|
1548
|
+
this.escapes = Uint32Array.from(escapes);
|
|
1549
|
+
this.starts = new Uint32Array(nodes.length);
|
|
1550
|
+
this.counts = new Uint8Array(nodes.length);
|
|
1551
|
+
this.triangleData = new Float64Array(triangleCount * 9);
|
|
1552
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
1553
|
+
const node = nodes[i];
|
|
1554
|
+
this.bounds.set(node.min, i * 6);
|
|
1555
|
+
this.bounds.set(node.max, i * 6 + 3);
|
|
1556
|
+
if (!node.triangles) continue;
|
|
1557
|
+
this.starts[i] = this.triangles.length;
|
|
1558
|
+
this.counts[i] = node.triangles.length;
|
|
1559
|
+
for (const triangle of node.triangles) {
|
|
1560
|
+
const offset = this.triangles.length * 9;
|
|
1561
|
+
this.triangleData.set(triangle.p, offset);
|
|
1562
|
+
this.triangleData.set(triangle.e1, offset + 3);
|
|
1563
|
+
this.triangleData.set(triangle.e2, offset + 6);
|
|
1564
|
+
this.triangles.push({
|
|
1565
|
+
normal: triangle.normal,
|
|
1566
|
+
material: triangle.material
|
|
1567
|
+
});
|
|
1568
|
+
}
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1509
1571
|
build(triangles) {
|
|
1510
1572
|
const min = [Infinity, Infinity, Infinity], max = [-Infinity, -Infinity, -Infinity];
|
|
1511
1573
|
for (const t of triangles)
|
|
@@ -1554,78 +1616,128 @@ var RayOcclusion = class {
|
|
|
1554
1616
|
right: this.build(best.slice(middle))
|
|
1555
1617
|
};
|
|
1556
1618
|
}
|
|
1619
|
+
prepareRay(direction) {
|
|
1620
|
+
this.parallelAxes = 0;
|
|
1621
|
+
for (let a = 0; a < 3; a++) {
|
|
1622
|
+
this.inverse[a] = 1 / direction[a];
|
|
1623
|
+
if (Math.abs(direction[a]) < 1e-12) this.parallelAxes |= 1 << a;
|
|
1624
|
+
}
|
|
1625
|
+
}
|
|
1557
1626
|
occluded(origin, direction, maxDistance = Infinity) {
|
|
1558
|
-
|
|
1627
|
+
this.prepareRay(direction);
|
|
1628
|
+
return this.intersect(origin, direction, maxDistance);
|
|
1559
1629
|
}
|
|
1560
1630
|
/** Closest opaque triangle for one-bounce reflections and diffuse fill. */
|
|
1561
1631
|
trace(origin, direction, maxDistance = Infinity) {
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1632
|
+
this.prepareRay(direction);
|
|
1633
|
+
const state = this.closest;
|
|
1634
|
+
state.hit = null;
|
|
1635
|
+
state.limit = maxDistance;
|
|
1636
|
+
this.intersect(origin, direction, maxDistance, state);
|
|
1637
|
+
if (!state.hit) return null;
|
|
1638
|
+
const { normal, material } = state.hit;
|
|
1639
|
+
const sign = dotNormal(normal, direction) > 0 ? -1 : 1;
|
|
1640
|
+
return {
|
|
1641
|
+
position: [
|
|
1642
|
+
origin[0] + direction[0] * state.limit,
|
|
1643
|
+
origin[1] + direction[1] * state.limit,
|
|
1644
|
+
origin[2] + direction[2] * state.limit
|
|
1645
|
+
],
|
|
1646
|
+
normal: [normal[0] * sign, normal[1] * sign, normal[2] * sign],
|
|
1647
|
+
material,
|
|
1648
|
+
distance: state.limit
|
|
1565
1649
|
};
|
|
1566
|
-
if (this.root)
|
|
1567
|
-
this.intersect(this.root, origin, direction, maxDistance, state);
|
|
1568
|
-
return state.hit;
|
|
1569
1650
|
}
|
|
1570
|
-
intersect(
|
|
1571
|
-
|
|
1572
|
-
let
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1651
|
+
intersect(o, d, maxDistance, state) {
|
|
1652
|
+
const ox = o[0], oy = o[1], oz = o[2], dx = d[0], dy = d[1], dz = d[2], ix = this.inverse[0], iy = this.inverse[1], iz = this.inverse[2], parallel = this.parallelAxes, bounds = this.bounds, data = this.triangleData;
|
|
1653
|
+
let node = 0;
|
|
1654
|
+
while (node < this.counts.length) {
|
|
1655
|
+
const offset = node * 6;
|
|
1656
|
+
let limit = state ? state.limit : maxDistance, near = 0, far = limit;
|
|
1657
|
+
if (parallel & 2) {
|
|
1658
|
+
if (oy < bounds[offset + 1] || oy > bounds[offset + 4]) {
|
|
1659
|
+
node = this.escapes[node];
|
|
1660
|
+
continue;
|
|
1661
|
+
}
|
|
1662
|
+
} else {
|
|
1663
|
+
let t0 = (bounds[offset + 1] - oy) * iy, t1 = (bounds[offset + 4] - oy) * iy;
|
|
1664
|
+
if (t0 > t1) {
|
|
1665
|
+
const swap = t0;
|
|
1666
|
+
t0 = t1;
|
|
1667
|
+
t1 = swap;
|
|
1668
|
+
}
|
|
1669
|
+
if (t0 > near) near = t0;
|
|
1670
|
+
if (t1 < far) far = t1;
|
|
1671
|
+
if (far < near) {
|
|
1672
|
+
node = this.escapes[node];
|
|
1673
|
+
continue;
|
|
1674
|
+
}
|
|
1577
1675
|
}
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1676
|
+
if (parallel & 1) {
|
|
1677
|
+
if (ox < bounds[offset + 0] || ox > bounds[offset + 3]) {
|
|
1678
|
+
node = this.escapes[node];
|
|
1679
|
+
continue;
|
|
1680
|
+
}
|
|
1681
|
+
} else {
|
|
1682
|
+
let t0 = (bounds[offset + 0] - ox) * ix, t1 = (bounds[offset + 3] - ox) * ix;
|
|
1683
|
+
if (t0 > t1) {
|
|
1684
|
+
const swap = t0;
|
|
1685
|
+
t0 = t1;
|
|
1686
|
+
t1 = swap;
|
|
1687
|
+
}
|
|
1688
|
+
if (t0 > near) near = t0;
|
|
1689
|
+
if (t1 < far) far = t1;
|
|
1690
|
+
if (far < near) {
|
|
1691
|
+
node = this.escapes[node];
|
|
1692
|
+
continue;
|
|
1693
|
+
}
|
|
1694
|
+
}
|
|
1695
|
+
if (parallel & 4) {
|
|
1696
|
+
if (oz < bounds[offset + 2] || oz > bounds[offset + 5]) {
|
|
1697
|
+
node = this.escapes[node];
|
|
1698
|
+
continue;
|
|
1699
|
+
}
|
|
1700
|
+
} else {
|
|
1701
|
+
let t0 = (bounds[offset + 2] - oz) * iz, t1 = (bounds[offset + 5] - oz) * iz;
|
|
1702
|
+
if (t0 > t1) {
|
|
1703
|
+
const swap = t0;
|
|
1704
|
+
t0 = t1;
|
|
1705
|
+
t1 = swap;
|
|
1706
|
+
}
|
|
1707
|
+
if (t0 > near) near = t0;
|
|
1708
|
+
if (t1 < far) far = t1;
|
|
1709
|
+
if (far < near) {
|
|
1710
|
+
node = this.escapes[node];
|
|
1711
|
+
continue;
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
const start = this.starts[node], end = start + this.counts[node];
|
|
1715
|
+
for (let i = start; i < end; i++) {
|
|
1716
|
+
const offset2 = i * 9, p0 = data[offset2], p1 = data[offset2 + 1], p2 = data[offset2 + 2], e10 = data[offset2 + 3], e11 = data[offset2 + 4], e12 = data[offset2 + 5], e20 = data[offset2 + 6], e21 = data[offset2 + 7], e22 = data[offset2 + 8];
|
|
1717
|
+
const px = dy * e22 - dz * e21, py = dz * e20 - dx * e22, pz = dx * e21 - dy * e20;
|
|
1718
|
+
const det = e10 * px + e11 * py + e12 * pz;
|
|
1587
1719
|
if (Math.abs(det) < 1e-12) continue;
|
|
1588
|
-
const inv = 1 / det, tx =
|
|
1720
|
+
const inv = 1 / det, tx = ox - p0, ty = oy - p1, tz = oz - p2;
|
|
1589
1721
|
const u = (tx * px + ty * py + tz * pz) * inv;
|
|
1590
1722
|
if (u < 0 || u > 1) continue;
|
|
1591
|
-
const qx = ty *
|
|
1592
|
-
const v = (
|
|
1723
|
+
const qx = ty * e12 - tz * e11, qy = tz * e10 - tx * e12, qz = tx * e11 - ty * e10;
|
|
1724
|
+
const v = (dx * qx + dy * qy + dz * qz) * inv;
|
|
1593
1725
|
if (v < 0 || u + v > 1) continue;
|
|
1594
|
-
const distance = (
|
|
1726
|
+
const distance = (e20 * qx + e21 * qy + e22 * qz) * inv;
|
|
1595
1727
|
if (distance > this.bias * 0.25 && distance < limit) {
|
|
1596
1728
|
if (!state) return true;
|
|
1597
|
-
|
|
1598
|
-
e1[1] * e2[2] - e1[2] * e2[1],
|
|
1599
|
-
e1[2] * e2[0] - e1[0] * e2[2],
|
|
1600
|
-
e1[0] * e2[1] - e1[1] * e2[0]
|
|
1601
|
-
];
|
|
1602
|
-
const length = Math.hypot(...normal) || 1;
|
|
1603
|
-
for (let a = 0; a < 3; a++) normal[a] /= length;
|
|
1604
|
-
if (normal[0] * d[0] + normal[1] * d[1] + normal[2] * d[2] > 0)
|
|
1605
|
-
for (let a = 0; a < 3; a++) normal[a] *= -1;
|
|
1606
|
-
state.hit = {
|
|
1607
|
-
position: [
|
|
1608
|
-
o[0] + d[0] * distance,
|
|
1609
|
-
o[1] + d[1] * distance,
|
|
1610
|
-
o[2] + d[2] * distance
|
|
1611
|
-
],
|
|
1612
|
-
normal,
|
|
1613
|
-
material,
|
|
1614
|
-
distance
|
|
1615
|
-
};
|
|
1729
|
+
state.hit = this.triangles[i];
|
|
1616
1730
|
state.limit = limit = distance;
|
|
1617
1731
|
}
|
|
1618
1732
|
}
|
|
1619
|
-
|
|
1620
|
-
}
|
|
1621
|
-
if (state) {
|
|
1622
|
-
this.intersect(node.left, o, d, limit, state);
|
|
1623
|
-
this.intersect(node.right, o, d, state.limit, state);
|
|
1624
|
-
return state.hit !== null;
|
|
1733
|
+
node++;
|
|
1625
1734
|
}
|
|
1626
|
-
return
|
|
1735
|
+
return state?.hit != null;
|
|
1627
1736
|
}
|
|
1628
1737
|
};
|
|
1738
|
+
function dotNormal(normal, direction) {
|
|
1739
|
+
return normal[0] * direction[0] + normal[1] * direction[1] + normal[2] * direction[2];
|
|
1740
|
+
}
|
|
1629
1741
|
|
|
1630
1742
|
// lib/render/StudioLighting.ts
|
|
1631
1743
|
var dot = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
@@ -1669,7 +1781,7 @@ function orient(x, y, z, n, t, b) {
|
|
|
1669
1781
|
t[2] * x + b[2] * y + n[2] * z
|
|
1670
1782
|
];
|
|
1671
1783
|
}
|
|
1672
|
-
function
|
|
1784
|
+
function tableLookup(n) {
|
|
1673
1785
|
const x = (Math.atan2(n[2], n[0]) / (2 * Math.PI) + 0.5) * W - 0.5;
|
|
1674
1786
|
const y = Math.max(
|
|
1675
1787
|
0,
|
|
@@ -1680,7 +1792,11 @@ function sampleTable(table, n) {
|
|
|
1680
1792
|
);
|
|
1681
1793
|
const ix = Math.floor(x), iy = Math.floor(y), fx = x - ix, fy = y - iy;
|
|
1682
1794
|
const a = (ix % W + W) % W, b = (a + 1) % W, c = Math.min(H - 1, iy + 1);
|
|
1683
|
-
return
|
|
1795
|
+
return [iy * W + a, iy * W + b, c * W + a, c * W + b, fx, fy];
|
|
1796
|
+
}
|
|
1797
|
+
function sampleTable(table, lookup) {
|
|
1798
|
+
const [i00, i10, i01, i11, fx, fy] = lookup;
|
|
1799
|
+
return (table[i00] * (1 - fx) + table[i10] * fx) * (1 - fy) + (table[i01] * (1 - fx) + table[i11] * fx) * fy;
|
|
1684
1800
|
}
|
|
1685
1801
|
var cachedTables;
|
|
1686
1802
|
function getTables() {
|
|
@@ -1764,6 +1880,7 @@ var StudioLighting = class {
|
|
|
1764
1880
|
occlusion;
|
|
1765
1881
|
eye;
|
|
1766
1882
|
shadowDirections;
|
|
1883
|
+
diffuseCache = /* @__PURE__ */ new WeakMap();
|
|
1767
1884
|
constructor(drawCalls, camera) {
|
|
1768
1885
|
this.occlusion = new RayOcclusion(drawCalls);
|
|
1769
1886
|
const world = mat46.invert(mat46.create(), camera.view);
|
|
@@ -1774,7 +1891,7 @@ var StudioLighting = class {
|
|
|
1774
1891
|
const [t, b] = basis(l.direction);
|
|
1775
1892
|
return Array.from({ length: 8 }, (_, i) => {
|
|
1776
1893
|
const u = (i + 0.5) / 8, cos = 1 + Math.log(1 - u) / l.sharpness, sin = Math.sqrt(Math.max(0, 1 - cos * cos)), phi = 2 * Math.PI * radicalInverse(i);
|
|
1777
|
-
|
|
1894
|
+
const direction = orient(
|
|
1778
1895
|
sin * Math.cos(phi),
|
|
1779
1896
|
sin * Math.sin(phi),
|
|
1780
1897
|
cos,
|
|
@@ -1782,16 +1899,36 @@ var StudioLighting = class {
|
|
|
1782
1899
|
t,
|
|
1783
1900
|
b
|
|
1784
1901
|
);
|
|
1902
|
+
const parallel = dot(direction, l.direction);
|
|
1903
|
+
return {
|
|
1904
|
+
parallel: l.direction.map((v) => v * parallel),
|
|
1905
|
+
perpendicular: direction.map(
|
|
1906
|
+
(v, k) => v - l.direction[k] * parallel
|
|
1907
|
+
),
|
|
1908
|
+
cross: [
|
|
1909
|
+
l.direction[1] * direction[2] - l.direction[2] * direction[1],
|
|
1910
|
+
l.direction[2] * direction[0] - l.direction[0] * direction[2],
|
|
1911
|
+
l.direction[0] * direction[1] - l.direction[1] * direction[0]
|
|
1912
|
+
]
|
|
1913
|
+
};
|
|
1785
1914
|
});
|
|
1786
1915
|
});
|
|
1787
1916
|
}
|
|
1788
1917
|
bounceRadiance(hit, incoming) {
|
|
1789
1918
|
const metal = Math.max(0, Math.min(1, hit.material.metallicFactor ?? 1));
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
const
|
|
1793
|
-
|
|
1919
|
+
let cached = this.diffuseCache.get(hit.material);
|
|
1920
|
+
if (!cached || cached.normal.some((v, k) => !Object.is(v, hit.normal[k]))) {
|
|
1921
|
+
const irradiance2 = [...studio_environment_default.ambient];
|
|
1922
|
+
const lookup = tableLookup(hit.normal);
|
|
1923
|
+
for (let j = 0; j < lobes.length; j++) {
|
|
1924
|
+
const value = sampleTable(this.tables.diffuse[j], lookup);
|
|
1925
|
+
for (let k = 0; k < 3; k++)
|
|
1926
|
+
irradiance2[k] += value * lobes[j].color[k];
|
|
1927
|
+
}
|
|
1928
|
+
cached = { normal: [...hit.normal], irradiance: irradiance2 };
|
|
1929
|
+
this.diffuseCache.set(hit.material, cached);
|
|
1794
1930
|
}
|
|
1931
|
+
const irradiance = cached.irradiance;
|
|
1795
1932
|
const base = hit.material.baseColorFactor;
|
|
1796
1933
|
const rough = Math.max(0.04, Math.min(1, hit.material.roughnessFactor ?? 1));
|
|
1797
1934
|
const nv = Math.max(1e-3, -dot(hit.normal, incoming));
|
|
@@ -1810,8 +1947,9 @@ var StudioLighting = class {
|
|
|
1810
1947
|
(c, k) => c * base[k] * (1 - metal) * 0.96
|
|
1811
1948
|
);
|
|
1812
1949
|
const pref = [...studio_environment_default.ambient];
|
|
1950
|
+
const reflectedLookup = tableLookup(reflected);
|
|
1813
1951
|
for (let j = 0; j < lobes.length; j++) {
|
|
1814
|
-
const value = sampleTable(this.tables.specular[level][j],
|
|
1952
|
+
const value = sampleTable(this.tables.specular[level][j], reflectedLookup) * (1 - blend) + sampleTable(this.tables.specular[level + 1][j], reflectedLookup) * blend;
|
|
1815
1953
|
for (let k = 0; k < 3; k++) pref[k] += value * lobes[j].color[k];
|
|
1816
1954
|
}
|
|
1817
1955
|
for (let k = 0; k < 3; k++)
|
|
@@ -1855,27 +1993,23 @@ var StudioLighting = class {
|
|
|
1855
1993
|
bits = Math.imul(bits ^ bits >>> 15, 2221713035);
|
|
1856
1994
|
const hash = ((bits ^ bits >>> 16) >>> 0) / 4294967296;
|
|
1857
1995
|
const phase = hash * 2 * Math.PI, cosPhase = Math.cos(phase), sinPhase = Math.sin(phase);
|
|
1996
|
+
const normalLookup = tableLookup(n), reflectedLookup = tableLookup(reflected);
|
|
1858
1997
|
for (let j = 0; j < lobes.length; j++) {
|
|
1859
1998
|
const dirs = this.shadowDirections[j];
|
|
1860
1999
|
let visible = 0, total = 0;
|
|
1861
|
-
const
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
axis[2] * direction[0] - axis[0] * direction[2],
|
|
1867
|
-
axis[0] * direction[1] - axis[1] * direction[0]
|
|
2000
|
+
for (const { parallel, perpendicular, cross } of dirs) {
|
|
2001
|
+
const l = [
|
|
2002
|
+
parallel[0] + perpendicular[0] * cosPhase + cross[0] * sinPhase,
|
|
2003
|
+
parallel[1] + perpendicular[1] * cosPhase + cross[1] * sinPhase,
|
|
2004
|
+
parallel[2] + perpendicular[2] * cosPhase + cross[2] * sinPhase
|
|
1868
2005
|
];
|
|
1869
|
-
const l = direction.map(
|
|
1870
|
-
(value, k) => axis[k] * parallel + (value - axis[k] * parallel) * cosPhase + cross[k] * sinPhase
|
|
1871
|
-
);
|
|
1872
2006
|
const weight = Math.max(0, dot(n, l));
|
|
1873
2007
|
total += weight;
|
|
1874
2008
|
if (weight > 0 && !this.occlusion.occluded(origin, l)) visible += weight;
|
|
1875
2009
|
}
|
|
1876
2010
|
const visibility = total > 0 ? visible / total : 1;
|
|
1877
|
-
const diffuse = sampleTable(this.tables.diffuse[j],
|
|
1878
|
-
const specular = (sampleTable(this.tables.specular[level][j],
|
|
2011
|
+
const diffuse = sampleTable(this.tables.diffuse[j], normalLookup) * visibility;
|
|
2012
|
+
const specular = (sampleTable(this.tables.specular[level][j], reflectedLookup) * (1 - blend) + sampleTable(this.tables.specular[level + 1][j], reflectedLookup) * blend) * visibility;
|
|
1879
2013
|
for (let k = 0; k < 3; k++) {
|
|
1880
2014
|
color[k] += lobes[j].color[k] * base[k] * (1 - metal) * 0.96 * diffuse;
|
|
1881
2015
|
specColor[k] += lobes[j].color[k] * (f0[k] * A + B) * specular;
|
|
@@ -1883,7 +2017,7 @@ var StudioLighting = class {
|
|
|
1883
2017
|
}
|
|
1884
2018
|
const [t, b] = basis(n);
|
|
1885
2019
|
let blocked = 0;
|
|
1886
|
-
for (let i = 0; i < 4; i++) {
|
|
2020
|
+
for (let i = 0; metal < 1 && i < 4; i++) {
|
|
1887
2021
|
const phi = i * Math.PI / 2 + phase, l = orient(
|
|
1888
2022
|
0.7 * Math.cos(phi),
|
|
1889
2023
|
0.7 * Math.sin(phi),
|