three-gpu-pathtracer 0.0.3 → 0.0.6
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 +136 -15
- package/build/index.module.js +2706 -529
- package/build/index.module.js.map +1 -1
- package/build/index.umd.cjs +2713 -529
- package/build/index.umd.cjs.map +1 -1
- package/package.json +68 -60
- package/src/core/DynamicPathTracingSceneGenerator.js +24 -11
- package/src/core/PathTracingRenderer.js +22 -0
- package/src/core/PathTracingSceneGenerator.js +36 -20
- package/src/index.js +9 -1
- package/src/materials/PhysicalPathTracingMaterial.js +350 -60
- package/src/objects/EquirectCamera.js +13 -0
- package/src/{core → objects}/PhysicalCamera.js +0 -0
- package/src/objects/PhysicalSpotLight.js +14 -0
- package/src/objects/ShapedAreaLight.js +12 -0
- package/src/shader/shaderEnvMapSampling.js +59 -67
- package/src/shader/shaderGGXFunctions.js +3 -2
- package/src/shader/shaderIridescenceFunctions.js +130 -0
- package/src/shader/shaderLightSampling.js +231 -0
- package/src/shader/shaderMaterialSampling.js +259 -53
- package/src/shader/shaderSheenFunctions.js +98 -0
- package/src/shader/shaderStructs.js +307 -92
- package/src/shader/shaderUtils.js +122 -0
- package/src/uniforms/EquirectHdrInfoUniform.js +10 -14
- package/src/uniforms/IESProfilesTexture.js +100 -0
- package/src/uniforms/LightsInfoUniformStruct.js +162 -0
- package/src/uniforms/MaterialsTexture.js +266 -33
- package/src/uniforms/PhysicalCameraUniform.js +1 -1
- package/src/uniforms/RenderTarget2DArray.js +93 -80
- package/src/utils/GeometryPreparationUtils.js +1 -1
- package/src/utils/IESLoader.js +325 -0
- package/src/workers/PathTracingSceneWorker.js +3 -1
package/build/index.module.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ShaderMaterial, NoBlending, NormalBlending, Color, Vector2, WebGLRenderTarget, RGBAFormat, FloatType, BufferAttribute, Mesh, BufferGeometry, PerspectiveCamera, DataTexture, ClampToEdgeWrapping, DoubleSide, BackSide, FrontSide, WebGLArrayRenderTarget, UnsignedByteType, LinearFilter, RepeatWrapping, MeshBasicMaterial, NoToneMapping, Source, HalfFloatType, DataUtils, RedFormat, PMREMGenerator, EquirectangularReflectionMapping,
|
|
1
|
+
import { ShaderMaterial, NoBlending, NormalBlending, Color, Vector2, WebGLRenderTarget, RGBAFormat, FloatType, BufferAttribute, Mesh, BufferGeometry, PerspectiveCamera, Camera, SpotLight, RectAreaLight, DataTexture, ClampToEdgeWrapping, DoubleSide, BackSide, FrontSide, WebGLArrayRenderTarget, UnsignedByteType, LinearFilter, RepeatWrapping, MeshBasicMaterial, NoToneMapping, Source, HalfFloatType, DataUtils, RedFormat, Vector3, Matrix4, Quaternion, Loader, MathUtils, FileLoader, PMREMGenerator, EquirectangularReflectionMapping, Matrix3 } from 'three';
|
|
2
2
|
import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
|
|
3
3
|
import { StaticGeometryGenerator, SAH, MeshBVH, MeshBVHUniformStruct, FloatVertexAttributeTexture, UIntVertexAttributeTexture, shaderStructs, shaderIntersectFunction } from 'three-mesh-bvh';
|
|
4
4
|
import { mergeVertices, mergeBufferGeometries } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
|
|
@@ -145,6 +145,7 @@ function* renderTask() {
|
|
|
145
145
|
|
|
146
146
|
blendMaterial.opacity = 1 / ( this.samples + 1 );
|
|
147
147
|
material.blending = NoBlending;
|
|
148
|
+
material.opacity = 1;
|
|
148
149
|
|
|
149
150
|
} else {
|
|
150
151
|
|
|
@@ -169,6 +170,27 @@ function* renderTask() {
|
|
|
169
170
|
material.cameraWorldMatrix.copy( camera.matrixWorld );
|
|
170
171
|
material.invProjectionMatrix.copy( camera.projectionMatrixInverse );
|
|
171
172
|
|
|
173
|
+
// Perspective camera (default)
|
|
174
|
+
let cameraType = 0;
|
|
175
|
+
|
|
176
|
+
// An orthographic projection matrix will always have the bottom right element == 1
|
|
177
|
+
// And a perspective projection matrix will always have the bottom right element == 0
|
|
178
|
+
if ( camera.projectionMatrix.elements[ 15 ] > 0 ) {
|
|
179
|
+
|
|
180
|
+
// Orthographic
|
|
181
|
+
cameraType = 1;
|
|
182
|
+
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
if ( camera.isEquirectCamera ) {
|
|
186
|
+
|
|
187
|
+
// Equirectangular
|
|
188
|
+
cameraType = 2;
|
|
189
|
+
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
material.setDefine( 'CAMERA_TYPE', cameraType );
|
|
193
|
+
|
|
172
194
|
const ogRenderTarget = _renderer.getRenderTarget();
|
|
173
195
|
const ogAutoClear = _renderer.autoClear;
|
|
174
196
|
|
|
@@ -506,7 +528,7 @@ function mergeMeshes( meshes, options = {} ) {
|
|
|
506
528
|
|
|
507
529
|
// apply the matrix world to the geometry
|
|
508
530
|
const originalGeometry = meshes[ i ].geometry;
|
|
509
|
-
|
|
531
|
+
const geometry = options.cloneGeometry ? originalGeometry.clone() : originalGeometry;
|
|
510
532
|
geometry.applyMatrix4( mesh.matrixWorld );
|
|
511
533
|
|
|
512
534
|
// ensure our geometry has common attributes
|
|
@@ -550,44 +572,60 @@ class PathTracingSceneGenerator {
|
|
|
550
572
|
|
|
551
573
|
prepScene( scene ) {
|
|
552
574
|
|
|
575
|
+
scene = Array.isArray( scene ) ? scene : [ scene ];
|
|
576
|
+
|
|
553
577
|
const meshes = [];
|
|
554
|
-
|
|
578
|
+
const lights = [];
|
|
555
579
|
|
|
556
|
-
|
|
580
|
+
for ( let i = 0, l = scene.length; i < l; i ++ ) {
|
|
557
581
|
|
|
558
|
-
|
|
559
|
-
generator.applyWorldTransforms = false;
|
|
560
|
-
const mesh = new Mesh(
|
|
561
|
-
generator.generate(),
|
|
562
|
-
c.material,
|
|
563
|
-
);
|
|
564
|
-
mesh.matrixWorld.copy( c.matrixWorld );
|
|
565
|
-
mesh.matrix.copy( c.matrixWorld );
|
|
566
|
-
mesh.matrix.decompose( mesh.position, mesh.quaternion, mesh.scale );
|
|
567
|
-
meshes.push( mesh );
|
|
582
|
+
scene[ i ].traverse( c => {
|
|
568
583
|
|
|
569
|
-
|
|
584
|
+
if ( c.isSkinnedMesh || c.isMesh && c.morphTargetInfluences ) {
|
|
570
585
|
|
|
571
|
-
|
|
586
|
+
const generator = new StaticGeometryGenerator( c );
|
|
587
|
+
generator.applyWorldTransforms = false;
|
|
588
|
+
const mesh = new Mesh(
|
|
589
|
+
generator.generate(),
|
|
590
|
+
c.material,
|
|
591
|
+
);
|
|
592
|
+
mesh.matrixWorld.copy( c.matrixWorld );
|
|
593
|
+
mesh.matrix.copy( c.matrixWorld );
|
|
594
|
+
mesh.matrix.decompose( mesh.position, mesh.quaternion, mesh.scale );
|
|
595
|
+
meshes.push( mesh );
|
|
572
596
|
|
|
573
|
-
|
|
597
|
+
} else if ( c.isMesh ) {
|
|
574
598
|
|
|
575
|
-
|
|
599
|
+
meshes.push( c );
|
|
576
600
|
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
601
|
+
} else if ( c.isRectAreaLight || c.isSpotLight ) {
|
|
602
|
+
|
|
603
|
+
lights.push( c );
|
|
604
|
+
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
} );
|
|
608
|
+
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
return {
|
|
612
|
+
...mergeMeshes( meshes, {
|
|
613
|
+
attributes: [ 'position', 'normal', 'tangent', 'uv' ],
|
|
614
|
+
} ),
|
|
615
|
+
lights,
|
|
616
|
+
};
|
|
580
617
|
|
|
581
618
|
}
|
|
582
619
|
|
|
583
620
|
generate( scene, options = {} ) {
|
|
584
621
|
|
|
585
|
-
const { materials, textures, geometry } = this.prepScene( scene );
|
|
622
|
+
const { materials, textures, geometry, lights } = this.prepScene( scene );
|
|
586
623
|
const bvhOptions = { strategy: SAH, ...options, maxLeafTris: 1 };
|
|
587
624
|
return {
|
|
588
625
|
scene,
|
|
589
626
|
materials,
|
|
590
627
|
textures,
|
|
628
|
+
lights,
|
|
591
629
|
bvh: new MeshBVH( geometry, bvhOptions ),
|
|
592
630
|
};
|
|
593
631
|
|
|
@@ -605,11 +643,12 @@ class DynamicPathTracingSceneGenerator {
|
|
|
605
643
|
|
|
606
644
|
constructor( scene ) {
|
|
607
645
|
|
|
608
|
-
this.
|
|
646
|
+
this.objects = Array.isArray( scene ) ? scene : [ scene ];
|
|
609
647
|
this.bvh = null;
|
|
610
648
|
this.geometry = new BufferGeometry();
|
|
611
649
|
this.materials = null;
|
|
612
650
|
this.textures = null;
|
|
651
|
+
this.lights = [];
|
|
613
652
|
this.staticGeometryGenerator = new StaticGeometryGenerator( scene );
|
|
614
653
|
|
|
615
654
|
}
|
|
@@ -621,7 +660,8 @@ class DynamicPathTracingSceneGenerator {
|
|
|
621
660
|
this.geometry = new BufferGeometry();
|
|
622
661
|
this.materials = null;
|
|
623
662
|
this.textures = null;
|
|
624
|
-
this.
|
|
663
|
+
this.lights = [];
|
|
664
|
+
this.staticGeometryGenerator = new StaticGeometryGenerator( this.objects );
|
|
625
665
|
|
|
626
666
|
}
|
|
627
667
|
|
|
@@ -629,20 +669,29 @@ class DynamicPathTracingSceneGenerator {
|
|
|
629
669
|
|
|
630
670
|
generate() {
|
|
631
671
|
|
|
632
|
-
const {
|
|
672
|
+
const { objects, staticGeometryGenerator, geometry } = this;
|
|
633
673
|
if ( this.bvh === null ) {
|
|
634
674
|
|
|
635
675
|
const attributes = [ 'position', 'normal', 'tangent', 'uv' ];
|
|
636
|
-
scene.traverse( c => {
|
|
637
676
|
|
|
638
|
-
|
|
677
|
+
for ( let i = 0, l = objects.length; i < l; i ++ ) {
|
|
639
678
|
|
|
640
|
-
|
|
641
|
-
setCommonAttributes( c.geometry, { attributes, normalMapRequired } );
|
|
679
|
+
objects[ i ].traverse( c => {
|
|
642
680
|
|
|
643
|
-
|
|
681
|
+
if ( c.isMesh ) {
|
|
644
682
|
|
|
645
|
-
|
|
683
|
+
const normalMapRequired = ! ! c.material.normalMap;
|
|
684
|
+
setCommonAttributes( c.geometry, { attributes, normalMapRequired } );
|
|
685
|
+
|
|
686
|
+
} else if ( c.isRectAreaLight || c.isSpotLight ) {
|
|
687
|
+
|
|
688
|
+
this.lights.push( c );
|
|
689
|
+
|
|
690
|
+
}
|
|
691
|
+
|
|
692
|
+
} );
|
|
693
|
+
|
|
694
|
+
}
|
|
646
695
|
|
|
647
696
|
const textureSet = new Set();
|
|
648
697
|
const materials = staticGeometryGenerator.getMaterials();
|
|
@@ -673,10 +722,11 @@ class DynamicPathTracingSceneGenerator {
|
|
|
673
722
|
this.textures = Array.from( textureSet );
|
|
674
723
|
|
|
675
724
|
return {
|
|
725
|
+
lights: this.lights,
|
|
676
726
|
bvh: this.bvh,
|
|
677
727
|
materials: this.materials,
|
|
678
728
|
textures: this.textures,
|
|
679
|
-
|
|
729
|
+
objects,
|
|
680
730
|
};
|
|
681
731
|
|
|
682
732
|
} else {
|
|
@@ -685,10 +735,11 @@ class DynamicPathTracingSceneGenerator {
|
|
|
685
735
|
staticGeometryGenerator.generate( geometry );
|
|
686
736
|
bvh.refit();
|
|
687
737
|
return {
|
|
738
|
+
lights: this.lights,
|
|
688
739
|
bvh: this.bvh,
|
|
689
740
|
materials: this.materials,
|
|
690
741
|
textures: this.textures,
|
|
691
|
-
|
|
742
|
+
objects,
|
|
692
743
|
};
|
|
693
744
|
|
|
694
745
|
}
|
|
@@ -982,8 +1033,48 @@ class PhysicalCamera extends PerspectiveCamera {
|
|
|
982
1033
|
|
|
983
1034
|
}
|
|
984
1035
|
|
|
985
|
-
|
|
986
|
-
|
|
1036
|
+
class EquirectCamera extends Camera {
|
|
1037
|
+
|
|
1038
|
+
constructor() {
|
|
1039
|
+
|
|
1040
|
+
super();
|
|
1041
|
+
|
|
1042
|
+
this.isEquirectCamera = true;
|
|
1043
|
+
|
|
1044
|
+
}
|
|
1045
|
+
|
|
1046
|
+
}
|
|
1047
|
+
|
|
1048
|
+
class PhysicalSpotLight extends SpotLight {
|
|
1049
|
+
|
|
1050
|
+
constructor( ...args ) {
|
|
1051
|
+
|
|
1052
|
+
super( ...args );
|
|
1053
|
+
|
|
1054
|
+
this.iesTexture = null;
|
|
1055
|
+
this.radius = 0;
|
|
1056
|
+
|
|
1057
|
+
}
|
|
1058
|
+
|
|
1059
|
+
}
|
|
1060
|
+
|
|
1061
|
+
class ShapedAreaLight extends RectAreaLight {
|
|
1062
|
+
|
|
1063
|
+
constructor( ...args ) {
|
|
1064
|
+
|
|
1065
|
+
super( ...args );
|
|
1066
|
+
this.isCircular = false;
|
|
1067
|
+
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
const MATERIAL_PIXELS = 44;
|
|
1073
|
+
const MATERIAL_STRIDE = MATERIAL_PIXELS * 4;
|
|
1074
|
+
|
|
1075
|
+
const SIDE_OFFSET = 12 * 4 + 3; // s12.a
|
|
1076
|
+
const MATTE_OFFSET = 13 * 4 + 0; // s13.r
|
|
1077
|
+
const SHADOW_OFFSET = 13 * 4 + 1; // s13.g
|
|
987
1078
|
|
|
988
1079
|
class MaterialsTexture extends DataTexture {
|
|
989
1080
|
|
|
@@ -996,24 +1087,42 @@ class MaterialsTexture extends DataTexture {
|
|
|
996
1087
|
this.wrapS = ClampToEdgeWrapping;
|
|
997
1088
|
this.wrapT = ClampToEdgeWrapping;
|
|
998
1089
|
this.generateMipmaps = false;
|
|
1090
|
+
this.threeCompatibilityTransforms = false;
|
|
1091
|
+
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
setCastShadow( materialIndex, cast ) {
|
|
1095
|
+
|
|
1096
|
+
// invert the shadow value so we default to "true" when initializing a material
|
|
1097
|
+
const array = this.image.data;
|
|
1098
|
+
const index = materialIndex * MATERIAL_STRIDE + SHADOW_OFFSET;
|
|
1099
|
+
array[ index ] = ! cast ? 1 : 0;
|
|
1100
|
+
|
|
1101
|
+
}
|
|
1102
|
+
|
|
1103
|
+
getCastShadow( materialIndex ) {
|
|
1104
|
+
|
|
1105
|
+
const array = this.image.data;
|
|
1106
|
+
const index = materialIndex * MATERIAL_STRIDE + SHADOW_OFFSET;
|
|
1107
|
+
return ! Boolean( array[ index ] );
|
|
999
1108
|
|
|
1000
1109
|
}
|
|
1001
1110
|
|
|
1002
1111
|
setSide( materialIndex, side ) {
|
|
1003
1112
|
|
|
1004
1113
|
const array = this.image.data;
|
|
1005
|
-
const index = materialIndex * MATERIAL_STRIDE +
|
|
1114
|
+
const index = materialIndex * MATERIAL_STRIDE + SIDE_OFFSET;
|
|
1006
1115
|
switch ( side ) {
|
|
1007
1116
|
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1117
|
+
case FrontSide:
|
|
1118
|
+
array[ index ] = 1;
|
|
1119
|
+
break;
|
|
1120
|
+
case BackSide:
|
|
1121
|
+
array[ index ] = - 1;
|
|
1122
|
+
break;
|
|
1123
|
+
case DoubleSide:
|
|
1124
|
+
array[ index ] = 0;
|
|
1125
|
+
break;
|
|
1017
1126
|
|
|
1018
1127
|
}
|
|
1019
1128
|
|
|
@@ -1022,15 +1131,15 @@ class MaterialsTexture extends DataTexture {
|
|
|
1022
1131
|
getSide( materialIndex ) {
|
|
1023
1132
|
|
|
1024
1133
|
const array = this.image.data;
|
|
1025
|
-
const index = materialIndex * MATERIAL_STRIDE +
|
|
1134
|
+
const index = materialIndex * MATERIAL_STRIDE + SIDE_OFFSET;
|
|
1026
1135
|
switch ( array[ index ] ) {
|
|
1027
1136
|
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1137
|
+
case 0:
|
|
1138
|
+
return DoubleSide;
|
|
1139
|
+
case 1:
|
|
1140
|
+
return FrontSide;
|
|
1141
|
+
case - 1:
|
|
1142
|
+
return BackSide;
|
|
1034
1143
|
|
|
1035
1144
|
}
|
|
1036
1145
|
|
|
@@ -1041,7 +1150,7 @@ class MaterialsTexture extends DataTexture {
|
|
|
1041
1150
|
setMatte( materialIndex, matte ) {
|
|
1042
1151
|
|
|
1043
1152
|
const array = this.image.data;
|
|
1044
|
-
const index = materialIndex * MATERIAL_STRIDE +
|
|
1153
|
+
const index = materialIndex * MATERIAL_STRIDE + MATTE_OFFSET;
|
|
1045
1154
|
array[ index ] = matte ? 1 : 0;
|
|
1046
1155
|
|
|
1047
1156
|
}
|
|
@@ -1049,7 +1158,7 @@ class MaterialsTexture extends DataTexture {
|
|
|
1049
1158
|
getMatte( materialIndex ) {
|
|
1050
1159
|
|
|
1051
1160
|
const array = this.image.data;
|
|
1052
|
-
const index = materialIndex * MATERIAL_STRIDE +
|
|
1161
|
+
const index = materialIndex * MATERIAL_STRIDE + MATTE_OFFSET;
|
|
1053
1162
|
return Boolean( array[ index ] );
|
|
1054
1163
|
|
|
1055
1164
|
}
|
|
@@ -1068,45 +1177,120 @@ class MaterialsTexture extends DataTexture {
|
|
|
1068
1177
|
|
|
1069
1178
|
}
|
|
1070
1179
|
|
|
1180
|
+
function getUVTransformTexture( material ) {
|
|
1181
|
+
|
|
1182
|
+
// https://github.com/mrdoob/three.js/blob/f3a832e637c98a404c64dae8174625958455e038/src/renderers/webgl/WebGLMaterials.js#L204-L306
|
|
1183
|
+
// https://threejs.org/docs/#api/en/textures/Texture.offset
|
|
1184
|
+
// fallback order of textures to use as a common uv transform
|
|
1185
|
+
return material.map ||
|
|
1186
|
+
material.specularMap ||
|
|
1187
|
+
material.displacementMap ||
|
|
1188
|
+
material.normalMap ||
|
|
1189
|
+
material.bumpMap ||
|
|
1190
|
+
material.roughnessMap ||
|
|
1191
|
+
material.metalnessMap ||
|
|
1192
|
+
material.alphaMap ||
|
|
1193
|
+
material.emissiveMap ||
|
|
1194
|
+
material.clearcoatMap ||
|
|
1195
|
+
material.clearcoatNormalMap ||
|
|
1196
|
+
material.clearcoatRoughnessMap ||
|
|
1197
|
+
material.iridescenceMap ||
|
|
1198
|
+
material.iridescenceThicknessMap ||
|
|
1199
|
+
material.specularIntensityMap ||
|
|
1200
|
+
material.specularColorMap ||
|
|
1201
|
+
material.transmissionMap ||
|
|
1202
|
+
material.thicknessMap ||
|
|
1203
|
+
material.sheenColorMap ||
|
|
1204
|
+
material.sheenRoughnessMap ||
|
|
1205
|
+
null;
|
|
1206
|
+
|
|
1207
|
+
}
|
|
1208
|
+
|
|
1209
|
+
function writeTextureMatrixToArray( material, textureKey, array, offset ) {
|
|
1210
|
+
|
|
1211
|
+
let texture;
|
|
1212
|
+
if ( threeCompatibilityTransforms ) {
|
|
1213
|
+
|
|
1214
|
+
texture = getUVTransformTexture( material );
|
|
1215
|
+
|
|
1216
|
+
} else {
|
|
1217
|
+
|
|
1218
|
+
texture = material[ textureKey ] && material[ textureKey ].isTexture ? material[ textureKey ] : null;
|
|
1219
|
+
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
// check if texture exists
|
|
1223
|
+
if ( texture ) {
|
|
1224
|
+
|
|
1225
|
+
const elements = texture.matrix.elements;
|
|
1226
|
+
|
|
1227
|
+
let i = 0;
|
|
1228
|
+
|
|
1229
|
+
// first row
|
|
1230
|
+
array[ offset + i ++ ] = elements[ 0 ];
|
|
1231
|
+
array[ offset + i ++ ] = elements[ 3 ];
|
|
1232
|
+
array[ offset + i ++ ] = elements[ 6 ];
|
|
1233
|
+
i ++;
|
|
1234
|
+
|
|
1235
|
+
// second row
|
|
1236
|
+
array[ offset + i ++ ] = elements[ 1 ];
|
|
1237
|
+
array[ offset + i ++ ] = elements[ 4 ];
|
|
1238
|
+
array[ offset + i ++ ] = elements[ 7 ];
|
|
1239
|
+
i ++;
|
|
1240
|
+
|
|
1241
|
+
}
|
|
1242
|
+
|
|
1243
|
+
return 8;
|
|
1244
|
+
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1071
1247
|
let index = 0;
|
|
1072
1248
|
const pixelCount = materials.length * MATERIAL_PIXELS;
|
|
1073
1249
|
const dimension = Math.ceil( Math.sqrt( pixelCount ) );
|
|
1250
|
+
const { threeCompatibilityTransforms, image } = this;
|
|
1074
1251
|
|
|
1075
|
-
if (
|
|
1252
|
+
if ( image.width !== dimension ) {
|
|
1076
1253
|
|
|
1077
1254
|
this.dispose();
|
|
1078
1255
|
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1256
|
+
image.data = new Float32Array( dimension * dimension * 4 );
|
|
1257
|
+
image.width = dimension;
|
|
1258
|
+
image.height = dimension;
|
|
1082
1259
|
|
|
1083
1260
|
}
|
|
1084
1261
|
|
|
1085
|
-
const floatArray =
|
|
1086
|
-
|
|
1262
|
+
const floatArray = image.data;
|
|
1263
|
+
|
|
1264
|
+
// on some devices (Google Pixel 6) the "floatBitsToInt" function does not work correctly so we
|
|
1265
|
+
// can't encode texture ids that way.
|
|
1266
|
+
// const intArray = new Int32Array( floatArray.buffer );
|
|
1087
1267
|
|
|
1088
1268
|
for ( let i = 0, l = materials.length; i < l; i ++ ) {
|
|
1089
1269
|
|
|
1090
1270
|
const m = materials[ i ];
|
|
1091
1271
|
|
|
1272
|
+
// sample 0
|
|
1092
1273
|
// color
|
|
1093
1274
|
floatArray[ index ++ ] = m.color.r;
|
|
1094
1275
|
floatArray[ index ++ ] = m.color.g;
|
|
1095
1276
|
floatArray[ index ++ ] = m.color.b;
|
|
1096
|
-
|
|
1277
|
+
floatArray[ index ++ ] = getTexture( m, 'map' );
|
|
1097
1278
|
|
|
1279
|
+
// sample 1
|
|
1098
1280
|
// metalness & roughness
|
|
1099
1281
|
floatArray[ index ++ ] = getField( m, 'metalness', 0.0 );
|
|
1100
|
-
|
|
1282
|
+
floatArray[ index ++ ] = textures.indexOf( m.metalnessMap );
|
|
1101
1283
|
floatArray[ index ++ ] = getField( m, 'roughness', 0.0 );
|
|
1102
|
-
|
|
1284
|
+
floatArray[ index ++ ] = textures.indexOf( m.roughnessMap );
|
|
1103
1285
|
|
|
1286
|
+
// sample 2
|
|
1104
1287
|
// transmission & emissiveIntensity
|
|
1105
1288
|
floatArray[ index ++ ] = getField( m, 'ior', 1.0 );
|
|
1106
1289
|
floatArray[ index ++ ] = getField( m, 'transmission', 0.0 );
|
|
1107
|
-
|
|
1290
|
+
floatArray[ index ++ ] = getTexture( m, 'transmissionMap' );
|
|
1108
1291
|
floatArray[ index ++ ] = getField( m, 'emissiveIntensity', 0.0 );
|
|
1109
1292
|
|
|
1293
|
+
// sample 3
|
|
1110
1294
|
// emission
|
|
1111
1295
|
if ( 'emissive' in m ) {
|
|
1112
1296
|
|
|
@@ -1122,10 +1306,11 @@ class MaterialsTexture extends DataTexture {
|
|
|
1122
1306
|
|
|
1123
1307
|
}
|
|
1124
1308
|
|
|
1125
|
-
|
|
1309
|
+
floatArray[ index ++ ] = getTexture( m, 'emissiveMap' );
|
|
1126
1310
|
|
|
1311
|
+
// sample 4
|
|
1127
1312
|
// normals
|
|
1128
|
-
|
|
1313
|
+
floatArray[ index ++ ] = getTexture( m, 'normalMap' );
|
|
1129
1314
|
if ( 'normalScale' in m ) {
|
|
1130
1315
|
|
|
1131
1316
|
floatArray[ index ++ ] = m.normalScale.x;
|
|
@@ -1138,13 +1323,148 @@ class MaterialsTexture extends DataTexture {
|
|
|
1138
1323
|
|
|
1139
1324
|
}
|
|
1140
1325
|
|
|
1326
|
+
// clearcoat
|
|
1327
|
+
floatArray[ index ++ ] = getField( m, 'clearcoat', 0.0 );
|
|
1328
|
+
floatArray[ index ++ ] = getTexture( m, 'clearcoatMap' ); // sample 5
|
|
1329
|
+
|
|
1330
|
+
floatArray[ index ++ ] = getField( m, 'clearcoatRoughness', 0.0 );
|
|
1331
|
+
floatArray[ index ++ ] = getTexture( m, 'clearcoatRoughnessMap' );
|
|
1332
|
+
|
|
1333
|
+
floatArray[ index ++ ] = getTexture( m, 'clearcoatNormalMap' );
|
|
1334
|
+
|
|
1335
|
+
// sample 6
|
|
1336
|
+
if ( 'clearcoatNormalScale' in m ) {
|
|
1337
|
+
|
|
1338
|
+
floatArray[ index ++ ] = m.clearcoatNormalScale.x;
|
|
1339
|
+
floatArray[ index ++ ] = m.clearcoatNormalScale.y;
|
|
1340
|
+
|
|
1341
|
+
} else {
|
|
1342
|
+
|
|
1343
|
+
floatArray[ index ++ ] = 1;
|
|
1344
|
+
floatArray[ index ++ ] = 1;
|
|
1345
|
+
|
|
1346
|
+
}
|
|
1347
|
+
|
|
1348
|
+
index ++;
|
|
1349
|
+
index ++;
|
|
1350
|
+
|
|
1351
|
+
// sample 7
|
|
1352
|
+
// sheen
|
|
1353
|
+
if ( 'sheenColor' in m ) {
|
|
1354
|
+
|
|
1355
|
+
floatArray[ index ++ ] = m.sheenColor.r;
|
|
1356
|
+
floatArray[ index ++ ] = m.sheenColor.g;
|
|
1357
|
+
floatArray[ index ++ ] = m.sheenColor.b;
|
|
1358
|
+
|
|
1359
|
+
} else {
|
|
1360
|
+
|
|
1361
|
+
floatArray[ index ++ ] = 0.0;
|
|
1362
|
+
floatArray[ index ++ ] = 0.0;
|
|
1363
|
+
floatArray[ index ++ ] = 0.0;
|
|
1364
|
+
|
|
1365
|
+
}
|
|
1366
|
+
|
|
1367
|
+
floatArray[ index ++ ] = getTexture( m, 'sheenColorMap' );
|
|
1368
|
+
|
|
1369
|
+
// sample 8
|
|
1370
|
+
floatArray[ index ++ ] = getField( m, 'sheenRoughness', 0.0 );
|
|
1371
|
+
floatArray[ index ++ ] = getTexture( m, 'sheenRoughnessMap' );
|
|
1372
|
+
|
|
1373
|
+
// iridescence
|
|
1374
|
+
floatArray[ index ++ ] = getTexture( m, 'iridescenceMap' );
|
|
1375
|
+
floatArray[ index ++ ] = getTexture( m, 'iridescenceThicknessMap' );
|
|
1376
|
+
|
|
1377
|
+
floatArray[ index ++ ] = getField( m, 'iridescence', 0.0 ); // sample 9
|
|
1378
|
+
floatArray[ index ++ ] = getField( m, 'iridescenceIOR', 1.3 );
|
|
1379
|
+
|
|
1380
|
+
const iridescenceThicknessRange = getField( m, 'iridescenceThicknessRange', [ 100, 400 ] );
|
|
1381
|
+
floatArray[ index ++ ] = iridescenceThicknessRange[ 0 ];
|
|
1382
|
+
floatArray[ index ++ ] = iridescenceThicknessRange[ 1 ];
|
|
1383
|
+
|
|
1384
|
+
// sample 10
|
|
1385
|
+
// specular color
|
|
1386
|
+
if ( 'specularColor' in m ) {
|
|
1387
|
+
|
|
1388
|
+
floatArray[ index ++ ] = m.specularColor.r;
|
|
1389
|
+
floatArray[ index ++ ] = m.specularColor.g;
|
|
1390
|
+
floatArray[ index ++ ] = m.specularColor.b;
|
|
1391
|
+
|
|
1392
|
+
} else {
|
|
1393
|
+
|
|
1394
|
+
floatArray[ index ++ ] = 0.0;
|
|
1395
|
+
floatArray[ index ++ ] = 0.0;
|
|
1396
|
+
floatArray[ index ++ ] = 0.0;
|
|
1397
|
+
|
|
1398
|
+
}
|
|
1399
|
+
|
|
1400
|
+
floatArray[ index ++ ] = getTexture( m, 'specularColorMap' );
|
|
1401
|
+
|
|
1402
|
+
// sample 11
|
|
1403
|
+
// specular intensity
|
|
1404
|
+
floatArray[ index ++ ] = getField( m, 'specularIntensity', 1.0 );
|
|
1405
|
+
floatArray[ index ++ ] = getTexture( m, 'specularIntensityMap' );
|
|
1406
|
+
index ++;
|
|
1141
1407
|
index ++;
|
|
1142
1408
|
|
|
1409
|
+
// sample 12
|
|
1410
|
+
// alphaMap
|
|
1411
|
+
floatArray[ index ++ ] = getTexture( m, 'alphaMap' );
|
|
1412
|
+
|
|
1143
1413
|
// side & matte
|
|
1144
1414
|
floatArray[ index ++ ] = m.opacity;
|
|
1145
1415
|
floatArray[ index ++ ] = m.alphaTest;
|
|
1146
1416
|
index ++; // side
|
|
1417
|
+
|
|
1418
|
+
// sample 13
|
|
1147
1419
|
index ++; // matte
|
|
1420
|
+
index ++; // shadow
|
|
1421
|
+
index ++;
|
|
1422
|
+
index ++;
|
|
1423
|
+
|
|
1424
|
+
// map transform 14
|
|
1425
|
+
index += writeTextureMatrixToArray( m, 'map', floatArray, index );
|
|
1426
|
+
|
|
1427
|
+
// metalnessMap transform 16
|
|
1428
|
+
index += writeTextureMatrixToArray( m, 'metalnessMap', floatArray, index );
|
|
1429
|
+
|
|
1430
|
+
// roughnessMap transform 18
|
|
1431
|
+
index += writeTextureMatrixToArray( m, 'roughnessMap', floatArray, index );
|
|
1432
|
+
|
|
1433
|
+
// transmissionMap transform 20
|
|
1434
|
+
index += writeTextureMatrixToArray( m, 'transmissionMap', floatArray, index );
|
|
1435
|
+
|
|
1436
|
+
// emissiveMap transform 22
|
|
1437
|
+
index += writeTextureMatrixToArray( m, 'emissiveMap', floatArray, index );
|
|
1438
|
+
|
|
1439
|
+
// normalMap transform 24
|
|
1440
|
+
index += writeTextureMatrixToArray( m, 'normalMap', floatArray, index );
|
|
1441
|
+
|
|
1442
|
+
// clearcoatMap transform 26
|
|
1443
|
+
index += writeTextureMatrixToArray( m, 'clearcoatMap', floatArray, index );
|
|
1444
|
+
|
|
1445
|
+
// clearcoatNormalMap transform 28
|
|
1446
|
+
index += writeTextureMatrixToArray( m, 'clearcoatNormalMap', floatArray, index );
|
|
1447
|
+
|
|
1448
|
+
// clearcoatRoughnessMap transform 30
|
|
1449
|
+
index += writeTextureMatrixToArray( m, 'clearcoatRoughnessMap', floatArray, index );
|
|
1450
|
+
|
|
1451
|
+
// sheenColorMap transform 32
|
|
1452
|
+
index += writeTextureMatrixToArray( m, 'sheenColorMap', floatArray, index );
|
|
1453
|
+
|
|
1454
|
+
// sheenRoughnessMap transform 34
|
|
1455
|
+
index += writeTextureMatrixToArray( m, 'sheenRoughnessMap', floatArray, index );
|
|
1456
|
+
|
|
1457
|
+
// iridescenceMap transform 36
|
|
1458
|
+
index += writeTextureMatrixToArray( m, 'iridescenceMap', floatArray, index );
|
|
1459
|
+
|
|
1460
|
+
// iridescenceThicknessMap transform 38
|
|
1461
|
+
index += writeTextureMatrixToArray( m, 'iridescenceThicknessMap', floatArray, index );
|
|
1462
|
+
|
|
1463
|
+
// specularColorMap transform 40
|
|
1464
|
+
index += writeTextureMatrixToArray( m, 'specularColorMap', floatArray, index );
|
|
1465
|
+
|
|
1466
|
+
// specularIntensityMap transform 42
|
|
1467
|
+
index += writeTextureMatrixToArray( m, 'specularIntensityMap', floatArray, index );
|
|
1148
1468
|
|
|
1149
1469
|
}
|
|
1150
1470
|
|
|
@@ -1154,73 +1474,86 @@ class MaterialsTexture extends DataTexture {
|
|
|
1154
1474
|
|
|
1155
1475
|
}
|
|
1156
1476
|
|
|
1157
|
-
const prevColor = new Color();
|
|
1158
|
-
class RenderTarget2DArray extends WebGLArrayRenderTarget {
|
|
1159
|
-
|
|
1160
|
-
constructor( ...args ) {
|
|
1161
|
-
|
|
1162
|
-
super( ...args );
|
|
1163
|
-
|
|
1164
|
-
const tex = this.texture;
|
|
1165
|
-
tex.format = RGBAFormat;
|
|
1166
|
-
tex.type = UnsignedByteType;
|
|
1167
|
-
tex.minFilter = LinearFilter;
|
|
1168
|
-
tex.magFilter = LinearFilter;
|
|
1169
|
-
tex.wrapS = RepeatWrapping;
|
|
1170
|
-
tex.wrapT = RepeatWrapping;
|
|
1171
|
-
tex.setTextures = ( ...args ) => {
|
|
1172
|
-
|
|
1173
|
-
this.setTextures( ...args );
|
|
1174
|
-
|
|
1175
|
-
};
|
|
1176
|
-
|
|
1177
|
-
const fsQuad = new FullScreenQuad( new MeshBasicMaterial() );
|
|
1178
|
-
this.fsQuad = fsQuad;
|
|
1179
|
-
|
|
1180
|
-
}
|
|
1181
|
-
|
|
1182
|
-
setTextures( renderer, width, height, textures ) {
|
|
1183
|
-
|
|
1184
|
-
// save previous renderer state
|
|
1185
|
-
const prevRenderTarget = renderer.getRenderTarget();
|
|
1186
|
-
const prevToneMapping = renderer.toneMapping;
|
|
1187
|
-
const prevAlpha = renderer.getClearAlpha();
|
|
1188
|
-
renderer.getClearColor( prevColor );
|
|
1189
|
-
|
|
1190
|
-
// resize the render target and ensure we don't have an empty texture
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
renderer.
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1477
|
+
const prevColor$1 = new Color();
|
|
1478
|
+
class RenderTarget2DArray extends WebGLArrayRenderTarget {
|
|
1479
|
+
|
|
1480
|
+
constructor( ...args ) {
|
|
1481
|
+
|
|
1482
|
+
super( ...args );
|
|
1483
|
+
|
|
1484
|
+
const tex = this.texture;
|
|
1485
|
+
tex.format = RGBAFormat;
|
|
1486
|
+
tex.type = UnsignedByteType;
|
|
1487
|
+
tex.minFilter = LinearFilter;
|
|
1488
|
+
tex.magFilter = LinearFilter;
|
|
1489
|
+
tex.wrapS = RepeatWrapping;
|
|
1490
|
+
tex.wrapT = RepeatWrapping;
|
|
1491
|
+
tex.setTextures = ( ...args ) => {
|
|
1492
|
+
|
|
1493
|
+
this.setTextures( ...args );
|
|
1494
|
+
|
|
1495
|
+
};
|
|
1496
|
+
|
|
1497
|
+
const fsQuad = new FullScreenQuad( new MeshBasicMaterial() );
|
|
1498
|
+
this.fsQuad = fsQuad;
|
|
1499
|
+
|
|
1500
|
+
}
|
|
1501
|
+
|
|
1502
|
+
setTextures( renderer, width, height, textures ) {
|
|
1503
|
+
|
|
1504
|
+
// save previous renderer state
|
|
1505
|
+
const prevRenderTarget = renderer.getRenderTarget();
|
|
1506
|
+
const prevToneMapping = renderer.toneMapping;
|
|
1507
|
+
const prevAlpha = renderer.getClearAlpha();
|
|
1508
|
+
renderer.getClearColor( prevColor$1 );
|
|
1509
|
+
|
|
1510
|
+
// resize the render target and ensure we don't have an empty texture
|
|
1511
|
+
// render target depth must be >= 1 to avoid unbound texture error on android devices
|
|
1512
|
+
const depth = textures.length || 1;
|
|
1513
|
+
this.setSize( width, height, depth );
|
|
1514
|
+
renderer.setClearColor( 0, 0 );
|
|
1515
|
+
renderer.toneMapping = NoToneMapping;
|
|
1516
|
+
|
|
1517
|
+
// render each texture into each layer of the target
|
|
1518
|
+
const fsQuad = this.fsQuad;
|
|
1519
|
+
for ( let i = 0, l = depth; i < l; i ++ ) {
|
|
1520
|
+
|
|
1521
|
+
const texture = textures[ i ];
|
|
1522
|
+
if ( texture ) {
|
|
1523
|
+
|
|
1524
|
+
// revert to default texture transform before rendering
|
|
1525
|
+
texture.matrixAutoUpdate = false;
|
|
1526
|
+
texture.matrix.identity();
|
|
1527
|
+
|
|
1528
|
+
fsQuad.material.map = texture;
|
|
1529
|
+
fsQuad.material.transparent = true;
|
|
1530
|
+
|
|
1531
|
+
renderer.setRenderTarget( this, i );
|
|
1532
|
+
fsQuad.render( renderer );
|
|
1533
|
+
|
|
1534
|
+
// restore custom texture transform
|
|
1535
|
+
texture.updateMatrix();
|
|
1536
|
+
texture.matrixAutoUpdate = true;
|
|
1537
|
+
|
|
1538
|
+
}
|
|
1539
|
+
|
|
1540
|
+
}
|
|
1541
|
+
|
|
1542
|
+
// reset the renderer
|
|
1543
|
+
fsQuad.material.map = null;
|
|
1544
|
+
renderer.setClearColor( prevColor$1, prevAlpha );
|
|
1545
|
+
renderer.setRenderTarget( prevRenderTarget );
|
|
1546
|
+
renderer.toneMapping = prevToneMapping;
|
|
1547
|
+
|
|
1548
|
+
}
|
|
1549
|
+
|
|
1550
|
+
dispose() {
|
|
1551
|
+
|
|
1552
|
+
super.dispose();
|
|
1553
|
+
this.fsQuad.dispose();
|
|
1554
|
+
|
|
1555
|
+
}
|
|
1556
|
+
|
|
1224
1557
|
}
|
|
1225
1558
|
|
|
1226
1559
|
function binarySearchFindClosestIndexOf( array, targetValue, offset = 0, count = array.length ) {
|
|
@@ -1334,27 +1667,21 @@ class EquirectHdrInfoUniform {
|
|
|
1334
1667
|
conditionalWeights.magFilter = LinearFilter;
|
|
1335
1668
|
conditionalWeights.generateMipmaps = false;
|
|
1336
1669
|
|
|
1337
|
-
// store the total sum in a 1x1 tex since some android mobile devices have issues
|
|
1338
|
-
// storing large values in structs.
|
|
1339
|
-
const totalSumTex = new DataTexture();
|
|
1340
|
-
totalSumTex.type = FloatType;
|
|
1341
|
-
totalSumTex.format = RedFormat;
|
|
1342
|
-
totalSumTex.minFilter = LinearFilter;
|
|
1343
|
-
totalSumTex.magFilter = LinearFilter;
|
|
1344
|
-
totalSumTex.generateMipmaps = false;
|
|
1345
|
-
|
|
1346
1670
|
this.marginalWeights = marginalWeights;
|
|
1347
1671
|
this.conditionalWeights = conditionalWeights;
|
|
1348
|
-
this.totalSum = totalSumTex;
|
|
1349
1672
|
this.map = null;
|
|
1350
1673
|
|
|
1674
|
+
// the total sum value is separated into two values to work around low precision
|
|
1675
|
+
// storage of floating values in structs
|
|
1676
|
+
this.totalSumWhole = 0;
|
|
1677
|
+
this.totalSumDecimal = 0;
|
|
1678
|
+
|
|
1351
1679
|
}
|
|
1352
1680
|
|
|
1353
1681
|
dispose() {
|
|
1354
1682
|
|
|
1355
1683
|
this.marginalWeights.dispose();
|
|
1356
1684
|
this.conditionalWeights.dispose();
|
|
1357
|
-
this.totalSum.dispose();
|
|
1358
1685
|
if ( this.map ) this.map.dispose();
|
|
1359
1686
|
|
|
1360
1687
|
}
|
|
@@ -1469,15 +1796,17 @@ class EquirectHdrInfoUniform {
|
|
|
1469
1796
|
|
|
1470
1797
|
this.dispose();
|
|
1471
1798
|
|
|
1472
|
-
const { marginalWeights, conditionalWeights
|
|
1799
|
+
const { marginalWeights, conditionalWeights } = this;
|
|
1473
1800
|
marginalWeights.image = { width: height, height: 1, data: marginalDataArray };
|
|
1474
1801
|
marginalWeights.needsUpdate = true;
|
|
1475
1802
|
|
|
1476
1803
|
conditionalWeights.image = { width, height, data: conditionalDataArray };
|
|
1477
1804
|
conditionalWeights.needsUpdate = true;
|
|
1478
1805
|
|
|
1479
|
-
|
|
1480
|
-
|
|
1806
|
+
const totalSumWhole = ~ ~ totalSumValue;
|
|
1807
|
+
const totalSumDecimal = ( totalSumValue - totalSumWhole );
|
|
1808
|
+
this.totalSumWhole = totalSumWhole;
|
|
1809
|
+
this.totalSumDecimal = totalSumDecimal;
|
|
1481
1810
|
|
|
1482
1811
|
this.map = map;
|
|
1483
1812
|
|
|
@@ -1521,66 +1850,637 @@ class PhysicalCameraUniform {
|
|
|
1521
1850
|
|
|
1522
1851
|
}
|
|
1523
1852
|
|
|
1524
|
-
const
|
|
1853
|
+
const LIGHT_PIXELS = 6;
|
|
1854
|
+
const RECT_AREA_LIGHT = 0;
|
|
1855
|
+
const CIRC_AREA_LIGHT = 1;
|
|
1856
|
+
const SPOT_LIGHT = 2;
|
|
1857
|
+
class LightsInfoUniformStruct {
|
|
1525
1858
|
|
|
1526
|
-
|
|
1527
|
-
float schlickFresnel( float cosine, float f0 ) {
|
|
1859
|
+
constructor() {
|
|
1528
1860
|
|
|
1529
|
-
|
|
1861
|
+
const tex = new DataTexture( new Float32Array( 4 ), 1, 1 );
|
|
1862
|
+
tex.format = RGBAFormat;
|
|
1863
|
+
tex.type = FloatType;
|
|
1864
|
+
tex.wrapS = ClampToEdgeWrapping;
|
|
1865
|
+
tex.wrapT = ClampToEdgeWrapping;
|
|
1866
|
+
tex.generateMipmaps = false;
|
|
1867
|
+
|
|
1868
|
+
this.tex = tex;
|
|
1869
|
+
this.count = 0;
|
|
1530
1870
|
|
|
1531
1871
|
}
|
|
1532
1872
|
|
|
1533
|
-
|
|
1534
|
-
float schlickFresnelFromIor( float cosine, float iorRatio ) {
|
|
1873
|
+
updateFrom( lights, iesTextures = [] ) {
|
|
1535
1874
|
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1875
|
+
const tex = this.tex;
|
|
1876
|
+
const pixelCount = Math.max( lights.length * LIGHT_PIXELS, 1 );
|
|
1877
|
+
const dimension = Math.ceil( Math.sqrt( pixelCount ) );
|
|
1539
1878
|
|
|
1540
|
-
|
|
1879
|
+
if ( tex.image.width !== dimension ) {
|
|
1541
1880
|
|
|
1542
|
-
|
|
1543
|
-
mat3 getBasisFromNormal( vec3 normal ) {
|
|
1881
|
+
tex.dispose();
|
|
1544
1882
|
|
|
1545
|
-
|
|
1546
|
-
|
|
1883
|
+
tex.image.data = new Float32Array( dimension * dimension * 4 );
|
|
1884
|
+
tex.image.width = dimension;
|
|
1885
|
+
tex.image.height = dimension;
|
|
1547
1886
|
|
|
1548
|
-
|
|
1887
|
+
}
|
|
1549
1888
|
|
|
1550
|
-
|
|
1889
|
+
const floatArray = tex.image.data;
|
|
1551
1890
|
|
|
1552
|
-
|
|
1891
|
+
const u = new Vector3();
|
|
1892
|
+
const v = new Vector3();
|
|
1893
|
+
const m = new Matrix4();
|
|
1894
|
+
const worldQuaternion = new Quaternion();
|
|
1895
|
+
const eye = new Vector3();
|
|
1896
|
+
const target = new Vector3();
|
|
1897
|
+
const up = new Vector3();
|
|
1553
1898
|
|
|
1554
|
-
|
|
1899
|
+
for ( let i = 0, l = lights.length; i < l; i ++ ) {
|
|
1555
1900
|
|
|
1556
|
-
|
|
1557
|
-
vec3 ortho2 = normalize( cross( normal, ortho ) );
|
|
1558
|
-
return mat3( ortho2, ortho, normal );
|
|
1901
|
+
const l = lights[ i ];
|
|
1559
1902
|
|
|
1560
|
-
|
|
1903
|
+
const baseIndex = i * LIGHT_PIXELS * 4;
|
|
1904
|
+
let index = 0;
|
|
1561
1905
|
|
|
1562
|
-
|
|
1906
|
+
// sample 1
|
|
1907
|
+
// position
|
|
1908
|
+
l.getWorldPosition( v );
|
|
1909
|
+
floatArray[ baseIndex + ( index ++ ) ] = v.x;
|
|
1910
|
+
floatArray[ baseIndex + ( index ++ ) ] = v.y;
|
|
1911
|
+
floatArray[ baseIndex + ( index ++ ) ] = v.z;
|
|
1563
1912
|
|
|
1564
|
-
|
|
1913
|
+
// type
|
|
1914
|
+
let type = RECT_AREA_LIGHT;
|
|
1915
|
+
if ( l.isRectAreaLight && l.isCircular ) type = CIRC_AREA_LIGHT;
|
|
1916
|
+
else if ( l.isSpotLight ) type = SPOT_LIGHT;
|
|
1917
|
+
floatArray[ baseIndex + ( index ++ ) ] = type;
|
|
1565
1918
|
|
|
1566
|
-
|
|
1919
|
+
// sample 2
|
|
1920
|
+
// color
|
|
1921
|
+
floatArray[ baseIndex + ( index ++ ) ] = l.color.r;
|
|
1922
|
+
floatArray[ baseIndex + ( index ++ ) ] = l.color.g;
|
|
1923
|
+
floatArray[ baseIndex + ( index ++ ) ] = l.color.b;
|
|
1567
1924
|
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
// we find a ray like that we ignore it to avoid artifacts.
|
|
1571
|
-
// This function returns if the direction is on the same side of both planes.
|
|
1572
|
-
bool isDirectionValid( vec3 direction, vec3 surfaceNormal, vec3 geometryNormal ) {
|
|
1925
|
+
// intensity
|
|
1926
|
+
floatArray[ baseIndex + ( index ++ ) ] = l.intensity;
|
|
1573
1927
|
|
|
1574
|
-
|
|
1575
|
-
bool aboveGeometryNormal = dot( direction, geometryNormal ) > 0.0;
|
|
1576
|
-
return aboveSurfaceNormal == aboveGeometryNormal;
|
|
1928
|
+
l.getWorldQuaternion( worldQuaternion );
|
|
1577
1929
|
|
|
1578
|
-
|
|
1930
|
+
if ( l.isRectAreaLight ) {
|
|
1579
1931
|
|
|
1580
|
-
|
|
1932
|
+
// sample 3
|
|
1933
|
+
// u vector
|
|
1934
|
+
u.set( l.width, 0, 0 ).applyQuaternion( worldQuaternion );
|
|
1581
1935
|
|
|
1582
|
-
|
|
1583
|
-
|
|
1936
|
+
floatArray[ baseIndex + ( index ++ ) ] = u.x;
|
|
1937
|
+
floatArray[ baseIndex + ( index ++ ) ] = u.y;
|
|
1938
|
+
floatArray[ baseIndex + ( index ++ ) ] = u.z;
|
|
1939
|
+
index ++;
|
|
1940
|
+
|
|
1941
|
+
// sample 4
|
|
1942
|
+
// v vector
|
|
1943
|
+
v.set( 0, l.height, 0 ).applyQuaternion( worldQuaternion );
|
|
1944
|
+
|
|
1945
|
+
floatArray[ baseIndex + ( index ++ ) ] = v.x;
|
|
1946
|
+
floatArray[ baseIndex + ( index ++ ) ] = v.y;
|
|
1947
|
+
floatArray[ baseIndex + ( index ++ ) ] = v.z;
|
|
1948
|
+
|
|
1949
|
+
// area
|
|
1950
|
+
floatArray[ baseIndex + ( index ++ ) ] = u.cross( v ).length() * ( l.isCircular ? ( Math.PI / 4.0 ) : 1.0 );
|
|
1951
|
+
|
|
1952
|
+
} else if ( l.isSpotLight ) {
|
|
1953
|
+
|
|
1954
|
+
const radius = l.radius;
|
|
1955
|
+
eye.setFromMatrixPosition( l.matrixWorld );
|
|
1956
|
+
target.setFromMatrixPosition( l.target.matrixWorld );
|
|
1957
|
+
m.lookAt( eye, target, up );
|
|
1958
|
+
worldQuaternion.setFromRotationMatrix( m );
|
|
1959
|
+
|
|
1960
|
+
// sample 3
|
|
1961
|
+
// u vector
|
|
1962
|
+
u.set( 1, 0, 0 ).applyQuaternion( worldQuaternion );
|
|
1963
|
+
|
|
1964
|
+
floatArray[ baseIndex + ( index ++ ) ] = u.x;
|
|
1965
|
+
floatArray[ baseIndex + ( index ++ ) ] = u.y;
|
|
1966
|
+
floatArray[ baseIndex + ( index ++ ) ] = u.z;
|
|
1967
|
+
index ++;
|
|
1968
|
+
|
|
1969
|
+
// sample 4
|
|
1970
|
+
// v vector
|
|
1971
|
+
v.set( 0, 1, 0 ).applyQuaternion( worldQuaternion );
|
|
1972
|
+
|
|
1973
|
+
floatArray[ baseIndex + ( index ++ ) ] = v.x;
|
|
1974
|
+
floatArray[ baseIndex + ( index ++ ) ] = v.y;
|
|
1975
|
+
floatArray[ baseIndex + ( index ++ ) ] = v.z;
|
|
1976
|
+
|
|
1977
|
+
// area
|
|
1978
|
+
floatArray[ baseIndex + ( index ++ ) ] = Math.PI * radius * radius;
|
|
1979
|
+
|
|
1980
|
+
// sample 5
|
|
1981
|
+
// radius
|
|
1982
|
+
floatArray[ baseIndex + ( index ++ ) ] = radius;
|
|
1983
|
+
|
|
1984
|
+
// near
|
|
1985
|
+
floatArray[ baseIndex + ( index ++ ) ] = l.shadow.camera.near;
|
|
1986
|
+
|
|
1987
|
+
// decay
|
|
1988
|
+
floatArray[ baseIndex + ( index ++ ) ] = l.decay;
|
|
1989
|
+
|
|
1990
|
+
// distance
|
|
1991
|
+
floatArray[ baseIndex + ( index ++ ) ] = l.distance;
|
|
1992
|
+
|
|
1993
|
+
// sample 6
|
|
1994
|
+
// coneCos
|
|
1995
|
+
floatArray[ baseIndex + ( index ++ ) ] = Math.cos( l.angle );
|
|
1996
|
+
|
|
1997
|
+
// penumbraCos
|
|
1998
|
+
floatArray[ baseIndex + ( index ++ ) ] = Math.cos( l.angle * ( 1 - l.penumbra ) );
|
|
1999
|
+
|
|
2000
|
+
// iesProfile
|
|
2001
|
+
floatArray[ baseIndex + ( index ++ ) ] = iesTextures.indexOf( l.iesTexture );
|
|
2002
|
+
|
|
2003
|
+
}
|
|
2004
|
+
|
|
2005
|
+
}
|
|
2006
|
+
|
|
2007
|
+
tex.needsUpdate = true;
|
|
2008
|
+
this.count = lights.length;
|
|
2009
|
+
|
|
2010
|
+
}
|
|
2011
|
+
|
|
2012
|
+
}
|
|
2013
|
+
|
|
2014
|
+
function IESLamp( text ) {
|
|
2015
|
+
|
|
2016
|
+
const _self = this;
|
|
2017
|
+
|
|
2018
|
+
const textArray = text.split( '\n' );
|
|
2019
|
+
|
|
2020
|
+
let lineNumber = 0;
|
|
2021
|
+
let line;
|
|
2022
|
+
|
|
2023
|
+
_self.verAngles = [ ];
|
|
2024
|
+
_self.horAngles = [ ];
|
|
2025
|
+
|
|
2026
|
+
_self.candelaValues = [ ];
|
|
2027
|
+
|
|
2028
|
+
_self.tiltData = { };
|
|
2029
|
+
_self.tiltData.angles = [ ];
|
|
2030
|
+
_self.tiltData.mulFactors = [ ];
|
|
2031
|
+
|
|
2032
|
+
function textToArray( text ) {
|
|
2033
|
+
|
|
2034
|
+
text = text.replace( /^\s+|\s+$/g, '' ); // remove leading or trailing spaces
|
|
2035
|
+
text = text.replace( /,/g, ' ' ); // replace commas with spaces
|
|
2036
|
+
text = text.replace( /\s\s+/g, ' ' ); // replace white space/tabs etc by single whitespace
|
|
2037
|
+
|
|
2038
|
+
const array = text.split( ' ' );
|
|
2039
|
+
|
|
2040
|
+
return array;
|
|
2041
|
+
|
|
2042
|
+
}
|
|
2043
|
+
|
|
2044
|
+
function readArray( count, array ) {
|
|
2045
|
+
|
|
2046
|
+
while ( true ) {
|
|
2047
|
+
|
|
2048
|
+
const line = textArray[ lineNumber ++ ];
|
|
2049
|
+
const lineData = textToArray( line );
|
|
2050
|
+
|
|
2051
|
+
for ( let i = 0; i < lineData.length; ++ i ) {
|
|
2052
|
+
|
|
2053
|
+
array.push( Number( lineData[ i ] ) );
|
|
2054
|
+
|
|
2055
|
+
}
|
|
2056
|
+
|
|
2057
|
+
if ( array.length === count )
|
|
2058
|
+
break;
|
|
2059
|
+
|
|
2060
|
+
}
|
|
2061
|
+
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
function readTilt() {
|
|
2065
|
+
|
|
2066
|
+
let line = textArray[ lineNumber ++ ];
|
|
2067
|
+
let lineData = textToArray( line );
|
|
2068
|
+
|
|
2069
|
+
_self.tiltData.lampToLumGeometry = Number( lineData[ 0 ] );
|
|
2070
|
+
|
|
2071
|
+
line = textArray[ lineNumber ++ ];
|
|
2072
|
+
lineData = textToArray( line );
|
|
2073
|
+
|
|
2074
|
+
_self.tiltData.numAngles = Number( lineData[ 0 ] );
|
|
2075
|
+
|
|
2076
|
+
readArray( _self.tiltData.numAngles, _self.tiltData.angles );
|
|
2077
|
+
readArray( _self.tiltData.numAngles, _self.tiltData.mulFactors );
|
|
2078
|
+
|
|
2079
|
+
}
|
|
2080
|
+
|
|
2081
|
+
function readLampValues() {
|
|
2082
|
+
|
|
2083
|
+
const values = [ ];
|
|
2084
|
+
readArray( 10, values );
|
|
2085
|
+
|
|
2086
|
+
_self.count = Number( values[ 0 ] );
|
|
2087
|
+
_self.lumens = Number( values[ 1 ] );
|
|
2088
|
+
_self.multiplier = Number( values[ 2 ] );
|
|
2089
|
+
_self.numVerAngles = Number( values[ 3 ] );
|
|
2090
|
+
_self.numHorAngles = Number( values[ 4 ] );
|
|
2091
|
+
_self.gonioType = Number( values[ 5 ] );
|
|
2092
|
+
_self.units = Number( values[ 6 ] );
|
|
2093
|
+
_self.width = Number( values[ 7 ] );
|
|
2094
|
+
_self.length = Number( values[ 8 ] );
|
|
2095
|
+
_self.height = Number( values[ 9 ] );
|
|
2096
|
+
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2099
|
+
function readLampFactors() {
|
|
2100
|
+
|
|
2101
|
+
const values = [ ];
|
|
2102
|
+
readArray( 3, values );
|
|
2103
|
+
|
|
2104
|
+
_self.ballFactor = Number( values[ 0 ] );
|
|
2105
|
+
_self.blpFactor = Number( values[ 1 ] );
|
|
2106
|
+
_self.inputWatts = Number( values[ 2 ] );
|
|
2107
|
+
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
while ( true ) {
|
|
2111
|
+
|
|
2112
|
+
line = textArray[ lineNumber ++ ];
|
|
2113
|
+
|
|
2114
|
+
if ( line.includes( 'TILT' ) ) {
|
|
2115
|
+
|
|
2116
|
+
break;
|
|
2117
|
+
|
|
2118
|
+
}
|
|
2119
|
+
|
|
2120
|
+
}
|
|
2121
|
+
|
|
2122
|
+
if ( ! line.includes( 'NONE' ) ) {
|
|
2123
|
+
|
|
2124
|
+
if ( line.includes( 'INCLUDE' ) ) {
|
|
2125
|
+
|
|
2126
|
+
readTilt();
|
|
2127
|
+
|
|
2128
|
+
} else {
|
|
2129
|
+
|
|
2130
|
+
// TODO:: Read tilt data from a file
|
|
2131
|
+
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
}
|
|
2135
|
+
|
|
2136
|
+
readLampValues();
|
|
2137
|
+
|
|
2138
|
+
readLampFactors();
|
|
2139
|
+
|
|
2140
|
+
// Initialize candela value array
|
|
2141
|
+
for ( let i = 0; i < _self.numHorAngles; ++ i ) {
|
|
2142
|
+
|
|
2143
|
+
_self.candelaValues.push( [ ] );
|
|
2144
|
+
|
|
2145
|
+
}
|
|
2146
|
+
|
|
2147
|
+
// Parse Angles
|
|
2148
|
+
readArray( _self.numVerAngles, _self.verAngles );
|
|
2149
|
+
readArray( _self.numHorAngles, _self.horAngles );
|
|
2150
|
+
|
|
2151
|
+
// Parse Candela values
|
|
2152
|
+
for ( let i = 0; i < _self.numHorAngles; ++ i ) {
|
|
2153
|
+
|
|
2154
|
+
readArray( _self.numVerAngles, _self.candelaValues[ i ] );
|
|
2155
|
+
|
|
2156
|
+
}
|
|
2157
|
+
|
|
2158
|
+
// Calculate actual candela values, and normalize.
|
|
2159
|
+
for ( let i = 0; i < _self.numHorAngles; ++ i ) {
|
|
2160
|
+
|
|
2161
|
+
for ( let j = 0; j < _self.numVerAngles; ++ j ) {
|
|
2162
|
+
|
|
2163
|
+
_self.candelaValues[ i ][ j ] *= _self.candelaValues[ i ][ j ] * _self.multiplier
|
|
2164
|
+
* _self.ballFactor * _self.blpFactor;
|
|
2165
|
+
|
|
2166
|
+
}
|
|
2167
|
+
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2170
|
+
let maxVal = - 1;
|
|
2171
|
+
for ( let i = 0; i < _self.numHorAngles; ++ i ) {
|
|
2172
|
+
|
|
2173
|
+
for ( let j = 0; j < _self.numVerAngles; ++ j ) {
|
|
2174
|
+
|
|
2175
|
+
const value = _self.candelaValues[ i ][ j ];
|
|
2176
|
+
maxVal = maxVal < value ? value : maxVal;
|
|
2177
|
+
|
|
2178
|
+
}
|
|
2179
|
+
|
|
2180
|
+
}
|
|
2181
|
+
|
|
2182
|
+
const bNormalize = true;
|
|
2183
|
+
if ( bNormalize && maxVal > 0 ) {
|
|
2184
|
+
|
|
2185
|
+
for ( let i = 0; i < _self.numHorAngles; ++ i ) {
|
|
2186
|
+
|
|
2187
|
+
for ( let j = 0; j < _self.numVerAngles; ++ j ) {
|
|
2188
|
+
|
|
2189
|
+
_self.candelaValues[ i ][ j ] /= maxVal;
|
|
2190
|
+
|
|
2191
|
+
}
|
|
2192
|
+
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
}
|
|
2196
|
+
|
|
2197
|
+
}
|
|
2198
|
+
|
|
2199
|
+
class IESLoader extends Loader {
|
|
2200
|
+
|
|
2201
|
+
_getIESValues( iesLamp ) {
|
|
2202
|
+
|
|
2203
|
+
const width = 360;
|
|
2204
|
+
const height = 180;
|
|
2205
|
+
const size = width * height;
|
|
2206
|
+
|
|
2207
|
+
const data = new Float32Array( size );
|
|
2208
|
+
|
|
2209
|
+
function interpolateCandelaValues( phi, theta ) {
|
|
2210
|
+
|
|
2211
|
+
let phiIndex = 0, thetaIndex = 0;
|
|
2212
|
+
let startTheta = 0, endTheta = 0, startPhi = 0, endPhi = 0;
|
|
2213
|
+
|
|
2214
|
+
for ( let i = 0; i < iesLamp.numHorAngles - 1; ++ i ) { // numHorAngles = horAngles.length-1 because of extra padding, so this wont cause an out of bounds error
|
|
2215
|
+
|
|
2216
|
+
if ( theta < iesLamp.horAngles[ i + 1 ] || i == iesLamp.numHorAngles - 2 ) {
|
|
2217
|
+
|
|
2218
|
+
thetaIndex = i;
|
|
2219
|
+
startTheta = iesLamp.horAngles[ i ];
|
|
2220
|
+
endTheta = iesLamp.horAngles[ i + 1 ];
|
|
2221
|
+
|
|
2222
|
+
break;
|
|
2223
|
+
|
|
2224
|
+
}
|
|
2225
|
+
|
|
2226
|
+
}
|
|
2227
|
+
|
|
2228
|
+
for ( let i = 0; i < iesLamp.numVerAngles - 1; ++ i ) {
|
|
2229
|
+
|
|
2230
|
+
if ( phi < iesLamp.verAngles[ i + 1 ] || i == iesLamp.numVerAngles - 2 ) {
|
|
2231
|
+
|
|
2232
|
+
phiIndex = i;
|
|
2233
|
+
startPhi = iesLamp.verAngles[ i ];
|
|
2234
|
+
endPhi = iesLamp.verAngles[ i + 1 ];
|
|
2235
|
+
|
|
2236
|
+
break;
|
|
2237
|
+
|
|
2238
|
+
}
|
|
2239
|
+
|
|
2240
|
+
}
|
|
2241
|
+
|
|
2242
|
+
const deltaTheta = endTheta - startTheta;
|
|
2243
|
+
const deltaPhi = endPhi - startPhi;
|
|
2244
|
+
|
|
2245
|
+
if ( deltaPhi === 0 ) // Outside range
|
|
2246
|
+
return 0;
|
|
2247
|
+
|
|
2248
|
+
const t1 = deltaTheta === 0 ? 0 : ( theta - startTheta ) / deltaTheta;
|
|
2249
|
+
const t2 = ( phi - startPhi ) / deltaPhi;
|
|
2250
|
+
|
|
2251
|
+
const nextThetaIndex = deltaTheta === 0 ? thetaIndex : thetaIndex + 1;
|
|
2252
|
+
|
|
2253
|
+
const v1 = MathUtils.lerp( iesLamp.candelaValues[ thetaIndex ][ phiIndex ], iesLamp.candelaValues[ nextThetaIndex ][ phiIndex ], t1 );
|
|
2254
|
+
const v2 = MathUtils.lerp( iesLamp.candelaValues[ thetaIndex ][ phiIndex + 1 ], iesLamp.candelaValues[ nextThetaIndex ][ phiIndex + 1 ], t1 );
|
|
2255
|
+
const v = MathUtils.lerp( v1, v2, t2 );
|
|
2256
|
+
|
|
2257
|
+
return v;
|
|
2258
|
+
|
|
2259
|
+
}
|
|
2260
|
+
|
|
2261
|
+
const startTheta = iesLamp.horAngles[ 0 ], endTheta = iesLamp.horAngles[ iesLamp.numHorAngles - 1 ];
|
|
2262
|
+
for ( let i = 0; i < size; ++ i ) {
|
|
2263
|
+
|
|
2264
|
+
let theta = i % width;
|
|
2265
|
+
const phi = Math.floor( i / width );
|
|
2266
|
+
|
|
2267
|
+
if ( endTheta - startTheta !== 0 && ( theta < startTheta || theta >= endTheta ) ) { // Handle symmetry for hor angles
|
|
2268
|
+
|
|
2269
|
+
theta %= endTheta * 2;
|
|
2270
|
+
if ( theta > endTheta )
|
|
2271
|
+
theta = endTheta * 2 - theta;
|
|
2272
|
+
|
|
2273
|
+
}
|
|
2274
|
+
|
|
2275
|
+
data[ i ] = interpolateCandelaValues( phi, theta );
|
|
2276
|
+
|
|
2277
|
+
}
|
|
2278
|
+
|
|
2279
|
+
return data;
|
|
2280
|
+
|
|
2281
|
+
}
|
|
2282
|
+
|
|
2283
|
+
load( url, onLoad, onProgress, onError ) {
|
|
2284
|
+
|
|
2285
|
+
const loader = new FileLoader( this.manager );
|
|
2286
|
+
loader.setResponseType( 'text' );
|
|
2287
|
+
loader.setCrossOrigin( this.crossOrigin );
|
|
2288
|
+
loader.setWithCredentials( this.withCredentials );
|
|
2289
|
+
loader.setPath( this.path );
|
|
2290
|
+
loader.setRequestHeader( this.requestHeader );
|
|
2291
|
+
|
|
2292
|
+
const texture = new DataTexture( null, 360, 180, RedFormat, FloatType );
|
|
2293
|
+
texture.minFilter = LinearFilter;
|
|
2294
|
+
texture.magFilter = LinearFilter;
|
|
2295
|
+
|
|
2296
|
+
loader.load( url, text => {
|
|
2297
|
+
|
|
2298
|
+
const iesLamp = new IESLamp( text );
|
|
2299
|
+
|
|
2300
|
+
texture.image.data = this._getIESValues( iesLamp );
|
|
2301
|
+
texture.needsUpdate = true;
|
|
2302
|
+
|
|
2303
|
+
if ( onLoad !== undefined ) {
|
|
2304
|
+
|
|
2305
|
+
onLoad( texture );
|
|
2306
|
+
|
|
2307
|
+
}
|
|
2308
|
+
|
|
2309
|
+
}, onProgress, onError );
|
|
2310
|
+
|
|
2311
|
+
return texture;
|
|
2312
|
+
|
|
2313
|
+
}
|
|
2314
|
+
|
|
2315
|
+
parse( text ) {
|
|
2316
|
+
|
|
2317
|
+
const iesLamp = new IESLamp( text );
|
|
2318
|
+
const texture = new DataTexture( null, 360, 180, RedFormat, FloatType );
|
|
2319
|
+
texture.minFilter = LinearFilter;
|
|
2320
|
+
texture.magFilter = LinearFilter;
|
|
2321
|
+
texture.image.data = this._getIESValues( iesLamp );
|
|
2322
|
+
texture.needsUpdate = true;
|
|
2323
|
+
|
|
2324
|
+
return texture;
|
|
2325
|
+
|
|
2326
|
+
}
|
|
2327
|
+
|
|
2328
|
+
}
|
|
2329
|
+
|
|
2330
|
+
const prevColor = new Color();
|
|
2331
|
+
class IESProfilesTexture extends WebGLArrayRenderTarget {
|
|
2332
|
+
|
|
2333
|
+
constructor( ...args ) {
|
|
2334
|
+
|
|
2335
|
+
super( ...args );
|
|
2336
|
+
|
|
2337
|
+
const tex = this.texture;
|
|
2338
|
+
tex.format = RGBAFormat;
|
|
2339
|
+
tex.type = FloatType;
|
|
2340
|
+
tex.minFilter = LinearFilter;
|
|
2341
|
+
tex.magFilter = LinearFilter;
|
|
2342
|
+
tex.wrapS = ClampToEdgeWrapping;
|
|
2343
|
+
tex.wrapT = ClampToEdgeWrapping;
|
|
2344
|
+
tex.generateMipmaps = false;
|
|
2345
|
+
|
|
2346
|
+
tex.updateFrom = ( ...args ) => {
|
|
2347
|
+
|
|
2348
|
+
this.updateFrom( ...args );
|
|
2349
|
+
|
|
2350
|
+
};
|
|
2351
|
+
|
|
2352
|
+
const fsQuad = new FullScreenQuad( new MeshBasicMaterial() );
|
|
2353
|
+
this.fsQuad = fsQuad;
|
|
2354
|
+
|
|
2355
|
+
this.iesLoader = new IESLoader();
|
|
2356
|
+
|
|
2357
|
+
}
|
|
2358
|
+
|
|
2359
|
+
async updateFrom( renderer, textures ) {
|
|
2360
|
+
|
|
2361
|
+
// save previous renderer state
|
|
2362
|
+
const prevRenderTarget = renderer.getRenderTarget();
|
|
2363
|
+
const prevToneMapping = renderer.toneMapping;
|
|
2364
|
+
const prevAlpha = renderer.getClearAlpha();
|
|
2365
|
+
renderer.getClearColor( prevColor );
|
|
2366
|
+
|
|
2367
|
+
// resize the render target and ensure we don't have an empty texture
|
|
2368
|
+
// render target depth must be >= 1 to avoid unbound texture error on android devices
|
|
2369
|
+
const depth = textures.length || 1;
|
|
2370
|
+
this.setSize( 360, 180, depth );
|
|
2371
|
+
renderer.setClearColor( 0, 0 );
|
|
2372
|
+
renderer.toneMapping = NoToneMapping;
|
|
2373
|
+
|
|
2374
|
+
// render each texture into each layer of the target
|
|
2375
|
+
const fsQuad = this.fsQuad;
|
|
2376
|
+
for ( let i = 0, l = depth; i < l; i ++ ) {
|
|
2377
|
+
|
|
2378
|
+
const texture = textures[ i ];
|
|
2379
|
+
if ( texture ) {
|
|
2380
|
+
|
|
2381
|
+
// revert to default texture transform before rendering
|
|
2382
|
+
texture.matrixAutoUpdate = false;
|
|
2383
|
+
texture.matrix.identity();
|
|
2384
|
+
|
|
2385
|
+
fsQuad.material.map = texture;
|
|
2386
|
+
fsQuad.material.transparent = true;
|
|
2387
|
+
|
|
2388
|
+
renderer.setRenderTarget( this, i );
|
|
2389
|
+
fsQuad.render( renderer );
|
|
2390
|
+
|
|
2391
|
+
// restore custom texture transform
|
|
2392
|
+
texture.updateMatrix();
|
|
2393
|
+
texture.matrixAutoUpdate = true;
|
|
2394
|
+
|
|
2395
|
+
}
|
|
2396
|
+
|
|
2397
|
+
}
|
|
2398
|
+
|
|
2399
|
+
// reset the renderer
|
|
2400
|
+
fsQuad.material.map = null;
|
|
2401
|
+
renderer.setClearColor( prevColor, prevAlpha );
|
|
2402
|
+
renderer.setRenderTarget( prevRenderTarget );
|
|
2403
|
+
renderer.toneMapping = prevToneMapping;
|
|
2404
|
+
|
|
2405
|
+
fsQuad.dispose();
|
|
2406
|
+
|
|
2407
|
+
}
|
|
2408
|
+
|
|
2409
|
+
dispose() {
|
|
2410
|
+
|
|
2411
|
+
super.dispose();
|
|
2412
|
+
this.fsQuad.dispose();
|
|
2413
|
+
|
|
2414
|
+
}
|
|
2415
|
+
|
|
2416
|
+
}
|
|
2417
|
+
|
|
2418
|
+
const shaderUtils = /* glsl */`
|
|
2419
|
+
|
|
2420
|
+
// https://google.github.io/filament/Filament.md.html#materialsystem/diffusebrdf
|
|
2421
|
+
float schlickFresnel( float cosine, float f0 ) {
|
|
2422
|
+
|
|
2423
|
+
return f0 + ( 1.0 - f0 ) * pow( 1.0 - cosine, 5.0 );
|
|
2424
|
+
|
|
2425
|
+
}
|
|
2426
|
+
|
|
2427
|
+
vec3 schlickFresnel( float cosine, vec3 f0 ) {
|
|
2428
|
+
|
|
2429
|
+
return f0 + ( 1.0 - f0 ) * pow( 1.0 - cosine, 5.0 );
|
|
2430
|
+
|
|
2431
|
+
}
|
|
2432
|
+
|
|
2433
|
+
// https://raytracing.github.io/books/RayTracingInOneWeekend.html#dielectrics/schlickapproximation
|
|
2434
|
+
float schlickFresnelFromIor( float cosine, float iorRatio ) {
|
|
2435
|
+
|
|
2436
|
+
// Schlick approximation
|
|
2437
|
+
float r_0 = pow( ( 1.0 - iorRatio ) / ( 1.0 + iorRatio ), 2.0 );
|
|
2438
|
+
return schlickFresnel( cosine, r_0 );
|
|
2439
|
+
|
|
2440
|
+
}
|
|
2441
|
+
|
|
2442
|
+
// forms a basis with the normal vector as Z
|
|
2443
|
+
mat3 getBasisFromNormal( vec3 normal ) {
|
|
2444
|
+
|
|
2445
|
+
vec3 other;
|
|
2446
|
+
if ( abs( normal.x ) > 0.5 ) {
|
|
2447
|
+
|
|
2448
|
+
other = vec3( 0.0, 1.0, 0.0 );
|
|
2449
|
+
|
|
2450
|
+
} else {
|
|
2451
|
+
|
|
2452
|
+
other = vec3( 1.0, 0.0, 0.0 );
|
|
2453
|
+
|
|
2454
|
+
}
|
|
2455
|
+
|
|
2456
|
+
vec3 ortho = normalize( cross( normal, other ) );
|
|
2457
|
+
vec3 ortho2 = normalize( cross( normal, ortho ) );
|
|
2458
|
+
return mat3( ortho2, ortho, normal );
|
|
2459
|
+
|
|
2460
|
+
}
|
|
2461
|
+
|
|
2462
|
+
vec3 getHalfVector( vec3 a, vec3 b ) {
|
|
2463
|
+
|
|
2464
|
+
return normalize( a + b );
|
|
2465
|
+
|
|
2466
|
+
}
|
|
2467
|
+
|
|
2468
|
+
// The discrepancy between interpolated surface normal and geometry normal can cause issues when a ray
|
|
2469
|
+
// is cast that is on the top side of the geometry normal plane but below the surface normal plane. If
|
|
2470
|
+
// we find a ray like that we ignore it to avoid artifacts.
|
|
2471
|
+
// This function returns if the direction is on the same side of both planes.
|
|
2472
|
+
bool isDirectionValid( vec3 direction, vec3 surfaceNormal, vec3 geometryNormal ) {
|
|
2473
|
+
|
|
2474
|
+
bool aboveSurfaceNormal = dot( direction, surfaceNormal ) > 0.0;
|
|
2475
|
+
bool aboveGeometryNormal = dot( direction, geometryNormal ) > 0.0;
|
|
2476
|
+
return aboveSurfaceNormal == aboveGeometryNormal;
|
|
2477
|
+
|
|
2478
|
+
}
|
|
2479
|
+
|
|
2480
|
+
vec3 getHemisphereSample( vec3 n, vec2 uv ) {
|
|
2481
|
+
|
|
2482
|
+
// https://www.rorydriscoll.com/2009/01/07/better-sampling/
|
|
2483
|
+
// https://graphics.pixar.com/library/OrthonormalB/paper.pdf
|
|
1584
2484
|
float sign = n.z == 0.0 ? 1.0 : sign( n.z );
|
|
1585
2485
|
float a = - 1.0 / ( sign + n.z );
|
|
1586
2486
|
float b = n.x * n.y * a;
|
|
@@ -1670,84 +2570,200 @@ const shaderUtils = /* glsl */`
|
|
|
1670
2570
|
vec2 e2 = c - b;
|
|
1671
2571
|
vec2 diag = normalize( e1 + e2 );
|
|
1672
2572
|
|
|
1673
|
-
// pick a random point in the parallelogram
|
|
1674
|
-
vec2 r = rand2();
|
|
1675
|
-
if ( r.x + r.y > 1.0 ) {
|
|
2573
|
+
// pick a random point in the parallelogram
|
|
2574
|
+
vec2 r = rand2();
|
|
2575
|
+
if ( r.x + r.y > 1.0 ) {
|
|
2576
|
+
|
|
2577
|
+
r = vec2( 1.0 ) - r;
|
|
2578
|
+
|
|
2579
|
+
}
|
|
2580
|
+
|
|
2581
|
+
return e1 * r.x + e2 * r.y;
|
|
2582
|
+
|
|
2583
|
+
}
|
|
2584
|
+
|
|
2585
|
+
// samples an aperture shape with the given number of sides. 0 means circle
|
|
2586
|
+
vec2 sampleAperture( int blades ) {
|
|
2587
|
+
|
|
2588
|
+
if ( blades == 0 ) {
|
|
2589
|
+
|
|
2590
|
+
vec2 r = rand2();
|
|
2591
|
+
float angle = 2.0 * PI * r.x;
|
|
2592
|
+
float radius = sqrt( rand() );
|
|
2593
|
+
return vec2( cos( angle ), sin( angle ) ) * radius;
|
|
2594
|
+
|
|
2595
|
+
} else {
|
|
2596
|
+
|
|
2597
|
+
blades = max( blades, 3 );
|
|
2598
|
+
|
|
2599
|
+
vec3 r = rand3();
|
|
2600
|
+
float anglePerSegment = 2.0 * PI / float( blades );
|
|
2601
|
+
float segment = floor( float( blades ) * r.x );
|
|
2602
|
+
|
|
2603
|
+
float angle1 = anglePerSegment * segment;
|
|
2604
|
+
float angle2 = angle1 + anglePerSegment;
|
|
2605
|
+
vec2 a = vec2( sin( angle1 ), cos( angle1 ) );
|
|
2606
|
+
vec2 b = vec2( 0.0, 0.0 );
|
|
2607
|
+
vec2 c = vec2( sin( angle2 ), cos( angle2 ) );
|
|
2608
|
+
|
|
2609
|
+
return triangleSample( a, b, c );
|
|
2610
|
+
|
|
2611
|
+
}
|
|
2612
|
+
|
|
2613
|
+
}
|
|
2614
|
+
|
|
2615
|
+
float colorToLuminance( vec3 color ) {
|
|
2616
|
+
|
|
2617
|
+
// https://en.wikipedia.org/wiki/Relative_luminance
|
|
2618
|
+
return 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
|
|
2619
|
+
|
|
2620
|
+
}
|
|
2621
|
+
|
|
2622
|
+
// ray sampling x and z are swapped to align with expected background view
|
|
2623
|
+
vec2 equirectDirectionToUv( vec3 direction ) {
|
|
2624
|
+
|
|
2625
|
+
// from Spherical.setFromCartesianCoords
|
|
2626
|
+
vec2 uv = vec2( atan( direction.z, direction.x ), acos( direction.y ) );
|
|
2627
|
+
uv /= vec2( 2.0 * PI, PI );
|
|
2628
|
+
|
|
2629
|
+
// apply adjustments to get values in range [0, 1] and y right side up
|
|
2630
|
+
uv.x += 0.5;
|
|
2631
|
+
uv.y = 1.0 - uv.y;
|
|
2632
|
+
return uv;
|
|
2633
|
+
|
|
2634
|
+
}
|
|
2635
|
+
|
|
2636
|
+
vec3 equirectUvToDirection( vec2 uv ) {
|
|
2637
|
+
|
|
2638
|
+
// undo above adjustments
|
|
2639
|
+
uv.x -= 0.5;
|
|
2640
|
+
uv.y = 1.0 - uv.y;
|
|
2641
|
+
|
|
2642
|
+
// from Vector3.setFromSphericalCoords
|
|
2643
|
+
float theta = uv.x * 2.0 * PI;
|
|
2644
|
+
float phi = uv.y * PI;
|
|
2645
|
+
|
|
2646
|
+
float sinPhi = sin( phi );
|
|
2647
|
+
|
|
2648
|
+
return vec3( sinPhi * cos( theta ), cos( phi ), sinPhi * sin( theta ) );
|
|
2649
|
+
|
|
2650
|
+
}
|
|
2651
|
+
|
|
2652
|
+
// Fast arccos approximation used to remove banding artifacts caused by numerical errors in acos.
|
|
2653
|
+
// This is a cubic Lagrange interpolating polynomial for x = [-1, -1/2, 0, 1/2, 1].
|
|
2654
|
+
// For more information see: https://github.com/gkjohnson/three-gpu-pathtracer/pull/171#issuecomment-1152275248
|
|
2655
|
+
float acosApprox( float x ) {
|
|
2656
|
+
|
|
2657
|
+
x = clamp( x, -1.0, 1.0 );
|
|
2658
|
+
return ( - 0.69813170079773212 * x * x - 0.87266462599716477 ) * x + 1.5707963267948966;
|
|
2659
|
+
|
|
2660
|
+
}
|
|
2661
|
+
|
|
2662
|
+
// An acos with input values bound to the range [-1, 1].
|
|
2663
|
+
float acosSafe( float x ) {
|
|
2664
|
+
|
|
2665
|
+
return acos( clamp( x, -1.0, 1.0 ) );
|
|
2666
|
+
|
|
2667
|
+
}
|
|
2668
|
+
|
|
2669
|
+
float saturateCos( float val ) {
|
|
2670
|
+
|
|
2671
|
+
return clamp( val, 0.001, 1.0 );
|
|
2672
|
+
|
|
2673
|
+
}
|
|
2674
|
+
|
|
2675
|
+
float square( float t ) {
|
|
2676
|
+
|
|
2677
|
+
return t * t;
|
|
2678
|
+
|
|
2679
|
+
}
|
|
2680
|
+
|
|
2681
|
+
vec2 square( vec2 t) {
|
|
2682
|
+
|
|
2683
|
+
return t * t;
|
|
1676
2684
|
|
|
1677
|
-
|
|
2685
|
+
}
|
|
1678
2686
|
|
|
1679
|
-
|
|
2687
|
+
vec3 square( vec3 t ) {
|
|
1680
2688
|
|
|
1681
|
-
return
|
|
2689
|
+
return t * t;
|
|
1682
2690
|
|
|
1683
2691
|
}
|
|
1684
2692
|
|
|
1685
|
-
|
|
1686
|
-
vec2 sampleAperture( int blades ) {
|
|
2693
|
+
vec4 square( vec4 t ) {
|
|
1687
2694
|
|
|
1688
|
-
|
|
2695
|
+
return t * t;
|
|
1689
2696
|
|
|
1690
|
-
|
|
1691
|
-
float angle = 2.0 * PI * r.x;
|
|
1692
|
-
float radius = sqrt( rand() );
|
|
1693
|
-
return vec2( cos( angle ), sin( angle ) ) * radius;
|
|
2697
|
+
}
|
|
1694
2698
|
|
|
1695
|
-
|
|
2699
|
+
// Finds the point where the ray intersects the plane defined by u and v and checks if this point
|
|
2700
|
+
// falls in the bounds of the rectangle on that same plane.
|
|
2701
|
+
// Plane intersection: https://lousodrome.net/blog/light/2020/07/03/intersection-of-a-ray-and-a-plane/
|
|
2702
|
+
bool intersectsRectangle( vec3 center, vec3 normal, vec3 u, vec3 v, vec3 rayOrigin, vec3 rayDirection, out float dist ) {
|
|
1696
2703
|
|
|
1697
|
-
|
|
2704
|
+
float t = dot( center - rayOrigin, normal ) / dot( rayDirection, normal );
|
|
1698
2705
|
|
|
1699
|
-
|
|
1700
|
-
float anglePerSegment = 2.0 * PI / float( blades );
|
|
1701
|
-
float segment = floor( float( blades ) * r.x );
|
|
2706
|
+
if ( t > EPSILON ) {
|
|
1702
2707
|
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
vec2 a = vec2( sin( angle1 ), cos( angle1 ) );
|
|
1706
|
-
vec2 b = vec2( 0.0, 0.0 );
|
|
1707
|
-
vec2 c = vec2( sin( angle2 ), cos( angle2 ) );
|
|
2708
|
+
vec3 p = rayOrigin + rayDirection * t;
|
|
2709
|
+
vec3 vi = p - center;
|
|
1708
2710
|
|
|
1709
|
-
|
|
2711
|
+
// check if p falls inside the rectangle
|
|
2712
|
+
float a1 = dot( u, vi );
|
|
2713
|
+
if ( abs( a1 ) <= 0.5 ) {
|
|
2714
|
+
|
|
2715
|
+
float a2 = dot( v, vi );
|
|
2716
|
+
if ( abs( a2 ) <= 0.5 ) {
|
|
2717
|
+
|
|
2718
|
+
dist = t;
|
|
2719
|
+
return true;
|
|
2720
|
+
|
|
2721
|
+
}
|
|
2722
|
+
|
|
2723
|
+
}
|
|
1710
2724
|
|
|
1711
2725
|
}
|
|
1712
2726
|
|
|
2727
|
+
return false;
|
|
2728
|
+
|
|
1713
2729
|
}
|
|
1714
2730
|
|
|
1715
|
-
|
|
2731
|
+
// Finds the point where the ray intersects the plane defined by u and v and checks if this point
|
|
2732
|
+
// falls in the bounds of the circle on that same plane. See above URL for a description of the plane intersection algorithm.
|
|
2733
|
+
bool intersectsCircle( vec3 position, vec3 normal, vec3 u, vec3 v, vec3 rayOrigin, vec3 rayDirection, out float dist ) {
|
|
1716
2734
|
|
|
1717
|
-
|
|
1718
|
-
return 0.2126 * color.r + 0.7152 * color.g + 0.0722 * color.b;
|
|
2735
|
+
float t = dot( position - rayOrigin, normal ) / dot( rayDirection, normal );
|
|
1719
2736
|
|
|
1720
|
-
|
|
2737
|
+
if ( t > EPSILON ) {
|
|
1721
2738
|
|
|
1722
|
-
|
|
1723
|
-
|
|
2739
|
+
vec3 hit = rayOrigin + rayDirection * t;
|
|
2740
|
+
vec3 vi = hit - position;
|
|
1724
2741
|
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
uv /= vec2( 2.0 * PI, PI );
|
|
2742
|
+
float a1 = dot( u, vi );
|
|
2743
|
+
float a2 = dot( v, vi );
|
|
1728
2744
|
|
|
1729
|
-
|
|
1730
|
-
uv.x += 0.5;
|
|
1731
|
-
uv.y = 1.0 - uv.y;
|
|
1732
|
-
return uv;
|
|
2745
|
+
if( length( vec2( a1, a2 ) ) <= 0.5 ) {
|
|
1733
2746
|
|
|
1734
|
-
|
|
2747
|
+
dist = t;
|
|
2748
|
+
return true;
|
|
1735
2749
|
|
|
1736
|
-
|
|
2750
|
+
}
|
|
1737
2751
|
|
|
1738
|
-
|
|
1739
|
-
uv.x -= 0.5;
|
|
1740
|
-
uv.y = 1.0 - uv.y;
|
|
2752
|
+
}
|
|
1741
2753
|
|
|
1742
|
-
|
|
1743
|
-
float theta = uv.x * 2.0 * PI;
|
|
1744
|
-
float phi = uv.y * PI;
|
|
2754
|
+
return false;
|
|
1745
2755
|
|
|
1746
|
-
|
|
2756
|
+
}
|
|
1747
2757
|
|
|
1748
|
-
|
|
2758
|
+
// power heuristic for multiple importance sampling
|
|
2759
|
+
float misHeuristic( float a, float b ) {
|
|
2760
|
+
|
|
2761
|
+
float aa = a * a;
|
|
2762
|
+
float bb = b * b;
|
|
2763
|
+
return aa / ( aa + bb );
|
|
1749
2764
|
|
|
1750
2765
|
}
|
|
2766
|
+
|
|
1751
2767
|
`;
|
|
1752
2768
|
|
|
1753
2769
|
class PMREMCopyMaterial extends MaterialBase {
|
|
@@ -1859,97 +2875,312 @@ class BlurredEnvMapGenerator {
|
|
|
1859
2875
|
|
|
1860
2876
|
}
|
|
1861
2877
|
|
|
1862
|
-
const shaderMaterialStructs = /* glsl */ `
|
|
1863
|
-
|
|
1864
|
-
struct PhysicalCamera {
|
|
1865
|
-
|
|
1866
|
-
float focusDistance;
|
|
1867
|
-
float anamorphicRatio;
|
|
1868
|
-
float bokehSize;
|
|
1869
|
-
int apertureBlades;
|
|
1870
|
-
float apertureRotation;
|
|
1871
|
-
|
|
1872
|
-
};
|
|
1873
|
-
|
|
1874
|
-
struct EquirectHdrInfo {
|
|
1875
|
-
|
|
1876
|
-
sampler2D marginalWeights;
|
|
1877
|
-
sampler2D conditionalWeights;
|
|
1878
|
-
sampler2D map;
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
int
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
|
|
1922
|
-
|
|
1923
|
-
|
|
1924
|
-
|
|
1925
|
-
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1930
|
-
|
|
1931
|
-
|
|
1932
|
-
|
|
1933
|
-
|
|
1934
|
-
|
|
1935
|
-
|
|
1936
|
-
|
|
1937
|
-
|
|
1938
|
-
|
|
1939
|
-
|
|
1940
|
-
|
|
1941
|
-
|
|
1942
|
-
|
|
1943
|
-
|
|
1944
|
-
|
|
1945
|
-
|
|
1946
|
-
|
|
1947
|
-
|
|
1948
|
-
|
|
1949
|
-
|
|
1950
|
-
|
|
1951
|
-
|
|
1952
|
-
|
|
2878
|
+
const shaderMaterialStructs = /* glsl */ `
|
|
2879
|
+
|
|
2880
|
+
struct PhysicalCamera {
|
|
2881
|
+
|
|
2882
|
+
float focusDistance;
|
|
2883
|
+
float anamorphicRatio;
|
|
2884
|
+
float bokehSize;
|
|
2885
|
+
int apertureBlades;
|
|
2886
|
+
float apertureRotation;
|
|
2887
|
+
|
|
2888
|
+
};
|
|
2889
|
+
|
|
2890
|
+
struct EquirectHdrInfo {
|
|
2891
|
+
|
|
2892
|
+
sampler2D marginalWeights;
|
|
2893
|
+
sampler2D conditionalWeights;
|
|
2894
|
+
sampler2D map;
|
|
2895
|
+
|
|
2896
|
+
float totalSumWhole;
|
|
2897
|
+
float totalSumDecimal;
|
|
2898
|
+
|
|
2899
|
+
};
|
|
2900
|
+
|
|
2901
|
+
struct Material {
|
|
2902
|
+
|
|
2903
|
+
vec3 color;
|
|
2904
|
+
int map;
|
|
2905
|
+
|
|
2906
|
+
float metalness;
|
|
2907
|
+
int metalnessMap;
|
|
2908
|
+
|
|
2909
|
+
float roughness;
|
|
2910
|
+
int roughnessMap;
|
|
2911
|
+
|
|
2912
|
+
float ior;
|
|
2913
|
+
float transmission;
|
|
2914
|
+
int transmissionMap;
|
|
2915
|
+
|
|
2916
|
+
float emissiveIntensity;
|
|
2917
|
+
vec3 emissive;
|
|
2918
|
+
int emissiveMap;
|
|
2919
|
+
|
|
2920
|
+
int normalMap;
|
|
2921
|
+
vec2 normalScale;
|
|
2922
|
+
|
|
2923
|
+
float clearcoat;
|
|
2924
|
+
int clearcoatMap;
|
|
2925
|
+
int clearcoatNormalMap;
|
|
2926
|
+
vec2 clearcoatNormalScale;
|
|
2927
|
+
float clearcoatRoughness;
|
|
2928
|
+
int clearcoatRoughnessMap;
|
|
2929
|
+
|
|
2930
|
+
int iridescenceMap;
|
|
2931
|
+
int iridescenceThicknessMap;
|
|
2932
|
+
float iridescence;
|
|
2933
|
+
float iridescenceIor;
|
|
2934
|
+
float iridescenceThicknessMinimum;
|
|
2935
|
+
float iridescenceThicknessMaximum;
|
|
2936
|
+
|
|
2937
|
+
vec3 specularColor;
|
|
2938
|
+
int specularColorMap;
|
|
2939
|
+
|
|
2940
|
+
float specularIntensity;
|
|
2941
|
+
int specularIntensityMap;
|
|
2942
|
+
|
|
2943
|
+
int alphaMap;
|
|
2944
|
+
|
|
2945
|
+
bool castShadow;
|
|
2946
|
+
float opacity;
|
|
2947
|
+
float alphaTest;
|
|
2948
|
+
|
|
2949
|
+
float side;
|
|
2950
|
+
bool matte;
|
|
2951
|
+
|
|
2952
|
+
vec3 sheenColor;
|
|
2953
|
+
int sheenColorMap;
|
|
2954
|
+
float sheenRoughness;
|
|
2955
|
+
int sheenRoughnessMap;
|
|
2956
|
+
|
|
2957
|
+
mat3 mapTransform;
|
|
2958
|
+
mat3 metalnessMapTransform;
|
|
2959
|
+
mat3 roughnessMapTransform;
|
|
2960
|
+
mat3 transmissionMapTransform;
|
|
2961
|
+
mat3 emissiveMapTransform;
|
|
2962
|
+
mat3 normalMapTransform;
|
|
2963
|
+
mat3 clearcoatMapTransform;
|
|
2964
|
+
mat3 clearcoatNormalMapTransform;
|
|
2965
|
+
mat3 clearcoatRoughnessMapTransform;
|
|
2966
|
+
mat3 sheenColorMapTransform;
|
|
2967
|
+
mat3 sheenRoughnessMapTransform;
|
|
2968
|
+
mat3 iridescenceMapTransform;
|
|
2969
|
+
mat3 iridescenceThicknessMapTransform;
|
|
2970
|
+
mat3 specularColorMapTransform;
|
|
2971
|
+
mat3 specularIntensityMapTransform;
|
|
2972
|
+
|
|
2973
|
+
};
|
|
2974
|
+
|
|
2975
|
+
mat3 readTextureTransform( sampler2D tex, uint index ) {
|
|
2976
|
+
|
|
2977
|
+
mat3 textureTransform;
|
|
2978
|
+
|
|
2979
|
+
vec4 row1 = texelFetch1D( tex, index );
|
|
2980
|
+
vec4 row2 = texelFetch1D( tex, index + 1u );
|
|
2981
|
+
|
|
2982
|
+
textureTransform[0] = vec3(row1.r, row2.r, 0.0);
|
|
2983
|
+
textureTransform[1] = vec3(row1.g, row2.g, 0.0);
|
|
2984
|
+
textureTransform[2] = vec3(row1.b, row2.b, 1.0);
|
|
2985
|
+
|
|
2986
|
+
return textureTransform;
|
|
2987
|
+
|
|
2988
|
+
}
|
|
2989
|
+
|
|
2990
|
+
Material readMaterialInfo( sampler2D tex, uint index ) {
|
|
2991
|
+
|
|
2992
|
+
uint i = index * 44u;
|
|
2993
|
+
|
|
2994
|
+
vec4 s0 = texelFetch1D( tex, i + 0u );
|
|
2995
|
+
vec4 s1 = texelFetch1D( tex, i + 1u );
|
|
2996
|
+
vec4 s2 = texelFetch1D( tex, i + 2u );
|
|
2997
|
+
vec4 s3 = texelFetch1D( tex, i + 3u );
|
|
2998
|
+
vec4 s4 = texelFetch1D( tex, i + 4u );
|
|
2999
|
+
vec4 s5 = texelFetch1D( tex, i + 5u );
|
|
3000
|
+
vec4 s6 = texelFetch1D( tex, i + 6u );
|
|
3001
|
+
vec4 s7 = texelFetch1D( tex, i + 7u );
|
|
3002
|
+
vec4 s8 = texelFetch1D( tex, i + 8u );
|
|
3003
|
+
vec4 s9 = texelFetch1D( tex, i + 9u );
|
|
3004
|
+
vec4 s10 = texelFetch1D( tex, i + 10u );
|
|
3005
|
+
vec4 s11 = texelFetch1D( tex, i + 11u );
|
|
3006
|
+
vec4 s12 = texelFetch1D( tex, i + 12u );
|
|
3007
|
+
vec4 s13 = texelFetch1D( tex, i + 13u );
|
|
3008
|
+
|
|
3009
|
+
Material m;
|
|
3010
|
+
m.color = s0.rgb;
|
|
3011
|
+
m.map = int( round( s0.a ) );
|
|
3012
|
+
|
|
3013
|
+
m.metalness = s1.r;
|
|
3014
|
+
m.metalnessMap = int( round( s1.g ) );
|
|
3015
|
+
m.roughness = s1.b;
|
|
3016
|
+
m.roughnessMap = int( round( s1.a ) );
|
|
3017
|
+
|
|
3018
|
+
m.ior = s2.r;
|
|
3019
|
+
m.transmission = s2.g;
|
|
3020
|
+
m.transmissionMap = int( round( s2.b ) );
|
|
3021
|
+
m.emissiveIntensity = s2.a;
|
|
3022
|
+
|
|
3023
|
+
m.emissive = s3.rgb;
|
|
3024
|
+
m.emissiveMap = int( round( s3.a ) );
|
|
3025
|
+
|
|
3026
|
+
m.normalMap = int( round( s4.r ) );
|
|
3027
|
+
m.normalScale = s4.gb;
|
|
3028
|
+
|
|
3029
|
+
m.clearcoat = s4.a;
|
|
3030
|
+
m.clearcoatMap = int( round( s5.r ) );
|
|
3031
|
+
m.clearcoatRoughness = s5.g;
|
|
3032
|
+
m.clearcoatRoughnessMap = int( round( s5.b ) );
|
|
3033
|
+
m.clearcoatNormalMap = int( round( s5.a ) );
|
|
3034
|
+
m.clearcoatNormalScale = s6.rg;
|
|
3035
|
+
|
|
3036
|
+
m.sheenColor = s7.rgb;
|
|
3037
|
+
m.sheenColorMap = int( round( s7.a ) );
|
|
3038
|
+
m.sheenRoughness = s8.r;
|
|
3039
|
+
m.sheenRoughnessMap = int( round( s8.g ) );
|
|
3040
|
+
|
|
3041
|
+
m.iridescenceMap = int( round( s8.b ) );
|
|
3042
|
+
m.iridescenceThicknessMap = int( round( s8.a ) );
|
|
3043
|
+
m.iridescence = s9.r;
|
|
3044
|
+
m.iridescenceIor = s9.g;
|
|
3045
|
+
m.iridescenceThicknessMinimum = s9.b;
|
|
3046
|
+
m.iridescenceThicknessMaximum = s9.a;
|
|
3047
|
+
|
|
3048
|
+
m.specularColor = s10.rgb;
|
|
3049
|
+
m.specularColorMap = int( round( s10.a ) );
|
|
3050
|
+
|
|
3051
|
+
m.specularIntensity = s11.r;
|
|
3052
|
+
m.specularIntensityMap = int( round( s11.g ) );
|
|
3053
|
+
|
|
3054
|
+
m.alphaMap = int( round( s12.r ) );
|
|
3055
|
+
|
|
3056
|
+
m.opacity = s12.g;
|
|
3057
|
+
m.alphaTest = s12.b;
|
|
3058
|
+
m.side = s12.a;
|
|
3059
|
+
|
|
3060
|
+
m.matte = bool( s13.r );
|
|
3061
|
+
m.castShadow = ! bool( s13.g );
|
|
3062
|
+
|
|
3063
|
+
uint firstTextureTransformIdx = i + 14u;
|
|
3064
|
+
|
|
3065
|
+
m.mapTransform = m.map == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx );
|
|
3066
|
+
m.metalnessMapTransform = m.metalnessMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 2u );
|
|
3067
|
+
m.roughnessMapTransform = m.roughnessMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 4u );
|
|
3068
|
+
m.transmissionMapTransform = m.transmissionMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 6u );
|
|
3069
|
+
m.emissiveMapTransform = m.emissiveMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 8u );
|
|
3070
|
+
m.normalMapTransform = m.normalMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 10u );
|
|
3071
|
+
m.clearcoatMapTransform = m.clearcoatMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 12u );
|
|
3072
|
+
m.clearcoatNormalMapTransform = m.clearcoatNormalMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 14u );
|
|
3073
|
+
m.clearcoatRoughnessMapTransform = m.clearcoatRoughnessMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 16u );
|
|
3074
|
+
m.sheenColorMapTransform = m.sheenColorMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 18u );
|
|
3075
|
+
m.sheenRoughnessMapTransform = m.sheenRoughnessMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 20u );
|
|
3076
|
+
m.iridescenceMapTransform = m.iridescenceMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 22u );
|
|
3077
|
+
m.iridescenceThicknessMapTransform = m.iridescenceThicknessMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 24u );
|
|
3078
|
+
m.specularColorMapTransform = m.specularColorMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 26u );
|
|
3079
|
+
m.specularIntensityMapTransform = m.specularIntensityMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 28u );
|
|
3080
|
+
|
|
3081
|
+
return m;
|
|
3082
|
+
|
|
3083
|
+
}
|
|
3084
|
+
|
|
3085
|
+
`;
|
|
3086
|
+
|
|
3087
|
+
const shaderLightStruct = /* glsl */ `
|
|
3088
|
+
|
|
3089
|
+
#define RECT_AREA_LIGHT_TYPE 0
|
|
3090
|
+
#define CIRC_AREA_LIGHT_TYPE 1
|
|
3091
|
+
#define SPOT_LIGHT_TYPE 2
|
|
3092
|
+
|
|
3093
|
+
struct LightsInfo {
|
|
3094
|
+
|
|
3095
|
+
sampler2D tex;
|
|
3096
|
+
uint count;
|
|
3097
|
+
|
|
3098
|
+
};
|
|
3099
|
+
|
|
3100
|
+
struct Light {
|
|
3101
|
+
|
|
3102
|
+
vec3 position;
|
|
3103
|
+
int type;
|
|
3104
|
+
|
|
3105
|
+
vec3 color;
|
|
3106
|
+
float intensity;
|
|
3107
|
+
|
|
3108
|
+
vec3 u;
|
|
3109
|
+
vec3 v;
|
|
3110
|
+
float area;
|
|
3111
|
+
|
|
3112
|
+
// spot light fields
|
|
3113
|
+
float radius;
|
|
3114
|
+
float near;
|
|
3115
|
+
float decay;
|
|
3116
|
+
float distance;
|
|
3117
|
+
float coneCos;
|
|
3118
|
+
float penumbraCos;
|
|
3119
|
+
int iesProfile;
|
|
3120
|
+
|
|
3121
|
+
};
|
|
3122
|
+
|
|
3123
|
+
Light readLightInfo( sampler2D tex, uint index ) {
|
|
3124
|
+
|
|
3125
|
+
uint i = index * 6u;
|
|
3126
|
+
|
|
3127
|
+
vec4 s0 = texelFetch1D( tex, i + 0u );
|
|
3128
|
+
vec4 s1 = texelFetch1D( tex, i + 1u );
|
|
3129
|
+
vec4 s2 = texelFetch1D( tex, i + 2u );
|
|
3130
|
+
vec4 s3 = texelFetch1D( tex, i + 3u );
|
|
3131
|
+
|
|
3132
|
+
Light l;
|
|
3133
|
+
l.position = s0.rgb;
|
|
3134
|
+
l.type = int( round( s0.a ) );
|
|
3135
|
+
|
|
3136
|
+
l.color = s1.rgb;
|
|
3137
|
+
l.intensity = s1.a;
|
|
3138
|
+
|
|
3139
|
+
l.u = s2.rgb;
|
|
3140
|
+
l.v = s3.rgb;
|
|
3141
|
+
l.area = s3.a;
|
|
3142
|
+
|
|
3143
|
+
if ( l.type == SPOT_LIGHT_TYPE ) {
|
|
3144
|
+
|
|
3145
|
+
vec4 s4 = texelFetch1D( tex, i + 4u );
|
|
3146
|
+
vec4 s5 = texelFetch1D( tex, i + 5u );
|
|
3147
|
+
l.radius = s4.r;
|
|
3148
|
+
l.near = s4.g;
|
|
3149
|
+
l.decay = s4.b;
|
|
3150
|
+
l.distance = s4.a;
|
|
3151
|
+
|
|
3152
|
+
l.coneCos = s5.r;
|
|
3153
|
+
l.penumbraCos = s5.g;
|
|
3154
|
+
l.iesProfile = int( round ( s5.b ) );
|
|
3155
|
+
|
|
3156
|
+
}
|
|
3157
|
+
|
|
3158
|
+
return l;
|
|
3159
|
+
|
|
3160
|
+
}
|
|
3161
|
+
|
|
3162
|
+
struct SpotLight {
|
|
3163
|
+
|
|
3164
|
+
vec3 position;
|
|
3165
|
+
int type;
|
|
3166
|
+
|
|
3167
|
+
vec3 color;
|
|
3168
|
+
float intensity;
|
|
3169
|
+
|
|
3170
|
+
vec3 u;
|
|
3171
|
+
vec3 v;
|
|
3172
|
+
float area;
|
|
3173
|
+
|
|
3174
|
+
float radius;
|
|
3175
|
+
float near;
|
|
3176
|
+
float decay;
|
|
3177
|
+
float distance;
|
|
3178
|
+
float coneCos;
|
|
3179
|
+
float penumbraCos;
|
|
3180
|
+
int iesProfile;
|
|
3181
|
+
|
|
3182
|
+
};
|
|
3183
|
+
|
|
1953
3184
|
`;
|
|
1954
3185
|
|
|
1955
3186
|
const shaderGGXFunctions = /* glsl */`
|
|
@@ -2026,17 +3257,18 @@ float ggxDistribution( vec3 halfVector, float roughness ) {
|
|
|
2026
3257
|
|
|
2027
3258
|
// See equation (33) from reference [0]
|
|
2028
3259
|
float a2 = roughness * roughness;
|
|
3260
|
+
a2 = max( EPSILON, a2 );
|
|
2029
3261
|
float cosTheta = halfVector.z;
|
|
2030
3262
|
float cosTheta4 = pow( cosTheta, 4.0 );
|
|
2031
3263
|
|
|
2032
3264
|
if ( cosTheta == 0.0 ) return 0.0;
|
|
2033
3265
|
|
|
2034
|
-
float theta =
|
|
3266
|
+
float theta = acosSafe( halfVector.z );
|
|
2035
3267
|
float tanTheta = tan( theta );
|
|
2036
3268
|
float tanTheta2 = pow( tanTheta, 2.0 );
|
|
2037
3269
|
|
|
2038
3270
|
float denom = PI * cosTheta4 * pow( a2 + tanTheta2, 2.0 );
|
|
2039
|
-
return a2 / denom;
|
|
3271
|
+
return ( a2 / denom );
|
|
2040
3272
|
|
|
2041
3273
|
// See equation (1) from reference [2]
|
|
2042
3274
|
// const { x, y, z } = halfVector;
|
|
@@ -2055,9 +3287,239 @@ float ggxPDF( vec3 wi, vec3 halfVector, float roughness ) {
|
|
|
2055
3287
|
float D = ggxDistribution( halfVector, roughness );
|
|
2056
3288
|
float G1 = ggxShadowMaskG1( incidentTheta, roughness );
|
|
2057
3289
|
|
|
2058
|
-
return D * G1 * max( 0.0, dot( wi, halfVector ) ) / wi.z;
|
|
3290
|
+
return D * G1 * max( 0.0, dot( wi, halfVector ) ) / wi.z;
|
|
3291
|
+
|
|
3292
|
+
}
|
|
3293
|
+
`;
|
|
3294
|
+
|
|
3295
|
+
const shaderSheenFunctions = /* glsl */`
|
|
3296
|
+
|
|
3297
|
+
// See equation (2) in http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
|
|
3298
|
+
float velvetD( float cosThetaH, float roughness ) {
|
|
3299
|
+
|
|
3300
|
+
float alpha = max( roughness, 0.07 );
|
|
3301
|
+
alpha = alpha * alpha;
|
|
3302
|
+
|
|
3303
|
+
float invAlpha = 1.0 / alpha;
|
|
3304
|
+
|
|
3305
|
+
float sqrCosThetaH = cosThetaH * cosThetaH;
|
|
3306
|
+
float sinThetaH = max( 1.0 - sqrCosThetaH, 0.001 );
|
|
3307
|
+
|
|
3308
|
+
return ( 2.0 + invAlpha ) * pow( sinThetaH, 0.5 * invAlpha ) / ( 2.0 * PI );
|
|
3309
|
+
|
|
3310
|
+
}
|
|
3311
|
+
|
|
3312
|
+
float velvetParamsInterpolate( int i, float oneMinusAlphaSquared ) {
|
|
3313
|
+
|
|
3314
|
+
const float p0[5] = float[5]( 25.3245, 3.32435, 0.16801, -1.27393, -4.85967 );
|
|
3315
|
+
const float p1[5] = float[5]( 21.5473, 3.82987, 0.19823, -1.97760, -4.32054 );
|
|
3316
|
+
|
|
3317
|
+
return mix( p1[i], p0[i], oneMinusAlphaSquared );
|
|
3318
|
+
|
|
3319
|
+
}
|
|
3320
|
+
|
|
3321
|
+
float velvetL( float x, float alpha ) {
|
|
3322
|
+
|
|
3323
|
+
float oneMinusAlpha = 1.0 - alpha;
|
|
3324
|
+
float oneMinusAlphaSquared = oneMinusAlpha * oneMinusAlpha;
|
|
3325
|
+
|
|
3326
|
+
float a = velvetParamsInterpolate( 0, oneMinusAlphaSquared );
|
|
3327
|
+
float b = velvetParamsInterpolate( 1, oneMinusAlphaSquared );
|
|
3328
|
+
float c = velvetParamsInterpolate( 2, oneMinusAlphaSquared );
|
|
3329
|
+
float d = velvetParamsInterpolate( 3, oneMinusAlphaSquared );
|
|
3330
|
+
float e = velvetParamsInterpolate( 4, oneMinusAlphaSquared );
|
|
3331
|
+
|
|
3332
|
+
return a / ( 1.0 + b * pow( abs( x ), c ) ) + d * x + e;
|
|
3333
|
+
|
|
3334
|
+
}
|
|
3335
|
+
|
|
3336
|
+
// See equation (3) in http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
|
|
3337
|
+
float velvetLambda( float cosTheta, float alpha ) {
|
|
3338
|
+
|
|
3339
|
+
return abs( cosTheta ) < 0.5 ? exp( velvetL( cosTheta, alpha ) ) : exp( 2.0 * velvetL( 0.5, alpha ) - velvetL( 1.0 - cosTheta, alpha ) );
|
|
3340
|
+
|
|
3341
|
+
}
|
|
3342
|
+
|
|
3343
|
+
// See Section 3, Shadowing Term, in http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
|
|
3344
|
+
float velvetG( float cosThetaO, float cosThetaI, float roughness ) {
|
|
3345
|
+
|
|
3346
|
+
float alpha = max( roughness, 0.07 );
|
|
3347
|
+
alpha = alpha * alpha;
|
|
3348
|
+
|
|
3349
|
+
return 1.0 / ( 1.0 + velvetLambda( cosThetaO, alpha ) + velvetLambda( cosThetaI, alpha ) );
|
|
3350
|
+
|
|
3351
|
+
}
|
|
3352
|
+
|
|
3353
|
+
float directionalAlbedoSheen( float cosTheta, float alpha ) {
|
|
3354
|
+
|
|
3355
|
+
cosTheta = saturate( cosTheta );
|
|
3356
|
+
|
|
3357
|
+
float c = 1.0 - cosTheta;
|
|
3358
|
+
float c3 = c * c * c;
|
|
3359
|
+
|
|
3360
|
+
return 0.65584461 * c3 + 1.0 / ( 4.16526551 + exp( -7.97291361 * sqrt( alpha ) + 6.33516894 ) );
|
|
3361
|
+
|
|
3362
|
+
}
|
|
3363
|
+
|
|
3364
|
+
float sheenAlbedoScaling( vec3 wo, vec3 wi, SurfaceRec surf ) {
|
|
3365
|
+
|
|
3366
|
+
float alpha = max( surf.sheenRoughness, 0.07 );
|
|
3367
|
+
alpha = alpha * alpha;
|
|
3368
|
+
|
|
3369
|
+
float maxSheenColor = max( max( surf.sheenColor.r, surf.sheenColor.g ), surf.sheenColor.b );
|
|
3370
|
+
|
|
3371
|
+
float eWo = directionalAlbedoSheen( saturateCos( wo.z ), alpha );
|
|
3372
|
+
float eWi = directionalAlbedoSheen( saturateCos( wi.z ), alpha );
|
|
3373
|
+
|
|
3374
|
+
return min( 1.0 - maxSheenColor * eWo, 1.0 - maxSheenColor * eWi );
|
|
3375
|
+
|
|
3376
|
+
}
|
|
3377
|
+
|
|
3378
|
+
// See Section 5, Layering, in http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
|
|
3379
|
+
float sheenAlbedoScaling( vec3 wo, SurfaceRec surf ) {
|
|
3380
|
+
|
|
3381
|
+
float alpha = max( surf.sheenRoughness, 0.07 );
|
|
3382
|
+
alpha = alpha * alpha;
|
|
3383
|
+
|
|
3384
|
+
float maxSheenColor = max( max( surf.sheenColor.r, surf.sheenColor.g ), surf.sheenColor.b );
|
|
3385
|
+
|
|
3386
|
+
float eWo = directionalAlbedoSheen( saturateCos( wo.z ), alpha );
|
|
3387
|
+
|
|
3388
|
+
return 1.0 - maxSheenColor * eWo;
|
|
3389
|
+
|
|
3390
|
+
}
|
|
3391
|
+
|
|
3392
|
+
`;
|
|
3393
|
+
|
|
3394
|
+
const shaderIridescenceFunctions = /* glsl */`
|
|
3395
|
+
|
|
3396
|
+
// XYZ to sRGB color space
|
|
3397
|
+
const mat3 XYZ_TO_REC709 = mat3(
|
|
3398
|
+
3.2404542, -0.9692660, 0.0556434,
|
|
3399
|
+
-1.5371385, 1.8760108, -0.2040259,
|
|
3400
|
+
-0.4985314, 0.0415560, 1.0572252
|
|
3401
|
+
);
|
|
3402
|
+
|
|
3403
|
+
vec3 fresnel0ToIor( vec3 fresnel0 ) {
|
|
3404
|
+
|
|
3405
|
+
vec3 sqrtF0 = sqrt( fresnel0 );
|
|
3406
|
+
return ( vec3( 1.0 ) + sqrtF0 ) / ( vec3( 1.0 ) - sqrtF0 );
|
|
3407
|
+
|
|
3408
|
+
}
|
|
3409
|
+
|
|
3410
|
+
// Conversion FO/IOR
|
|
3411
|
+
vec3 iorToFresnel0( vec3 transmittedIor, float incidentIor ) {
|
|
3412
|
+
|
|
3413
|
+
return square( ( transmittedIor - vec3( incidentIor ) ) / ( transmittedIor + vec3( incidentIor ) ) );
|
|
3414
|
+
|
|
3415
|
+
}
|
|
3416
|
+
|
|
3417
|
+
// ior is a value between 1.0 and 3.0. 1.0 is air interface
|
|
3418
|
+
float iorToFresnel0( float transmittedIor, float incidentIor ) {
|
|
3419
|
+
|
|
3420
|
+
return square( ( transmittedIor - incidentIor ) / ( transmittedIor + incidentIor ) );
|
|
3421
|
+
|
|
3422
|
+
}
|
|
3423
|
+
|
|
3424
|
+
// Fresnel equations for dielectric/dielectric interfaces. See https://belcour.github.io/blog/research/2017/05/01/brdf-thin-film.html
|
|
3425
|
+
vec3 evalSensitivity( float OPD, vec3 shift ) {
|
|
3426
|
+
|
|
3427
|
+
float phase = 2.0 * PI * OPD * 1.0e-9;
|
|
3428
|
+
|
|
3429
|
+
vec3 val = vec3( 5.4856e-13, 4.4201e-13, 5.2481e-13 );
|
|
3430
|
+
vec3 pos = vec3( 1.6810e+06, 1.7953e+06, 2.2084e+06 );
|
|
3431
|
+
vec3 var = vec3( 4.3278e+09, 9.3046e+09, 6.6121e+09 );
|
|
3432
|
+
|
|
3433
|
+
vec3 xyz = val * sqrt( 2.0 * PI * var ) * cos( pos * phase + shift ) * exp( - square( phase ) * var );
|
|
3434
|
+
xyz.x += 9.7470e-14 * sqrt( 2.0 * PI * 4.5282e+09 ) * cos( 2.2399e+06 * phase + shift[ 0 ] ) * exp( - 4.5282e+09 * square( phase ) );
|
|
3435
|
+
xyz /= 1.0685e-7;
|
|
3436
|
+
|
|
3437
|
+
vec3 srgb = XYZ_TO_REC709 * xyz;
|
|
3438
|
+
return srgb;
|
|
3439
|
+
|
|
3440
|
+
}
|
|
3441
|
+
|
|
3442
|
+
// See Section 4. Analytic Spectral Integration, A Practical Extension to Microfacet Theory for the Modeling of Varying Iridescence, https://hal.archives-ouvertes.fr/hal-01518344/document
|
|
3443
|
+
vec3 evalIridescence( float outsideIOR, float eta2, float cosTheta1, float thinFilmThickness, vec3 baseF0 ) {
|
|
3444
|
+
|
|
3445
|
+
vec3 I;
|
|
3446
|
+
|
|
3447
|
+
// Force iridescenceIor -> outsideIOR when thinFilmThickness -> 0.0
|
|
3448
|
+
float iridescenceIor = mix( outsideIOR, eta2, smoothstep( 0.0, 0.03, thinFilmThickness ) );
|
|
3449
|
+
|
|
3450
|
+
// Evaluate the cosTheta on the base layer (Snell law)
|
|
3451
|
+
float sinTheta2Sq = square( outsideIOR / iridescenceIor ) * ( 1.0 - square( cosTheta1 ) );
|
|
3452
|
+
|
|
3453
|
+
// Handle TIR:
|
|
3454
|
+
float cosTheta2Sq = 1.0 - sinTheta2Sq;
|
|
3455
|
+
if ( cosTheta2Sq < 0.0 ) {
|
|
3456
|
+
|
|
3457
|
+
return vec3( 1.0 );
|
|
3458
|
+
|
|
3459
|
+
}
|
|
3460
|
+
|
|
3461
|
+
float cosTheta2 = sqrt( cosTheta2Sq );
|
|
3462
|
+
|
|
3463
|
+
// First interface
|
|
3464
|
+
float R0 = iorToFresnel0( iridescenceIor, outsideIOR );
|
|
3465
|
+
float R12 = schlickFresnel( cosTheta1, R0 );
|
|
3466
|
+
float R21 = R12;
|
|
3467
|
+
float T121 = 1.0 - R12;
|
|
3468
|
+
float phi12 = 0.0;
|
|
3469
|
+
if ( iridescenceIor < outsideIOR ) {
|
|
3470
|
+
|
|
3471
|
+
phi12 = PI;
|
|
3472
|
+
|
|
3473
|
+
}
|
|
3474
|
+
float phi21 = PI - phi12;
|
|
3475
|
+
|
|
3476
|
+
// Second interface
|
|
3477
|
+
vec3 baseIOR = fresnel0ToIor( clamp( baseF0, 0.0, 0.9999 ) ); // guard against 1.0
|
|
3478
|
+
vec3 R1 = iorToFresnel0( baseIOR, iridescenceIor );
|
|
3479
|
+
vec3 R23 = schlickFresnel( cosTheta2, R1 );
|
|
3480
|
+
vec3 phi23 = vec3( 0.0 );
|
|
3481
|
+
if ( baseIOR[0] < iridescenceIor ) {
|
|
3482
|
+
|
|
3483
|
+
phi23[ 0 ] = PI;
|
|
3484
|
+
|
|
3485
|
+
}
|
|
3486
|
+
if ( baseIOR[1] < iridescenceIor ) {
|
|
3487
|
+
|
|
3488
|
+
phi23[ 1 ] = PI;
|
|
3489
|
+
|
|
3490
|
+
}
|
|
3491
|
+
if ( baseIOR[2] < iridescenceIor ) {
|
|
3492
|
+
|
|
3493
|
+
phi23[ 2 ] = PI;
|
|
3494
|
+
|
|
3495
|
+
}
|
|
3496
|
+
|
|
3497
|
+
// Phase shift
|
|
3498
|
+
float OPD = 2.0 * iridescenceIor * thinFilmThickness * cosTheta2;
|
|
3499
|
+
vec3 phi = vec3( phi21 ) + phi23;
|
|
3500
|
+
|
|
3501
|
+
// Compound terms
|
|
3502
|
+
vec3 R123 = clamp( R12 * R23, 1e-5, 0.9999 );
|
|
3503
|
+
vec3 r123 = sqrt( R123 );
|
|
3504
|
+
vec3 Rs = square( T121 ) * R23 / ( vec3( 1.0 ) - R123 );
|
|
3505
|
+
|
|
3506
|
+
// Reflectance term for m = 0 (DC term amplitude)
|
|
3507
|
+
vec3 C0 = R12 + Rs;
|
|
3508
|
+
I = C0;
|
|
3509
|
+
|
|
3510
|
+
// Reflectance term for m > 0 (pairs of diracs)
|
|
3511
|
+
vec3 Cm = Rs - T121;
|
|
3512
|
+
for ( int m = 1; m <= 2; ++ m )
|
|
3513
|
+
{
|
|
3514
|
+
Cm *= r123;
|
|
3515
|
+
vec3 Sm = 2.0 * evalSensitivity( float( m ) * OPD, float( m ) * phi );
|
|
3516
|
+
I += Cm * Sm;
|
|
3517
|
+
}
|
|
2059
3518
|
|
|
3519
|
+
// Since out of gamut colors might be produced, negative color values are clamped to 0.
|
|
3520
|
+
return max( I, vec3( 0.0 ) );
|
|
2060
3521
|
}
|
|
3522
|
+
|
|
2061
3523
|
`;
|
|
2062
3524
|
|
|
2063
3525
|
const shaderMaterialSampling = /* glsl */`
|
|
@@ -2073,15 +3535,29 @@ struct SurfaceRec {
|
|
|
2073
3535
|
vec3 emission;
|
|
2074
3536
|
float transmission;
|
|
2075
3537
|
float ior;
|
|
3538
|
+
float clearcoat;
|
|
3539
|
+
float clearcoatRoughness;
|
|
3540
|
+
float filteredClearcoatRoughness;
|
|
3541
|
+
vec3 sheenColor;
|
|
3542
|
+
float sheenRoughness;
|
|
3543
|
+
float iridescence;
|
|
3544
|
+
float iridescenceIor;
|
|
3545
|
+
float iridescenceThickness;
|
|
3546
|
+
vec3 specularColor;
|
|
3547
|
+
float specularIntensity;
|
|
2076
3548
|
};
|
|
2077
3549
|
|
|
2078
3550
|
struct SampleRec {
|
|
3551
|
+
float specularPdf;
|
|
2079
3552
|
float pdf;
|
|
2080
3553
|
vec3 direction;
|
|
3554
|
+
vec3 clearcoatDirection;
|
|
2081
3555
|
vec3 color;
|
|
2082
3556
|
};
|
|
2083
3557
|
|
|
2084
3558
|
${ shaderGGXFunctions }
|
|
3559
|
+
${ shaderSheenFunctions }
|
|
3560
|
+
${ shaderIridescenceFunctions }
|
|
2085
3561
|
|
|
2086
3562
|
// diffuse
|
|
2087
3563
|
float diffusePDF( vec3 wo, vec3 wi, SurfaceRec surf ) {
|
|
@@ -2116,10 +3592,15 @@ vec3 diffuseColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
|
|
|
2116
3592
|
// specular
|
|
2117
3593
|
float specularPDF( vec3 wo, vec3 wi, SurfaceRec surf ) {
|
|
2118
3594
|
|
|
2119
|
-
// See
|
|
3595
|
+
// See 14.1.1 Microfacet BxDFs in https://www.pbr-book.org/
|
|
2120
3596
|
float filteredRoughness = surf.filteredRoughness;
|
|
2121
3597
|
vec3 halfVector = getHalfVector( wi, wo );
|
|
2122
|
-
|
|
3598
|
+
|
|
3599
|
+
float incidentTheta = acos( wo.z );
|
|
3600
|
+
float D = ggxDistribution( halfVector, filteredRoughness );
|
|
3601
|
+
float G1 = ggxShadowMaskG1( incidentTheta, filteredRoughness );
|
|
3602
|
+
float ggxPdf = D * G1 * max( 0.0, abs( dot( wo, halfVector ) ) ) / abs ( wo.z );
|
|
3603
|
+
return ggxPdf / ( 4.0 * dot( wo, halfVector ) );
|
|
2123
3604
|
|
|
2124
3605
|
}
|
|
2125
3606
|
|
|
@@ -2152,21 +3633,25 @@ vec3 specularColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
|
|
|
2152
3633
|
float iorRatio = frontFace ? 1.0 / ior : ior;
|
|
2153
3634
|
float G = ggxShadowMaskG2( wi, wo, filteredRoughness );
|
|
2154
3635
|
float D = ggxDistribution( halfVector, filteredRoughness );
|
|
3636
|
+
vec3 F = vec3( schlickFresnelFromIor( dot( wi, halfVector ), iorRatio ) ) * surf.specularColor * surf.specularIntensity;
|
|
2155
3637
|
|
|
2156
|
-
float F = schlickFresnelFromIor( dot( wi, halfVector ), iorRatio );
|
|
2157
3638
|
float cosTheta = min( wo.z, 1.0 );
|
|
2158
3639
|
float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
|
|
2159
3640
|
bool cannotRefract = iorRatio * sinTheta > 1.0;
|
|
2160
3641
|
if ( cannotRefract ) {
|
|
2161
3642
|
|
|
2162
|
-
F = 1.0;
|
|
3643
|
+
F = vec3( 1.0 );
|
|
2163
3644
|
|
|
2164
3645
|
}
|
|
2165
3646
|
|
|
3647
|
+
float f0 = pow( ( iorRatio - 1.0 ) / ( iorRatio + 1.0 ), 2.0 );
|
|
3648
|
+
vec3 iridescenceFresnel = evalIridescence( 1.0, surf.iridescenceIor, dot( wi, halfVector ), surf.iridescenceThickness, vec3( f0 ) );
|
|
3649
|
+
F = mix( F, iridescenceFresnel, surf.iridescence );
|
|
3650
|
+
|
|
2166
3651
|
vec3 color = mix( vec3( 1.0 ), surf.color, metalness );
|
|
2167
3652
|
color = mix( color, vec3( 1.0 ), F );
|
|
2168
3653
|
color *= G * D / ( 4.0 * abs( wi.z * wo.z ) );
|
|
2169
|
-
color *= mix( F, 1.0, metalness );
|
|
3654
|
+
color *= mix( F, vec3( 1.0 ), metalness );
|
|
2170
3655
|
color *= wi.z; // scale the light by the direction the light is coming in from
|
|
2171
3656
|
|
|
2172
3657
|
return color;
|
|
@@ -2273,198 +3758,603 @@ vec3 transmissionColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
|
|
|
2273
3758
|
|
|
2274
3759
|
}
|
|
2275
3760
|
|
|
2276
|
-
|
|
3761
|
+
// clearcoat
|
|
3762
|
+
float clearcoatPDF( vec3 wo, vec3 wi, SurfaceRec surf ) {
|
|
3763
|
+
|
|
3764
|
+
// See equation (27) in http://jcgt.org/published/0003/02/03/
|
|
3765
|
+
float filteredClearcoatRoughness = surf.filteredClearcoatRoughness;
|
|
3766
|
+
vec3 halfVector = getHalfVector( wi, wo );
|
|
3767
|
+
return ggxPDF( wo, halfVector, filteredClearcoatRoughness ) / ( 4.0 * dot( wi, halfVector ) );
|
|
3768
|
+
|
|
3769
|
+
}
|
|
3770
|
+
|
|
3771
|
+
vec3 clearcoatDirection( vec3 wo, SurfaceRec surf ) {
|
|
3772
|
+
|
|
3773
|
+
// sample ggx vndf distribution which gives a new normal
|
|
3774
|
+
float filteredClearcoatRoughness = surf.filteredClearcoatRoughness;
|
|
3775
|
+
vec3 halfVector = ggxDirection(
|
|
3776
|
+
wo,
|
|
3777
|
+
filteredClearcoatRoughness,
|
|
3778
|
+
filteredClearcoatRoughness,
|
|
3779
|
+
rand(),
|
|
3780
|
+
rand()
|
|
3781
|
+
);
|
|
3782
|
+
|
|
3783
|
+
// apply to new ray by reflecting off the new normal
|
|
3784
|
+
return - reflect( wo, halfVector );
|
|
3785
|
+
|
|
3786
|
+
}
|
|
3787
|
+
|
|
3788
|
+
void clearcoatColor( inout vec3 color, vec3 wo, vec3 wi, SurfaceRec surf ) {
|
|
3789
|
+
|
|
3790
|
+
float ior = 1.5;
|
|
3791
|
+
bool frontFace = surf.frontFace;
|
|
3792
|
+
float filteredClearcoatRoughness = surf.filteredClearcoatRoughness;
|
|
3793
|
+
|
|
3794
|
+
vec3 halfVector = getHalfVector( wo, wi );
|
|
3795
|
+
float iorRatio = frontFace ? 1.0 / ior : ior;
|
|
3796
|
+
float G = ggxShadowMaskG2( wi, wo, filteredClearcoatRoughness );
|
|
3797
|
+
float D = ggxDistribution( halfVector, filteredClearcoatRoughness );
|
|
3798
|
+
|
|
3799
|
+
float F = schlickFresnelFromIor( dot( wi, halfVector ), ior );
|
|
3800
|
+
float cosTheta = min( wo.z, 1.0 );
|
|
3801
|
+
float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
|
|
3802
|
+
bool cannotRefract = iorRatio * sinTheta > 1.0;
|
|
3803
|
+
if ( cannotRefract ) {
|
|
3804
|
+
|
|
3805
|
+
F = 1.0;
|
|
3806
|
+
|
|
3807
|
+
}
|
|
3808
|
+
|
|
3809
|
+
float fClearcoat = F * D * G / ( 4.0 * abs( wi.z * wo.z ) );
|
|
3810
|
+
|
|
3811
|
+
color = color * ( 1.0 - surf.clearcoat * F ) + fClearcoat * surf.clearcoat * wi.z;
|
|
3812
|
+
|
|
3813
|
+
}
|
|
3814
|
+
|
|
3815
|
+
// sheen
|
|
3816
|
+
vec3 sheenColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
|
|
3817
|
+
|
|
3818
|
+
vec3 halfVector = getHalfVector( wo, wi );
|
|
3819
|
+
|
|
3820
|
+
float cosThetaO = saturateCos( wo.z );
|
|
3821
|
+
float cosThetaI = saturateCos( wi.z );
|
|
3822
|
+
float cosThetaH = halfVector.z;
|
|
3823
|
+
|
|
3824
|
+
float D = velvetD( cosThetaH, surf.sheenRoughness );
|
|
3825
|
+
float G = velvetG( cosThetaO, cosThetaI, surf.sheenRoughness );
|
|
3826
|
+
|
|
3827
|
+
// See equation (1) in http://www.aconty.com/pdf/s2017_pbs_imageworks_sheen.pdf
|
|
3828
|
+
vec3 color = surf.sheenColor;
|
|
3829
|
+
color *= D * G / ( 4.0 * abs( cosThetaO * cosThetaI ) );
|
|
3830
|
+
color *= wi.z;
|
|
3831
|
+
|
|
3832
|
+
return color;
|
|
3833
|
+
|
|
3834
|
+
}
|
|
3835
|
+
|
|
3836
|
+
// bsdf
|
|
3837
|
+
void getLobeWeights( vec3 wo, vec3 clearcoatWo, SurfaceRec surf, out float diffuseWeight, out float specularWeight, out float transmissionWeight, out float clearcoatWeight ) {
|
|
3838
|
+
|
|
3839
|
+
float ior = surf.ior;
|
|
3840
|
+
float metalness = surf.metalness;
|
|
3841
|
+
float transmission = surf.transmission;
|
|
3842
|
+
bool frontFace = surf.frontFace;
|
|
3843
|
+
|
|
3844
|
+
float ratio = frontFace ? 1.0 / ior : ior;
|
|
3845
|
+
float cosTheta = min( wo.z, 1.0 );
|
|
3846
|
+
float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
|
|
3847
|
+
float reflectance = schlickFresnelFromIor( cosTheta, ratio );
|
|
3848
|
+
bool cannotRefract = ratio * sinTheta > 1.0;
|
|
3849
|
+
if ( cannotRefract ) {
|
|
3850
|
+
|
|
3851
|
+
reflectance = 1.0;
|
|
3852
|
+
|
|
3853
|
+
}
|
|
3854
|
+
|
|
3855
|
+
float transSpecularProb = mix( reflectance, 1.0, metalness );
|
|
3856
|
+
float diffSpecularProb = 0.5 + 0.5 * metalness;
|
|
3857
|
+
|
|
3858
|
+
clearcoatWeight = surf.clearcoat * schlickFresnel( clearcoatWo.z, 0.04 );
|
|
3859
|
+
diffuseWeight = ( 1.0 - transmission ) * ( 1.0 - diffSpecularProb ) * ( 1.0 - clearcoatWeight );
|
|
3860
|
+
specularWeight = transmission * transSpecularProb + ( 1.0 - transmission ) * diffSpecularProb * ( 1.0 - clearcoatWeight );
|
|
3861
|
+
transmissionWeight = transmission * ( 1.0 - transSpecularProb ) * ( 1.0 - clearcoatWeight );
|
|
3862
|
+
|
|
3863
|
+
float totalWeight = diffuseWeight + specularWeight + transmissionWeight + clearcoatWeight;
|
|
3864
|
+
float invTotalWeight = 1.0 / totalWeight;
|
|
3865
|
+
|
|
3866
|
+
diffuseWeight *= invTotalWeight;
|
|
3867
|
+
specularWeight *= invTotalWeight;
|
|
3868
|
+
transmissionWeight *= invTotalWeight;
|
|
3869
|
+
clearcoatWeight *= invTotalWeight;
|
|
3870
|
+
|
|
3871
|
+
}
|
|
3872
|
+
|
|
3873
|
+
float bsdfPdf( vec3 wo, vec3 clearcoatWo, vec3 wi, vec3 clearcoatWi, SurfaceRec surf, out float specularPdf, float diffuseWeight, float specularWeight, float transmissionWeight, float clearcoatWeight ) {
|
|
2277
3874
|
|
|
2278
3875
|
float ior = surf.ior;
|
|
2279
3876
|
float metalness = surf.metalness;
|
|
2280
3877
|
float transmission = surf.transmission;
|
|
2281
3878
|
bool frontFace = surf.frontFace;
|
|
2282
3879
|
|
|
2283
|
-
float ratio = frontFace ? 1.0 / ior : ior;
|
|
2284
|
-
float cosTheta = min( wo.z, 1.0 );
|
|
2285
|
-
float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
|
|
2286
|
-
float reflectance = schlickFresnelFromIor( cosTheta, ratio );
|
|
2287
|
-
bool cannotRefract = ratio * sinTheta > 1.0;
|
|
2288
|
-
if ( cannotRefract ) {
|
|
3880
|
+
float ratio = frontFace ? 1.0 / ior : ior;
|
|
3881
|
+
float cosTheta = min( wo.z, 1.0 );
|
|
3882
|
+
float sinTheta = sqrt( 1.0 - cosTheta * cosTheta );
|
|
3883
|
+
float reflectance = schlickFresnelFromIor( cosTheta, ratio );
|
|
3884
|
+
bool cannotRefract = ratio * sinTheta > 1.0;
|
|
3885
|
+
if ( cannotRefract ) {
|
|
3886
|
+
|
|
3887
|
+
reflectance = 1.0;
|
|
3888
|
+
|
|
3889
|
+
}
|
|
3890
|
+
|
|
3891
|
+
float spdf = 0.0;
|
|
3892
|
+
float dpdf = 0.0;
|
|
3893
|
+
float tpdf = 0.0;
|
|
3894
|
+
float cpdf = 0.0;
|
|
3895
|
+
|
|
3896
|
+
if ( wi.z < 0.0 ) {
|
|
3897
|
+
|
|
3898
|
+
if( transmissionWeight > 0.0 ) {
|
|
3899
|
+
|
|
3900
|
+
tpdf = transmissionPDF( wo, wi, surf );
|
|
3901
|
+
|
|
3902
|
+
}
|
|
3903
|
+
|
|
3904
|
+
} else {
|
|
3905
|
+
|
|
3906
|
+
if( diffuseWeight > 0.0 ) {
|
|
3907
|
+
|
|
3908
|
+
dpdf = diffusePDF( wo, wi, surf );
|
|
3909
|
+
|
|
3910
|
+
}
|
|
3911
|
+
|
|
3912
|
+
if( specularWeight > 0.0 ) {
|
|
3913
|
+
|
|
3914
|
+
spdf = specularPDF( wo, wi, surf );
|
|
3915
|
+
|
|
3916
|
+
}
|
|
3917
|
+
|
|
3918
|
+
}
|
|
3919
|
+
|
|
3920
|
+
if( clearcoatWi.z >= 0.0 && clearcoatWeight > 0.0 ) {
|
|
3921
|
+
|
|
3922
|
+
cpdf = clearcoatPDF( clearcoatWo, clearcoatWi, surf );
|
|
3923
|
+
|
|
3924
|
+
}
|
|
3925
|
+
|
|
3926
|
+
float pdf =
|
|
3927
|
+
dpdf * diffuseWeight
|
|
3928
|
+
+ spdf * specularWeight
|
|
3929
|
+
+ tpdf * transmissionWeight
|
|
3930
|
+
+ cpdf * clearcoatWeight;
|
|
3931
|
+
|
|
3932
|
+
// retrieve specular rays for the shadows flag
|
|
3933
|
+
specularPdf = spdf * specularWeight + cpdf * clearcoatWeight;
|
|
3934
|
+
|
|
3935
|
+
return pdf;
|
|
3936
|
+
|
|
3937
|
+
}
|
|
3938
|
+
|
|
3939
|
+
vec3 bsdfColor( vec3 wo, vec3 clearcoatWo, vec3 wi, vec3 clearcoatWi, SurfaceRec surf, float diffuseWeight, float specularWeight, float transmissionWeight, float clearcoatWeight ) {
|
|
3940
|
+
|
|
3941
|
+
vec3 color = vec3( 0.0 );
|
|
3942
|
+
if ( wi.z < 0.0 ) {
|
|
3943
|
+
|
|
3944
|
+
if( transmissionWeight > 0.0 ) {
|
|
3945
|
+
|
|
3946
|
+
color = transmissionColor( wo, wi, surf );
|
|
3947
|
+
|
|
3948
|
+
}
|
|
3949
|
+
|
|
3950
|
+
} else {
|
|
3951
|
+
|
|
3952
|
+
if( diffuseWeight > 0.0 ) {
|
|
3953
|
+
|
|
3954
|
+
color = diffuseColor( wo, wi, surf );
|
|
3955
|
+
color *= 1.0 - surf.transmission;
|
|
3956
|
+
|
|
3957
|
+
}
|
|
3958
|
+
|
|
3959
|
+
if( specularWeight > 0.0 ) {
|
|
3960
|
+
|
|
3961
|
+
color += specularColor( wo, wi, surf );
|
|
3962
|
+
|
|
3963
|
+
}
|
|
3964
|
+
|
|
3965
|
+
color *= sheenAlbedoScaling( wo, wi, surf );
|
|
3966
|
+
color += sheenColor( wo, wi, surf );
|
|
3967
|
+
|
|
3968
|
+
}
|
|
3969
|
+
|
|
3970
|
+
if( clearcoatWi.z >= 0.0 && clearcoatWeight > 0.0 ) {
|
|
3971
|
+
|
|
3972
|
+
clearcoatColor( color, clearcoatWo, clearcoatWi, surf );
|
|
3973
|
+
|
|
3974
|
+
}
|
|
3975
|
+
|
|
3976
|
+
return color;
|
|
3977
|
+
|
|
3978
|
+
}
|
|
3979
|
+
|
|
3980
|
+
float bsdfResult( vec3 wo, vec3 clearcoatWo, vec3 wi, vec3 clearcoatWi, SurfaceRec surf, out vec3 color ) {
|
|
3981
|
+
|
|
3982
|
+
float diffuseWeight;
|
|
3983
|
+
float specularWeight;
|
|
3984
|
+
float transmissionWeight;
|
|
3985
|
+
float clearcoatWeight;
|
|
3986
|
+
getLobeWeights( wo, clearcoatWo, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
|
|
3987
|
+
|
|
3988
|
+
float specularPdf;
|
|
3989
|
+
color = bsdfColor( wo, clearcoatWo, wi, clearcoatWi, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
|
|
3990
|
+
return bsdfPdf( wo, clearcoatWo, wi, clearcoatWi, surf, specularPdf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
|
|
3991
|
+
|
|
3992
|
+
}
|
|
3993
|
+
|
|
3994
|
+
SampleRec bsdfSample( vec3 wo, vec3 clearcoatWo, mat3 normalBasis, mat3 invBasis, mat3 clearcoatNormalBasis, mat3 clearcoatInvBasis, SurfaceRec surf ) {
|
|
3995
|
+
|
|
3996
|
+
float diffuseWeight;
|
|
3997
|
+
float specularWeight;
|
|
3998
|
+
float transmissionWeight;
|
|
3999
|
+
float clearcoatWeight;
|
|
4000
|
+
getLobeWeights( wo, clearcoatWo, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
|
|
4001
|
+
|
|
4002
|
+
float pdf[4];
|
|
4003
|
+
pdf[0] = diffuseWeight;
|
|
4004
|
+
pdf[1] = specularWeight;
|
|
4005
|
+
pdf[2] = transmissionWeight;
|
|
4006
|
+
pdf[3] = clearcoatWeight;
|
|
4007
|
+
|
|
4008
|
+
float cdf[4];
|
|
4009
|
+
cdf[0] = pdf[0];
|
|
4010
|
+
cdf[1] = pdf[1] + cdf[0];
|
|
4011
|
+
cdf[2] = pdf[2] + cdf[1];
|
|
4012
|
+
cdf[3] = pdf[3] + cdf[2];
|
|
4013
|
+
|
|
4014
|
+
if( cdf[3] != 0.0 ) {
|
|
4015
|
+
|
|
4016
|
+
float invMaxCdf = 1.0 / cdf[3];
|
|
4017
|
+
cdf[0] *= invMaxCdf;
|
|
4018
|
+
cdf[1] *= invMaxCdf;
|
|
4019
|
+
cdf[2] *= invMaxCdf;
|
|
4020
|
+
cdf[3] *= invMaxCdf;
|
|
4021
|
+
|
|
4022
|
+
} else {
|
|
4023
|
+
|
|
4024
|
+
cdf[0] = 1.0;
|
|
4025
|
+
cdf[1] = 0.0;
|
|
4026
|
+
cdf[2] = 0.0;
|
|
4027
|
+
cdf[3] = 0.0;
|
|
4028
|
+
|
|
4029
|
+
}
|
|
4030
|
+
|
|
4031
|
+
vec3 wi;
|
|
4032
|
+
vec3 clearcoatWi;
|
|
4033
|
+
|
|
4034
|
+
float r = rand();
|
|
4035
|
+
if ( r <= cdf[0] ) {
|
|
4036
|
+
|
|
4037
|
+
wi = diffuseDirection( wo, surf );
|
|
4038
|
+
clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
|
|
4039
|
+
|
|
4040
|
+
} else if ( r <= cdf[1] ) {
|
|
4041
|
+
|
|
4042
|
+
wi = specularDirection( wo, surf );
|
|
4043
|
+
clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
|
|
4044
|
+
|
|
4045
|
+
} else if ( r <= cdf[2] ) {
|
|
4046
|
+
|
|
4047
|
+
wi = transmissionDirection( wo, surf );
|
|
4048
|
+
clearcoatWi = normalize( clearcoatInvBasis * normalize( normalBasis * wi ) );
|
|
4049
|
+
|
|
4050
|
+
} else if ( r <= cdf[3] ) {
|
|
4051
|
+
|
|
4052
|
+
clearcoatWi = clearcoatDirection( clearcoatWo, surf );
|
|
4053
|
+
wi = normalize( invBasis * normalize( clearcoatNormalBasis * clearcoatWi ) );
|
|
4054
|
+
|
|
4055
|
+
}
|
|
4056
|
+
|
|
4057
|
+
SampleRec result;
|
|
4058
|
+
result.pdf = bsdfPdf( wo, clearcoatWo, wi, clearcoatWi, surf, result.specularPdf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
|
|
4059
|
+
result.color = bsdfColor( wo, clearcoatWo, wi, clearcoatWi, surf, diffuseWeight, specularWeight, transmissionWeight, clearcoatWeight );
|
|
4060
|
+
result.direction = wi;
|
|
4061
|
+
result.clearcoatDirection = clearcoatWi;
|
|
4062
|
+
|
|
4063
|
+
return result;
|
|
4064
|
+
|
|
4065
|
+
}
|
|
4066
|
+
`;
|
|
4067
|
+
|
|
4068
|
+
const shaderEnvMapSampling = /* glsl */`
|
|
4069
|
+
|
|
4070
|
+
vec3 sampleEquirectEnvMapColor( vec3 direction, sampler2D map ) {
|
|
4071
|
+
|
|
4072
|
+
return texture2D( map, equirectDirectionToUv( direction ) ).rgb;
|
|
4073
|
+
|
|
4074
|
+
}
|
|
4075
|
+
|
|
4076
|
+
float envMapDirectionPdf( vec3 direction ) {
|
|
4077
|
+
|
|
4078
|
+
vec2 uv = equirectDirectionToUv( direction );
|
|
4079
|
+
float theta = uv.y * PI;
|
|
4080
|
+
float sinTheta = sin( theta );
|
|
4081
|
+
if ( sinTheta == 0.0 ) {
|
|
4082
|
+
|
|
4083
|
+
return 0.0;
|
|
4084
|
+
|
|
4085
|
+
}
|
|
4086
|
+
|
|
4087
|
+
return 1.0 / ( 2.0 * PI * PI * sinTheta );
|
|
4088
|
+
|
|
4089
|
+
}
|
|
4090
|
+
|
|
4091
|
+
float envMapSample( vec3 direction, EquirectHdrInfo info, out vec3 color ) {
|
|
4092
|
+
|
|
4093
|
+
vec2 uv = equirectDirectionToUv( direction );
|
|
4094
|
+
color = texture2D( info.map, uv ).rgb;
|
|
4095
|
+
|
|
4096
|
+
float totalSum = info.totalSumWhole + info.totalSumDecimal;
|
|
4097
|
+
float lum = colorToLuminance( color );
|
|
4098
|
+
ivec2 resolution = textureSize( info.map, 0 );
|
|
4099
|
+
float pdf = lum / totalSum;
|
|
4100
|
+
|
|
4101
|
+
return float( resolution.x * resolution.y ) * pdf * envMapDirectionPdf( direction );
|
|
4102
|
+
|
|
4103
|
+
}
|
|
4104
|
+
|
|
4105
|
+
float randomEnvMapSample( EquirectHdrInfo info, out vec3 color, out vec3 direction ) {
|
|
4106
|
+
|
|
4107
|
+
// sample env map cdf
|
|
4108
|
+
vec2 r = rand2();
|
|
4109
|
+
float v = texture2D( info.marginalWeights, vec2( r.x, 0.0 ) ).x;
|
|
4110
|
+
float u = texture2D( info.conditionalWeights, vec2( r.y, v ) ).x;
|
|
4111
|
+
vec2 uv = vec2( u, v );
|
|
4112
|
+
|
|
4113
|
+
vec3 derivedDirection = equirectUvToDirection( uv );
|
|
4114
|
+
direction = derivedDirection;
|
|
4115
|
+
color = texture2D( info.map, uv ).rgb;
|
|
4116
|
+
|
|
4117
|
+
float totalSum = info.totalSumWhole + info.totalSumDecimal;
|
|
4118
|
+
float lum = colorToLuminance( color );
|
|
4119
|
+
ivec2 resolution = textureSize( info.map, 0 );
|
|
4120
|
+
float pdf = lum / totalSum;
|
|
4121
|
+
|
|
4122
|
+
return float( resolution.x * resolution.y ) * pdf * envMapDirectionPdf( direction );
|
|
4123
|
+
|
|
4124
|
+
}
|
|
4125
|
+
|
|
4126
|
+
`;
|
|
4127
|
+
|
|
4128
|
+
const shaderLightSampling = /* glsl */`
|
|
4129
|
+
|
|
4130
|
+
float getSpotAttenuation( const in float coneCosine, const in float penumbraCosine, const in float angleCosine ) {
|
|
4131
|
+
|
|
4132
|
+
return smoothstep( coneCosine, penumbraCosine, angleCosine );
|
|
4133
|
+
|
|
4134
|
+
}
|
|
4135
|
+
|
|
4136
|
+
float getDistanceAttenuation( const in float lightDistance, const in float cutoffDistance, const in float decayExponent ) {
|
|
4137
|
+
|
|
4138
|
+
// based upon Frostbite 3 Moving to Physically-based Rendering
|
|
4139
|
+
// page 32, equation 26: E[window1]
|
|
4140
|
+
// https://seblagarde.files.wordpress.com/2015/07/course_notes_moving_frostbite_to_pbr_v32.pdf
|
|
4141
|
+
float distanceFalloff = 1.0 / max( pow( lightDistance, decayExponent ), EPSILON );
|
|
4142
|
+
|
|
4143
|
+
if ( cutoffDistance > 0.0 ) {
|
|
4144
|
+
|
|
4145
|
+
distanceFalloff *= pow2( saturate( 1.0 - pow4( lightDistance / cutoffDistance ) ) );
|
|
4146
|
+
|
|
4147
|
+
}
|
|
4148
|
+
|
|
4149
|
+
return distanceFalloff;
|
|
4150
|
+
|
|
4151
|
+
}
|
|
4152
|
+
|
|
4153
|
+
float getPhotometricAttenuation( sampler2DArray iesProfiles, int iesProfile, vec3 posToLight, vec3 lightDir, vec3 u, vec3 v ) {
|
|
4154
|
+
|
|
4155
|
+
float cosTheta = dot( posToLight, lightDir );
|
|
4156
|
+
float angle = acos( cosTheta ) * ( 1.0 / PI );
|
|
4157
|
+
|
|
4158
|
+
return texture2D( iesProfiles, vec3( 0.0, angle, iesProfile ) ).r;
|
|
4159
|
+
|
|
4160
|
+
}
|
|
4161
|
+
|
|
4162
|
+
struct LightSampleRec {
|
|
4163
|
+
|
|
4164
|
+
bool hit;
|
|
4165
|
+
float dist;
|
|
4166
|
+
vec3 direction;
|
|
4167
|
+
float pdf;
|
|
4168
|
+
vec3 emission;
|
|
4169
|
+
int type;
|
|
4170
|
+
|
|
4171
|
+
};
|
|
4172
|
+
|
|
4173
|
+
LightSampleRec lightsClosestHit( sampler2D lights, uint lightCount, vec3 rayOrigin, vec3 rayDirection ) {
|
|
4174
|
+
|
|
4175
|
+
LightSampleRec lightSampleRec;
|
|
4176
|
+
lightSampleRec.hit = false;
|
|
4177
|
+
|
|
4178
|
+
uint l;
|
|
4179
|
+
for ( l = 0u; l < lightCount; l ++ ) {
|
|
4180
|
+
|
|
4181
|
+
Light light = readLightInfo( lights, l );
|
|
4182
|
+
|
|
4183
|
+
vec3 u = light.u;
|
|
4184
|
+
vec3 v = light.v;
|
|
4185
|
+
|
|
4186
|
+
// check for backface
|
|
4187
|
+
vec3 normal = normalize( cross( u, v ) );
|
|
4188
|
+
if ( dot( normal, rayDirection ) < 0.0 ) {
|
|
4189
|
+
continue;
|
|
4190
|
+
}
|
|
4191
|
+
|
|
4192
|
+
u *= 1.0 / dot( u, u );
|
|
4193
|
+
v *= 1.0 / dot( v, v );
|
|
4194
|
+
|
|
4195
|
+
float dist;
|
|
4196
|
+
|
|
4197
|
+
if(
|
|
4198
|
+
( light.type == RECT_AREA_LIGHT_TYPE && intersectsRectangle( light.position, normal, u, v, rayOrigin, rayDirection, dist ) ) ||
|
|
4199
|
+
( light.type == CIRC_AREA_LIGHT_TYPE && intersectsCircle( light.position, normal, u, v, rayOrigin, rayDirection, dist ) )
|
|
4200
|
+
) {
|
|
4201
|
+
|
|
4202
|
+
if ( dist < lightSampleRec.dist || ! lightSampleRec.hit ) {
|
|
4203
|
+
|
|
4204
|
+
float cosTheta = dot( rayDirection, normal );
|
|
4205
|
+
|
|
4206
|
+
lightSampleRec.hit = true;
|
|
4207
|
+
lightSampleRec.dist = dist;
|
|
4208
|
+
lightSampleRec.pdf = ( dist * dist ) / ( light.area * cosTheta );
|
|
4209
|
+
lightSampleRec.emission = light.color * light.intensity;
|
|
4210
|
+
lightSampleRec.direction = rayDirection;
|
|
4211
|
+
lightSampleRec.type = light.type;
|
|
4212
|
+
|
|
4213
|
+
}
|
|
4214
|
+
|
|
4215
|
+
} else if ( light.type == SPOT_LIGHT_TYPE ) {
|
|
2289
4216
|
|
|
2290
|
-
|
|
4217
|
+
// TODO: forward path tracing sampling needs to be made consistent with direct light sampling logic
|
|
4218
|
+
// float radius = light.radius;
|
|
4219
|
+
// vec3 lightNormal = normalize( cross( light.u, light.v ) );
|
|
4220
|
+
// float angle = acos( light.coneCos );
|
|
4221
|
+
// float angleTan = tan( angle );
|
|
4222
|
+
// float startDistance = radius / max( angleTan, EPSILON );
|
|
2291
4223
|
|
|
2292
|
-
|
|
4224
|
+
// u = light.u / radius;
|
|
4225
|
+
// v = light.v / radius;
|
|
2293
4226
|
|
|
2294
|
-
|
|
2295
|
-
|
|
2296
|
-
|
|
4227
|
+
// if (
|
|
4228
|
+
// intersectsCircle( light.position - normal * startDistance, normal, u, v, rayOrigin, rayDirection, dist ) &&
|
|
4229
|
+
// ( dist < lightSampleRec.dist || ! lightSampleRec.hit )
|
|
4230
|
+
// ) {
|
|
2297
4231
|
|
|
2298
|
-
|
|
4232
|
+
// float cosTheta = dot( rayDirection, normal );
|
|
4233
|
+
// float spotAttenuation = light.iesProfile != - 1 ?
|
|
4234
|
+
// getPhotometricAttenuation( iesProfiles, light.iesProfile, rayDirection, normal, u, v )
|
|
4235
|
+
// : getSpotAttenuation( light.coneCos, light.penumbraCos, cosTheta );
|
|
2299
4236
|
|
|
2300
|
-
|
|
4237
|
+
// float distanceAttenuation = getDistanceAttenuation( dist, light.distance, light.decay );
|
|
2301
4238
|
|
|
2302
|
-
|
|
4239
|
+
// lightSampleRec.hit = true;
|
|
4240
|
+
// lightSampleRec.dist = dist;
|
|
4241
|
+
// lightSampleRec.direction = rayDirection;
|
|
4242
|
+
// lightSampleRec.emission = light.color * light.intensity * distanceAttenuation * spotAttenuation;
|
|
4243
|
+
// lightSampleRec.pdf = ( dist * dist ) / ( light.area * cosTheta );
|
|
2303
4244
|
|
|
2304
|
-
|
|
2305
|
-
dpdf = diffusePDF( wo, wi, surf );
|
|
4245
|
+
// }
|
|
2306
4246
|
|
|
2307
|
-
|
|
4247
|
+
}
|
|
2308
4248
|
|
|
2309
|
-
|
|
2310
|
-
float diffSpecularProb = 0.5 + 0.5 * metalness;
|
|
2311
|
-
float pdf =
|
|
2312
|
-
spdf * transmission * transSpecularProb
|
|
2313
|
-
+ tpdf * transmission * ( 1.0 - transSpecularProb )
|
|
2314
|
-
+ spdf * ( 1.0 - transmission ) * diffSpecularProb
|
|
2315
|
-
+ dpdf * ( 1.0 - transmission ) * ( 1.0 - diffSpecularProb );
|
|
4249
|
+
}
|
|
2316
4250
|
|
|
2317
|
-
return
|
|
4251
|
+
return lightSampleRec;
|
|
2318
4252
|
|
|
2319
4253
|
}
|
|
2320
4254
|
|
|
2321
|
-
|
|
4255
|
+
LightSampleRec randomAreaLightSample( Light light, vec3 rayOrigin ) {
|
|
2322
4256
|
|
|
2323
|
-
|
|
2324
|
-
|
|
4257
|
+
LightSampleRec lightSampleRec;
|
|
4258
|
+
lightSampleRec.hit = true;
|
|
4259
|
+
lightSampleRec.type = light.type;
|
|
2325
4260
|
|
|
2326
|
-
|
|
4261
|
+
lightSampleRec.emission = light.color * light.intensity;
|
|
2327
4262
|
|
|
2328
|
-
|
|
4263
|
+
vec3 randomPos;
|
|
4264
|
+
if( light.type == RECT_AREA_LIGHT_TYPE ) {
|
|
2329
4265
|
|
|
2330
|
-
|
|
2331
|
-
|
|
4266
|
+
// rectangular area light
|
|
4267
|
+
randomPos = light.position + light.u * ( rand() - 0.5 ) + light.v * ( rand() - 0.5 );
|
|
2332
4268
|
|
|
2333
|
-
|
|
4269
|
+
} else if( light.type == 1 ) {
|
|
4270
|
+
|
|
4271
|
+
// circular area light
|
|
4272
|
+
float r = 0.5 * sqrt( rand() );
|
|
4273
|
+
float theta = rand() * 2.0 * PI;
|
|
4274
|
+
float x = r * cos( theta );
|
|
4275
|
+
float y = r * sin( theta );
|
|
4276
|
+
|
|
4277
|
+
randomPos = light.position + light.u * x + light.v * y;
|
|
2334
4278
|
|
|
2335
4279
|
}
|
|
2336
4280
|
|
|
2337
|
-
|
|
4281
|
+
vec3 toLight = randomPos - rayOrigin;
|
|
4282
|
+
float lightDistSq = dot( toLight, toLight );
|
|
4283
|
+
lightSampleRec.dist = sqrt( lightDistSq );
|
|
2338
4284
|
|
|
2339
|
-
|
|
4285
|
+
vec3 direction = toLight / lightSampleRec.dist;
|
|
4286
|
+
lightSampleRec.direction = direction;
|
|
2340
4287
|
|
|
2341
|
-
|
|
4288
|
+
vec3 lightNormal = normalize( cross( light.u, light.v ) );
|
|
4289
|
+
lightSampleRec.pdf = lightDistSq / ( light.area * dot( direction, lightNormal ) );
|
|
2342
4290
|
|
|
2343
|
-
|
|
2344
|
-
return bsdfPdf( wo, wi, surf );
|
|
4291
|
+
return lightSampleRec;
|
|
2345
4292
|
|
|
2346
4293
|
}
|
|
2347
4294
|
|
|
2348
|
-
|
|
4295
|
+
LightSampleRec randomSpotLightSample( Light light, sampler2DArray iesProfiles, vec3 rayOrigin ) {
|
|
2349
4296
|
|
|
2350
|
-
float
|
|
2351
|
-
float
|
|
2352
|
-
float
|
|
2353
|
-
|
|
4297
|
+
float radius = light.radius * sqrt( rand() );
|
|
4298
|
+
float theta = rand() * 2.0 * PI;
|
|
4299
|
+
float x = radius * cos( theta );
|
|
4300
|
+
float y = radius * sin( theta );
|
|
2354
4301
|
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
float reflectance = schlickFresnelFromIor( cosTheta, ratio );
|
|
2359
|
-
bool cannotRefract = ratio * sinTheta > 1.0;
|
|
2360
|
-
if ( cannotRefract ) {
|
|
4302
|
+
vec3 u = light.u;
|
|
4303
|
+
vec3 v = light.v;
|
|
4304
|
+
vec3 normal = normalize( cross( u, v ) );
|
|
2361
4305
|
|
|
2362
|
-
|
|
4306
|
+
float angle = acos( light.coneCos );
|
|
4307
|
+
float angleTan = tan( angle );
|
|
4308
|
+
float startDistance = light.radius / max( angleTan, EPSILON );
|
|
2363
4309
|
|
|
2364
|
-
|
|
4310
|
+
vec3 randomPos = light.position - normal * startDistance + u * x + v * y;
|
|
4311
|
+
vec3 toLight = randomPos - rayOrigin;
|
|
4312
|
+
float lightDistSq = dot( toLight, toLight );
|
|
4313
|
+
float dist = sqrt( lightDistSq );
|
|
2365
4314
|
|
|
2366
|
-
|
|
2367
|
-
|
|
4315
|
+
vec3 direction = toLight / max( dist, EPSILON );
|
|
4316
|
+
float cosTheta = dot( direction, normal );
|
|
2368
4317
|
|
|
2369
|
-
|
|
2370
|
-
|
|
4318
|
+
float spotAttenuation = light.iesProfile != - 1 ?
|
|
4319
|
+
getPhotometricAttenuation( iesProfiles, light.iesProfile, direction, normal, u, v )
|
|
4320
|
+
: getSpotAttenuation( light.coneCos, light.penumbraCos, cosTheta );
|
|
2371
4321
|
|
|
2372
|
-
|
|
4322
|
+
float distanceAttenuation = getDistanceAttenuation( dist, light.distance, light.decay );
|
|
4323
|
+
LightSampleRec lightSampleRec;
|
|
4324
|
+
lightSampleRec.hit = true;
|
|
4325
|
+
lightSampleRec.type = light.type;
|
|
4326
|
+
lightSampleRec.dist = dist;
|
|
4327
|
+
lightSampleRec.direction = direction;
|
|
4328
|
+
lightSampleRec.emission = light.color * light.intensity * distanceAttenuation * spotAttenuation;
|
|
2373
4329
|
|
|
2374
|
-
|
|
4330
|
+
// TODO: this makes the result consistent between MIS and non MIS paths but at radius 0 the pdf is infinite
|
|
4331
|
+
// and the intensity of the light is not correct
|
|
4332
|
+
lightSampleRec.pdf = 1.0;
|
|
4333
|
+
// lightSampleRec.pdf = lightDistSq / ( light.area * cosTheta );
|
|
2375
4334
|
|
|
2376
|
-
|
|
4335
|
+
return lightSampleRec;
|
|
2377
4336
|
|
|
2378
|
-
|
|
4337
|
+
}
|
|
2379
4338
|
|
|
2380
|
-
|
|
4339
|
+
LightSampleRec randomLightSample( sampler2D lights, sampler2DArray iesProfiles, uint lightCount, vec3 rayOrigin ) {
|
|
2381
4340
|
|
|
2382
|
-
|
|
2383
|
-
|
|
4341
|
+
// pick a random light
|
|
4342
|
+
uint l = uint( rand() * float( lightCount ) );
|
|
4343
|
+
Light light = readLightInfo( lights, l );
|
|
2384
4344
|
|
|
2385
|
-
|
|
4345
|
+
if ( light.type == SPOT_LIGHT_TYPE ) {
|
|
2386
4346
|
|
|
2387
|
-
|
|
4347
|
+
return randomSpotLightSample( light, iesProfiles, rayOrigin );
|
|
2388
4348
|
|
|
2389
|
-
|
|
4349
|
+
} else {
|
|
2390
4350
|
|
|
2391
|
-
|
|
4351
|
+
// sample the light
|
|
4352
|
+
return randomAreaLightSample( light, rayOrigin );
|
|
2392
4353
|
|
|
2393
4354
|
}
|
|
2394
4355
|
|
|
2395
|
-
result.pdf = bsdfPdf( wo, result.direction, surf );
|
|
2396
|
-
result.color = bsdfColor( wo, result.direction, surf );
|
|
2397
|
-
return result;
|
|
2398
|
-
|
|
2399
4356
|
}
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
const shaderEnvMapSampling = /* glsl */`
|
|
2403
|
-
|
|
2404
|
-
vec3 sampleEquirectEnvMapColor( vec3 direction, sampler2D map ) {
|
|
2405
|
-
|
|
2406
|
-
return texture2D( map, equirectDirectionToUv( direction ) ).rgb;
|
|
2407
|
-
|
|
2408
|
-
}
|
|
2409
|
-
|
|
2410
|
-
float envMapDirectionPdf( vec3 direction ) {
|
|
2411
|
-
|
|
2412
|
-
vec2 uv = equirectDirectionToUv( direction );
|
|
2413
|
-
float theta = uv.y * PI;
|
|
2414
|
-
float sinTheta = sin( theta );
|
|
2415
|
-
if ( sinTheta == 0.0 ) {
|
|
2416
|
-
|
|
2417
|
-
return 0.0;
|
|
2418
|
-
|
|
2419
|
-
}
|
|
2420
|
-
|
|
2421
|
-
return 1.0 / ( 2.0 * PI * PI * sinTheta );
|
|
2422
|
-
|
|
2423
|
-
}
|
|
2424
|
-
|
|
2425
|
-
float envMapSample( vec3 direction, EquirectHdrInfo info, out vec3 color ) {
|
|
2426
|
-
|
|
2427
|
-
vec2 uv = equirectDirectionToUv( direction );
|
|
2428
|
-
color = texture2D( info.map, uv ).rgb;
|
|
2429
|
-
|
|
2430
|
-
float totalSum = texture2D( info.totalSum, vec2( 0.0 ) ).r;
|
|
2431
|
-
float lum = colorToLuminance( color );
|
|
2432
|
-
ivec2 resolution = textureSize( info.map, 0 );
|
|
2433
|
-
float pdf = lum / totalSum;
|
|
2434
|
-
|
|
2435
|
-
return float( resolution.x * resolution.y ) * pdf * envMapDirectionPdf( direction );
|
|
2436
|
-
|
|
2437
|
-
}
|
|
2438
|
-
|
|
2439
|
-
float randomEnvMapSample( EquirectHdrInfo info, out vec3 color, out vec3 direction ) {
|
|
2440
|
-
|
|
2441
|
-
// sample env map cdf
|
|
2442
|
-
vec2 r = rand2();
|
|
2443
|
-
float v = texture2D( info.marginalWeights, vec2( r.x, 0.0 ) ).x;
|
|
2444
|
-
float u = texture2D( info.conditionalWeights, vec2( r.y, v ) ).x;
|
|
2445
|
-
vec2 uv = vec2( u, v );
|
|
2446
|
-
|
|
2447
|
-
vec3 derivedDirection = equirectUvToDirection( uv );
|
|
2448
|
-
direction = derivedDirection;
|
|
2449
|
-
color = texture2D( info.map, uv ).rgb;
|
|
2450
|
-
|
|
2451
|
-
float totalSum = texture2D( info.totalSum, vec2( 0.0 ) ).r;
|
|
2452
|
-
float lum = colorToLuminance( color );
|
|
2453
|
-
ivec2 resolution = textureSize( info.map, 0 );
|
|
2454
|
-
float pdf = lum / totalSum;
|
|
2455
|
-
|
|
2456
|
-
return float( resolution.x * resolution.y ) * pdf * envMapDirectionPdf( direction );
|
|
2457
|
-
|
|
2458
|
-
}
|
|
2459
|
-
|
|
2460
|
-
float misHeuristic( float a, float b ) {
|
|
2461
|
-
|
|
2462
|
-
float aa = a * a;
|
|
2463
|
-
float bb = a * b;
|
|
2464
|
-
return aa / ( bb + aa );
|
|
2465
|
-
|
|
2466
|
-
}
|
|
2467
|
-
|
|
4357
|
+
|
|
2468
4358
|
`;
|
|
2469
4359
|
|
|
2470
4360
|
class PhysicalPathTracingMaterial extends MaterialBase {
|
|
@@ -2487,6 +4377,10 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
2487
4377
|
FEATURE_DOF: 1,
|
|
2488
4378
|
FEATURE_GRADIENT_BG: 0,
|
|
2489
4379
|
TRANSPARENT_TRAVERSALS: 5,
|
|
4380
|
+
// 0 = Perspective
|
|
4381
|
+
// 1 = Orthographic
|
|
4382
|
+
// 2 = Equirectangular
|
|
4383
|
+
CAMERA_TYPE: 0,
|
|
2490
4384
|
},
|
|
2491
4385
|
|
|
2492
4386
|
uniforms: {
|
|
@@ -2502,10 +4396,12 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
2502
4396
|
materialIndexAttribute: { value: new UIntVertexAttributeTexture() },
|
|
2503
4397
|
materials: { value: new MaterialsTexture() },
|
|
2504
4398
|
textures: { value: new RenderTarget2DArray().texture },
|
|
4399
|
+
lights: { value: new LightsInfoUniformStruct() },
|
|
4400
|
+
iesProfiles: { value: new IESProfilesTexture().texture },
|
|
2505
4401
|
cameraWorldMatrix: { value: new Matrix4() },
|
|
2506
4402
|
invProjectionMatrix: { value: new Matrix4() },
|
|
2507
4403
|
backgroundBlur: { value: 0.0 },
|
|
2508
|
-
environmentIntensity: { value:
|
|
4404
|
+
environmentIntensity: { value: 1.0 },
|
|
2509
4405
|
environmentRotation: { value: new Matrix3() },
|
|
2510
4406
|
envMapInfo: { value: new EquirectHdrInfoUniform() },
|
|
2511
4407
|
|
|
@@ -2545,6 +4441,7 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
2545
4441
|
${ shaderStructs }
|
|
2546
4442
|
${ shaderIntersectFunction }
|
|
2547
4443
|
${ shaderMaterialStructs }
|
|
4444
|
+
${ shaderLightStruct }
|
|
2548
4445
|
|
|
2549
4446
|
${ shaderUtils }
|
|
2550
4447
|
${ shaderMaterialSampling }
|
|
@@ -2581,6 +4478,10 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
2581
4478
|
uniform int seed;
|
|
2582
4479
|
uniform float opacity;
|
|
2583
4480
|
uniform sampler2D materials;
|
|
4481
|
+
uniform LightsInfo lights;
|
|
4482
|
+
uniform sampler2DArray iesProfiles;
|
|
4483
|
+
|
|
4484
|
+
${ shaderLightSampling }
|
|
2584
4485
|
|
|
2585
4486
|
uniform EquirectHdrInfo envMapInfo;
|
|
2586
4487
|
|
|
@@ -2608,7 +4509,7 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
2608
4509
|
}
|
|
2609
4510
|
|
|
2610
4511
|
// step through multiple surface hits and accumulate color attenuation based on transmissive surfaces
|
|
2611
|
-
bool attenuateHit( BVH bvh, vec3 rayOrigin, vec3 rayDirection, int traversals, out vec3 color ) {
|
|
4512
|
+
bool attenuateHit( BVH bvh, vec3 rayOrigin, vec3 rayDirection, int traversals, bool isShadowRay, out vec3 color ) {
|
|
2612
4513
|
|
|
2613
4514
|
// hit results
|
|
2614
4515
|
uvec4 faceIndices = uvec4( 0u );
|
|
@@ -2631,13 +4532,34 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
2631
4532
|
uint materialIndex = uTexelFetch1D( materialIndexAttribute, faceIndices.x ).r;
|
|
2632
4533
|
Material material = readMaterialInfo( materials, materialIndex );
|
|
2633
4534
|
|
|
4535
|
+
// adjust the ray to the new surface
|
|
4536
|
+
bool isBelowSurface = dot( rayDirection, faceNormal ) < 0.0;
|
|
4537
|
+
vec3 point = rayOrigin + rayDirection * dist;
|
|
4538
|
+
vec3 absPoint = abs( point );
|
|
4539
|
+
float maxPoint = max( absPoint.x, max( absPoint.y, absPoint.z ) );
|
|
4540
|
+
rayOrigin = point + faceNormal * ( maxPoint + 1.0 ) * ( isBelowSurface ? - RAY_OFFSET : RAY_OFFSET );
|
|
4541
|
+
|
|
4542
|
+
if ( ! material.castShadow && isShadowRay ) {
|
|
4543
|
+
|
|
4544
|
+
continue;
|
|
4545
|
+
|
|
4546
|
+
}
|
|
4547
|
+
|
|
2634
4548
|
// Opacity Test
|
|
2635
4549
|
|
|
2636
4550
|
// albedo
|
|
2637
4551
|
vec4 albedo = vec4( material.color, material.opacity );
|
|
2638
4552
|
if ( material.map != - 1 ) {
|
|
2639
4553
|
|
|
2640
|
-
|
|
4554
|
+
vec3 uvPrime = material.mapTransform * vec3( uv, 1 );
|
|
4555
|
+
albedo *= texture2D( textures, vec3( uvPrime.xy, material.map ) );
|
|
4556
|
+
|
|
4557
|
+
}
|
|
4558
|
+
|
|
4559
|
+
// alphaMap
|
|
4560
|
+
if ( material.alphaMap != -1 ) {
|
|
4561
|
+
|
|
4562
|
+
albedo.a *= texture2D( textures, vec3( uv, material.alphaMap ) ).x;
|
|
2641
4563
|
|
|
2642
4564
|
}
|
|
2643
4565
|
|
|
@@ -2645,7 +4567,8 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
2645
4567
|
float transmission = material.transmission;
|
|
2646
4568
|
if ( material.transmissionMap != - 1 ) {
|
|
2647
4569
|
|
|
2648
|
-
|
|
4570
|
+
vec3 uvPrime = material.transmissionMapTransform * vec3( uv, 1 );
|
|
4571
|
+
transmission *= texture2D( textures, vec3( uvPrime.xy, material.transmissionMap ) ).r;
|
|
2649
4572
|
|
|
2650
4573
|
}
|
|
2651
4574
|
|
|
@@ -2653,7 +4576,8 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
2653
4576
|
float metalness = material.metalness;
|
|
2654
4577
|
if ( material.metalnessMap != - 1 ) {
|
|
2655
4578
|
|
|
2656
|
-
|
|
4579
|
+
vec3 uvPrime = material.metalnessMapTransform * vec3( uv, 1 );
|
|
4580
|
+
metalness *= texture2D( textures, vec3( uvPrime.xy, material.metalnessMap ) ).b;
|
|
2657
4581
|
|
|
2658
4582
|
}
|
|
2659
4583
|
|
|
@@ -2678,19 +4602,12 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
2678
4602
|
}
|
|
2679
4603
|
|
|
2680
4604
|
// only attenuate on the way in
|
|
2681
|
-
bool isBelowSurface = dot( rayDirection, faceNormal ) < 0.0;
|
|
2682
4605
|
if ( isBelowSurface ) {
|
|
2683
4606
|
|
|
2684
|
-
color *= albedo.rgb;
|
|
4607
|
+
color *= mix( vec3( 1.0 ), albedo.rgb, transmissionFactor );
|
|
2685
4608
|
|
|
2686
4609
|
}
|
|
2687
4610
|
|
|
2688
|
-
// adjust the ray to the new surface
|
|
2689
|
-
vec3 point = rayOrigin + rayDirection * dist;
|
|
2690
|
-
vec3 absPoint = abs( point );
|
|
2691
|
-
float maxPoint = max( absPoint.x, max( absPoint.y, absPoint.z ) );
|
|
2692
|
-
rayOrigin = point + faceNormal * ( maxPoint + 1.0 ) * ( isBelowSurface ? - RAY_OFFSET : RAY_OFFSET );
|
|
2693
|
-
|
|
2694
4611
|
} else {
|
|
2695
4612
|
|
|
2696
4613
|
return false;
|
|
@@ -2703,15 +4620,16 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
2703
4620
|
|
|
2704
4621
|
}
|
|
2705
4622
|
|
|
2706
|
-
// returns whether the ray hit anything, not just the first surface. Could be optimized to not check the full hierarchy.
|
|
2707
|
-
bool
|
|
4623
|
+
// returns whether the ray hit anything before a certain distance, not just the first surface. Could be optimized to not check the full hierarchy.
|
|
4624
|
+
bool anyCloserHit( BVH bvh, vec3 rayOrigin, vec3 rayDirection, float maxDist ) {
|
|
2708
4625
|
|
|
2709
4626
|
uvec4 faceIndices = uvec4( 0u );
|
|
2710
4627
|
vec3 faceNormal = vec3( 0.0, 0.0, 1.0 );
|
|
2711
4628
|
vec3 barycoord = vec3( 0.0 );
|
|
2712
4629
|
float side = 1.0;
|
|
2713
4630
|
float dist = 0.0;
|
|
2714
|
-
|
|
4631
|
+
bool hit = bvhIntersectFirstHit( bvh, rayOrigin, rayDirection, faceIndices, faceNormal, barycoord, side, dist );
|
|
4632
|
+
return hit && dist < maxDist;
|
|
2715
4633
|
|
|
2716
4634
|
}
|
|
2717
4635
|
|
|
@@ -2723,33 +4641,56 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
2723
4641
|
|
|
2724
4642
|
}
|
|
2725
4643
|
|
|
2726
|
-
|
|
4644
|
+
vec3 ndcToRayOrigin( vec2 coord ) {
|
|
2727
4645
|
|
|
2728
|
-
|
|
4646
|
+
vec4 rayOrigin4 = cameraWorldMatrix * invProjectionMatrix * vec4( coord, - 1.0, 1.0 );
|
|
4647
|
+
return rayOrigin4.xyz / rayOrigin4.w;
|
|
4648
|
+
}
|
|
2729
4649
|
|
|
2730
|
-
|
|
2731
|
-
vec2 ndc = 2.0 * vUv - vec2( 1.0 );
|
|
2732
|
-
vec3 rayOrigin, rayDirection;
|
|
2733
|
-
ndcToCameraRay( ndc, cameraWorldMatrix, invProjectionMatrix, rayOrigin, rayDirection );
|
|
4650
|
+
void getCameraRay( out vec3 rayDirection, out vec3 rayOrigin ) {
|
|
2734
4651
|
|
|
2735
|
-
|
|
2736
|
-
// This is better than just jittering the camera position since it actually results in divergent
|
|
2737
|
-
// rays providing better coverage for the pixel
|
|
2738
|
-
{
|
|
4652
|
+
vec2 ssd = vec2( 1.0 ) / resolution;
|
|
2739
4653
|
|
|
2740
|
-
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
ndcToCameraRay( vec2( - 1.0, - 1.0 ), cameraWorldMatrix, invProjectionMatrix, ss00, temp );
|
|
2744
|
-
ndcToCameraRay( vec2( - 1.0, 1.0 ), cameraWorldMatrix, invProjectionMatrix, ss01, temp );
|
|
2745
|
-
ndcToCameraRay( vec2( 1.0, - 1.0 ), cameraWorldMatrix, invProjectionMatrix, ss10, temp );
|
|
4654
|
+
// Jitter the camera ray by finding a uv coordinate at a random sample
|
|
4655
|
+
// around this pixel's UV coordinate
|
|
4656
|
+
vec2 jitteredUv = vUv + vec2( tentFilter( rand() ) * ssd.x, tentFilter( rand() ) * ssd.y );
|
|
2746
4657
|
|
|
2747
|
-
|
|
2748
|
-
vec3 ssdY = ( ss01 - ss00 ) / resolution.y;
|
|
2749
|
-
rayOrigin += tentFilter( rand() ) * ssdX + tentFilter( rand() ) * ssdY;
|
|
2750
|
-
rayDirection = normalize( rayOrigin - cameraOrigin );
|
|
4658
|
+
#if CAMERA_TYPE == 2
|
|
2751
4659
|
|
|
2752
|
-
|
|
4660
|
+
// Equirectangular projection
|
|
4661
|
+
|
|
4662
|
+
vec4 rayDirection4 = vec4( equirectUvToDirection( jitteredUv ), 0.0 );
|
|
4663
|
+
vec4 rayOrigin4 = vec4( 0.0, 0.0, 0.0, 1.0 );
|
|
4664
|
+
|
|
4665
|
+
rayDirection4 = cameraWorldMatrix * rayDirection4;
|
|
4666
|
+
rayOrigin4 = cameraWorldMatrix * rayOrigin4;
|
|
4667
|
+
|
|
4668
|
+
rayDirection = normalize( rayDirection4.xyz );
|
|
4669
|
+
rayOrigin = rayOrigin4.xyz / rayOrigin4.w;
|
|
4670
|
+
|
|
4671
|
+
#else
|
|
4672
|
+
|
|
4673
|
+
// get [-1, 1] normalized device coordinates
|
|
4674
|
+
vec2 ndc = 2.0 * jitteredUv - vec2( 1.0 );
|
|
4675
|
+
|
|
4676
|
+
rayOrigin = ndcToRayOrigin( ndc );
|
|
4677
|
+
|
|
4678
|
+
#if CAMERA_TYPE == 1
|
|
4679
|
+
|
|
4680
|
+
// Orthographic projection
|
|
4681
|
+
|
|
4682
|
+
rayDirection = ( cameraWorldMatrix * vec4( 0.0, 0.0, -1.0, 0.0 ) ).xyz;
|
|
4683
|
+
rayDirection = normalize( rayDirection );
|
|
4684
|
+
|
|
4685
|
+
#else
|
|
4686
|
+
|
|
4687
|
+
// Perspective projection
|
|
4688
|
+
|
|
4689
|
+
rayDirection = normalize( mat3(cameraWorldMatrix) * ( invProjectionMatrix * vec4( ndc, 0.0, 1.0 ) ).xyz );
|
|
4690
|
+
|
|
4691
|
+
#endif
|
|
4692
|
+
|
|
4693
|
+
#endif
|
|
2753
4694
|
|
|
2754
4695
|
#if FEATURE_DOF
|
|
2755
4696
|
{
|
|
@@ -2776,8 +4717,20 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
2776
4717
|
|
|
2777
4718
|
}
|
|
2778
4719
|
#endif
|
|
4720
|
+
|
|
2779
4721
|
rayDirection = normalize( rayDirection );
|
|
2780
4722
|
|
|
4723
|
+
}
|
|
4724
|
+
|
|
4725
|
+
void main() {
|
|
4726
|
+
|
|
4727
|
+
rng_initialize( gl_FragCoord.xy, seed );
|
|
4728
|
+
|
|
4729
|
+
vec3 rayDirection;
|
|
4730
|
+
vec3 rayOrigin;
|
|
4731
|
+
|
|
4732
|
+
getCameraRay( rayDirection, rayOrigin );
|
|
4733
|
+
|
|
2781
4734
|
// inverse environment rotation
|
|
2782
4735
|
mat3 invEnvironmentRotation = inverse( environmentRotation );
|
|
2783
4736
|
|
|
@@ -2794,15 +4747,56 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
2794
4747
|
|
|
2795
4748
|
// path tracing state
|
|
2796
4749
|
float accumulatedRoughness = 0.0;
|
|
4750
|
+
float accumulatedClearcoatRoughness = 0.0;
|
|
2797
4751
|
bool transmissiveRay = true;
|
|
2798
4752
|
int transparentTraversals = TRANSPARENT_TRAVERSALS;
|
|
2799
4753
|
vec3 throughputColor = vec3( 1.0 );
|
|
2800
4754
|
SampleRec sampleRec;
|
|
2801
4755
|
int i;
|
|
4756
|
+
bool isShadowRay = false;
|
|
2802
4757
|
|
|
2803
4758
|
for ( i = 0; i < bounces; i ++ ) {
|
|
2804
4759
|
|
|
2805
|
-
|
|
4760
|
+
bool hit = bvhIntersectFirstHit( bvh, rayOrigin, rayDirection, faceIndices, faceNormal, barycoord, side, dist );
|
|
4761
|
+
|
|
4762
|
+
LightSampleRec lightHit = lightsClosestHit( lights.tex, lights.count, rayOrigin, rayDirection );
|
|
4763
|
+
|
|
4764
|
+
if ( lightHit.hit && ( lightHit.dist < dist || !hit ) ) {
|
|
4765
|
+
|
|
4766
|
+
if ( i == 0 || transmissiveRay ) {
|
|
4767
|
+
|
|
4768
|
+
gl_FragColor.rgb += lightHit.emission * throughputColor;
|
|
4769
|
+
|
|
4770
|
+
} else {
|
|
4771
|
+
|
|
4772
|
+
#if FEATURE_MIS
|
|
4773
|
+
|
|
4774
|
+
// NOTE: we skip MIS for spotlights since we haven't fixed the forward
|
|
4775
|
+
// path tracing code path, yet
|
|
4776
|
+
if ( lightHit.type == SPOT_LIGHT_TYPE ) {
|
|
4777
|
+
|
|
4778
|
+
gl_FragColor.rgb += lightHit.emission * throughputColor;
|
|
4779
|
+
|
|
4780
|
+
} else {
|
|
4781
|
+
|
|
4782
|
+
// weight the contribution
|
|
4783
|
+
float misWeight = misHeuristic( sampleRec.pdf, lightHit.pdf / float( lights.count + 1u ) );
|
|
4784
|
+
gl_FragColor.rgb += lightHit.emission * throughputColor * misWeight;
|
|
4785
|
+
|
|
4786
|
+
}
|
|
4787
|
+
|
|
4788
|
+
#else
|
|
4789
|
+
|
|
4790
|
+
gl_FragColor.rgb += lightHit.emission * throughputColor;
|
|
4791
|
+
|
|
4792
|
+
#endif
|
|
4793
|
+
|
|
4794
|
+
}
|
|
4795
|
+
break;
|
|
4796
|
+
|
|
4797
|
+
}
|
|
4798
|
+
|
|
4799
|
+
if ( ! hit ) {
|
|
2806
4800
|
|
|
2807
4801
|
if ( i == 0 || transmissiveRay ) {
|
|
2808
4802
|
|
|
@@ -2816,6 +4810,7 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
2816
4810
|
// get the PDF of the hit envmap point
|
|
2817
4811
|
vec3 envColor;
|
|
2818
4812
|
float envPdf = envMapSample( environmentRotation * rayDirection, envMapInfo, envColor );
|
|
4813
|
+
envPdf /= float( lights.count + 1u );
|
|
2819
4814
|
|
|
2820
4815
|
// and weight the contribution
|
|
2821
4816
|
float misWeight = misHeuristic( sampleRec.pdf, envPdf );
|
|
@@ -2845,13 +4840,32 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
2845
4840
|
|
|
2846
4841
|
}
|
|
2847
4842
|
|
|
2848
|
-
|
|
4843
|
+
// if we've determined that this is a shadow ray and we've hit an item with no shadow casting
|
|
4844
|
+
// then skip it
|
|
4845
|
+
if ( ! material.castShadow && isShadowRay ) {
|
|
4846
|
+
|
|
4847
|
+
vec3 point = rayOrigin + rayDirection * dist;
|
|
4848
|
+
vec3 absPoint = abs( point );
|
|
4849
|
+
float maxPoint = max( absPoint.x, max( absPoint.y, absPoint.z ) );
|
|
4850
|
+
rayOrigin = point - ( maxPoint + 1.0 ) * faceNormal * RAY_OFFSET;
|
|
4851
|
+
|
|
4852
|
+
continue;
|
|
2849
4853
|
|
|
4854
|
+
}
|
|
4855
|
+
|
|
4856
|
+
vec2 uv = textureSampleBarycoord( uvAttribute, barycoord, faceIndices.xyz ).xy;
|
|
2850
4857
|
// albedo
|
|
2851
4858
|
vec4 albedo = vec4( material.color, material.opacity );
|
|
2852
4859
|
if ( material.map != - 1 ) {
|
|
2853
4860
|
|
|
2854
|
-
|
|
4861
|
+
vec3 uvPrime = material.mapTransform * vec3( uv, 1 );
|
|
4862
|
+
albedo *= texture2D( textures, vec3( uvPrime.xy, material.map ) );
|
|
4863
|
+
}
|
|
4864
|
+
|
|
4865
|
+
// alphaMap
|
|
4866
|
+
if ( material.alphaMap != -1 ) {
|
|
4867
|
+
|
|
4868
|
+
albedo.a *= texture2D( textures, vec3( uv, material.alphaMap ) ).x;
|
|
2855
4869
|
|
|
2856
4870
|
}
|
|
2857
4871
|
|
|
@@ -2899,7 +4913,8 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
2899
4913
|
float roughness = material.roughness;
|
|
2900
4914
|
if ( material.roughnessMap != - 1 ) {
|
|
2901
4915
|
|
|
2902
|
-
|
|
4916
|
+
vec3 uvPrime = material.roughnessMapTransform * vec3( uv, 1 );
|
|
4917
|
+
roughness *= texture2D( textures, vec3( uvPrime.xy, material.roughnessMap ) ).g;
|
|
2903
4918
|
|
|
2904
4919
|
}
|
|
2905
4920
|
|
|
@@ -2907,7 +4922,8 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
2907
4922
|
float metalness = material.metalness;
|
|
2908
4923
|
if ( material.metalnessMap != - 1 ) {
|
|
2909
4924
|
|
|
2910
|
-
|
|
4925
|
+
vec3 uvPrime = material.metalnessMapTransform * vec3( uv, 1 );
|
|
4926
|
+
metalness *= texture2D( textures, vec3( uvPrime.xy, material.metalnessMap ) ).b;
|
|
2911
4927
|
|
|
2912
4928
|
}
|
|
2913
4929
|
|
|
@@ -2915,7 +4931,8 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
2915
4931
|
vec3 emission = material.emissiveIntensity * material.emissive;
|
|
2916
4932
|
if ( material.emissiveMap != - 1 ) {
|
|
2917
4933
|
|
|
2918
|
-
|
|
4934
|
+
vec3 uvPrime = material.emissiveMapTransform * vec3( uv, 1 );
|
|
4935
|
+
emission *= texture2D( textures, vec3( uvPrime.xy, material.emissiveMap ) ).xyz;
|
|
2919
4936
|
|
|
2920
4937
|
}
|
|
2921
4938
|
|
|
@@ -2923,11 +4940,13 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
2923
4940
|
float transmission = material.transmission;
|
|
2924
4941
|
if ( material.transmissionMap != - 1 ) {
|
|
2925
4942
|
|
|
2926
|
-
|
|
4943
|
+
vec3 uvPrime = material.transmissionMapTransform * vec3( uv, 1 );
|
|
4944
|
+
transmission *= texture2D( textures, vec3( uvPrime.xy, material.transmissionMap ) ).r;
|
|
2927
4945
|
|
|
2928
4946
|
}
|
|
2929
4947
|
|
|
2930
4948
|
// normal
|
|
4949
|
+
vec3 baseNormal = normal;
|
|
2931
4950
|
if ( material.normalMap != - 1 ) {
|
|
2932
4951
|
|
|
2933
4952
|
vec4 tangentSample = textureSampleBarycoord(
|
|
@@ -2944,7 +4963,8 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
2944
4963
|
vec3 bitangent = normalize( cross( normal, tangent ) * tangentSample.w );
|
|
2945
4964
|
mat3 vTBN = mat3( tangent, bitangent, normal );
|
|
2946
4965
|
|
|
2947
|
-
vec3
|
|
4966
|
+
vec3 uvPrime = material.normalMapTransform * vec3( uv, 1 );
|
|
4967
|
+
vec3 texNormal = texture2D( textures, vec3( uvPrime.xy, material.normalMap ) ).xyz * 2.0 - 1.0;
|
|
2948
4968
|
texNormal.xy *= material.normalScale;
|
|
2949
4969
|
normal = vTBN * texNormal;
|
|
2950
4970
|
|
|
@@ -2954,6 +4974,110 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
2954
4974
|
|
|
2955
4975
|
normal *= side;
|
|
2956
4976
|
|
|
4977
|
+
// clearcoat
|
|
4978
|
+
float clearcoat = material.clearcoat;
|
|
4979
|
+
if ( material.clearcoatMap != - 1 ) {
|
|
4980
|
+
|
|
4981
|
+
vec3 uvPrime = material.clearcoatMapTransform * vec3( uv, 1 );
|
|
4982
|
+
clearcoat *= texture2D( textures, vec3( uvPrime.xy, material.clearcoatMap ) ).r;
|
|
4983
|
+
|
|
4984
|
+
}
|
|
4985
|
+
|
|
4986
|
+
// clearcoatRoughness
|
|
4987
|
+
float clearcoatRoughness = material.clearcoatRoughness;
|
|
4988
|
+
if ( material.clearcoatRoughnessMap != - 1 ) {
|
|
4989
|
+
|
|
4990
|
+
vec3 uvPrime = material.clearcoatRoughnessMapTransform * vec3( uv, 1 );
|
|
4991
|
+
clearcoat *= texture2D( textures, vec3( uvPrime.xy, material.clearcoatRoughnessMap ) ).g;
|
|
4992
|
+
|
|
4993
|
+
}
|
|
4994
|
+
|
|
4995
|
+
// clearcoatNormal
|
|
4996
|
+
vec3 clearcoatNormal = baseNormal;
|
|
4997
|
+
if ( material.clearcoatNormalMap != - 1 ) {
|
|
4998
|
+
|
|
4999
|
+
vec4 tangentSample = textureSampleBarycoord(
|
|
5000
|
+
tangentAttribute,
|
|
5001
|
+
barycoord,
|
|
5002
|
+
faceIndices.xyz
|
|
5003
|
+
);
|
|
5004
|
+
|
|
5005
|
+
// some provided tangents can be malformed (0, 0, 0) causing the normal to be degenerate
|
|
5006
|
+
// resulting in NaNs and slow path tracing.
|
|
5007
|
+
if ( length( tangentSample.xyz ) > 0.0 ) {
|
|
5008
|
+
|
|
5009
|
+
vec3 tangent = normalize( tangentSample.xyz );
|
|
5010
|
+
vec3 bitangent = normalize( cross( clearcoatNormal, tangent ) * tangentSample.w );
|
|
5011
|
+
mat3 vTBN = mat3( tangent, bitangent, clearcoatNormal );
|
|
5012
|
+
|
|
5013
|
+
vec3 uvPrime = material.clearcoatNormalMapTransform * vec3( uv, 1 );
|
|
5014
|
+
vec3 texNormal = texture2D( textures, vec3( uvPrime.xy, material.clearcoatNormalMap ) ).xyz * 2.0 - 1.0;
|
|
5015
|
+
texNormal.xy *= material.clearcoatNormalScale;
|
|
5016
|
+
clearcoatNormal = vTBN * texNormal;
|
|
5017
|
+
|
|
5018
|
+
}
|
|
5019
|
+
|
|
5020
|
+
}
|
|
5021
|
+
|
|
5022
|
+
clearcoatNormal *= side;
|
|
5023
|
+
|
|
5024
|
+
// sheenColor
|
|
5025
|
+
vec3 sheenColor = material.sheenColor;
|
|
5026
|
+
if ( material.sheenColorMap != - 1 ) {
|
|
5027
|
+
|
|
5028
|
+
vec3 uvPrime = material.sheenColorMapTransform * vec3( uv, 1 );
|
|
5029
|
+
sheenColor *= texture2D( textures, vec3( uvPrime.xy, material.sheenColorMap ) ).rgb;
|
|
5030
|
+
|
|
5031
|
+
}
|
|
5032
|
+
|
|
5033
|
+
// sheenRoughness
|
|
5034
|
+
float sheenRoughness = material.sheenRoughness;
|
|
5035
|
+
if ( material.sheenRoughnessMap != - 1 ) {
|
|
5036
|
+
|
|
5037
|
+
vec3 uvPrime = material.sheenRoughnessMapTransform * vec3( uv, 1 );
|
|
5038
|
+
sheenRoughness *= texture2D( textures, vec3( uvPrime.xy, material.sheenRoughnessMap ) ).a;
|
|
5039
|
+
|
|
5040
|
+
}
|
|
5041
|
+
|
|
5042
|
+
// iridescence
|
|
5043
|
+
float iridescence = material.iridescence;
|
|
5044
|
+
if ( material.iridescenceMap != - 1 ) {
|
|
5045
|
+
|
|
5046
|
+
vec3 uvPrime = material.iridescenceMapTransform * vec3( uv, 1 );
|
|
5047
|
+
iridescence *= texture2D( textures, vec3( uvPrime.xy, material.iridescenceMap ) ).r;
|
|
5048
|
+
|
|
5049
|
+
}
|
|
5050
|
+
|
|
5051
|
+
// iridescence thickness
|
|
5052
|
+
float iridescenceThickness = material.iridescenceThicknessMaximum;
|
|
5053
|
+
if ( material.iridescenceThicknessMap != - 1 ) {
|
|
5054
|
+
|
|
5055
|
+
vec3 uvPrime = material.iridescenceThicknessMapTransform * vec3( uv, 1 );
|
|
5056
|
+
float iridescenceThicknessSampled = texture2D( textures, vec3( uvPrime.xy, material.iridescenceThicknessMap ) ).g;
|
|
5057
|
+
iridescenceThickness = mix( material.iridescenceThicknessMinimum, material.iridescenceThicknessMaximum, iridescenceThicknessSampled );
|
|
5058
|
+
|
|
5059
|
+
}
|
|
5060
|
+
|
|
5061
|
+
iridescence = iridescenceThickness == 0.0 ? 0.0 : iridescence;
|
|
5062
|
+
|
|
5063
|
+
// specular color
|
|
5064
|
+
vec3 specularColor = material.specularColor;
|
|
5065
|
+
if ( material.specularColorMap != - 1 ) {
|
|
5066
|
+
|
|
5067
|
+
vec3 uvPrime = material.specularColorMapTransform * vec3( uv, 1 );
|
|
5068
|
+
specularColor *= texture2D( textures, vec3( uvPrime.xy, material.specularColorMap ) ).rgb;
|
|
5069
|
+
|
|
5070
|
+
}
|
|
5071
|
+
|
|
5072
|
+
// specular intensity
|
|
5073
|
+
float specularIntensity = material.specularIntensity;
|
|
5074
|
+
if ( material.specularIntensityMap != - 1 ) {
|
|
5075
|
+
|
|
5076
|
+
vec3 uvPrime = material.specularIntensityMapTransform * vec3( uv, 1 );
|
|
5077
|
+
specularIntensity *= texture2D( textures, vec3( uvPrime.xy, material.specularIntensityMap ) ).a;
|
|
5078
|
+
|
|
5079
|
+
}
|
|
5080
|
+
|
|
2957
5081
|
SurfaceRec surfaceRec;
|
|
2958
5082
|
surfaceRec.normal = normal;
|
|
2959
5083
|
surfaceRec.faceNormal = faceNormal;
|
|
@@ -2963,27 +5087,38 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
2963
5087
|
surfaceRec.metalness = metalness;
|
|
2964
5088
|
surfaceRec.color = albedo.rgb;
|
|
2965
5089
|
surfaceRec.roughness = roughness;
|
|
5090
|
+
surfaceRec.clearcoat = clearcoat;
|
|
5091
|
+
surfaceRec.clearcoatRoughness = clearcoatRoughness;
|
|
5092
|
+
surfaceRec.sheenColor = sheenColor;
|
|
5093
|
+
surfaceRec.sheenRoughness = sheenRoughness;
|
|
5094
|
+
surfaceRec.iridescence = iridescence;
|
|
5095
|
+
surfaceRec.iridescenceIor = material.iridescenceIor;
|
|
5096
|
+
surfaceRec.iridescenceThickness = iridescenceThickness;
|
|
5097
|
+
surfaceRec.specularColor = specularColor;
|
|
5098
|
+
surfaceRec.specularIntensity = specularIntensity;
|
|
2966
5099
|
|
|
2967
5100
|
// frontFace is used to determine transmissive properties and PDF. If no transmission is used
|
|
2968
5101
|
// then we can just always assume this is a front face.
|
|
2969
5102
|
surfaceRec.frontFace = side == 1.0 || transmission == 0.0;
|
|
2970
5103
|
|
|
2971
|
-
// Compute the filtered roughness value to use during specular reflection computations.
|
|
2972
|
-
//
|
|
2973
|
-
// the accumulated roughness value is scaled by a user setting and a "magic value" of 5.0.
|
|
5104
|
+
// Compute the filtered roughness value to use during specular reflection computations.
|
|
5105
|
+
// The accumulated roughness value is scaled by a user setting and a "magic value" of 5.0.
|
|
2974
5106
|
// If we're exiting something transmissive then scale the factor down significantly so we can retain
|
|
2975
5107
|
// sharp internal reflections
|
|
2976
|
-
surfaceRec.filteredRoughness = clamp(
|
|
2977
|
-
|
|
2978
|
-
1e-3,
|
|
2979
|
-
1.0
|
|
2980
|
-
);
|
|
5108
|
+
surfaceRec.filteredRoughness = clamp( max( surfaceRec.roughness, accumulatedRoughness * filterGlossyFactor * 5.0 ), 0.0, 1.0 );
|
|
5109
|
+
surfaceRec.filteredClearcoatRoughness = clamp( max( surfaceRec.clearcoatRoughness, accumulatedClearcoatRoughness * filterGlossyFactor * 5.0 ), 0.0, 1.0 );
|
|
2981
5110
|
|
|
2982
5111
|
mat3 normalBasis = getBasisFromNormal( surfaceRec.normal );
|
|
2983
5112
|
mat3 invBasis = inverse( normalBasis );
|
|
2984
5113
|
|
|
5114
|
+
mat3 clearcoatNormalBasis = getBasisFromNormal( clearcoatNormal );
|
|
5115
|
+
mat3 clearcoatInvBasis = inverse( clearcoatNormalBasis );
|
|
5116
|
+
|
|
2985
5117
|
vec3 outgoing = - normalize( invBasis * rayDirection );
|
|
2986
|
-
|
|
5118
|
+
vec3 clearcoatOutgoing = - normalize( clearcoatInvBasis * rayDirection );
|
|
5119
|
+
sampleRec = bsdfSample( outgoing, clearcoatOutgoing, normalBasis, invBasis, clearcoatNormalBasis, clearcoatInvBasis, surfaceRec );
|
|
5120
|
+
|
|
5121
|
+
isShadowRay = sampleRec.specularPdf < rand();
|
|
2987
5122
|
|
|
2988
5123
|
// adjust the hit point by the surface normal by a factor of some offset and the
|
|
2989
5124
|
// maximum component-wise value of the current point to accommodate floating point
|
|
@@ -2998,7 +5133,43 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
2998
5133
|
|
|
2999
5134
|
// direct env map sampling
|
|
3000
5135
|
#if FEATURE_MIS
|
|
3001
|
-
|
|
5136
|
+
|
|
5137
|
+
// uniformly pick a light or environment map
|
|
5138
|
+
if( rand() > 1.0 / float( lights.count + 1u ) ) {
|
|
5139
|
+
|
|
5140
|
+
// sample a light or environment
|
|
5141
|
+
LightSampleRec lightSampleRec = randomLightSample( lights.tex, iesProfiles, lights.count, rayOrigin );
|
|
5142
|
+
|
|
5143
|
+
bool isSampleBelowSurface = dot( faceNormal, lightSampleRec.direction ) < 0.0;
|
|
5144
|
+
if ( isSampleBelowSurface ) {
|
|
5145
|
+
|
|
5146
|
+
lightSampleRec.pdf = 0.0;
|
|
5147
|
+
|
|
5148
|
+
}
|
|
5149
|
+
|
|
5150
|
+
// check if a ray could even reach the light area
|
|
5151
|
+
if (
|
|
5152
|
+
lightSampleRec.pdf > 0.0 &&
|
|
5153
|
+
isDirectionValid( lightSampleRec.direction, normal, faceNormal ) &&
|
|
5154
|
+
! anyCloserHit( bvh, rayOrigin, lightSampleRec.direction, lightSampleRec.dist )
|
|
5155
|
+
) {
|
|
5156
|
+
|
|
5157
|
+
// get the material pdf
|
|
5158
|
+
vec3 sampleColor;
|
|
5159
|
+
float lightMaterialPdf = bsdfResult( outgoing, clearcoatOutgoing, normalize( invBasis * lightSampleRec.direction ), normalize( clearcoatInvBasis * lightSampleRec.direction ), surfaceRec, sampleColor );
|
|
5160
|
+
bool isValidSampleColor = all( greaterThanEqual( sampleColor, vec3( 0.0 ) ) );
|
|
5161
|
+
if ( lightMaterialPdf > 0.0 && isValidSampleColor ) {
|
|
5162
|
+
|
|
5163
|
+
// weight the direct light contribution
|
|
5164
|
+
float lightPdf = lightSampleRec.pdf / float( lights.count + 1u );
|
|
5165
|
+
float misWeight = misHeuristic( lightPdf, lightMaterialPdf );
|
|
5166
|
+
gl_FragColor.rgb += lightSampleRec.emission * throughputColor * sampleColor * misWeight / lightPdf;
|
|
5167
|
+
|
|
5168
|
+
}
|
|
5169
|
+
|
|
5170
|
+
}
|
|
5171
|
+
|
|
5172
|
+
} else {
|
|
3002
5173
|
|
|
3003
5174
|
// find a sample in the environment map to include in the contribution
|
|
3004
5175
|
vec3 envColor, envDirection;
|
|
@@ -3020,15 +5191,17 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
3020
5191
|
if (
|
|
3021
5192
|
envPdf > 0.0 &&
|
|
3022
5193
|
isDirectionValid( envDirection, normal, faceNormal ) &&
|
|
3023
|
-
! attenuateHit( bvh, rayOrigin, envDirection, bounces - i, attenuatedColor )
|
|
5194
|
+
! attenuateHit( bvh, rayOrigin, envDirection, bounces - i, isShadowRay, attenuatedColor )
|
|
3024
5195
|
) {
|
|
3025
5196
|
|
|
3026
5197
|
// get the material pdf
|
|
3027
5198
|
vec3 sampleColor;
|
|
3028
|
-
float envMaterialPdf = bsdfResult( outgoing, normalize( invBasis * envDirection ), surfaceRec, sampleColor );
|
|
3029
|
-
|
|
5199
|
+
float envMaterialPdf = bsdfResult( outgoing, clearcoatOutgoing, normalize( invBasis * envDirection ), normalize( clearcoatInvBasis * envDirection ), surfaceRec, sampleColor );
|
|
5200
|
+
bool isValidSampleColor = all( greaterThanEqual( sampleColor, vec3( 0.0 ) ) );
|
|
5201
|
+
if ( envMaterialPdf > 0.0 && isValidSampleColor ) {
|
|
3030
5202
|
|
|
3031
5203
|
// weight the direct light contribution
|
|
5204
|
+
envPdf /= float( lights.count + 1u );
|
|
3032
5205
|
float misWeight = misHeuristic( envPdf, envMaterialPdf );
|
|
3033
5206
|
gl_FragColor.rgb += attenuatedColor * environmentIntensity * envColor * throughputColor * sampleColor * misWeight / envPdf;
|
|
3034
5207
|
|
|
@@ -3045,7 +5218,11 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
3045
5218
|
|
|
3046
5219
|
// determine if this is a rough normal or not by checking how far off straight up it is
|
|
3047
5220
|
vec3 halfVector = normalize( outgoing + sampleRec.direction );
|
|
3048
|
-
accumulatedRoughness += sin(
|
|
5221
|
+
accumulatedRoughness += sin( acosApprox( halfVector.z ) );
|
|
5222
|
+
|
|
5223
|
+
vec3 clearcoatHalfVector = normalize( clearcoatOutgoing + sampleRec.clearcoatDirection );
|
|
5224
|
+
accumulatedClearcoatRoughness += sin( acosApprox( clearcoatHalfVector.z ) );
|
|
5225
|
+
|
|
3049
5226
|
transmissiveRay = false;
|
|
3050
5227
|
|
|
3051
5228
|
}
|
|
@@ -3087,5 +5264,5 @@ class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
3087
5264
|
|
|
3088
5265
|
// core
|
|
3089
5266
|
|
|
3090
|
-
export { BlurredEnvMapGenerator, DynamicPathTracingSceneGenerator, EquirectHdrInfoUniform, MaterialBase, MaterialReducer, MaterialsTexture, PathTracingRenderer, PathTracingSceneGenerator, PhysicalCamera, PhysicalCameraUniform, PhysicalPathTracingMaterial, RenderTarget2DArray, getGroupMaterialIndicesAttribute, mergeMeshes, setCommonAttributes, shaderMaterialSampling, shaderMaterialStructs, shaderUtils, trimToAttributes };
|
|
5267
|
+
export { BlurredEnvMapGenerator, DynamicPathTracingSceneGenerator, EquirectCamera, EquirectHdrInfoUniform, IESLoader, IESProfilesTexture, LightsInfoUniformStruct, MaterialBase, MaterialReducer, MaterialsTexture, PathTracingRenderer, PathTracingSceneGenerator, PhysicalCamera, PhysicalCameraUniform, PhysicalPathTracingMaterial, PhysicalSpotLight, RenderTarget2DArray, ShapedAreaLight, getGroupMaterialIndicesAttribute, mergeMeshes, setCommonAttributes, shaderLightStruct, shaderMaterialSampling, shaderMaterialStructs, shaderUtils, trimToAttributes };
|
|
3091
5268
|
//# sourceMappingURL=index.module.js.map
|