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