three-gpu-pathtracer 0.0.12 → 0.0.14
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 +102 -7
- package/build/index.module.js +2891 -2065
- package/build/index.module.js.map +1 -1
- package/build/index.umd.cjs +2886 -2062
- package/build/index.umd.cjs.map +1 -1
- package/package.json +2 -1
- package/src/core/PathTracingRenderer.js +87 -16
- package/src/core/PathTracingSceneGenerator.js +1 -1
- package/src/core/QuiltPathTracingRenderer.js +223 -0
- package/src/index.js +5 -8
- package/src/materials/{GraphMaterial.js → debug/GraphMaterial.js} +1 -1
- package/src/materials/{AlphaDisplayMaterial.js → fullscreen/AlphaDisplayMaterial.js} +1 -1
- package/src/materials/{BlendMaterial.js → fullscreen/BlendMaterial.js} +1 -1
- package/src/materials/{DenoiseMaterial.js → fullscreen/DenoiseMaterial.js} +1 -1
- package/src/materials/{LambertPathTracingMaterial.js → pathtracing/LambertPathTracingMaterial.js} +18 -7
- package/src/materials/pathtracing/PhysicalPathTracingMaterial.js +635 -0
- package/src/materials/pathtracing/glsl/attenuateHit.glsl.js +179 -0
- package/src/materials/pathtracing/glsl/cameraUtils.glsl.js +81 -0
- package/src/materials/pathtracing/glsl/getSurfaceRecord.glsl.js +317 -0
- package/src/materials/pathtracing/glsl/traceScene.glsl.js +54 -0
- package/src/materials/{AmbientOcclusionMaterial.js → surface/AmbientOcclusionMaterial.js} +16 -8
- package/src/materials/surface/FogVolumeMaterial.js +23 -0
- package/src/shader/bsdf/bsdfSampling.glsl.js +490 -0
- package/src/shader/bsdf/fog.glsl.js +23 -0
- package/src/shader/bsdf/ggx.glsl.js +102 -0
- package/src/shader/bsdf/iridescence.glsl.js +135 -0
- package/src/shader/bsdf/sheen.glsl.js +98 -0
- package/src/shader/{shaderLayerTexelFetchFunctions.js → common/arraySamplerTexelFetch.glsl.js} +1 -1
- package/src/shader/common/bvhAnyHit.glsl.js +76 -0
- package/src/shader/common/fresnel.glsl.js +98 -0
- package/src/shader/common/intersectShapes.glsl.js +62 -0
- package/src/shader/common/math.glsl.js +81 -0
- package/src/shader/common/utils.glsl.js +116 -0
- package/src/shader/{shaderRandFunctions.js → rand/pcg.glsl.js} +1 -1
- package/src/shader/{shaderSobolSampling.js → rand/sobol.glsl.js} +3 -3
- package/src/shader/sampling/equirectSampling.glsl.js +62 -0
- package/src/shader/sampling/lightSampling.glsl.js +223 -0
- package/src/shader/sampling/shapeSampling.glsl.js +86 -0
- package/src/shader/structs/cameraStruct.glsl.js +13 -0
- package/src/shader/structs/equirectStruct.glsl.js +14 -0
- package/src/shader/structs/fogMaterialBvh.glsl.js +62 -0
- package/src/shader/structs/lightsStruct.glsl.js +78 -0
- package/src/shader/{shaderStructs.js → structs/materialStruct.glsl.js} +5 -123
- package/src/uniforms/EquirectHdrInfoUniform.js +29 -11
- package/src/uniforms/LightsInfoUniformStruct.js +9 -4
- package/src/uniforms/MaterialsTexture.js +80 -3
- package/src/utils/BlurredEnvMapGenerator.js +2 -2
- package/src/utils/SobolNumberMapGenerator.js +3 -3
- package/src/utils/macroify.js +9 -0
- package/src/materials/PhysicalPathTracingMaterial.js +0 -982
- package/src/shader/shaderBvhAnyHit.js +0 -76
- package/src/shader/shaderEnvMapSampling.js +0 -58
- package/src/shader/shaderGGXFunctions.js +0 -100
- package/src/shader/shaderIridescenceFunctions.js +0 -130
- package/src/shader/shaderLightSampling.js +0 -229
- package/src/shader/shaderMaterialSampling.js +0 -506
- package/src/shader/shaderSheenFunctions.js +0 -98
- package/src/shader/shaderUtils.js +0 -361
|
@@ -0,0 +1,635 @@
|
|
|
1
|
+
import { Matrix4, Vector2 } from 'three';
|
|
2
|
+
import { MaterialBase } from '../MaterialBase.js';
|
|
3
|
+
import {
|
|
4
|
+
MeshBVHUniformStruct, UIntVertexAttributeTexture,
|
|
5
|
+
shaderStructs, shaderIntersectFunction,
|
|
6
|
+
} from 'three-mesh-bvh';
|
|
7
|
+
|
|
8
|
+
// uniforms
|
|
9
|
+
import { PhysicalCameraUniform } from '../../uniforms/PhysicalCameraUniform.js';
|
|
10
|
+
import { EquirectHdrInfoUniform } from '../../uniforms/EquirectHdrInfoUniform.js';
|
|
11
|
+
import { LightsInfoUniformStruct } from '../../uniforms/LightsInfoUniformStruct.js';
|
|
12
|
+
import { IESProfilesTexture } from '../../uniforms/IESProfilesTexture.js';
|
|
13
|
+
import { AttributesTextureArray } from '../../uniforms/AttributesTextureArray.js';
|
|
14
|
+
import { MaterialsTexture } from '../../uniforms/MaterialsTexture.js';
|
|
15
|
+
import { RenderTarget2DArray } from '../../uniforms/RenderTarget2DArray.js';
|
|
16
|
+
|
|
17
|
+
// glsl
|
|
18
|
+
import { cameraStructGLSL } from '../../shader/structs/cameraStruct.glsl.js';
|
|
19
|
+
import { equirectStructGLSL } from '../../shader/structs/equirectStruct.glsl.js';
|
|
20
|
+
import { lightsStructGLSL } from '../../shader/structs/lightsStruct.glsl.js';
|
|
21
|
+
import { materialStructGLSL } from '../../shader/structs/materialStruct.glsl.js';
|
|
22
|
+
import { fogMaterialBvhGLSL } from '../../shader/structs/fogMaterialBvh.glsl.js';
|
|
23
|
+
|
|
24
|
+
// material sampling
|
|
25
|
+
import { bsdfSamplingGLSL } from '../../shader/bsdf/bsdfSampling.glsl.js';
|
|
26
|
+
import { fogGLSL } from '../../shader/bsdf/fog.glsl.js';
|
|
27
|
+
|
|
28
|
+
// sampling
|
|
29
|
+
import { equirectSamplingGLSL } from '../../shader/sampling/equirectSampling.glsl.js';
|
|
30
|
+
import { lightSamplingGLSL } from '../../shader/sampling/lightSampling.glsl.js';
|
|
31
|
+
import { shapeSamplingGLSL } from '../../shader/sampling/shapeSampling.glsl.js';
|
|
32
|
+
|
|
33
|
+
// common glsl
|
|
34
|
+
import { intersectShapesGLSL } from '../../shader/common/intersectShapes.glsl';
|
|
35
|
+
import { mathGLSL } from '../../shader/common/math.glsl.js';
|
|
36
|
+
import { utilsGLSL } from '../../shader/common/utils.glsl.js';
|
|
37
|
+
import { fresnelGLSL } from '../../shader/common/fresnel.glsl.js';
|
|
38
|
+
import { arraySamplerTexelFetchGLSL } from '../../shader/common/arraySamplerTexelFetch.glsl.js';
|
|
39
|
+
|
|
40
|
+
// random glsl
|
|
41
|
+
import { pcgGLSL } from '../../shader/rand/pcg.glsl.js';
|
|
42
|
+
import { sobolCommonGLSL, sobolSamplingGLSL } from '../../shader/rand/sobol.glsl.js';
|
|
43
|
+
|
|
44
|
+
// path tracer utils
|
|
45
|
+
import { cameraUtilsGLSL } from './glsl/cameraUtils.glsl.js';
|
|
46
|
+
import { attenuateHitGLSL } from './glsl/attenuateHit.glsl.js';
|
|
47
|
+
import { traceSceneGLSL } from './glsl/traceScene.glsl.js';
|
|
48
|
+
import { getSurfaceRecordGLSL } from './glsl/getSurfaceRecord.glsl.js';
|
|
49
|
+
|
|
50
|
+
export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
51
|
+
|
|
52
|
+
onBeforeRender() {
|
|
53
|
+
|
|
54
|
+
this.setDefine( 'FEATURE_DOF', this.physicalCamera.bokehSize === 0 ? 0 : 1 );
|
|
55
|
+
this.setDefine( 'FEATURE_BACKGROUND_MAP', this.backgroundMap ? 1 : 0 );
|
|
56
|
+
this.setDefine( 'FEATURE_FOG', this.materials.features.isUsed( 'FOG' ) ? 1 : 0 );
|
|
57
|
+
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
constructor( parameters ) {
|
|
61
|
+
|
|
62
|
+
super( {
|
|
63
|
+
|
|
64
|
+
transparent: true,
|
|
65
|
+
depthWrite: false,
|
|
66
|
+
|
|
67
|
+
defines: {
|
|
68
|
+
FEATURE_MIS: 1,
|
|
69
|
+
FEATURE_RUSSIAN_ROULETTE: 1,
|
|
70
|
+
FEATURE_DOF: 1,
|
|
71
|
+
FEATURE_BACKGROUND_MAP: 0,
|
|
72
|
+
FEATURE_FOG: 1,
|
|
73
|
+
// 0 = Perspective
|
|
74
|
+
// 1 = Orthographic
|
|
75
|
+
// 2 = Equirectangular
|
|
76
|
+
CAMERA_TYPE: 0,
|
|
77
|
+
|
|
78
|
+
ATTR_NORMAL: 0,
|
|
79
|
+
ATTR_TANGENT: 1,
|
|
80
|
+
ATTR_UV: 2,
|
|
81
|
+
ATTR_COLOR: 3,
|
|
82
|
+
},
|
|
83
|
+
|
|
84
|
+
uniforms: {
|
|
85
|
+
resolution: { value: new Vector2() },
|
|
86
|
+
|
|
87
|
+
bounces: { value: 10 },
|
|
88
|
+
transmissiveBounces: { value: 10 },
|
|
89
|
+
physicalCamera: { value: new PhysicalCameraUniform() },
|
|
90
|
+
|
|
91
|
+
bvh: { value: new MeshBVHUniformStruct() },
|
|
92
|
+
attributesArray: { value: new AttributesTextureArray() },
|
|
93
|
+
materialIndexAttribute: { value: new UIntVertexAttributeTexture() },
|
|
94
|
+
materials: { value: new MaterialsTexture() },
|
|
95
|
+
textures: { value: new RenderTarget2DArray().texture },
|
|
96
|
+
lights: { value: new LightsInfoUniformStruct() },
|
|
97
|
+
iesProfiles: { value: new IESProfilesTexture().texture },
|
|
98
|
+
cameraWorldMatrix: { value: new Matrix4() },
|
|
99
|
+
invProjectionMatrix: { value: new Matrix4() },
|
|
100
|
+
backgroundBlur: { value: 0.0 },
|
|
101
|
+
environmentIntensity: { value: 1.0 },
|
|
102
|
+
environmentRotation: { value: new Matrix4() },
|
|
103
|
+
envMapInfo: { value: new EquirectHdrInfoUniform() },
|
|
104
|
+
backgroundMap: { value: null },
|
|
105
|
+
|
|
106
|
+
seed: { value: 0 },
|
|
107
|
+
opacity: { value: 1 },
|
|
108
|
+
filterGlossyFactor: { value: 0.0 },
|
|
109
|
+
|
|
110
|
+
backgroundAlpha: { value: 1.0 },
|
|
111
|
+
sobolTexture: { value: null },
|
|
112
|
+
},
|
|
113
|
+
|
|
114
|
+
vertexShader: /* glsl */`
|
|
115
|
+
|
|
116
|
+
varying vec2 vUv;
|
|
117
|
+
void main() {
|
|
118
|
+
|
|
119
|
+
vec4 mvPosition = vec4( position, 1.0 );
|
|
120
|
+
mvPosition = modelViewMatrix * mvPosition;
|
|
121
|
+
gl_Position = projectionMatrix * mvPosition;
|
|
122
|
+
|
|
123
|
+
vUv = uv;
|
|
124
|
+
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
`,
|
|
128
|
+
|
|
129
|
+
fragmentShader: /* glsl */`
|
|
130
|
+
#define RAY_OFFSET 1e-4
|
|
131
|
+
#define INFINITY 1e20
|
|
132
|
+
|
|
133
|
+
precision highp isampler2D;
|
|
134
|
+
precision highp usampler2D;
|
|
135
|
+
precision highp sampler2DArray;
|
|
136
|
+
vec4 envMapTexelToLinear( vec4 a ) { return a; }
|
|
137
|
+
#include <common>
|
|
138
|
+
|
|
139
|
+
// bvh intersection
|
|
140
|
+
${ shaderStructs }
|
|
141
|
+
${ shaderIntersectFunction }
|
|
142
|
+
|
|
143
|
+
// random
|
|
144
|
+
${ pcgGLSL }
|
|
145
|
+
${ sobolCommonGLSL }
|
|
146
|
+
${ sobolSamplingGLSL }
|
|
147
|
+
|
|
148
|
+
// common
|
|
149
|
+
${ arraySamplerTexelFetchGLSL }
|
|
150
|
+
${ fresnelGLSL }
|
|
151
|
+
${ utilsGLSL }
|
|
152
|
+
${ mathGLSL }
|
|
153
|
+
${ intersectShapesGLSL }
|
|
154
|
+
|
|
155
|
+
// uniform structs
|
|
156
|
+
${ cameraStructGLSL }
|
|
157
|
+
${ lightsStructGLSL }
|
|
158
|
+
${ equirectStructGLSL }
|
|
159
|
+
${ materialStructGLSL }
|
|
160
|
+
${ fogMaterialBvhGLSL }
|
|
161
|
+
|
|
162
|
+
// sampling
|
|
163
|
+
${ shapeSamplingGLSL }
|
|
164
|
+
${ bsdfSamplingGLSL }
|
|
165
|
+
${ equirectSamplingGLSL }
|
|
166
|
+
${ lightSamplingGLSL }
|
|
167
|
+
${ fogGLSL }
|
|
168
|
+
|
|
169
|
+
// environment
|
|
170
|
+
uniform EquirectHdrInfo envMapInfo;
|
|
171
|
+
uniform mat4 environmentRotation;
|
|
172
|
+
uniform float environmentIntensity;
|
|
173
|
+
|
|
174
|
+
// lighting
|
|
175
|
+
uniform sampler2DArray iesProfiles;
|
|
176
|
+
uniform LightsInfo lights;
|
|
177
|
+
|
|
178
|
+
// background
|
|
179
|
+
uniform float backgroundBlur;
|
|
180
|
+
uniform float backgroundAlpha;
|
|
181
|
+
#if FEATURE_BACKGROUND_MAP
|
|
182
|
+
|
|
183
|
+
uniform sampler2D backgroundMap;
|
|
184
|
+
|
|
185
|
+
#endif
|
|
186
|
+
|
|
187
|
+
// camera
|
|
188
|
+
uniform mat4 cameraWorldMatrix;
|
|
189
|
+
uniform mat4 invProjectionMatrix;
|
|
190
|
+
#if FEATURE_DOF
|
|
191
|
+
|
|
192
|
+
uniform PhysicalCamera physicalCamera;
|
|
193
|
+
|
|
194
|
+
#endif
|
|
195
|
+
|
|
196
|
+
// geometry
|
|
197
|
+
uniform sampler2DArray attributesArray;
|
|
198
|
+
uniform usampler2D materialIndexAttribute;
|
|
199
|
+
uniform sampler2D materials;
|
|
200
|
+
uniform sampler2DArray textures;
|
|
201
|
+
uniform BVH bvh;
|
|
202
|
+
|
|
203
|
+
// path tracer
|
|
204
|
+
uniform int bounces;
|
|
205
|
+
uniform int transmissiveBounces;
|
|
206
|
+
uniform float filterGlossyFactor;
|
|
207
|
+
uniform int seed;
|
|
208
|
+
|
|
209
|
+
// image
|
|
210
|
+
uniform vec2 resolution;
|
|
211
|
+
uniform float opacity;
|
|
212
|
+
|
|
213
|
+
varying vec2 vUv;
|
|
214
|
+
|
|
215
|
+
${ cameraUtilsGLSL }
|
|
216
|
+
${ traceSceneGLSL }
|
|
217
|
+
${ attenuateHitGLSL }
|
|
218
|
+
|
|
219
|
+
float applyFilteredGlossy( float roughness, float accumulatedRoughness ) {
|
|
220
|
+
|
|
221
|
+
return clamp(
|
|
222
|
+
max(
|
|
223
|
+
roughness,
|
|
224
|
+
accumulatedRoughness * filterGlossyFactor * 5.0 ),
|
|
225
|
+
0.0,
|
|
226
|
+
1.0
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
vec3 sampleBackground( vec3 direction, vec2 uv ) {
|
|
232
|
+
|
|
233
|
+
vec3 sampleDir = normalize( direction + sampleHemisphere( direction, uv ) * 0.5 * backgroundBlur );
|
|
234
|
+
|
|
235
|
+
#if FEATURE_BACKGROUND_MAP
|
|
236
|
+
|
|
237
|
+
return sampleEquirectColor( backgroundMap, sampleDir );
|
|
238
|
+
|
|
239
|
+
#else
|
|
240
|
+
|
|
241
|
+
return environmentIntensity * sampleEquirectColor( envMapInfo.map, sampleDir );
|
|
242
|
+
|
|
243
|
+
#endif
|
|
244
|
+
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
${ getSurfaceRecordGLSL }
|
|
248
|
+
|
|
249
|
+
void main() {
|
|
250
|
+
|
|
251
|
+
// init
|
|
252
|
+
rng_initialize( gl_FragCoord.xy, seed );
|
|
253
|
+
sobolPixelIndex = ( uint( gl_FragCoord.x ) << 16 ) | uint( gl_FragCoord.y );
|
|
254
|
+
sobolPathIndex = uint( seed );
|
|
255
|
+
|
|
256
|
+
// get camera ray
|
|
257
|
+
vec3 rayDirection, rayOrigin;
|
|
258
|
+
getCameraRay( rayDirection, rayOrigin );
|
|
259
|
+
|
|
260
|
+
// inverse environment rotation
|
|
261
|
+
mat3 envRotation3x3 = mat3( environmentRotation );
|
|
262
|
+
mat3 invEnvRotation3x3 = inverse( envRotation3x3 );
|
|
263
|
+
float lightsDenom = environmentIntensity == 0.0 && lights.count != 0u ? float( lights.count ) : float( lights.count + 1u );
|
|
264
|
+
|
|
265
|
+
// final color
|
|
266
|
+
gl_FragColor = vec4( 0.0 );
|
|
267
|
+
gl_FragColor.a = 1.0;
|
|
268
|
+
|
|
269
|
+
// hit results
|
|
270
|
+
uvec4 faceIndices = uvec4( 0u );
|
|
271
|
+
vec3 faceNormal = vec3( 0.0, 0.0, 1.0 );
|
|
272
|
+
vec3 barycoord = vec3( 0.0 );
|
|
273
|
+
float side = 1.0;
|
|
274
|
+
float dist = 0.0;
|
|
275
|
+
|
|
276
|
+
// path tracing state
|
|
277
|
+
float accumulatedRoughness = 0.0;
|
|
278
|
+
bool transmissiveRay = true;
|
|
279
|
+
bool isShadowRay = false;
|
|
280
|
+
int transmissiveTraversals = transmissiveBounces;
|
|
281
|
+
vec3 throughputColor = vec3( 1.0 );
|
|
282
|
+
ScatterRecord sampleRec;
|
|
283
|
+
int i;
|
|
284
|
+
|
|
285
|
+
Material fogMaterial;
|
|
286
|
+
#if FEATURE_FOG
|
|
287
|
+
|
|
288
|
+
fogMaterial.fogVolume = bvhIntersectFogVolumeHit(
|
|
289
|
+
bvh, rayOrigin, - rayDirection,
|
|
290
|
+
materialIndexAttribute, materials,
|
|
291
|
+
fogMaterial
|
|
292
|
+
);
|
|
293
|
+
|
|
294
|
+
#endif
|
|
295
|
+
|
|
296
|
+
for ( i = 0; i < bounces; i ++ ) {
|
|
297
|
+
|
|
298
|
+
sobolBounceIndex ++;
|
|
299
|
+
|
|
300
|
+
bool firstRay = i == 0 && transmissiveTraversals == transmissiveBounces;
|
|
301
|
+
|
|
302
|
+
LightSampleRecord lightSampleRec;
|
|
303
|
+
int hitType = traceScene(
|
|
304
|
+
rayOrigin, rayDirection,
|
|
305
|
+
bvh, lights, fogMaterial,
|
|
306
|
+
faceIndices, faceNormal, barycoord, side, dist,
|
|
307
|
+
lightSampleRec
|
|
308
|
+
);
|
|
309
|
+
|
|
310
|
+
if ( hitType == LIGHT_HIT ) {
|
|
311
|
+
|
|
312
|
+
if ( firstRay || transmissiveRay ) {
|
|
313
|
+
|
|
314
|
+
gl_FragColor.rgb += lightSampleRec.emission * throughputColor;
|
|
315
|
+
|
|
316
|
+
} else {
|
|
317
|
+
|
|
318
|
+
#if FEATURE_MIS
|
|
319
|
+
|
|
320
|
+
// NOTE: we skip MIS for punctual lights since they are not supported in forward PT case
|
|
321
|
+
if ( lightSampleRec.type == SPOT_LIGHT_TYPE || lightSampleRec.type == DIR_LIGHT_TYPE || lightSampleRec.type == POINT_LIGHT_TYPE ) {
|
|
322
|
+
|
|
323
|
+
gl_FragColor.rgb += lightSampleRec.emission * throughputColor;
|
|
324
|
+
|
|
325
|
+
} else {
|
|
326
|
+
|
|
327
|
+
// weight the contribution
|
|
328
|
+
float misWeight = misHeuristic( sampleRec.pdf, lightSampleRec.pdf / lightsDenom );
|
|
329
|
+
gl_FragColor.rgb += lightSampleRec.emission * throughputColor * misWeight;
|
|
330
|
+
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
#else
|
|
334
|
+
|
|
335
|
+
gl_FragColor.rgb += lightSampleRec.emission * throughputColor;
|
|
336
|
+
|
|
337
|
+
#endif
|
|
338
|
+
|
|
339
|
+
}
|
|
340
|
+
break;
|
|
341
|
+
|
|
342
|
+
} else if ( hitType == NO_HIT ) {
|
|
343
|
+
|
|
344
|
+
if ( firstRay || transmissiveRay ) {
|
|
345
|
+
|
|
346
|
+
gl_FragColor.rgb += sampleBackground( envRotation3x3 * rayDirection, sobol2( 2 ) ) * throughputColor;
|
|
347
|
+
gl_FragColor.a = backgroundAlpha;
|
|
348
|
+
|
|
349
|
+
} else {
|
|
350
|
+
|
|
351
|
+
#if FEATURE_MIS
|
|
352
|
+
|
|
353
|
+
// get the PDF of the hit envmap point
|
|
354
|
+
vec3 envColor;
|
|
355
|
+
float envPdf = sampleEquirect( envMapInfo, envRotation3x3 * rayDirection, envColor );
|
|
356
|
+
envPdf /= lightsDenom;
|
|
357
|
+
|
|
358
|
+
// and weight the contribution
|
|
359
|
+
float misWeight = misHeuristic( sampleRec.pdf, envPdf );
|
|
360
|
+
gl_FragColor.rgb += environmentIntensity * envColor * throughputColor * misWeight;
|
|
361
|
+
|
|
362
|
+
#else
|
|
363
|
+
|
|
364
|
+
gl_FragColor.rgb +=
|
|
365
|
+
environmentIntensity *
|
|
366
|
+
sampleEquirectColor( envMapInfo.map, envRotation3x3 * rayDirection ) *
|
|
367
|
+
throughputColor;
|
|
368
|
+
|
|
369
|
+
#endif
|
|
370
|
+
|
|
371
|
+
}
|
|
372
|
+
break;
|
|
373
|
+
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
uint materialIndex = uTexelFetch1D( materialIndexAttribute, faceIndices.x ).r;
|
|
377
|
+
Material material = readMaterialInfo( materials, materialIndex );
|
|
378
|
+
|
|
379
|
+
#if FEATURE_FOG
|
|
380
|
+
|
|
381
|
+
if ( hitType == FOG_HIT ) {
|
|
382
|
+
|
|
383
|
+
material = fogMaterial;
|
|
384
|
+
accumulatedRoughness += 0.2;
|
|
385
|
+
|
|
386
|
+
} else if ( material.fogVolume ) {
|
|
387
|
+
|
|
388
|
+
fogMaterial = material;
|
|
389
|
+
fogMaterial.fogVolume = side == 1.0;
|
|
390
|
+
|
|
391
|
+
rayOrigin = stepRayOrigin( rayOrigin, rayDirection, - faceNormal, dist );
|
|
392
|
+
|
|
393
|
+
i -= sign( transmissiveTraversals );
|
|
394
|
+
transmissiveTraversals -= sign( transmissiveTraversals );
|
|
395
|
+
continue;
|
|
396
|
+
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
#endif
|
|
400
|
+
|
|
401
|
+
if ( material.matte && firstRay ) {
|
|
402
|
+
|
|
403
|
+
gl_FragColor = vec4( 0.0 );
|
|
404
|
+
break;
|
|
405
|
+
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
// if we've determined that this is a shadow ray and we've hit an item with no shadow casting
|
|
409
|
+
// then skip it
|
|
410
|
+
if ( ! material.castShadow && isShadowRay ) {
|
|
411
|
+
|
|
412
|
+
rayOrigin = stepRayOrigin( rayOrigin, rayDirection, - faceNormal, dist );
|
|
413
|
+
continue;
|
|
414
|
+
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
SurfaceRecord surf;
|
|
418
|
+
if (
|
|
419
|
+
getSurfaceRecord(
|
|
420
|
+
material, attributesArray, side, barycoord, faceIndices,
|
|
421
|
+
faceNormal, accumulatedRoughness,
|
|
422
|
+
surf
|
|
423
|
+
) == SKIP_SURFACE
|
|
424
|
+
) {
|
|
425
|
+
|
|
426
|
+
// only allow a limited number of transparency discards otherwise we could
|
|
427
|
+
// crash the context with too long a loop.
|
|
428
|
+
i -= sign( transmissiveTraversals );
|
|
429
|
+
transmissiveTraversals -= sign( transmissiveTraversals );
|
|
430
|
+
|
|
431
|
+
rayOrigin = stepRayOrigin( rayOrigin, rayDirection, - faceNormal, dist );
|
|
432
|
+
continue;
|
|
433
|
+
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
faceNormal = surf.faceNormal;
|
|
437
|
+
|
|
438
|
+
mat3 normalBasis = getBasisFromNormal( surf.normal );
|
|
439
|
+
mat3 invBasis = inverse( normalBasis );
|
|
440
|
+
|
|
441
|
+
mat3 clearcoatNormalBasis = getBasisFromNormal( surf.clearcoatNormal );
|
|
442
|
+
mat3 clearcoatInvBasis = inverse( clearcoatNormalBasis );
|
|
443
|
+
|
|
444
|
+
vec3 outgoing = - normalize( invBasis * rayDirection );
|
|
445
|
+
vec3 clearcoatOutgoing = - normalize( clearcoatInvBasis * rayDirection );
|
|
446
|
+
sampleRec = bsdfSample( outgoing, clearcoatOutgoing, normalBasis, invBasis, clearcoatNormalBasis, clearcoatInvBasis, surf );
|
|
447
|
+
|
|
448
|
+
bool wasBelowSurface = ! surf.volumeParticle && dot( rayDirection, faceNormal ) > 0.0;
|
|
449
|
+
isShadowRay = sampleRec.specularPdf < sobol( 4 );
|
|
450
|
+
|
|
451
|
+
vec3 prevRayDirection = rayDirection;
|
|
452
|
+
rayDirection = normalize( normalBasis * sampleRec.direction );
|
|
453
|
+
|
|
454
|
+
bool isBelowSurface = ! surf.volumeParticle && dot( rayDirection, faceNormal ) < 0.0;
|
|
455
|
+
rayOrigin = stepRayOrigin( rayOrigin, prevRayDirection, isBelowSurface ? - faceNormal : faceNormal, dist );
|
|
456
|
+
|
|
457
|
+
// direct env map sampling
|
|
458
|
+
#if FEATURE_MIS
|
|
459
|
+
|
|
460
|
+
// uniformly pick a light or environment map
|
|
461
|
+
if( lightsDenom != 0.0 && sobol( 5 ) < float( lights.count ) / lightsDenom ) {
|
|
462
|
+
|
|
463
|
+
// sample a light or environment
|
|
464
|
+
LightSampleRecord lightSampleRec = randomLightSample( lights.tex, iesProfiles, lights.count, rayOrigin, sobol3( 6 ) );
|
|
465
|
+
|
|
466
|
+
bool isSampleBelowSurface = ! surf.volumeParticle && dot( faceNormal, lightSampleRec.direction ) < 0.0;
|
|
467
|
+
if ( isSampleBelowSurface ) {
|
|
468
|
+
|
|
469
|
+
lightSampleRec.pdf = 0.0;
|
|
470
|
+
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
// check if a ray could even reach the light area
|
|
474
|
+
vec3 attenuatedColor;
|
|
475
|
+
if (
|
|
476
|
+
lightSampleRec.pdf > 0.0 &&
|
|
477
|
+
isDirectionValid( lightSampleRec.direction, surf.normal, faceNormal ) &&
|
|
478
|
+
! attenuateHit( bvh, rayOrigin, lightSampleRec.direction, lightSampleRec.dist, bounces - i, transmissiveTraversals, isShadowRay, fogMaterial, attenuatedColor )
|
|
479
|
+
) {
|
|
480
|
+
|
|
481
|
+
// get the material pdf
|
|
482
|
+
vec3 sampleColor;
|
|
483
|
+
float lightMaterialPdf = bsdfResult( outgoing, clearcoatOutgoing, normalize( invBasis * lightSampleRec.direction ), normalize( clearcoatInvBasis * lightSampleRec.direction ), surf, sampleColor );
|
|
484
|
+
bool isValidSampleColor = all( greaterThanEqual( sampleColor, vec3( 0.0 ) ) );
|
|
485
|
+
if ( lightMaterialPdf > 0.0 && isValidSampleColor ) {
|
|
486
|
+
|
|
487
|
+
// weight the direct light contribution
|
|
488
|
+
float lightPdf = lightSampleRec.pdf / lightsDenom;
|
|
489
|
+
float misWeight = lightSampleRec.type == SPOT_LIGHT_TYPE || lightSampleRec.type == DIR_LIGHT_TYPE || lightSampleRec.type == POINT_LIGHT_TYPE ? 1.0 : misHeuristic( lightPdf, lightMaterialPdf );
|
|
490
|
+
gl_FragColor.rgb += attenuatedColor * lightSampleRec.emission * throughputColor * sampleColor * misWeight / lightPdf;
|
|
491
|
+
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
}
|
|
495
|
+
|
|
496
|
+
} else {
|
|
497
|
+
|
|
498
|
+
// find a sample in the environment map to include in the contribution
|
|
499
|
+
vec3 envColor, envDirection;
|
|
500
|
+
float envPdf = sampleEquirectProbability( envMapInfo, sobol2( 7 ), envColor, envDirection );
|
|
501
|
+
envDirection = invEnvRotation3x3 * envDirection;
|
|
502
|
+
|
|
503
|
+
// this env sampling is not set up for transmissive sampling and yields overly bright
|
|
504
|
+
// results so we ignore the sample in this case.
|
|
505
|
+
// TODO: this should be improved but how? The env samples could traverse a few layers?
|
|
506
|
+
bool isSampleBelowSurface = ! surf.volumeParticle && dot( faceNormal, envDirection ) < 0.0;
|
|
507
|
+
if ( isSampleBelowSurface ) {
|
|
508
|
+
|
|
509
|
+
envPdf = 0.0;
|
|
510
|
+
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
// check if a ray could even reach the surface
|
|
514
|
+
vec3 attenuatedColor;
|
|
515
|
+
if (
|
|
516
|
+
envPdf > 0.0 &&
|
|
517
|
+
isDirectionValid( envDirection, surf.normal, faceNormal ) &&
|
|
518
|
+
! attenuateHit( bvh, rayOrigin, envDirection, INFINITY, bounces - i, transmissiveTraversals, isShadowRay, fogMaterial, attenuatedColor )
|
|
519
|
+
) {
|
|
520
|
+
|
|
521
|
+
// get the material pdf
|
|
522
|
+
vec3 sampleColor;
|
|
523
|
+
float envMaterialPdf = bsdfResult( outgoing, clearcoatOutgoing, normalize( invBasis * envDirection ), normalize( clearcoatInvBasis * envDirection ), surf, sampleColor );
|
|
524
|
+
bool isValidSampleColor = all( greaterThanEqual( sampleColor, vec3( 0.0 ) ) );
|
|
525
|
+
if ( envMaterialPdf > 0.0 && isValidSampleColor ) {
|
|
526
|
+
|
|
527
|
+
// weight the direct light contribution
|
|
528
|
+
envPdf /= lightsDenom;
|
|
529
|
+
float misWeight = misHeuristic( envPdf, envMaterialPdf );
|
|
530
|
+
gl_FragColor.rgb += attenuatedColor * environmentIntensity * envColor * throughputColor * sampleColor * misWeight / envPdf;
|
|
531
|
+
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
}
|
|
537
|
+
#endif
|
|
538
|
+
|
|
539
|
+
// accumulate a roughness value to offset diffuse, specular, diffuse rays that have high contribution
|
|
540
|
+
// to a single pixel resulting in fireflies
|
|
541
|
+
if ( ! surf.volumeParticle && ! isBelowSurface ) {
|
|
542
|
+
|
|
543
|
+
// determine if this is a rough normal or not by checking how far off straight up it is
|
|
544
|
+
vec3 halfVector = normalize( outgoing + sampleRec.direction );
|
|
545
|
+
vec3 clearcoatHalfVector = normalize( clearcoatOutgoing + sampleRec.clearcoatDirection );
|
|
546
|
+
accumulatedRoughness += max( sin( acosApprox( halfVector.z ) ), sin( acosApprox( clearcoatHalfVector.z ) ) );
|
|
547
|
+
|
|
548
|
+
transmissiveRay = false;
|
|
549
|
+
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
// if we're bouncing around the inside a transmissive material then decrement
|
|
553
|
+
// perform this separate from a bounce
|
|
554
|
+
bool isTransmissiveRay = ! surf.volumeParticle && dot( rayDirection, faceNormal * side ) < 0.0;
|
|
555
|
+
if ( ( isTransmissiveRay || isBelowSurface ) && transmissiveTraversals > 0 ) {
|
|
556
|
+
|
|
557
|
+
transmissiveTraversals --;
|
|
558
|
+
i --;
|
|
559
|
+
|
|
560
|
+
}
|
|
561
|
+
|
|
562
|
+
#if FEATURE_FOG
|
|
563
|
+
if ( material.fogVolume ) {
|
|
564
|
+
|
|
565
|
+
transmissiveTraversals --;
|
|
566
|
+
i --;
|
|
567
|
+
|
|
568
|
+
}
|
|
569
|
+
#endif
|
|
570
|
+
|
|
571
|
+
// accumulate color
|
|
572
|
+
gl_FragColor.rgb += ( surf.emission * throughputColor );
|
|
573
|
+
|
|
574
|
+
// skip the sample if our PDF or ray is impossible
|
|
575
|
+
if ( sampleRec.pdf <= 0.0 || ! isDirectionValid( rayDirection, surf.normal, faceNormal ) ) {
|
|
576
|
+
|
|
577
|
+
break;
|
|
578
|
+
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
#if FEATURE_RUSSIAN_ROULETTE
|
|
582
|
+
|
|
583
|
+
// russian roulette path termination
|
|
584
|
+
// https://www.arnoldrenderer.com/research/physically_based_shader_design_in_arnold.pdf
|
|
585
|
+
uint minBounces = 3u;
|
|
586
|
+
float depthProb = float( sobolBounceIndex < minBounces );
|
|
587
|
+
|
|
588
|
+
float rrProb = luminance( throughputColor * sampleRec.color / sampleRec.pdf );
|
|
589
|
+
rrProb /= luminance( throughputColor );
|
|
590
|
+
rrProb = sqrt( rrProb );
|
|
591
|
+
rrProb = max( rrProb, depthProb );
|
|
592
|
+
rrProb = min( rrProb, 1.0 );
|
|
593
|
+
if ( sobol( 8 ) > rrProb ) {
|
|
594
|
+
|
|
595
|
+
break;
|
|
596
|
+
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
// perform sample clamping here to avoid bright pixels
|
|
600
|
+
throughputColor *= min( 1.0 / rrProb, 20.0 );
|
|
601
|
+
|
|
602
|
+
#endif
|
|
603
|
+
|
|
604
|
+
throughputColor *= sampleRec.color / sampleRec.pdf;
|
|
605
|
+
|
|
606
|
+
// attenuate the throughput color by the medium color
|
|
607
|
+
if ( side == - 1.0 ) {
|
|
608
|
+
|
|
609
|
+
throughputColor *= transmissionAttenuation( dist, surf.attenuationColor, surf.attenuationDistance );
|
|
610
|
+
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
// discard the sample if there are any NaNs
|
|
614
|
+
if ( any( isnan( throughputColor ) ) || any( isinf( throughputColor ) ) ) {
|
|
615
|
+
|
|
616
|
+
break;
|
|
617
|
+
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
gl_FragColor.a *= opacity;
|
|
624
|
+
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
`
|
|
628
|
+
|
|
629
|
+
} );
|
|
630
|
+
|
|
631
|
+
this.setValues( parameters );
|
|
632
|
+
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
}
|