three-gpu-pathtracer 0.0.16 → 0.0.17
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/README.md +3 -1
- package/build/index.module.js +248 -198
- package/build/index.module.js.map +1 -1
- package/build/index.umd.cjs +245 -195
- package/build/index.umd.cjs.map +1 -1
- package/package.json +5 -6
- package/src/core/DynamicPathTracingSceneGenerator.js +8 -3
- package/src/core/PathTracingRenderer.js +8 -4
- package/src/core/PathTracingSceneGenerator.js +6 -1
- package/src/materials/debug/GraphMaterial.js +1 -1
- package/src/materials/fullscreen/AlphaDisplayMaterial.js +1 -1
- package/src/materials/fullscreen/DenoiseMaterial.js +1 -1
- package/src/materials/fullscreen/GradientMapMaterial.js +1 -1
- package/src/materials/pathtracing/LambertPathTracingMaterial.js +4 -3
- package/src/materials/pathtracing/PhysicalPathTracingMaterial.js +61 -46
- package/src/materials/pathtracing/glsl/attenuateHit.glsl.js +2 -11
- package/src/materials/pathtracing/glsl/directLightContribution.glsl.js +10 -6
- package/src/materials/pathtracing/glsl/getSurfaceRecord.glsl.js +1 -1
- package/src/materials/pathtracing/glsl/traceScene.glsl.js +10 -12
- package/src/materials/surface/AmbientOcclusionMaterial.js +4 -3
- package/src/shader/bsdf/bsdfSampling.glsl.js +7 -7
- package/src/shader/common/bvhAnyHit.glsl.js +2 -2
- package/src/shader/common/intersectShapes.glsl.js +2 -2
- package/src/shader/rand/sobol.glsl.js +3 -3
- package/src/shader/sampling/equirectSampling.glsl.js +10 -10
- package/src/shader/sampling/lightSampling.glsl.js +30 -37
- package/src/shader/structs/fogMaterialBvh.glsl.js +2 -2
- package/src/shader/structs/lightsStruct.glsl.js +12 -1
- package/src/shader/structs/materialStruct.glsl.js +16 -15
- package/src/textures/ProceduralEquirectTexture.js +9 -8
- package/src/uniforms/EquirectHdrInfoUniform.js +18 -17
- package/src/uniforms/IESProfilesTexture.js +2 -2
- package/src/uniforms/LightsInfoUniformStruct.js +4 -2
- package/src/uniforms/MaterialsTexture.js +3 -1
- package/src/utils/BlurredEnvMapGenerator.js +4 -4
- package/src/utils/GeometryPreparationUtils.js +8 -2
- package/src/utils/IESLoader.js +7 -5
- package/src/utils/TextureUtils.js +15 -0
package/build/index.module.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { ShaderMaterial, NoBlending, Vector2, WebGLRenderTarget, FloatType, RGBAFormat, NearestFilter, Vector4, NormalBlending, Color, Vector3, MathUtils, Matrix4, PerspectiveCamera, BufferAttribute, Mesh, BufferGeometry, Camera, SpotLight, RectAreaLight, Spherical, DataTexture, EquirectangularReflectionMapping, RepeatWrapping, ClampToEdgeWrapping, LinearFilter, DoubleSide, BackSide, FrontSide, WebGLArrayRenderTarget, UnsignedByteType, MeshBasicMaterial, NoToneMapping, Source,
|
|
1
|
+
import { ShaderMaterial, NoBlending, Vector2, WebGLRenderTarget, FloatType, RGBAFormat, NearestFilter, Vector4, NormalBlending, Color, HalfFloatType, Vector3, MathUtils, Matrix4, PerspectiveCamera, BufferAttribute, Mesh, BufferGeometry, Camera, SpotLight, RectAreaLight, Spherical, DataTexture, EquirectangularReflectionMapping, RepeatWrapping, ClampToEdgeWrapping, LinearFilter, DataUtils, DoubleSide, BackSide, FrontSide, WebGLArrayRenderTarget, UnsignedByteType, MeshBasicMaterial, NoToneMapping, Source, RedFormat, Quaternion, Loader, FileLoader, PMREMGenerator, DataArrayTexture, MeshStandardMaterial, BoxGeometry } from 'three';
|
|
2
2
|
import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
|
|
3
|
-
import { StaticGeometryGenerator, SAH, MeshBVH, FloatVertexAttributeTexture, MeshBVHUniformStruct, UIntVertexAttributeTexture,
|
|
4
|
-
import { mergeVertices,
|
|
3
|
+
import { StaticGeometryGenerator, SAH, MeshBVH, FloatVertexAttributeTexture, MeshBVHUniformStruct, UIntVertexAttributeTexture, BVHShaderGLSL } from 'three-mesh-bvh';
|
|
4
|
+
import { mergeVertices, mergeGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
|
|
5
5
|
|
|
6
6
|
class MaterialBase extends ShaderMaterial {
|
|
7
7
|
|
|
@@ -339,9 +339,9 @@ const sobolSamplingGLSL = /* glsl */`
|
|
|
339
339
|
|
|
340
340
|
// Seeds
|
|
341
341
|
uniform sampler2D sobolTexture;
|
|
342
|
-
uint sobolPixelIndex;
|
|
343
|
-
uint sobolPathIndex;
|
|
344
|
-
uint sobolBounceIndex;
|
|
342
|
+
uint sobolPixelIndex = 0u;
|
|
343
|
+
uint sobolPathIndex = 0u;
|
|
344
|
+
uint sobolBounceIndex = 0u;
|
|
345
345
|
|
|
346
346
|
uint sobolGetSeed( uint bounce, uint effect ) {
|
|
347
347
|
|
|
@@ -703,18 +703,22 @@ class PathTracingRenderer {
|
|
|
703
703
|
this._currentTile = 0;
|
|
704
704
|
|
|
705
705
|
this._sobolTarget = new SobolNumberMapGenerator().generate( renderer );
|
|
706
|
+
|
|
707
|
+
// will be null if extension not supported
|
|
708
|
+
const floatLinearExtensionSupported = renderer.extensions.get( 'OES_texture_float_linear' );
|
|
709
|
+
|
|
706
710
|
this._primaryTarget = new WebGLRenderTarget( 1, 1, {
|
|
707
711
|
format: RGBAFormat,
|
|
708
|
-
type: FloatType,
|
|
712
|
+
type: floatLinearExtensionSupported ? FloatType : HalfFloatType,
|
|
709
713
|
} );
|
|
710
714
|
this._blendTargets = [
|
|
711
715
|
new WebGLRenderTarget( 1, 1, {
|
|
712
716
|
format: RGBAFormat,
|
|
713
|
-
type: FloatType,
|
|
717
|
+
type: floatLinearExtensionSupported ? FloatType : HalfFloatType,
|
|
714
718
|
} ),
|
|
715
719
|
new WebGLRenderTarget( 1, 1, {
|
|
716
720
|
format: RGBAFormat,
|
|
717
|
-
type: FloatType,
|
|
721
|
+
type: floatLinearExtensionSupported ? FloatType : HalfFloatType,
|
|
718
722
|
} ),
|
|
719
723
|
];
|
|
720
724
|
|
|
@@ -1195,6 +1199,12 @@ function mergeMeshes( meshes, options = {} ) {
|
|
|
1195
1199
|
const geometry = options.cloneGeometry ? originalGeometry.clone() : originalGeometry;
|
|
1196
1200
|
geometry.applyMatrix4( mesh.matrixWorld );
|
|
1197
1201
|
|
|
1202
|
+
if ( mesh.matrixWorld.determinant() < 0 ) {
|
|
1203
|
+
|
|
1204
|
+
geometry.index.array.reverse();
|
|
1205
|
+
|
|
1206
|
+
}
|
|
1207
|
+
|
|
1198
1208
|
// ensure our geometry has common attributes
|
|
1199
1209
|
setCommonAttributes( geometry, {
|
|
1200
1210
|
attributes: options.attributes,
|
|
@@ -1226,7 +1236,7 @@ function mergeMeshes( meshes, options = {} ) {
|
|
|
1226
1236
|
|
|
1227
1237
|
} );
|
|
1228
1238
|
|
|
1229
|
-
const geometry =
|
|
1239
|
+
const geometry = mergeGeometries( transformedGeometry, false );
|
|
1230
1240
|
const textures = Array.from( textureSet );
|
|
1231
1241
|
return { geometry, materials, textures };
|
|
1232
1242
|
|
|
@@ -1263,7 +1273,12 @@ class PathTracingSceneGenerator {
|
|
|
1263
1273
|
|
|
1264
1274
|
meshes.push( c );
|
|
1265
1275
|
|
|
1266
|
-
} else if (
|
|
1276
|
+
} else if (
|
|
1277
|
+
c.isRectAreaLight ||
|
|
1278
|
+
c.isSpotLight ||
|
|
1279
|
+
c.isPointLight ||
|
|
1280
|
+
c.isDirectionalLight
|
|
1281
|
+
) {
|
|
1267
1282
|
|
|
1268
1283
|
lights.push( c );
|
|
1269
1284
|
|
|
@@ -1334,7 +1349,7 @@ class DynamicPathTracingSceneGenerator {
|
|
|
1334
1349
|
|
|
1335
1350
|
generate() {
|
|
1336
1351
|
|
|
1337
|
-
const { objects, staticGeometryGenerator, geometry } = this;
|
|
1352
|
+
const { objects, staticGeometryGenerator, geometry, lights } = this;
|
|
1338
1353
|
if ( this.bvh === null ) {
|
|
1339
1354
|
|
|
1340
1355
|
const attributes = [ 'position', 'normal', 'tangent', 'uv', 'color' ];
|
|
@@ -1348,9 +1363,14 @@ class DynamicPathTracingSceneGenerator {
|
|
|
1348
1363
|
const normalMapRequired = ! ! c.material.normalMap;
|
|
1349
1364
|
setCommonAttributes( c.geometry, { attributes, normalMapRequired } );
|
|
1350
1365
|
|
|
1351
|
-
} else if (
|
|
1366
|
+
} else if (
|
|
1367
|
+
c.isRectAreaLight ||
|
|
1368
|
+
c.isSpotLight ||
|
|
1369
|
+
c.isPointLight ||
|
|
1370
|
+
c.isDirectionalLight
|
|
1371
|
+
) {
|
|
1352
1372
|
|
|
1353
|
-
|
|
1373
|
+
lights.push( c );
|
|
1354
1374
|
|
|
1355
1375
|
}
|
|
1356
1376
|
|
|
@@ -1775,11 +1795,11 @@ const _polar = new Spherical();
|
|
|
1775
1795
|
const _color = new Color();
|
|
1776
1796
|
class ProceduralEquirectTexture extends DataTexture {
|
|
1777
1797
|
|
|
1778
|
-
constructor( width, height ) {
|
|
1798
|
+
constructor( width = 512, height = 512 ) {
|
|
1779
1799
|
|
|
1780
1800
|
super(
|
|
1781
|
-
new
|
|
1782
|
-
width, height, RGBAFormat,
|
|
1801
|
+
new Uint16Array( width * height * 4 ),
|
|
1802
|
+
width, height, RGBAFormat, HalfFloatType, EquirectangularReflectionMapping,
|
|
1783
1803
|
RepeatWrapping, ClampToEdgeWrapping, LinearFilter, LinearFilter,
|
|
1784
1804
|
);
|
|
1785
1805
|
|
|
@@ -1811,10 +1831,10 @@ class ProceduralEquirectTexture extends DataTexture {
|
|
|
1811
1831
|
|
|
1812
1832
|
const i = y * width + x;
|
|
1813
1833
|
const i4 = 4 * i;
|
|
1814
|
-
data[ i4 + 0 ] = _color.r;
|
|
1815
|
-
data[ i4 + 1 ] = _color.g;
|
|
1816
|
-
data[ i4 + 2 ] = _color.b;
|
|
1817
|
-
data[ i4 + 3 ] = 1.0;
|
|
1834
|
+
data[ i4 + 0 ] = DataUtils.toHalfFloat( _color.r );
|
|
1835
|
+
data[ i4 + 1 ] = DataUtils.toHalfFloat( _color.g );
|
|
1836
|
+
data[ i4 + 2 ] = DataUtils.toHalfFloat( _color.b );
|
|
1837
|
+
data[ i4 + 3 ] = DataUtils.toHalfFloat( 1.0 );
|
|
1818
1838
|
|
|
1819
1839
|
}
|
|
1820
1840
|
|
|
@@ -1948,6 +1968,8 @@ class MaterialsTexture extends DataTexture {
|
|
|
1948
1968
|
this.type = FloatType;
|
|
1949
1969
|
this.wrapS = ClampToEdgeWrapping;
|
|
1950
1970
|
this.wrapT = ClampToEdgeWrapping;
|
|
1971
|
+
this.minFilter = NearestFilter;
|
|
1972
|
+
this.magFilter = NearestFilter;
|
|
1951
1973
|
this.generateMipmaps = false;
|
|
1952
1974
|
this.threeCompatibilityTransforms = false;
|
|
1953
1975
|
this.features = new MaterialFeatures();
|
|
@@ -2482,6 +2504,19 @@ class RenderTarget2DArray extends WebGLArrayRenderTarget {
|
|
|
2482
2504
|
|
|
2483
2505
|
}
|
|
2484
2506
|
|
|
2507
|
+
function toHalfFloatArray( f32Array ) {
|
|
2508
|
+
|
|
2509
|
+
const f16Array = new Uint16Array( f32Array.length );
|
|
2510
|
+
for ( let i = 0, n = f32Array.length; i < n; ++ i ) {
|
|
2511
|
+
|
|
2512
|
+
f16Array[ i ] = DataUtils.toHalfFloat( f32Array[ i ] );
|
|
2513
|
+
|
|
2514
|
+
}
|
|
2515
|
+
|
|
2516
|
+
return f16Array;
|
|
2517
|
+
|
|
2518
|
+
}
|
|
2519
|
+
|
|
2485
2520
|
function binarySearchFindClosestIndexOf( array, targetValue, offset = 0, count = array.length ) {
|
|
2486
2521
|
|
|
2487
2522
|
let lower = offset;
|
|
@@ -2531,15 +2566,15 @@ function preprocessEnvMap( envMap ) {
|
|
|
2531
2566
|
let newData = data;
|
|
2532
2567
|
if ( map.type === HalfFloatType ) {
|
|
2533
2568
|
|
|
2534
|
-
newData = new
|
|
2569
|
+
newData = new Uint16Array( data.length );
|
|
2535
2570
|
for ( const i in data ) {
|
|
2536
2571
|
|
|
2537
|
-
newData[ i ] =
|
|
2572
|
+
newData[ i ] = data[ i ];
|
|
2538
2573
|
|
|
2539
2574
|
}
|
|
2540
2575
|
|
|
2541
2576
|
map.image.data = newData;
|
|
2542
|
-
map.type =
|
|
2577
|
+
map.type = HalfFloatType;
|
|
2543
2578
|
|
|
2544
2579
|
}
|
|
2545
2580
|
|
|
@@ -2580,8 +2615,8 @@ class EquirectHdrInfoUniform {
|
|
|
2580
2615
|
|
|
2581
2616
|
// Default to a white texture and associated weights so we don't
|
|
2582
2617
|
// just render black initially.
|
|
2583
|
-
const whiteTex = new DataTexture( new Float32Array( [ 1, 1, 1, 1 ] ), 1, 1 );
|
|
2584
|
-
whiteTex.type =
|
|
2618
|
+
const whiteTex = new DataTexture( toHalfFloatArray( new Float32Array( [ 1, 1, 1, 1 ] ) ), 1, 1 );
|
|
2619
|
+
whiteTex.type = HalfFloatType;
|
|
2585
2620
|
whiteTex.format = RGBAFormat;
|
|
2586
2621
|
whiteTex.minFilter = LinearFilter;
|
|
2587
2622
|
whiteTex.magFilter = LinearFilter;
|
|
@@ -2592,8 +2627,8 @@ class EquirectHdrInfoUniform {
|
|
|
2592
2627
|
|
|
2593
2628
|
// Stores a map of [0, 1] value -> cumulative importance row & pdf
|
|
2594
2629
|
// used to sampling a random value to a relevant row to sample from
|
|
2595
|
-
const marginalWeights = new DataTexture( new Float32Array( [ 0, 1 ] ), 1, 2 );
|
|
2596
|
-
marginalWeights.type =
|
|
2630
|
+
const marginalWeights = new DataTexture( toHalfFloatArray( new Float32Array( [ 0, 1 ] ) ), 1, 2 );
|
|
2631
|
+
marginalWeights.type = HalfFloatType;
|
|
2597
2632
|
marginalWeights.format = RedFormat;
|
|
2598
2633
|
marginalWeights.minFilter = LinearFilter;
|
|
2599
2634
|
marginalWeights.magFilter = LinearFilter;
|
|
@@ -2602,8 +2637,8 @@ class EquirectHdrInfoUniform {
|
|
|
2602
2637
|
|
|
2603
2638
|
// Stores a map of [0, 1] value -> cumulative importance column & pdf
|
|
2604
2639
|
// used to sampling a random value to a relevant pixel to sample from
|
|
2605
|
-
const conditionalWeights = new DataTexture( new Float32Array( [ 0, 0, 1, 1 ] ), 2, 2 );
|
|
2606
|
-
conditionalWeights.type =
|
|
2640
|
+
const conditionalWeights = new DataTexture( toHalfFloatArray( new Float32Array( [ 0, 0, 1, 1 ] ) ), 2, 2 );
|
|
2641
|
+
conditionalWeights.type = HalfFloatType;
|
|
2607
2642
|
conditionalWeights.format = RedFormat;
|
|
2608
2643
|
conditionalWeights.minFilter = LinearFilter;
|
|
2609
2644
|
conditionalWeights.magFilter = LinearFilter;
|
|
@@ -2653,9 +2688,9 @@ class EquirectHdrInfoUniform {
|
|
|
2653
2688
|
for ( let x = 0; x < width; x ++ ) {
|
|
2654
2689
|
|
|
2655
2690
|
const i = y * width + x;
|
|
2656
|
-
const r = data[ 4 * i + 0 ];
|
|
2657
|
-
const g = data[ 4 * i + 1 ];
|
|
2658
|
-
const b = data[ 4 * i + 2 ];
|
|
2691
|
+
const r = DataUtils.fromHalfFloat( data[ 4 * i + 0 ] );
|
|
2692
|
+
const g = DataUtils.fromHalfFloat( data[ 4 * i + 1 ] );
|
|
2693
|
+
const b = DataUtils.fromHalfFloat( data[ 4 * i + 2 ] );
|
|
2659
2694
|
|
|
2660
2695
|
// the probability of the pixel being selected in this row is the
|
|
2661
2696
|
// scale of the luminance relative to the rest of the pixels.
|
|
@@ -2707,8 +2742,8 @@ class EquirectHdrInfoUniform {
|
|
|
2707
2742
|
// the marginal and conditional data. These will be used to sample with a random number
|
|
2708
2743
|
// to retrieve a uv value to sample in the environment map.
|
|
2709
2744
|
// These values continually increase so it's okay to interpolate between them.
|
|
2710
|
-
const marginalDataArray = new
|
|
2711
|
-
const conditionalDataArray = new
|
|
2745
|
+
const marginalDataArray = new Uint16Array( height );
|
|
2746
|
+
const conditionalDataArray = new Uint16Array( width * height );
|
|
2712
2747
|
|
|
2713
2748
|
// we add a half texel offset so we're sampling the center of the pixel
|
|
2714
2749
|
for ( let i = 0; i < height; i ++ ) {
|
|
@@ -2716,7 +2751,7 @@ class EquirectHdrInfoUniform {
|
|
|
2716
2751
|
const dist = ( i + 1 ) / height;
|
|
2717
2752
|
const row = binarySearchFindClosestIndexOf( cdfMarginal, dist );
|
|
2718
2753
|
|
|
2719
|
-
marginalDataArray[ i ] = ( row + 0.5 ) / height;
|
|
2754
|
+
marginalDataArray[ i ] = DataUtils.toHalfFloat( ( row + 0.5 ) / height );
|
|
2720
2755
|
|
|
2721
2756
|
}
|
|
2722
2757
|
|
|
@@ -2728,7 +2763,7 @@ class EquirectHdrInfoUniform {
|
|
|
2728
2763
|
const dist = ( x + 1 ) / width;
|
|
2729
2764
|
const col = binarySearchFindClosestIndexOf( cdfConditional, dist, y * width, width );
|
|
2730
2765
|
|
|
2731
|
-
conditionalDataArray[ i ] = ( col + 0.5 ) / width;
|
|
2766
|
+
conditionalDataArray[ i ] = DataUtils.toHalfFloat( ( col + 0.5 ) / width );
|
|
2732
2767
|
|
|
2733
2768
|
}
|
|
2734
2769
|
|
|
@@ -2802,6 +2837,8 @@ class LightsInfoUniformStruct {
|
|
|
2802
2837
|
tex.wrapS = ClampToEdgeWrapping;
|
|
2803
2838
|
tex.wrapT = ClampToEdgeWrapping;
|
|
2804
2839
|
tex.generateMipmaps = false;
|
|
2840
|
+
tex.minFilter = NearestFilter;
|
|
2841
|
+
tex.magFilter = NearestFilter;
|
|
2805
2842
|
|
|
2806
2843
|
this.tex = tex;
|
|
2807
2844
|
this.count = 0;
|
|
@@ -2832,7 +2869,7 @@ class LightsInfoUniformStruct {
|
|
|
2832
2869
|
const worldQuaternion = new Quaternion();
|
|
2833
2870
|
const eye = new Vector3();
|
|
2834
2871
|
const target = new Vector3();
|
|
2835
|
-
const up = new Vector3();
|
|
2872
|
+
const up = new Vector3( 0, 1, 0 );
|
|
2836
2873
|
|
|
2837
2874
|
for ( let i = 0, l = lights.length; i < l; i ++ ) {
|
|
2838
2875
|
|
|
@@ -3275,7 +3312,7 @@ class IESLoader extends Loader {
|
|
|
3275
3312
|
loader.setPath( this.path );
|
|
3276
3313
|
loader.setRequestHeader( this.requestHeader );
|
|
3277
3314
|
|
|
3278
|
-
const texture = new DataTexture( null, 360, 180, RedFormat,
|
|
3315
|
+
const texture = new DataTexture( null, 360, 180, RedFormat, HalfFloatType );
|
|
3279
3316
|
texture.minFilter = LinearFilter;
|
|
3280
3317
|
texture.magFilter = LinearFilter;
|
|
3281
3318
|
|
|
@@ -3283,7 +3320,7 @@ class IESLoader extends Loader {
|
|
|
3283
3320
|
|
|
3284
3321
|
const iesLamp = new IESLamp( text );
|
|
3285
3322
|
|
|
3286
|
-
texture.image.data = this._getIESValues( iesLamp );
|
|
3323
|
+
texture.image.data = toHalfFloatArray( this._getIESValues( iesLamp ) );
|
|
3287
3324
|
texture.needsUpdate = true;
|
|
3288
3325
|
|
|
3289
3326
|
if ( onLoad !== undefined ) {
|
|
@@ -3301,10 +3338,10 @@ class IESLoader extends Loader {
|
|
|
3301
3338
|
parse( text ) {
|
|
3302
3339
|
|
|
3303
3340
|
const iesLamp = new IESLamp( text );
|
|
3304
|
-
const texture = new DataTexture( null, 360, 180, RedFormat,
|
|
3341
|
+
const texture = new DataTexture( null, 360, 180, RedFormat, HalfFloatType );
|
|
3305
3342
|
texture.minFilter = LinearFilter;
|
|
3306
3343
|
texture.magFilter = LinearFilter;
|
|
3307
|
-
texture.image.data = this._getIESValues( iesLamp );
|
|
3344
|
+
texture.image.data = toHalfFloatArray( this._getIESValues( iesLamp ) );
|
|
3308
3345
|
texture.needsUpdate = true;
|
|
3309
3346
|
|
|
3310
3347
|
return texture;
|
|
@@ -3322,7 +3359,7 @@ class IESProfilesTexture extends WebGLArrayRenderTarget {
|
|
|
3322
3359
|
|
|
3323
3360
|
const tex = this.texture;
|
|
3324
3361
|
tex.format = RGBAFormat;
|
|
3325
|
-
tex.type =
|
|
3362
|
+
tex.type = HalfFloatType;
|
|
3326
3363
|
tex.minFilter = LinearFilter;
|
|
3327
3364
|
tex.magFilter = LinearFilter;
|
|
3328
3365
|
tex.wrapS = ClampToEdgeWrapping;
|
|
@@ -3573,7 +3610,7 @@ class BlurredEnvMapGenerator {
|
|
|
3573
3610
|
this.renderer = renderer;
|
|
3574
3611
|
this.pmremGenerator = new PMREMGenerator( renderer );
|
|
3575
3612
|
this.copyQuad = new FullScreenQuad( new PMREMCopyMaterial() );
|
|
3576
|
-
this.renderTarget = new WebGLRenderTarget( 1, 1, { type:
|
|
3613
|
+
this.renderTarget = new WebGLRenderTarget( 1, 1, { type: HalfFloatType, format: RGBAFormat } );
|
|
3577
3614
|
|
|
3578
3615
|
}
|
|
3579
3616
|
|
|
@@ -3610,10 +3647,10 @@ class BlurredEnvMapGenerator {
|
|
|
3610
3647
|
renderer.autoClear = prevClear;
|
|
3611
3648
|
|
|
3612
3649
|
// read the data back
|
|
3613
|
-
const buffer = new
|
|
3650
|
+
const buffer = new Uint16Array( width * height * 4 );
|
|
3614
3651
|
renderer.readRenderTargetPixels( renderTarget, 0, 0, width, height, buffer );
|
|
3615
3652
|
|
|
3616
|
-
const result = new DataTexture( buffer, width, height, RGBAFormat,
|
|
3653
|
+
const result = new DataTexture( buffer, width, height, RGBAFormat, HalfFloatType );
|
|
3617
3654
|
result.minFilter = texture.minFilter;
|
|
3618
3655
|
result.magFilter = texture.magFilter;
|
|
3619
3656
|
result.wrapS = texture.wrapS;
|
|
@@ -3755,7 +3792,7 @@ class DenoiseMaterial extends MaterialBase {
|
|
|
3755
3792
|
|
|
3756
3793
|
gl_FragColor = smartDeNoise( map, vec2( vUv.x, vUv.y ), sigma, kSigma, threshold );
|
|
3757
3794
|
#include <tonemapping_fragment>
|
|
3758
|
-
#include <
|
|
3795
|
+
#include <colorspace_fragment>
|
|
3759
3796
|
#include <premultiplied_alpha_fragment>
|
|
3760
3797
|
|
|
3761
3798
|
}
|
|
@@ -3838,7 +3875,7 @@ class GradientMapMaterial extends MaterialBase {
|
|
|
3838
3875
|
gl_FragColor.rgb = vec3( mix( minColor, maxColor, t ) );
|
|
3839
3876
|
gl_FragColor.a = 1.0;
|
|
3840
3877
|
|
|
3841
|
-
#include <
|
|
3878
|
+
#include <colorspace_fragment>
|
|
3842
3879
|
|
|
3843
3880
|
}`,
|
|
3844
3881
|
|
|
@@ -4047,7 +4084,7 @@ class GraphMaterial extends MaterialBase {
|
|
|
4047
4084
|
|
|
4048
4085
|
}
|
|
4049
4086
|
|
|
4050
|
-
#include <
|
|
4087
|
+
#include <colorspace_fragment>
|
|
4051
4088
|
|
|
4052
4089
|
}
|
|
4053
4090
|
|
|
@@ -4389,7 +4426,18 @@ const lightsStructGLSL = /* glsl */`
|
|
|
4389
4426
|
|
|
4390
4427
|
l.coneCos = s5.r;
|
|
4391
4428
|
l.penumbraCos = s5.g;
|
|
4392
|
-
l.iesProfile = int( round
|
|
4429
|
+
l.iesProfile = int( round( s5.b ) );
|
|
4430
|
+
|
|
4431
|
+
} else {
|
|
4432
|
+
|
|
4433
|
+
l.radius = 0.0;
|
|
4434
|
+
l.near = 0.0;
|
|
4435
|
+
l.decay = 0.0;
|
|
4436
|
+
l.distance = 0.0;
|
|
4437
|
+
|
|
4438
|
+
l.coneCos = 0.0;
|
|
4439
|
+
l.penumbraCos = 0.0;
|
|
4440
|
+
l.iesProfile = - 1;
|
|
4393
4441
|
|
|
4394
4442
|
}
|
|
4395
4443
|
|
|
@@ -4585,21 +4633,22 @@ const materialStructGLSL = /* glsl */ `
|
|
|
4585
4633
|
|
|
4586
4634
|
uint firstTextureTransformIdx = i + 15u;
|
|
4587
4635
|
|
|
4588
|
-
|
|
4589
|
-
m.
|
|
4590
|
-
m.
|
|
4591
|
-
m.
|
|
4592
|
-
m.
|
|
4593
|
-
m.
|
|
4594
|
-
m.
|
|
4595
|
-
m.
|
|
4596
|
-
m.
|
|
4597
|
-
m.
|
|
4598
|
-
m.
|
|
4599
|
-
m.
|
|
4600
|
-
m.
|
|
4601
|
-
m.
|
|
4602
|
-
m.
|
|
4636
|
+
// mat3( 1.0 ) is an identity matrix
|
|
4637
|
+
m.mapTransform = m.map == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx );
|
|
4638
|
+
m.metalnessMapTransform = m.metalnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 2u );
|
|
4639
|
+
m.roughnessMapTransform = m.roughnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 4u );
|
|
4640
|
+
m.transmissionMapTransform = m.transmissionMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 6u );
|
|
4641
|
+
m.emissiveMapTransform = m.emissiveMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 8u );
|
|
4642
|
+
m.normalMapTransform = m.normalMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 10u );
|
|
4643
|
+
m.clearcoatMapTransform = m.clearcoatMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 12u );
|
|
4644
|
+
m.clearcoatNormalMapTransform = m.clearcoatNormalMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 14u );
|
|
4645
|
+
m.clearcoatRoughnessMapTransform = m.clearcoatRoughnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 16u );
|
|
4646
|
+
m.sheenColorMapTransform = m.sheenColorMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 18u );
|
|
4647
|
+
m.sheenRoughnessMapTransform = m.sheenRoughnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 20u );
|
|
4648
|
+
m.iridescenceMapTransform = m.iridescenceMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 22u );
|
|
4649
|
+
m.iridescenceThicknessMapTransform = m.iridescenceThicknessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 24u );
|
|
4650
|
+
m.specularColorMapTransform = m.specularColorMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 26u );
|
|
4651
|
+
m.specularIntensityMapTransform = m.specularIntensityMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 28u );
|
|
4603
4652
|
|
|
4604
4653
|
return m;
|
|
4605
4654
|
|
|
@@ -4624,9 +4673,9 @@ bool isMaterialFogVolume( sampler2D materials, uint materialIndex ) {
|
|
|
4624
4673
|
|
|
4625
4674
|
// returns true if we're within the first fog volume we hit
|
|
4626
4675
|
bool bvhIntersectFogVolumeHit(
|
|
4627
|
-
|
|
4676
|
+
vec3 rayOrigin, vec3 rayDirection,
|
|
4628
4677
|
usampler2D materialIndexAttribute, sampler2D materials,
|
|
4629
|
-
|
|
4678
|
+
inout Material material
|
|
4630
4679
|
) {
|
|
4631
4680
|
|
|
4632
4681
|
material.fogVolume = false;
|
|
@@ -5086,7 +5135,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5086
5135
|
${ iridescenceGLSL }
|
|
5087
5136
|
|
|
5088
5137
|
// diffuse
|
|
5089
|
-
float diffuseEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf,
|
|
5138
|
+
float diffuseEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, inout vec3 color ) {
|
|
5090
5139
|
|
|
5091
5140
|
// https://schuttejoe.github.io/post/disneybsdf/
|
|
5092
5141
|
float fl = schlickFresnel( wi.z, 0.0 );
|
|
@@ -5117,7 +5166,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5117
5166
|
}
|
|
5118
5167
|
|
|
5119
5168
|
// specular
|
|
5120
|
-
float specularEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf,
|
|
5169
|
+
float specularEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, inout vec3 color ) {
|
|
5121
5170
|
|
|
5122
5171
|
// if roughness is set to 0 then D === NaN which results in black pixels
|
|
5123
5172
|
float metalness = surf.metalness;
|
|
@@ -5164,7 +5213,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5164
5213
|
|
|
5165
5214
|
// transmission
|
|
5166
5215
|
/*
|
|
5167
|
-
float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf,
|
|
5216
|
+
float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, inout vec3 color ) {
|
|
5168
5217
|
|
|
5169
5218
|
// See section 4.2 in https://www.cs.cornell.edu/~srm/publications/EGSR07-btdf.pdf
|
|
5170
5219
|
|
|
@@ -5207,7 +5256,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5207
5256
|
|
|
5208
5257
|
// TODO: This is just using a basic cosine-weighted specular distribution with an
|
|
5209
5258
|
// incorrect PDF value at the moment. Update it to correctly use a GGX distribution
|
|
5210
|
-
float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf,
|
|
5259
|
+
float transmissionEval( vec3 wo, vec3 wi, vec3 wh, SurfaceRecord surf, inout vec3 color ) {
|
|
5211
5260
|
|
|
5212
5261
|
color = surf.transmission * surf.color;
|
|
5213
5262
|
|
|
@@ -5298,7 +5347,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5298
5347
|
// bsdf
|
|
5299
5348
|
void getLobeWeights(
|
|
5300
5349
|
vec3 wo, vec3 wi, vec3 wh, vec3 clearcoatWo, SurfaceRecord surf,
|
|
5301
|
-
|
|
5350
|
+
inout float diffuseWeight, inout float specularWeight, inout float transmissionWeight, inout float clearcoatWeight
|
|
5302
5351
|
) {
|
|
5303
5352
|
|
|
5304
5353
|
float metalness = surf.metalness;
|
|
@@ -5322,7 +5371,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5322
5371
|
|
|
5323
5372
|
float bsdfEval(
|
|
5324
5373
|
vec3 wo, vec3 clearcoatWo, vec3 wi, vec3 clearcoatWi, SurfaceRecord surf,
|
|
5325
|
-
float diffuseWeight, float specularWeight, float transmissionWeight, float clearcoatWeight,
|
|
5374
|
+
float diffuseWeight, float specularWeight, float transmissionWeight, float clearcoatWeight, inout float specularPdf, inout vec3 color
|
|
5326
5375
|
) {
|
|
5327
5376
|
|
|
5328
5377
|
float metalness = surf.metalness;
|
|
@@ -5385,7 +5434,7 @@ const bsdfSamplingGLSL = /* glsl */`
|
|
|
5385
5434
|
|
|
5386
5435
|
}
|
|
5387
5436
|
|
|
5388
|
-
float bsdfResult( vec3 worldWo, vec3 worldWi, SurfaceRecord surf,
|
|
5437
|
+
float bsdfResult( vec3 worldWo, vec3 worldWi, SurfaceRecord surf, inout vec3 color ) {
|
|
5389
5438
|
|
|
5390
5439
|
if ( surf.volumeParticle ) {
|
|
5391
5440
|
|
|
@@ -5553,14 +5602,14 @@ const equirectSamplingGLSL = /* glsl */`
|
|
|
5553
5602
|
}
|
|
5554
5603
|
|
|
5555
5604
|
// samples the color given env map with CDF and returns the pdf of the direction
|
|
5556
|
-
float sampleEquirect(
|
|
5605
|
+
float sampleEquirect( vec3 direction, inout vec3 color ) {
|
|
5557
5606
|
|
|
5558
5607
|
vec2 uv = equirectDirectionToUv( direction );
|
|
5559
|
-
color = texture2D(
|
|
5608
|
+
color = texture2D( envMapInfo.map, uv ).rgb;
|
|
5560
5609
|
|
|
5561
|
-
float totalSum =
|
|
5610
|
+
float totalSum = envMapInfo.totalSum;
|
|
5562
5611
|
float lum = luminance( color );
|
|
5563
|
-
ivec2 resolution = textureSize(
|
|
5612
|
+
ivec2 resolution = textureSize( envMapInfo.map, 0 );
|
|
5564
5613
|
float pdf = lum / totalSum;
|
|
5565
5614
|
|
|
5566
5615
|
return float( resolution.x * resolution.y ) * pdf * equirectDirectionPdf( direction );
|
|
@@ -5568,20 +5617,20 @@ const equirectSamplingGLSL = /* glsl */`
|
|
|
5568
5617
|
}
|
|
5569
5618
|
|
|
5570
5619
|
// samples a direction of the envmap with color and retrieves pdf
|
|
5571
|
-
float sampleEquirectProbability(
|
|
5620
|
+
float sampleEquirectProbability( vec2 r, inout vec3 color, inout vec3 direction ) {
|
|
5572
5621
|
|
|
5573
5622
|
// sample env map cdf
|
|
5574
|
-
float v = texture2D(
|
|
5575
|
-
float u = texture2D(
|
|
5623
|
+
float v = texture2D( envMapInfo.marginalWeights, vec2( r.x, 0.0 ) ).x;
|
|
5624
|
+
float u = texture2D( envMapInfo.conditionalWeights, vec2( r.y, v ) ).x;
|
|
5576
5625
|
vec2 uv = vec2( u, v );
|
|
5577
5626
|
|
|
5578
5627
|
vec3 derivedDirection = equirectUvToDirection( uv );
|
|
5579
5628
|
direction = derivedDirection;
|
|
5580
|
-
color = texture2D(
|
|
5629
|
+
color = texture2D( envMapInfo.map, uv ).rgb;
|
|
5581
5630
|
|
|
5582
|
-
float totalSum =
|
|
5631
|
+
float totalSum = envMapInfo.totalSum;
|
|
5583
5632
|
float lum = luminance( color );
|
|
5584
|
-
ivec2 resolution = textureSize(
|
|
5633
|
+
ivec2 resolution = textureSize( envMapInfo.map, 0 );
|
|
5585
5634
|
float pdf = lum / totalSum;
|
|
5586
5635
|
|
|
5587
5636
|
return float( resolution.x * resolution.y ) * pdf * equirectDirectionPdf( direction );
|
|
@@ -5634,24 +5683,17 @@ const lightSamplingGLSL = /* glsl */`
|
|
|
5634
5683
|
|
|
5635
5684
|
};
|
|
5636
5685
|
|
|
5637
|
-
bool
|
|
5686
|
+
bool intersectLightAtIndex( sampler2D lights, vec3 rayOrigin, vec3 rayDirection, uint l, inout LightRecord lightRec ) {
|
|
5638
5687
|
|
|
5639
5688
|
bool didHit = false;
|
|
5640
|
-
|
|
5641
|
-
for ( l = 0u; l < lightCount; l ++ ) {
|
|
5642
|
-
|
|
5643
|
-
Light light = readLightInfo( lights, l );
|
|
5644
|
-
|
|
5645
|
-
vec3 u = light.u;
|
|
5646
|
-
vec3 v = light.v;
|
|
5647
|
-
|
|
5648
|
-
// check for backface
|
|
5649
|
-
vec3 normal = normalize( cross( u, v ) );
|
|
5650
|
-
if ( dot( normal, rayDirection ) < 0.0 ) {
|
|
5689
|
+
Light light = readLightInfo( lights, l );
|
|
5651
5690
|
|
|
5652
|
-
|
|
5691
|
+
vec3 u = light.u;
|
|
5692
|
+
vec3 v = light.v;
|
|
5653
5693
|
|
|
5654
|
-
|
|
5694
|
+
// check for backface
|
|
5695
|
+
vec3 normal = normalize( cross( u, v ) );
|
|
5696
|
+
if ( dot( normal, rayDirection ) > 0.0 ) {
|
|
5655
5697
|
|
|
5656
5698
|
u *= 1.0 / dot( u, u );
|
|
5657
5699
|
v *= 1.0 / dot( v, v );
|
|
@@ -5664,17 +5706,13 @@ const lightSamplingGLSL = /* glsl */`
|
|
|
5664
5706
|
( light.type == CIRC_AREA_LIGHT_TYPE && intersectsCircle( light.position, normal, u, v, rayOrigin, rayDirection, dist ) )
|
|
5665
5707
|
) {
|
|
5666
5708
|
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
lightRec.direction = rayDirection;
|
|
5675
|
-
lightRec.type = light.type;
|
|
5676
|
-
|
|
5677
|
-
}
|
|
5709
|
+
float cosTheta = dot( rayDirection, normal );
|
|
5710
|
+
didHit = true;
|
|
5711
|
+
lightRec.dist = dist;
|
|
5712
|
+
lightRec.pdf = ( dist * dist ) / ( light.area * cosTheta );
|
|
5713
|
+
lightRec.emission = light.color * light.intensity;
|
|
5714
|
+
lightRec.direction = rayDirection;
|
|
5715
|
+
lightRec.type = light.type;
|
|
5678
5716
|
|
|
5679
5717
|
}
|
|
5680
5718
|
|
|
@@ -5686,11 +5724,6 @@ const lightSamplingGLSL = /* glsl */`
|
|
|
5686
5724
|
|
|
5687
5725
|
LightRecord randomAreaLightSample( Light light, vec3 rayOrigin, vec2 ruv ) {
|
|
5688
5726
|
|
|
5689
|
-
LightRecord lightRec;
|
|
5690
|
-
lightRec.type = light.type;
|
|
5691
|
-
|
|
5692
|
-
lightRec.emission = light.color * light.intensity;
|
|
5693
|
-
|
|
5694
5727
|
vec3 randomPos;
|
|
5695
5728
|
if( light.type == RECT_AREA_LIGHT_TYPE ) {
|
|
5696
5729
|
|
|
@@ -5711,12 +5744,17 @@ const lightSamplingGLSL = /* glsl */`
|
|
|
5711
5744
|
|
|
5712
5745
|
vec3 toLight = randomPos - rayOrigin;
|
|
5713
5746
|
float lightDistSq = dot( toLight, toLight );
|
|
5714
|
-
|
|
5747
|
+
float dist = sqrt( lightDistSq );
|
|
5748
|
+
vec3 direction = toLight / dist;
|
|
5749
|
+
vec3 lightNormal = normalize( cross( light.u, light.v ) );
|
|
5715
5750
|
|
|
5716
|
-
|
|
5751
|
+
LightRecord lightRec;
|
|
5752
|
+
lightRec.type = light.type;
|
|
5753
|
+
lightRec.emission = light.color * light.intensity;
|
|
5754
|
+
lightRec.dist = dist;
|
|
5717
5755
|
lightRec.direction = direction;
|
|
5718
5756
|
|
|
5719
|
-
|
|
5757
|
+
// TODO: the denominator is potentially zero
|
|
5720
5758
|
lightRec.pdf = lightDistSq / ( light.area * dot( direction, lightNormal ) );
|
|
5721
5759
|
|
|
5722
5760
|
return lightRec;
|
|
@@ -5764,13 +5802,15 @@ const lightSamplingGLSL = /* glsl */`
|
|
|
5764
5802
|
|
|
5765
5803
|
LightRecord randomLightSample( sampler2D lights, sampler2DArray iesProfiles, uint lightCount, vec3 rayOrigin, vec3 ruv ) {
|
|
5766
5804
|
|
|
5805
|
+
LightRecord result;
|
|
5806
|
+
|
|
5767
5807
|
// pick a random light
|
|
5768
5808
|
uint l = uint( ruv.x * float( lightCount ) );
|
|
5769
5809
|
Light light = readLightInfo( lights, l );
|
|
5770
5810
|
|
|
5771
5811
|
if ( light.type == SPOT_LIGHT_TYPE ) {
|
|
5772
5812
|
|
|
5773
|
-
|
|
5813
|
+
result = randomSpotLightSample( light, iesProfiles, rayOrigin, ruv.yz );
|
|
5774
5814
|
|
|
5775
5815
|
} else if ( light.type == POINT_LIGHT_TYPE ) {
|
|
5776
5816
|
|
|
@@ -5790,7 +5830,7 @@ const lightSamplingGLSL = /* glsl */`
|
|
|
5790
5830
|
rec.pdf = 1.0;
|
|
5791
5831
|
rec.emission = light.color * light.intensity * distanceFalloff;
|
|
5792
5832
|
rec.type = light.type;
|
|
5793
|
-
|
|
5833
|
+
result = rec;
|
|
5794
5834
|
|
|
5795
5835
|
} else if ( light.type == DIR_LIGHT_TYPE ) {
|
|
5796
5836
|
|
|
@@ -5801,15 +5841,17 @@ const lightSamplingGLSL = /* glsl */`
|
|
|
5801
5841
|
rec.emission = light.color * light.intensity;
|
|
5802
5842
|
rec.type = light.type;
|
|
5803
5843
|
|
|
5804
|
-
|
|
5844
|
+
result = rec;
|
|
5805
5845
|
|
|
5806
5846
|
} else {
|
|
5807
5847
|
|
|
5808
5848
|
// sample the light
|
|
5809
|
-
|
|
5849
|
+
result = randomAreaLightSample( light, rayOrigin, ruv.yz );
|
|
5810
5850
|
|
|
5811
5851
|
}
|
|
5812
5852
|
|
|
5853
|
+
return result;
|
|
5854
|
+
|
|
5813
5855
|
}
|
|
5814
5856
|
|
|
5815
5857
|
`;
|
|
@@ -5906,7 +5948,7 @@ const intersectShapesGLSL = /* glsl */`
|
|
|
5906
5948
|
// Finds the point where the ray intersects the plane defined by u and v and checks if this point
|
|
5907
5949
|
// falls in the bounds of the rectangle on that same plane.
|
|
5908
5950
|
// Plane intersection: https://lousodrome.net/blog/light/2020/07/03/intersection-of-a-ray-and-a-plane/
|
|
5909
|
-
bool intersectsRectangle( vec3 center, vec3 normal, vec3 u, vec3 v, vec3 rayOrigin, vec3 rayDirection,
|
|
5951
|
+
bool intersectsRectangle( vec3 center, vec3 normal, vec3 u, vec3 v, vec3 rayOrigin, vec3 rayDirection, inout float dist ) {
|
|
5910
5952
|
|
|
5911
5953
|
float t = dot( center - rayOrigin, normal ) / dot( rayDirection, normal );
|
|
5912
5954
|
|
|
@@ -5937,7 +5979,7 @@ const intersectShapesGLSL = /* glsl */`
|
|
|
5937
5979
|
|
|
5938
5980
|
// Finds the point where the ray intersects the plane defined by u and v and checks if this point
|
|
5939
5981
|
// falls in the bounds of the circle on that same plane. See above URL for a description of the plane intersection algorithm.
|
|
5940
|
-
bool intersectsCircle( vec3 position, vec3 normal, vec3 u, vec3 v, vec3 rayOrigin, vec3 rayDirection,
|
|
5982
|
+
bool intersectsCircle( vec3 position, vec3 normal, vec3 u, vec3 v, vec3 rayOrigin, vec3 rayDirection, inout float dist ) {
|
|
5941
5983
|
|
|
5942
5984
|
float t = dot( position - rayOrigin, normal ) / dot( rayDirection, normal );
|
|
5943
5985
|
|
|
@@ -6369,7 +6411,7 @@ const attenuateHitGLSL = /* glsl */`
|
|
|
6369
6411
|
// step through multiple surface hits and accumulate color attenuation based on transmissive surfaces
|
|
6370
6412
|
// returns true if a solid surface was hit
|
|
6371
6413
|
bool attenuateHit(
|
|
6372
|
-
|
|
6414
|
+
RenderState state,
|
|
6373
6415
|
Ray ray, float rayDist,
|
|
6374
6416
|
out vec3 color
|
|
6375
6417
|
) {
|
|
@@ -6383,7 +6425,6 @@ const attenuateHitGLSL = /* glsl */`
|
|
|
6383
6425
|
|
|
6384
6426
|
// hit results
|
|
6385
6427
|
SurfaceHit surfaceHit;
|
|
6386
|
-
LightRecord lightRec;
|
|
6387
6428
|
|
|
6388
6429
|
color = vec3( 1.0 );
|
|
6389
6430
|
|
|
@@ -6391,20 +6432,12 @@ const attenuateHitGLSL = /* glsl */`
|
|
|
6391
6432
|
// and then reset.
|
|
6392
6433
|
for ( int i = 0; i < traversals; i ++ ) {
|
|
6393
6434
|
|
|
6394
|
-
int hitType = traceScene(
|
|
6395
|
-
ray, bvh, lights, fogMaterial,
|
|
6396
|
-
surfaceHit, lightRec
|
|
6397
|
-
);
|
|
6435
|
+
int hitType = traceScene( ray, fogMaterial, surfaceHit );
|
|
6398
6436
|
|
|
6399
6437
|
if ( hitType == FOG_HIT ) {
|
|
6400
6438
|
|
|
6401
6439
|
return true;
|
|
6402
6440
|
|
|
6403
|
-
} else if ( hitType == LIGHT_HIT ) {
|
|
6404
|
-
|
|
6405
|
-
float totalDist = distance( startPoint, ray.origin + ray.direction * lightRec.dist );
|
|
6406
|
-
return totalDist < rayDist - max( totalDist, rayDist ) * 1e-4;
|
|
6407
|
-
|
|
6408
6441
|
} else if ( hitType == SURFACE_HIT ) {
|
|
6409
6442
|
|
|
6410
6443
|
float totalDist = distance( startPoint, ray.origin + ray.direction * surfaceHit.dist );
|
|
@@ -6549,22 +6582,26 @@ const traceSceneGLSL = /* glsl */`
|
|
|
6549
6582
|
#define LIGHT_HIT 2
|
|
6550
6583
|
#define FOG_HIT 3
|
|
6551
6584
|
|
|
6585
|
+
// Passing the global variable 'lights' into this function caused shader program errors.
|
|
6586
|
+
// So global variables like 'lights' and 'bvh' were moved out of the function parameters.
|
|
6587
|
+
// For more information, refer to: https://github.com/gkjohnson/three-gpu-pathtracer/pull/457
|
|
6552
6588
|
int traceScene(
|
|
6553
6589
|
|
|
6554
|
-
Ray ray,
|
|
6555
|
-
out SurfaceHit surfaceHit, out LightRecord lightRec
|
|
6590
|
+
Ray ray, Material fogMaterial, inout SurfaceHit surfaceHit
|
|
6556
6591
|
|
|
6557
6592
|
) {
|
|
6558
6593
|
|
|
6594
|
+
int result = NO_HIT;
|
|
6559
6595
|
bool hit = bvhIntersectFirstHit( bvh, ray.origin, ray.direction, surfaceHit.faceIndices, surfaceHit.faceNormal, surfaceHit.barycoord, surfaceHit.side, surfaceHit.dist );
|
|
6560
|
-
bool lightHit = lightsClosestHit( lights.tex, lights.count, ray.origin, ray.direction, lightRec );
|
|
6561
6596
|
|
|
6562
6597
|
#if FEATURE_FOG
|
|
6563
6598
|
|
|
6564
6599
|
if ( fogMaterial.fogVolume ) {
|
|
6565
6600
|
|
|
6601
|
+
// offset the distance so we don't run into issues with particles on the same surface
|
|
6602
|
+
// as other objects
|
|
6566
6603
|
float particleDist = intersectFogVolume( fogMaterial, sobol( 1 ) );
|
|
6567
|
-
if ( particleDist +
|
|
6604
|
+
if ( particleDist + RAY_OFFSET < surfaceHit.dist ) {
|
|
6568
6605
|
|
|
6569
6606
|
surfaceHit.side = 1.0;
|
|
6570
6607
|
surfaceHit.faceNormal = normalize( - ray.direction );
|
|
@@ -6577,19 +6614,13 @@ const traceSceneGLSL = /* glsl */`
|
|
|
6577
6614
|
|
|
6578
6615
|
#endif
|
|
6579
6616
|
|
|
6580
|
-
if ( lightHit && ( lightRec.dist < surfaceHit.dist || ! hit ) ) {
|
|
6581
|
-
|
|
6582
|
-
return LIGHT_HIT;
|
|
6583
|
-
|
|
6584
|
-
}
|
|
6585
|
-
|
|
6586
6617
|
if ( hit ) {
|
|
6587
6618
|
|
|
6588
|
-
|
|
6619
|
+
result = SURFACE_HIT;
|
|
6589
6620
|
|
|
6590
6621
|
}
|
|
6591
6622
|
|
|
6592
|
-
return
|
|
6623
|
+
return result;
|
|
6593
6624
|
|
|
6594
6625
|
}
|
|
6595
6626
|
|
|
@@ -6602,7 +6633,7 @@ const getSurfaceRecordGLSL = /* glsl */`
|
|
|
6602
6633
|
int getSurfaceRecord(
|
|
6603
6634
|
Material material, SurfaceHit surfaceHit, sampler2DArray attributesArray,
|
|
6604
6635
|
float accumulatedRoughness,
|
|
6605
|
-
|
|
6636
|
+
inout SurfaceRecord surf
|
|
6606
6637
|
) {
|
|
6607
6638
|
|
|
6608
6639
|
if ( material.fogVolume ) {
|
|
@@ -6922,6 +6953,8 @@ const directLightContributionGLSL = /*glsl*/`
|
|
|
6922
6953
|
|
|
6923
6954
|
vec3 directLightContribution( vec3 worldWo, SurfaceRecord surf, RenderState state, vec3 rayOrigin ) {
|
|
6924
6955
|
|
|
6956
|
+
vec3 result = vec3( 0.0 );
|
|
6957
|
+
|
|
6925
6958
|
// uniformly pick a light or environment map
|
|
6926
6959
|
if( lightsDenom != 0.0 && sobol( 5 ) < float( lights.count ) / lightsDenom ) {
|
|
6927
6960
|
|
|
@@ -6943,7 +6976,7 @@ const directLightContributionGLSL = /*glsl*/`
|
|
|
6943
6976
|
if (
|
|
6944
6977
|
lightRec.pdf > 0.0 &&
|
|
6945
6978
|
isDirectionValid( lightRec.direction, surf.normal, surf.faceNormal ) &&
|
|
6946
|
-
! attenuateHit(
|
|
6979
|
+
! attenuateHit( state, lightRay, lightRec.dist, attenuatedColor )
|
|
6947
6980
|
) {
|
|
6948
6981
|
|
|
6949
6982
|
// get the material pdf
|
|
@@ -6955,7 +6988,7 @@ const directLightContributionGLSL = /*glsl*/`
|
|
|
6955
6988
|
// weight the direct light contribution
|
|
6956
6989
|
float lightPdf = lightRec.pdf / lightsDenom;
|
|
6957
6990
|
float misWeight = lightRec.type == SPOT_LIGHT_TYPE || lightRec.type == DIR_LIGHT_TYPE || lightRec.type == POINT_LIGHT_TYPE ? 1.0 : misHeuristic( lightPdf, lightMaterialPdf );
|
|
6958
|
-
|
|
6991
|
+
result = attenuatedColor * lightRec.emission * state.throughputColor * sampleColor * misWeight / lightPdf;
|
|
6959
6992
|
|
|
6960
6993
|
}
|
|
6961
6994
|
|
|
@@ -6965,7 +6998,7 @@ const directLightContributionGLSL = /*glsl*/`
|
|
|
6965
6998
|
|
|
6966
6999
|
// find a sample in the environment map to include in the contribution
|
|
6967
7000
|
vec3 envColor, envDirection;
|
|
6968
|
-
float envPdf = sampleEquirectProbability(
|
|
7001
|
+
float envPdf = sampleEquirectProbability( sobol2( 7 ), envColor, envDirection );
|
|
6969
7002
|
envDirection = invEnvRotation3x3 * envDirection;
|
|
6970
7003
|
|
|
6971
7004
|
// this env sampling is not set up for transmissive sampling and yields overly bright
|
|
@@ -6986,7 +7019,7 @@ const directLightContributionGLSL = /*glsl*/`
|
|
|
6986
7019
|
if (
|
|
6987
7020
|
envPdf > 0.0 &&
|
|
6988
7021
|
isDirectionValid( envDirection, surf.normal, surf.faceNormal ) &&
|
|
6989
|
-
! attenuateHit(
|
|
7022
|
+
! attenuateHit( state, envRay, INFINITY, attenuatedColor )
|
|
6990
7023
|
) {
|
|
6991
7024
|
|
|
6992
7025
|
// get the material pdf
|
|
@@ -6998,7 +7031,7 @@ const directLightContributionGLSL = /*glsl*/`
|
|
|
6998
7031
|
// weight the direct light contribution
|
|
6999
7032
|
envPdf /= lightsDenom;
|
|
7000
7033
|
float misWeight = misHeuristic( envPdf, envMaterialPdf );
|
|
7001
|
-
|
|
7034
|
+
result = attenuatedColor * environmentIntensity * envColor * state.throughputColor * sampleColor * misWeight / envPdf;
|
|
7002
7035
|
|
|
7003
7036
|
}
|
|
7004
7037
|
|
|
@@ -7006,7 +7039,9 @@ const directLightContributionGLSL = /*glsl*/`
|
|
|
7006
7039
|
|
|
7007
7040
|
}
|
|
7008
7041
|
|
|
7009
|
-
return
|
|
7042
|
+
// Function changed to have a single return statement to potentially help with crashes on Mac OS.
|
|
7043
|
+
// See issue #470
|
|
7044
|
+
return result;
|
|
7010
7045
|
|
|
7011
7046
|
}
|
|
7012
7047
|
|
|
@@ -7035,6 +7070,7 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7035
7070
|
FEATURE_DOF: 1,
|
|
7036
7071
|
FEATURE_BACKGROUND_MAP: 0,
|
|
7037
7072
|
FEATURE_FOG: 1,
|
|
7073
|
+
FEATURE_SOBOL: 0,
|
|
7038
7074
|
// 0 = Perspective
|
|
7039
7075
|
// 1 = Orthographic
|
|
7040
7076
|
// 2 = Equirectangular
|
|
@@ -7104,13 +7140,36 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7104
7140
|
#include <common>
|
|
7105
7141
|
|
|
7106
7142
|
// bvh intersection
|
|
7107
|
-
${
|
|
7108
|
-
${
|
|
7143
|
+
${ BVHShaderGLSL.common_functions }
|
|
7144
|
+
${ BVHShaderGLSL.bvh_struct_definitions }
|
|
7145
|
+
${ BVHShaderGLSL.bvh_ray_functions }
|
|
7146
|
+
|
|
7147
|
+
// uniform structs
|
|
7148
|
+
${ cameraStructGLSL }
|
|
7149
|
+
${ lightsStructGLSL }
|
|
7150
|
+
${ equirectStructGLSL }
|
|
7151
|
+
${ materialStructGLSL }
|
|
7109
7152
|
|
|
7110
7153
|
// random
|
|
7111
7154
|
${ pcgGLSL }
|
|
7112
|
-
|
|
7113
|
-
|
|
7155
|
+
#if FEATURE_SOBOL
|
|
7156
|
+
|
|
7157
|
+
${ sobolCommonGLSL }
|
|
7158
|
+
${ sobolSamplingGLSL }
|
|
7159
|
+
|
|
7160
|
+
#else
|
|
7161
|
+
|
|
7162
|
+
// Using the sobol functions seems to break the the compiler on MacOS
|
|
7163
|
+
// - specifically the "sobolReverseBits" function.
|
|
7164
|
+
uint sobolPixelIndex = 0u;
|
|
7165
|
+
uint sobolPathIndex = 0u;
|
|
7166
|
+
uint sobolBounceIndex = 0u;
|
|
7167
|
+
float sobol( int v ) { return rand(); }
|
|
7168
|
+
vec2 sobol2( int v ) { return rand2(); }
|
|
7169
|
+
vec3 sobol3( int v ) { return rand3(); }
|
|
7170
|
+
vec4 sobol4( int v ) { return rand4(); }
|
|
7171
|
+
|
|
7172
|
+
#endif
|
|
7114
7173
|
|
|
7115
7174
|
// common
|
|
7116
7175
|
${ arraySamplerTexelFetchGLSL }
|
|
@@ -7119,20 +7178,6 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7119
7178
|
${ mathGLSL }
|
|
7120
7179
|
${ intersectShapesGLSL }
|
|
7121
7180
|
|
|
7122
|
-
// uniform structs
|
|
7123
|
-
${ cameraStructGLSL }
|
|
7124
|
-
${ lightsStructGLSL }
|
|
7125
|
-
${ equirectStructGLSL }
|
|
7126
|
-
${ materialStructGLSL }
|
|
7127
|
-
${ fogMaterialBvhGLSL }
|
|
7128
|
-
|
|
7129
|
-
// sampling
|
|
7130
|
-
${ shapeSamplingGLSL }
|
|
7131
|
-
${ bsdfSamplingGLSL }
|
|
7132
|
-
${ equirectSamplingGLSL }
|
|
7133
|
-
${ lightSamplingGLSL }
|
|
7134
|
-
${ fogGLSL }
|
|
7135
|
-
|
|
7136
7181
|
// environment
|
|
7137
7182
|
uniform EquirectHdrInfo envMapInfo;
|
|
7138
7183
|
uniform mat4 environmentRotation;
|
|
@@ -7184,6 +7229,14 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7184
7229
|
mat3 invEnvRotation3x3;
|
|
7185
7230
|
float lightsDenom;
|
|
7186
7231
|
|
|
7232
|
+
// sampling
|
|
7233
|
+
${ fogMaterialBvhGLSL }
|
|
7234
|
+
${ shapeSamplingGLSL }
|
|
7235
|
+
${ bsdfSamplingGLSL }
|
|
7236
|
+
${ equirectSamplingGLSL }
|
|
7237
|
+
${ lightSamplingGLSL }
|
|
7238
|
+
${ fogGLSL }
|
|
7239
|
+
|
|
7187
7240
|
float applyFilteredGlossy( float roughness, float accumulatedRoughness ) {
|
|
7188
7241
|
|
|
7189
7242
|
return clamp(
|
|
@@ -7223,7 +7276,7 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7223
7276
|
|
|
7224
7277
|
// init
|
|
7225
7278
|
rng_initialize( gl_FragCoord.xy, seed );
|
|
7226
|
-
sobolPixelIndex = ( uint( gl_FragCoord.x ) << 16 ) |
|
|
7279
|
+
sobolPixelIndex = ( uint( gl_FragCoord.x ) << 16 ) | uint( gl_FragCoord.y );
|
|
7227
7280
|
sobolPathIndex = uint( seed );
|
|
7228
7281
|
|
|
7229
7282
|
// get camera ray
|
|
@@ -7239,7 +7292,6 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7239
7292
|
|
|
7240
7293
|
// surface results
|
|
7241
7294
|
SurfaceHit surfaceHit;
|
|
7242
|
-
LightRecord lightRec;
|
|
7243
7295
|
ScatterRecord scatterRec;
|
|
7244
7296
|
|
|
7245
7297
|
// path tracing state
|
|
@@ -7248,7 +7300,7 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7248
7300
|
#if FEATURE_FOG
|
|
7249
7301
|
|
|
7250
7302
|
state.fogMaterial.fogVolume = bvhIntersectFogVolumeHit(
|
|
7251
|
-
|
|
7303
|
+
ray.origin, - ray.direction,
|
|
7252
7304
|
materialIndexAttribute, materials,
|
|
7253
7305
|
state.fogMaterial
|
|
7254
7306
|
);
|
|
@@ -7263,44 +7315,42 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7263
7315
|
state.traversals = bounces - i;
|
|
7264
7316
|
state.firstRay = i == 0 && state.transmissiveTraversals == transmissiveBounces;
|
|
7265
7317
|
|
|
7266
|
-
int hitType = traceScene(
|
|
7267
|
-
ray, bvh, lights, state.fogMaterial,
|
|
7268
|
-
surfaceHit, lightRec
|
|
7269
|
-
);
|
|
7318
|
+
int hitType = traceScene( ray, state.fogMaterial, surfaceHit );
|
|
7270
7319
|
|
|
7271
|
-
if
|
|
7320
|
+
// check if we intersect any lights and accumulate the light contribution
|
|
7321
|
+
// TODO: we can add support for light surface rendering in the else condition if we
|
|
7322
|
+
// add the ability to toggle visibility of the the light
|
|
7323
|
+
if ( ! state.firstRay && ! state.transmissiveRay ) {
|
|
7272
7324
|
|
|
7273
|
-
|
|
7325
|
+
LightRecord lightRec;
|
|
7326
|
+
float lightDist = hitType == NO_HIT ? INFINITY : surfaceHit.dist;
|
|
7327
|
+
for ( uint i = 0u; i < lights.count; i ++ ) {
|
|
7274
7328
|
|
|
7275
|
-
|
|
7329
|
+
if (
|
|
7330
|
+
intersectLightAtIndex( lights.tex, ray.origin, ray.direction, i, lightRec ) &&
|
|
7331
|
+
lightRec.dist < lightDist
|
|
7332
|
+
) {
|
|
7276
7333
|
|
|
7277
|
-
|
|
7278
|
-
|
|
7279
|
-
#if FEATURE_MIS
|
|
7280
|
-
|
|
7281
|
-
// NOTE: we skip MIS for punctual lights since they are not supported in forward PT case
|
|
7282
|
-
if ( lightRec.type == SPOT_LIGHT_TYPE || lightRec.type == DIR_LIGHT_TYPE || lightRec.type == POINT_LIGHT_TYPE ) {
|
|
7283
|
-
|
|
7284
|
-
gl_FragColor.rgb += lightRec.emission * state.throughputColor;
|
|
7285
|
-
|
|
7286
|
-
} else {
|
|
7334
|
+
#if FEATURE_MIS
|
|
7287
7335
|
|
|
7288
7336
|
// weight the contribution
|
|
7337
|
+
// NOTE: Only area lights are supported for forward sampling and can be hit
|
|
7289
7338
|
float misWeight = misHeuristic( scatterRec.pdf, lightRec.pdf / lightsDenom );
|
|
7290
7339
|
gl_FragColor.rgb += lightRec.emission * state.throughputColor * misWeight;
|
|
7291
7340
|
|
|
7292
|
-
|
|
7341
|
+
#else
|
|
7293
7342
|
|
|
7294
|
-
|
|
7343
|
+
gl_FragColor.rgb += lightRec.emission * state.throughputColor;
|
|
7295
7344
|
|
|
7296
|
-
|
|
7345
|
+
#endif
|
|
7297
7346
|
|
|
7298
|
-
|
|
7347
|
+
}
|
|
7299
7348
|
|
|
7300
7349
|
}
|
|
7301
|
-
break;
|
|
7302
7350
|
|
|
7303
|
-
}
|
|
7351
|
+
}
|
|
7352
|
+
|
|
7353
|
+
if ( hitType == NO_HIT ) {
|
|
7304
7354
|
|
|
7305
7355
|
if ( state.firstRay || state.transmissiveRay ) {
|
|
7306
7356
|
|
|
@@ -7313,7 +7363,7 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
7313
7363
|
|
|
7314
7364
|
// get the PDF of the hit envmap point
|
|
7315
7365
|
vec3 envColor;
|
|
7316
|
-
float envPdf = sampleEquirect(
|
|
7366
|
+
float envPdf = sampleEquirect( envRotation3x3 * ray.direction, envColor );
|
|
7317
7367
|
envPdf /= lightsDenom;
|
|
7318
7368
|
|
|
7319
7369
|
// and weight the contribution
|