poppygl 0.0.31 → 0.0.33
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 +4 -0
- package/dist/index.js +233 -79
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,9 @@ interface Material {
|
|
|
15
15
|
baseColorTexture: BitmapLike | null;
|
|
16
16
|
metallicFactor?: number;
|
|
17
17
|
roughnessFactor?: number;
|
|
18
|
+
emissiveFactor?: [number, number, number];
|
|
19
|
+
emissiveTexture?: BitmapLike | null;
|
|
20
|
+
emissiveStrength?: number;
|
|
18
21
|
alphaMode?: "OPAQUE" | "MASK" | "BLEND";
|
|
19
22
|
alphaCutoff?: number;
|
|
20
23
|
}
|
|
@@ -177,6 +180,7 @@ declare class StudioLighting {
|
|
|
177
180
|
private occlusion;
|
|
178
181
|
private eye;
|
|
179
182
|
private shadowDirections;
|
|
183
|
+
private diffuseCache;
|
|
180
184
|
constructor(drawCalls: DrawCall[], camera: Camera);
|
|
181
185
|
private bounceRadiance;
|
|
182
186
|
shade(position: V3, n: V3, base: V3, material: Material, sampleIndex?: number): V3;
|
package/dist/index.js
CHANGED
|
@@ -1038,6 +1038,23 @@ var SoftwareRenderer = class {
|
|
|
1038
1038
|
di
|
|
1039
1039
|
);
|
|
1040
1040
|
}
|
|
1041
|
+
const emission = material.emissiveFactor ?? [0, 0, 0];
|
|
1042
|
+
const strength = material.emissiveStrength ?? 1;
|
|
1043
|
+
let er = emission[0], eg = emission[1], eb = emission[2];
|
|
1044
|
+
if (uv && material.emissiveTexture) {
|
|
1045
|
+
const uvp = this.perspInterp(uv, invW, [l0, l1, l2]);
|
|
1046
|
+
const texel = this.sampleTextureNearest(
|
|
1047
|
+
material.emissiveTexture,
|
|
1048
|
+
uvp[0],
|
|
1049
|
+
uvp[1]
|
|
1050
|
+
);
|
|
1051
|
+
er *= srgbDecodeToLinear01(texel[0]);
|
|
1052
|
+
eg *= srgbDecodeToLinear01(texel[1]);
|
|
1053
|
+
eb *= srgbDecodeToLinear01(texel[2]);
|
|
1054
|
+
}
|
|
1055
|
+
r += er * strength;
|
|
1056
|
+
g += eg * strength;
|
|
1057
|
+
b += eb * strength;
|
|
1041
1058
|
let a = baseColor[3];
|
|
1042
1059
|
const linearR = r, linearG = g, linearB = b;
|
|
1043
1060
|
const alphaMode = material.alphaMode ?? "OPAQUE";
|
|
@@ -1466,8 +1483,19 @@ var studio_environment_default = {
|
|
|
1466
1483
|
// lib/render/RayOcclusion.ts
|
|
1467
1484
|
import { vec3 as vec33 } from "gl-matrix";
|
|
1468
1485
|
var RayOcclusion = class {
|
|
1469
|
-
|
|
1486
|
+
bounds = new Float64Array(0);
|
|
1487
|
+
escapes = new Uint32Array(0);
|
|
1488
|
+
starts = new Uint32Array(0);
|
|
1489
|
+
counts = new Uint8Array(0);
|
|
1490
|
+
triangleData = new Float64Array(0);
|
|
1491
|
+
triangles = [];
|
|
1470
1492
|
bias;
|
|
1493
|
+
inverse = [0, 0, 0];
|
|
1494
|
+
parallelAxes = 0;
|
|
1495
|
+
closest = {
|
|
1496
|
+
hit: null,
|
|
1497
|
+
limit: Infinity
|
|
1498
|
+
};
|
|
1471
1499
|
constructor(drawCalls) {
|
|
1472
1500
|
const triangles = [];
|
|
1473
1501
|
for (const dc of drawCalls) {
|
|
@@ -1491,10 +1519,19 @@ var RayOcclusion = class {
|
|
|
1491
1519
|
const max = [0, 1, 2].map(
|
|
1492
1520
|
(a) => Math.max(p[a], q[a], r[a])
|
|
1493
1521
|
);
|
|
1522
|
+
const e1 = q.map((v, a) => v - p[a]), e2 = r.map((v, a) => v - p[a]);
|
|
1523
|
+
const normal = [
|
|
1524
|
+
e1[1] * e2[2] - e1[2] * e2[1],
|
|
1525
|
+
e1[2] * e2[0] - e1[0] * e2[2],
|
|
1526
|
+
e1[0] * e2[1] - e1[1] * e2[0]
|
|
1527
|
+
];
|
|
1528
|
+
const length = Math.hypot(...normal) || 1;
|
|
1529
|
+
for (let a = 0; a < 3; a++) normal[a] /= length;
|
|
1494
1530
|
triangles.push({
|
|
1495
1531
|
p,
|
|
1496
|
-
e1
|
|
1497
|
-
e2
|
|
1532
|
+
e1,
|
|
1533
|
+
e2,
|
|
1534
|
+
normal,
|
|
1498
1535
|
min,
|
|
1499
1536
|
max,
|
|
1500
1537
|
center: min.map((v, a) => (v + max[a]) / 2),
|
|
@@ -1502,10 +1539,52 @@ var RayOcclusion = class {
|
|
|
1502
1539
|
});
|
|
1503
1540
|
}
|
|
1504
1541
|
}
|
|
1505
|
-
|
|
1506
|
-
|
|
1542
|
+
const root = triangles.length ? this.build(triangles) : null;
|
|
1543
|
+
if (root) this.flatten(root, triangles.length);
|
|
1544
|
+
const extent = root ? Math.hypot(...root.max.map((v, a) => v - root.min[a])) : 1;
|
|
1507
1545
|
this.bias = Math.max(1e-7, extent * 1e-5);
|
|
1508
1546
|
}
|
|
1547
|
+
/** Preorder bounds with subtree escape offsets retain the existing SAH
|
|
1548
|
+
* primitive order, while avoiding recursive calls and pointer-heavy nodes
|
|
1549
|
+
* during millions of ray queries. Float64 retains the original precision.
|
|
1550
|
+
*/
|
|
1551
|
+
flatten(root, triangleCount) {
|
|
1552
|
+
const nodes = [], escapes = [];
|
|
1553
|
+
const visit = (node) => {
|
|
1554
|
+
const index = nodes.length;
|
|
1555
|
+
nodes.push(node);
|
|
1556
|
+
escapes.push(0);
|
|
1557
|
+
if (!node.triangles) {
|
|
1558
|
+
visit(node.left);
|
|
1559
|
+
visit(node.right);
|
|
1560
|
+
}
|
|
1561
|
+
escapes[index] = nodes.length;
|
|
1562
|
+
};
|
|
1563
|
+
visit(root);
|
|
1564
|
+
this.bounds = new Float64Array(nodes.length * 6);
|
|
1565
|
+
this.escapes = Uint32Array.from(escapes);
|
|
1566
|
+
this.starts = new Uint32Array(nodes.length);
|
|
1567
|
+
this.counts = new Uint8Array(nodes.length);
|
|
1568
|
+
this.triangleData = new Float64Array(triangleCount * 9);
|
|
1569
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
1570
|
+
const node = nodes[i];
|
|
1571
|
+
this.bounds.set(node.min, i * 6);
|
|
1572
|
+
this.bounds.set(node.max, i * 6 + 3);
|
|
1573
|
+
if (!node.triangles) continue;
|
|
1574
|
+
this.starts[i] = this.triangles.length;
|
|
1575
|
+
this.counts[i] = node.triangles.length;
|
|
1576
|
+
for (const triangle of node.triangles) {
|
|
1577
|
+
const offset = this.triangles.length * 9;
|
|
1578
|
+
this.triangleData.set(triangle.p, offset);
|
|
1579
|
+
this.triangleData.set(triangle.e1, offset + 3);
|
|
1580
|
+
this.triangleData.set(triangle.e2, offset + 6);
|
|
1581
|
+
this.triangles.push({
|
|
1582
|
+
normal: triangle.normal,
|
|
1583
|
+
material: triangle.material
|
|
1584
|
+
});
|
|
1585
|
+
}
|
|
1586
|
+
}
|
|
1587
|
+
}
|
|
1509
1588
|
build(triangles) {
|
|
1510
1589
|
const min = [Infinity, Infinity, Infinity], max = [-Infinity, -Infinity, -Infinity];
|
|
1511
1590
|
for (const t of triangles)
|
|
@@ -1554,78 +1633,128 @@ var RayOcclusion = class {
|
|
|
1554
1633
|
right: this.build(best.slice(middle))
|
|
1555
1634
|
};
|
|
1556
1635
|
}
|
|
1636
|
+
prepareRay(direction) {
|
|
1637
|
+
this.parallelAxes = 0;
|
|
1638
|
+
for (let a = 0; a < 3; a++) {
|
|
1639
|
+
this.inverse[a] = 1 / direction[a];
|
|
1640
|
+
if (Math.abs(direction[a]) < 1e-12) this.parallelAxes |= 1 << a;
|
|
1641
|
+
}
|
|
1642
|
+
}
|
|
1557
1643
|
occluded(origin, direction, maxDistance = Infinity) {
|
|
1558
|
-
|
|
1644
|
+
this.prepareRay(direction);
|
|
1645
|
+
return this.intersect(origin, direction, maxDistance);
|
|
1559
1646
|
}
|
|
1560
1647
|
/** Closest opaque triangle for one-bounce reflections and diffuse fill. */
|
|
1561
1648
|
trace(origin, direction, maxDistance = Infinity) {
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1649
|
+
this.prepareRay(direction);
|
|
1650
|
+
const state = this.closest;
|
|
1651
|
+
state.hit = null;
|
|
1652
|
+
state.limit = maxDistance;
|
|
1653
|
+
this.intersect(origin, direction, maxDistance, state);
|
|
1654
|
+
if (!state.hit) return null;
|
|
1655
|
+
const { normal, material } = state.hit;
|
|
1656
|
+
const sign = dotNormal(normal, direction) > 0 ? -1 : 1;
|
|
1657
|
+
return {
|
|
1658
|
+
position: [
|
|
1659
|
+
origin[0] + direction[0] * state.limit,
|
|
1660
|
+
origin[1] + direction[1] * state.limit,
|
|
1661
|
+
origin[2] + direction[2] * state.limit
|
|
1662
|
+
],
|
|
1663
|
+
normal: [normal[0] * sign, normal[1] * sign, normal[2] * sign],
|
|
1664
|
+
material,
|
|
1665
|
+
distance: state.limit
|
|
1565
1666
|
};
|
|
1566
|
-
if (this.root)
|
|
1567
|
-
this.intersect(this.root, origin, direction, maxDistance, state);
|
|
1568
|
-
return state.hit;
|
|
1569
1667
|
}
|
|
1570
|
-
intersect(
|
|
1571
|
-
|
|
1572
|
-
let
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1668
|
+
intersect(o, d, maxDistance, state) {
|
|
1669
|
+
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;
|
|
1670
|
+
let node = 0;
|
|
1671
|
+
while (node < this.counts.length) {
|
|
1672
|
+
const offset = node * 6;
|
|
1673
|
+
let limit = state ? state.limit : maxDistance, near = 0, far = limit;
|
|
1674
|
+
if (parallel & 2) {
|
|
1675
|
+
if (oy < bounds[offset + 1] || oy > bounds[offset + 4]) {
|
|
1676
|
+
node = this.escapes[node];
|
|
1677
|
+
continue;
|
|
1678
|
+
}
|
|
1679
|
+
} else {
|
|
1680
|
+
let t0 = (bounds[offset + 1] - oy) * iy, t1 = (bounds[offset + 4] - oy) * iy;
|
|
1681
|
+
if (t0 > t1) {
|
|
1682
|
+
const swap = t0;
|
|
1683
|
+
t0 = t1;
|
|
1684
|
+
t1 = swap;
|
|
1685
|
+
}
|
|
1686
|
+
if (t0 > near) near = t0;
|
|
1687
|
+
if (t1 < far) far = t1;
|
|
1688
|
+
if (far < near) {
|
|
1689
|
+
node = this.escapes[node];
|
|
1690
|
+
continue;
|
|
1691
|
+
}
|
|
1577
1692
|
}
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1693
|
+
if (parallel & 1) {
|
|
1694
|
+
if (ox < bounds[offset + 0] || ox > bounds[offset + 3]) {
|
|
1695
|
+
node = this.escapes[node];
|
|
1696
|
+
continue;
|
|
1697
|
+
}
|
|
1698
|
+
} else {
|
|
1699
|
+
let t0 = (bounds[offset + 0] - ox) * ix, t1 = (bounds[offset + 3] - ox) * ix;
|
|
1700
|
+
if (t0 > t1) {
|
|
1701
|
+
const swap = t0;
|
|
1702
|
+
t0 = t1;
|
|
1703
|
+
t1 = swap;
|
|
1704
|
+
}
|
|
1705
|
+
if (t0 > near) near = t0;
|
|
1706
|
+
if (t1 < far) far = t1;
|
|
1707
|
+
if (far < near) {
|
|
1708
|
+
node = this.escapes[node];
|
|
1709
|
+
continue;
|
|
1710
|
+
}
|
|
1711
|
+
}
|
|
1712
|
+
if (parallel & 4) {
|
|
1713
|
+
if (oz < bounds[offset + 2] || oz > bounds[offset + 5]) {
|
|
1714
|
+
node = this.escapes[node];
|
|
1715
|
+
continue;
|
|
1716
|
+
}
|
|
1717
|
+
} else {
|
|
1718
|
+
let t0 = (bounds[offset + 2] - oz) * iz, t1 = (bounds[offset + 5] - oz) * iz;
|
|
1719
|
+
if (t0 > t1) {
|
|
1720
|
+
const swap = t0;
|
|
1721
|
+
t0 = t1;
|
|
1722
|
+
t1 = swap;
|
|
1723
|
+
}
|
|
1724
|
+
if (t0 > near) near = t0;
|
|
1725
|
+
if (t1 < far) far = t1;
|
|
1726
|
+
if (far < near) {
|
|
1727
|
+
node = this.escapes[node];
|
|
1728
|
+
continue;
|
|
1729
|
+
}
|
|
1730
|
+
}
|
|
1731
|
+
const start = this.starts[node], end = start + this.counts[node];
|
|
1732
|
+
for (let i = start; i < end; i++) {
|
|
1733
|
+
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];
|
|
1734
|
+
const px = dy * e22 - dz * e21, py = dz * e20 - dx * e22, pz = dx * e21 - dy * e20;
|
|
1735
|
+
const det = e10 * px + e11 * py + e12 * pz;
|
|
1587
1736
|
if (Math.abs(det) < 1e-12) continue;
|
|
1588
|
-
const inv = 1 / det, tx =
|
|
1737
|
+
const inv = 1 / det, tx = ox - p0, ty = oy - p1, tz = oz - p2;
|
|
1589
1738
|
const u = (tx * px + ty * py + tz * pz) * inv;
|
|
1590
1739
|
if (u < 0 || u > 1) continue;
|
|
1591
|
-
const qx = ty *
|
|
1592
|
-
const v = (
|
|
1740
|
+
const qx = ty * e12 - tz * e11, qy = tz * e10 - tx * e12, qz = tx * e11 - ty * e10;
|
|
1741
|
+
const v = (dx * qx + dy * qy + dz * qz) * inv;
|
|
1593
1742
|
if (v < 0 || u + v > 1) continue;
|
|
1594
|
-
const distance = (
|
|
1743
|
+
const distance = (e20 * qx + e21 * qy + e22 * qz) * inv;
|
|
1595
1744
|
if (distance > this.bias * 0.25 && distance < limit) {
|
|
1596
1745
|
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
|
-
};
|
|
1746
|
+
state.hit = this.triangles[i];
|
|
1616
1747
|
state.limit = limit = distance;
|
|
1617
1748
|
}
|
|
1618
1749
|
}
|
|
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;
|
|
1750
|
+
node++;
|
|
1625
1751
|
}
|
|
1626
|
-
return
|
|
1752
|
+
return state?.hit != null;
|
|
1627
1753
|
}
|
|
1628
1754
|
};
|
|
1755
|
+
function dotNormal(normal, direction) {
|
|
1756
|
+
return normal[0] * direction[0] + normal[1] * direction[1] + normal[2] * direction[2];
|
|
1757
|
+
}
|
|
1629
1758
|
|
|
1630
1759
|
// lib/render/StudioLighting.ts
|
|
1631
1760
|
var dot = (a, b) => a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
|
|
@@ -1669,7 +1798,7 @@ function orient(x, y, z, n, t, b) {
|
|
|
1669
1798
|
t[2] * x + b[2] * y + n[2] * z
|
|
1670
1799
|
];
|
|
1671
1800
|
}
|
|
1672
|
-
function
|
|
1801
|
+
function tableLookup(n) {
|
|
1673
1802
|
const x = (Math.atan2(n[2], n[0]) / (2 * Math.PI) + 0.5) * W - 0.5;
|
|
1674
1803
|
const y = Math.max(
|
|
1675
1804
|
0,
|
|
@@ -1680,7 +1809,11 @@ function sampleTable(table, n) {
|
|
|
1680
1809
|
);
|
|
1681
1810
|
const ix = Math.floor(x), iy = Math.floor(y), fx = x - ix, fy = y - iy;
|
|
1682
1811
|
const a = (ix % W + W) % W, b = (a + 1) % W, c = Math.min(H - 1, iy + 1);
|
|
1683
|
-
return
|
|
1812
|
+
return [iy * W + a, iy * W + b, c * W + a, c * W + b, fx, fy];
|
|
1813
|
+
}
|
|
1814
|
+
function sampleTable(table, lookup) {
|
|
1815
|
+
const [i00, i10, i01, i11, fx, fy] = lookup;
|
|
1816
|
+
return (table[i00] * (1 - fx) + table[i10] * fx) * (1 - fy) + (table[i01] * (1 - fx) + table[i11] * fx) * fy;
|
|
1684
1817
|
}
|
|
1685
1818
|
var cachedTables;
|
|
1686
1819
|
function getTables() {
|
|
@@ -1764,6 +1897,7 @@ var StudioLighting = class {
|
|
|
1764
1897
|
occlusion;
|
|
1765
1898
|
eye;
|
|
1766
1899
|
shadowDirections;
|
|
1900
|
+
diffuseCache = /* @__PURE__ */ new WeakMap();
|
|
1767
1901
|
constructor(drawCalls, camera) {
|
|
1768
1902
|
this.occlusion = new RayOcclusion(drawCalls);
|
|
1769
1903
|
const world = mat46.invert(mat46.create(), camera.view);
|
|
@@ -1774,7 +1908,7 @@ var StudioLighting = class {
|
|
|
1774
1908
|
const [t, b] = basis(l.direction);
|
|
1775
1909
|
return Array.from({ length: 8 }, (_, i) => {
|
|
1776
1910
|
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
|
-
|
|
1911
|
+
const direction = orient(
|
|
1778
1912
|
sin * Math.cos(phi),
|
|
1779
1913
|
sin * Math.sin(phi),
|
|
1780
1914
|
cos,
|
|
@@ -1782,16 +1916,36 @@ var StudioLighting = class {
|
|
|
1782
1916
|
t,
|
|
1783
1917
|
b
|
|
1784
1918
|
);
|
|
1919
|
+
const parallel = dot(direction, l.direction);
|
|
1920
|
+
return {
|
|
1921
|
+
parallel: l.direction.map((v) => v * parallel),
|
|
1922
|
+
perpendicular: direction.map(
|
|
1923
|
+
(v, k) => v - l.direction[k] * parallel
|
|
1924
|
+
),
|
|
1925
|
+
cross: [
|
|
1926
|
+
l.direction[1] * direction[2] - l.direction[2] * direction[1],
|
|
1927
|
+
l.direction[2] * direction[0] - l.direction[0] * direction[2],
|
|
1928
|
+
l.direction[0] * direction[1] - l.direction[1] * direction[0]
|
|
1929
|
+
]
|
|
1930
|
+
};
|
|
1785
1931
|
});
|
|
1786
1932
|
});
|
|
1787
1933
|
}
|
|
1788
1934
|
bounceRadiance(hit, incoming) {
|
|
1789
1935
|
const metal = Math.max(0, Math.min(1, hit.material.metallicFactor ?? 1));
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
const
|
|
1793
|
-
|
|
1936
|
+
let cached = this.diffuseCache.get(hit.material);
|
|
1937
|
+
if (!cached || cached.normal.some((v, k) => !Object.is(v, hit.normal[k]))) {
|
|
1938
|
+
const irradiance2 = [...studio_environment_default.ambient];
|
|
1939
|
+
const lookup = tableLookup(hit.normal);
|
|
1940
|
+
for (let j = 0; j < lobes.length; j++) {
|
|
1941
|
+
const value = sampleTable(this.tables.diffuse[j], lookup);
|
|
1942
|
+
for (let k = 0; k < 3; k++)
|
|
1943
|
+
irradiance2[k] += value * lobes[j].color[k];
|
|
1944
|
+
}
|
|
1945
|
+
cached = { normal: [...hit.normal], irradiance: irradiance2 };
|
|
1946
|
+
this.diffuseCache.set(hit.material, cached);
|
|
1794
1947
|
}
|
|
1948
|
+
const irradiance = cached.irradiance;
|
|
1795
1949
|
const base = hit.material.baseColorFactor;
|
|
1796
1950
|
const rough = Math.max(0.04, Math.min(1, hit.material.roughnessFactor ?? 1));
|
|
1797
1951
|
const nv = Math.max(1e-3, -dot(hit.normal, incoming));
|
|
@@ -1810,8 +1964,9 @@ var StudioLighting = class {
|
|
|
1810
1964
|
(c, k) => c * base[k] * (1 - metal) * 0.96
|
|
1811
1965
|
);
|
|
1812
1966
|
const pref = [...studio_environment_default.ambient];
|
|
1967
|
+
const reflectedLookup = tableLookup(reflected);
|
|
1813
1968
|
for (let j = 0; j < lobes.length; j++) {
|
|
1814
|
-
const value = sampleTable(this.tables.specular[level][j],
|
|
1969
|
+
const value = sampleTable(this.tables.specular[level][j], reflectedLookup) * (1 - blend) + sampleTable(this.tables.specular[level + 1][j], reflectedLookup) * blend;
|
|
1815
1970
|
for (let k = 0; k < 3; k++) pref[k] += value * lobes[j].color[k];
|
|
1816
1971
|
}
|
|
1817
1972
|
for (let k = 0; k < 3; k++)
|
|
@@ -1855,27 +2010,23 @@ var StudioLighting = class {
|
|
|
1855
2010
|
bits = Math.imul(bits ^ bits >>> 15, 2221713035);
|
|
1856
2011
|
const hash = ((bits ^ bits >>> 16) >>> 0) / 4294967296;
|
|
1857
2012
|
const phase = hash * 2 * Math.PI, cosPhase = Math.cos(phase), sinPhase = Math.sin(phase);
|
|
2013
|
+
const normalLookup = tableLookup(n), reflectedLookup = tableLookup(reflected);
|
|
1858
2014
|
for (let j = 0; j < lobes.length; j++) {
|
|
1859
2015
|
const dirs = this.shadowDirections[j];
|
|
1860
2016
|
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]
|
|
2017
|
+
for (const { parallel, perpendicular, cross } of dirs) {
|
|
2018
|
+
const l = [
|
|
2019
|
+
parallel[0] + perpendicular[0] * cosPhase + cross[0] * sinPhase,
|
|
2020
|
+
parallel[1] + perpendicular[1] * cosPhase + cross[1] * sinPhase,
|
|
2021
|
+
parallel[2] + perpendicular[2] * cosPhase + cross[2] * sinPhase
|
|
1868
2022
|
];
|
|
1869
|
-
const l = direction.map(
|
|
1870
|
-
(value, k) => axis[k] * parallel + (value - axis[k] * parallel) * cosPhase + cross[k] * sinPhase
|
|
1871
|
-
);
|
|
1872
2023
|
const weight = Math.max(0, dot(n, l));
|
|
1873
2024
|
total += weight;
|
|
1874
2025
|
if (weight > 0 && !this.occlusion.occluded(origin, l)) visible += weight;
|
|
1875
2026
|
}
|
|
1876
2027
|
const visibility = total > 0 ? visible / total : 1;
|
|
1877
|
-
const diffuse = sampleTable(this.tables.diffuse[j],
|
|
1878
|
-
const specular = (sampleTable(this.tables.specular[level][j],
|
|
2028
|
+
const diffuse = sampleTable(this.tables.diffuse[j], normalLookup) * visibility;
|
|
2029
|
+
const specular = (sampleTable(this.tables.specular[level][j], reflectedLookup) * (1 - blend) + sampleTable(this.tables.specular[level + 1][j], reflectedLookup) * blend) * visibility;
|
|
1879
2030
|
for (let k = 0; k < 3; k++) {
|
|
1880
2031
|
color[k] += lobes[j].color[k] * base[k] * (1 - metal) * 0.96 * diffuse;
|
|
1881
2032
|
specColor[k] += lobes[j].color[k] * (f0[k] * A + B) * specular;
|
|
@@ -1883,7 +2034,7 @@ var StudioLighting = class {
|
|
|
1883
2034
|
}
|
|
1884
2035
|
const [t, b] = basis(n);
|
|
1885
2036
|
let blocked = 0;
|
|
1886
|
-
for (let i = 0; i < 4; i++) {
|
|
2037
|
+
for (let i = 0; metal < 1 && i < 4; i++) {
|
|
1887
2038
|
const phi = i * Math.PI / 2 + phase, l = orient(
|
|
1888
2039
|
0.7 * Math.cos(phi),
|
|
1889
2040
|
0.7 * Math.sin(phi),
|
|
@@ -2385,6 +2536,9 @@ function getMaterial(gltf, textures, images, materialIndex) {
|
|
|
2385
2536
|
baseColorTexture: texImg,
|
|
2386
2537
|
metallicFactor: pbr.metallicFactor ?? 1,
|
|
2387
2538
|
roughnessFactor: pbr.roughnessFactor ?? 1,
|
|
2539
|
+
emissiveFactor: material.emissiveFactor ?? [0, 0, 0],
|
|
2540
|
+
emissiveTexture: images[textures[material.emissiveTexture?.index]?.source] ?? null,
|
|
2541
|
+
emissiveStrength: material.extensions?.KHR_materials_emissive_strength?.emissiveStrength ?? 1,
|
|
2388
2542
|
alphaMode,
|
|
2389
2543
|
alphaCutoff
|
|
2390
2544
|
};
|