three-gpu-pathtracer 0.0.2 → 0.0.3
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 +107 -44
- package/build/index.module.js +2077 -1141
- package/build/index.module.js.map +1 -1
- package/build/index.umd.cjs +1922 -984
- package/build/index.umd.cjs.map +1 -1
- package/package.json +60 -61
- package/src/core/DynamicPathTracingSceneGenerator.js +106 -105
- package/src/core/PathTracingRenderer.js +233 -140
- package/src/index.js +4 -2
- package/src/materials/AlphaDisplayMaterial.js +48 -0
- package/src/materials/BlendMaterial.js +67 -0
- package/src/materials/PhysicalPathTracingMaterial.js +287 -75
- package/src/shader/shaderEnvMapSampling.js +67 -0
- package/src/shader/shaderMaterialSampling.js +7 -0
- package/src/shader/shaderStructs.js +92 -42
- package/src/shader/shaderUtils.js +37 -0
- package/src/uniforms/EquirectHdrInfoUniform.js +263 -0
- package/src/uniforms/MaterialsTexture.js +173 -0
- package/src/uniforms/RenderTarget2DArray.js +80 -80
- package/src/utils/BlurredEnvMapGenerator.js +113 -0
- package/src/utils/GeometryPreparationUtils.js +194 -200
- package/src/uniforms/EquirectPdfUniform.js +0 -132
- package/src/uniforms/MaterialStructArrayUniform.js +0 -18
- package/src/uniforms/MaterialStructUniform.js +0 -114
|
@@ -1,22 +1,23 @@
|
|
|
1
|
-
import { Matrix4, Matrix3, Color } from 'three';
|
|
1
|
+
import { Matrix4, Matrix3, Color, Vector2 } from 'three';
|
|
2
2
|
import { MaterialBase } from './MaterialBase.js';
|
|
3
3
|
import {
|
|
4
4
|
MeshBVHUniformStruct, FloatVertexAttributeTexture, UIntVertexAttributeTexture,
|
|
5
5
|
shaderStructs, shaderIntersectFunction,
|
|
6
6
|
} from 'three-mesh-bvh';
|
|
7
7
|
import { shaderMaterialStructs } from '../shader/shaderStructs.js';
|
|
8
|
-
import {
|
|
8
|
+
import { MaterialsTexture } from '../uniforms/MaterialsTexture.js';
|
|
9
9
|
import { RenderTarget2DArray } from '../uniforms/RenderTarget2DArray.js';
|
|
10
10
|
import { shaderMaterialSampling } from '../shader/shaderMaterialSampling.js';
|
|
11
|
+
import { shaderEnvMapSampling } from '../shader/shaderEnvMapSampling.js';
|
|
11
12
|
import { shaderUtils } from '../shader/shaderUtils.js';
|
|
12
13
|
import { PhysicalCameraUniform } from '../uniforms/PhysicalCameraUniform.js';
|
|
14
|
+
import { EquirectHdrInfoUniform } from '../uniforms/EquirectHdrInfoUniform.js';
|
|
13
15
|
|
|
14
16
|
export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
15
17
|
|
|
16
|
-
|
|
17
|
-
get envMap() {
|
|
18
|
+
onBeforeRender() {
|
|
18
19
|
|
|
19
|
-
|
|
20
|
+
this.setDefine( 'FEATURE_DOF', this.physicalCamera.bokehSize === 0 ? 0 : 1 );
|
|
20
21
|
|
|
21
22
|
}
|
|
22
23
|
|
|
@@ -28,13 +29,15 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
28
29
|
depthWrite: false,
|
|
29
30
|
|
|
30
31
|
defines: {
|
|
31
|
-
|
|
32
|
+
FEATURE_MIS: 1,
|
|
33
|
+
FEATURE_DOF: 1,
|
|
34
|
+
FEATURE_GRADIENT_BG: 0,
|
|
32
35
|
TRANSPARENT_TRAVERSALS: 5,
|
|
33
|
-
MATERIAL_LENGTH: 0,
|
|
34
|
-
GRADIENT_BG: 0,
|
|
35
36
|
},
|
|
36
37
|
|
|
37
38
|
uniforms: {
|
|
39
|
+
resolution: { value: new Vector2() },
|
|
40
|
+
|
|
38
41
|
bounces: { value: 3 },
|
|
39
42
|
physicalCamera: { value: new PhysicalCameraUniform() },
|
|
40
43
|
|
|
@@ -43,23 +46,22 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
43
46
|
tangentAttribute: { value: new FloatVertexAttributeTexture() },
|
|
44
47
|
uvAttribute: { value: new FloatVertexAttributeTexture() },
|
|
45
48
|
materialIndexAttribute: { value: new UIntVertexAttributeTexture() },
|
|
46
|
-
materials: { value: new
|
|
49
|
+
materials: { value: new MaterialsTexture() },
|
|
47
50
|
textures: { value: new RenderTarget2DArray().texture },
|
|
48
51
|
cameraWorldMatrix: { value: new Matrix4() },
|
|
49
52
|
invProjectionMatrix: { value: new Matrix4() },
|
|
50
|
-
|
|
53
|
+
backgroundBlur: { value: 0.0 },
|
|
51
54
|
environmentIntensity: { value: 2.0 },
|
|
52
|
-
environmentMap: { value: null },
|
|
53
55
|
environmentRotation: { value: new Matrix3() },
|
|
56
|
+
envMapInfo: { value: new EquirectHdrInfoUniform() },
|
|
57
|
+
|
|
54
58
|
seed: { value: 0 },
|
|
55
59
|
opacity: { value: 1 },
|
|
56
60
|
filterGlossyFactor: { value: 0.0 },
|
|
57
61
|
|
|
58
|
-
gradientTop: { value: new Color( 0xbfd8ff ) },
|
|
59
|
-
gradientBottom: { value: new Color( 0xffffff ) },
|
|
60
|
-
|
|
61
62
|
bgGradientTop: { value: new Color( 0x111111 ) },
|
|
62
63
|
bgGradientBottom: { value: new Color( 0x000000 ) },
|
|
64
|
+
backgroundAlpha: { value: 1.0 },
|
|
63
65
|
},
|
|
64
66
|
|
|
65
67
|
vertexShader: /* glsl */`
|
|
@@ -78,14 +80,13 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
78
80
|
`,
|
|
79
81
|
|
|
80
82
|
fragmentShader: /* glsl */`
|
|
81
|
-
#define RAY_OFFSET 1e-
|
|
83
|
+
#define RAY_OFFSET 1e-4
|
|
82
84
|
|
|
83
85
|
precision highp isampler2D;
|
|
84
86
|
precision highp usampler2D;
|
|
85
87
|
precision highp sampler2DArray;
|
|
86
88
|
vec4 envMapTexelToLinear( vec4 a ) { return a; }
|
|
87
89
|
#include <common>
|
|
88
|
-
#include <cube_uv_reflection_fragment>
|
|
89
90
|
|
|
90
91
|
${ shaderStructs }
|
|
91
92
|
${ shaderIntersectFunction }
|
|
@@ -93,35 +94,27 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
93
94
|
|
|
94
95
|
${ shaderUtils }
|
|
95
96
|
${ shaderMaterialSampling }
|
|
97
|
+
${ shaderEnvMapSampling }
|
|
96
98
|
|
|
97
|
-
#ifdef USE_ENVMAP
|
|
98
|
-
|
|
99
|
-
uniform float environmentBlur;
|
|
100
|
-
uniform sampler2D environmentMap;
|
|
101
99
|
uniform mat3 environmentRotation;
|
|
100
|
+
uniform float backgroundBlur;
|
|
101
|
+
uniform float backgroundAlpha;
|
|
102
102
|
|
|
103
|
-
#
|
|
104
|
-
|
|
105
|
-
uniform vec3 gradientTop;
|
|
106
|
-
uniform vec3 gradientBottom;
|
|
107
|
-
|
|
108
|
-
#endif
|
|
109
|
-
|
|
110
|
-
#if GRADIENT_BG
|
|
103
|
+
#if FEATURE_GRADIENT_BG
|
|
111
104
|
|
|
112
105
|
uniform vec3 bgGradientTop;
|
|
113
106
|
uniform vec3 bgGradientBottom;
|
|
114
107
|
|
|
115
108
|
#endif
|
|
116
109
|
|
|
117
|
-
#if
|
|
110
|
+
#if FEATURE_DOF
|
|
118
111
|
|
|
119
112
|
uniform PhysicalCamera physicalCamera;
|
|
120
113
|
|
|
121
114
|
#endif
|
|
122
115
|
|
|
116
|
+
uniform vec2 resolution;
|
|
123
117
|
uniform int bounces;
|
|
124
|
-
|
|
125
118
|
uniform mat4 cameraWorldMatrix;
|
|
126
119
|
uniform mat4 invProjectionMatrix;
|
|
127
120
|
uniform sampler2D normalAttribute;
|
|
@@ -133,10 +126,149 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
133
126
|
uniform float filterGlossyFactor;
|
|
134
127
|
uniform int seed;
|
|
135
128
|
uniform float opacity;
|
|
136
|
-
uniform
|
|
129
|
+
uniform sampler2D materials;
|
|
130
|
+
|
|
131
|
+
uniform EquirectHdrInfo envMapInfo;
|
|
132
|
+
|
|
137
133
|
uniform sampler2DArray textures;
|
|
138
134
|
varying vec2 vUv;
|
|
139
135
|
|
|
136
|
+
vec3 sampleBackground( vec3 direction ) {
|
|
137
|
+
|
|
138
|
+
#if FEATURE_GRADIENT_BG
|
|
139
|
+
|
|
140
|
+
direction = normalize( direction + randDirection() * 0.05 );
|
|
141
|
+
|
|
142
|
+
float value = ( direction.y + 1.0 ) / 2.0;
|
|
143
|
+
value = pow( value, 2.0 );
|
|
144
|
+
|
|
145
|
+
return mix( bgGradientBottom, bgGradientTop, value );
|
|
146
|
+
|
|
147
|
+
#else
|
|
148
|
+
|
|
149
|
+
vec3 sampleDir = normalize( direction + getHemisphereSample( direction, rand2() ) * 0.5 * backgroundBlur );
|
|
150
|
+
return environmentIntensity * sampleEquirectEnvMapColor( sampleDir, envMapInfo.map );
|
|
151
|
+
|
|
152
|
+
#endif
|
|
153
|
+
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// step through multiple surface hits and accumulate color attenuation based on transmissive surfaces
|
|
157
|
+
bool attenuateHit( BVH bvh, vec3 rayOrigin, vec3 rayDirection, int traversals, out vec3 color ) {
|
|
158
|
+
|
|
159
|
+
// hit results
|
|
160
|
+
uvec4 faceIndices = uvec4( 0u );
|
|
161
|
+
vec3 faceNormal = vec3( 0.0, 0.0, 1.0 );
|
|
162
|
+
vec3 barycoord = vec3( 0.0 );
|
|
163
|
+
float side = 1.0;
|
|
164
|
+
float dist = 0.0;
|
|
165
|
+
|
|
166
|
+
color = vec3( 1.0 );
|
|
167
|
+
|
|
168
|
+
for ( int i = 0; i < traversals; i ++ ) {
|
|
169
|
+
|
|
170
|
+
if ( bvhIntersectFirstHit( bvh, rayOrigin, rayDirection, faceIndices, faceNormal, barycoord, side, dist ) ) {
|
|
171
|
+
|
|
172
|
+
// TODO: attenuate the contribution based on the PDF of the resulting ray including refraction values
|
|
173
|
+
// Should be able to work using the material BSDF functions which will take into account specularity, etc.
|
|
174
|
+
// TODO: should we account for emissive surfaces here?
|
|
175
|
+
|
|
176
|
+
vec2 uv = textureSampleBarycoord( uvAttribute, barycoord, faceIndices.xyz ).xy;
|
|
177
|
+
uint materialIndex = uTexelFetch1D( materialIndexAttribute, faceIndices.x ).r;
|
|
178
|
+
Material material = readMaterialInfo( materials, materialIndex );
|
|
179
|
+
|
|
180
|
+
// Opacity Test
|
|
181
|
+
|
|
182
|
+
// albedo
|
|
183
|
+
vec4 albedo = vec4( material.color, material.opacity );
|
|
184
|
+
if ( material.map != - 1 ) {
|
|
185
|
+
|
|
186
|
+
albedo *= texture2D( textures, vec3( uv, material.map ) );
|
|
187
|
+
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// transmission
|
|
191
|
+
float transmission = material.transmission;
|
|
192
|
+
if ( material.transmissionMap != - 1 ) {
|
|
193
|
+
|
|
194
|
+
transmission *= texture2D( textures, vec3( uv, material.transmissionMap ) ).r;
|
|
195
|
+
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
// metalness
|
|
199
|
+
float metalness = material.metalness;
|
|
200
|
+
if ( material.metalnessMap != - 1 ) {
|
|
201
|
+
|
|
202
|
+
metalness *= texture2D( textures, vec3( uv, material.metalnessMap ) ).b;
|
|
203
|
+
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
float alphaTest = material.alphaTest;
|
|
207
|
+
bool useAlphaTest = alphaTest != 0.0;
|
|
208
|
+
float transmissionFactor = ( 1.0 - metalness ) * transmission;
|
|
209
|
+
if (
|
|
210
|
+
transmissionFactor < rand() && ! (
|
|
211
|
+
// material sidedness
|
|
212
|
+
material.side != 0.0 && side == material.side
|
|
213
|
+
|
|
214
|
+
// alpha test
|
|
215
|
+
|| useAlphaTest && albedo.a < alphaTest
|
|
216
|
+
|
|
217
|
+
// opacity
|
|
218
|
+
|| ! useAlphaTest && albedo.a < rand()
|
|
219
|
+
)
|
|
220
|
+
) {
|
|
221
|
+
|
|
222
|
+
return true;
|
|
223
|
+
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// only attenuate on the way in
|
|
227
|
+
bool isBelowSurface = dot( rayDirection, faceNormal ) < 0.0;
|
|
228
|
+
if ( isBelowSurface ) {
|
|
229
|
+
|
|
230
|
+
color *= albedo.rgb;
|
|
231
|
+
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
// adjust the ray to the new surface
|
|
235
|
+
vec3 point = rayOrigin + rayDirection * dist;
|
|
236
|
+
vec3 absPoint = abs( point );
|
|
237
|
+
float maxPoint = max( absPoint.x, max( absPoint.y, absPoint.z ) );
|
|
238
|
+
rayOrigin = point + faceNormal * ( maxPoint + 1.0 ) * ( isBelowSurface ? - RAY_OFFSET : RAY_OFFSET );
|
|
239
|
+
|
|
240
|
+
} else {
|
|
241
|
+
|
|
242
|
+
return false;
|
|
243
|
+
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
return true;
|
|
249
|
+
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// returns whether the ray hit anything, not just the first surface. Could be optimized to not check the full hierarchy.
|
|
253
|
+
bool anyHit( BVH bvh, vec3 rayOrigin, vec3 rayDirection ) {
|
|
254
|
+
|
|
255
|
+
uvec4 faceIndices = uvec4( 0u );
|
|
256
|
+
vec3 faceNormal = vec3( 0.0, 0.0, 1.0 );
|
|
257
|
+
vec3 barycoord = vec3( 0.0 );
|
|
258
|
+
float side = 1.0;
|
|
259
|
+
float dist = 0.0;
|
|
260
|
+
return bvhIntersectFirstHit( bvh, rayOrigin, rayDirection, faceIndices, faceNormal, barycoord, side, dist );
|
|
261
|
+
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
// tentFilter from Peter Shirley's 'Realistic Ray Tracing (2nd Edition)' book, pg. 60
|
|
265
|
+
// erichlof/THREE.js-PathTracing-Renderer/
|
|
266
|
+
float tentFilter( float x ) {
|
|
267
|
+
|
|
268
|
+
return x < 0.5 ? sqrt( 2.0 * x ) - 1.0 : 1.0 - sqrt( 2.0 - ( 2.0 * x ) );
|
|
269
|
+
|
|
270
|
+
}
|
|
271
|
+
|
|
140
272
|
void main() {
|
|
141
273
|
|
|
142
274
|
rng_initialize( gl_FragCoord.xy, seed );
|
|
@@ -146,34 +278,58 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
146
278
|
vec3 rayOrigin, rayDirection;
|
|
147
279
|
ndcToCameraRay( ndc, cameraWorldMatrix, invProjectionMatrix, rayOrigin, rayDirection );
|
|
148
280
|
|
|
149
|
-
|
|
281
|
+
// Jitter the camera ray by finding a new subpixel point to point to from the camera origin
|
|
282
|
+
// This is better than just jittering the camera position since it actually results in divergent
|
|
283
|
+
// rays providing better coverage for the pixel
|
|
284
|
+
{
|
|
150
285
|
|
|
151
|
-
|
|
152
|
-
|
|
286
|
+
// TODO: the complexity here could be improved
|
|
287
|
+
vec3 cameraOrigin = ( cameraWorldMatrix * vec4( 0.0, 0.0, 0.0, 1.0 ) ).xyz;
|
|
288
|
+
vec3 ss00, ss01, ss10, temp;
|
|
289
|
+
ndcToCameraRay( vec2( - 1.0, - 1.0 ), cameraWorldMatrix, invProjectionMatrix, ss00, temp );
|
|
290
|
+
ndcToCameraRay( vec2( - 1.0, 1.0 ), cameraWorldMatrix, invProjectionMatrix, ss01, temp );
|
|
291
|
+
ndcToCameraRay( vec2( 1.0, - 1.0 ), cameraWorldMatrix, invProjectionMatrix, ss10, temp );
|
|
153
292
|
|
|
154
|
-
|
|
155
|
-
|
|
293
|
+
vec3 ssdX = ( ss10 - ss00 ) / resolution.x;
|
|
294
|
+
vec3 ssdY = ( ss01 - ss00 ) / resolution.y;
|
|
295
|
+
rayOrigin += tentFilter( rand() ) * ssdX + tentFilter( rand() ) * ssdY;
|
|
296
|
+
rayDirection = normalize( rayOrigin - cameraOrigin );
|
|
297
|
+
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
#if FEATURE_DOF
|
|
301
|
+
{
|
|
302
|
+
|
|
303
|
+
// depth of field
|
|
304
|
+
vec3 focalPoint = rayOrigin + normalize( rayDirection ) * physicalCamera.focusDistance;
|
|
305
|
+
|
|
306
|
+
// get the aperture sample
|
|
307
|
+
vec2 apertureSample = sampleAperture( physicalCamera.apertureBlades ) * physicalCamera.bokehSize * 0.5 * 1e-3;
|
|
308
|
+
|
|
309
|
+
// rotate the aperture shape
|
|
310
|
+
float ac = cos( physicalCamera.apertureRotation );
|
|
311
|
+
float as = sin( physicalCamera.apertureRotation );
|
|
312
|
+
apertureSample = vec2(
|
|
313
|
+
apertureSample.x * ac - apertureSample.y * as,
|
|
314
|
+
apertureSample.x * as + apertureSample.y * ac
|
|
315
|
+
);
|
|
316
|
+
apertureSample.x *= saturate( physicalCamera.anamorphicRatio );
|
|
317
|
+
apertureSample.y *= saturate( 1.0 / physicalCamera.anamorphicRatio );
|
|
156
318
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
apertureSample = vec2(
|
|
161
|
-
apertureSample.x * ac - apertureSample.y * as,
|
|
162
|
-
apertureSample.x * as + apertureSample.y * ac
|
|
163
|
-
);
|
|
164
|
-
apertureSample.x *= saturate( physicalCamera.anamorphicRatio );
|
|
165
|
-
apertureSample.y *= saturate( 1.0 / physicalCamera.anamorphicRatio );
|
|
319
|
+
// create the new ray
|
|
320
|
+
rayOrigin += ( cameraWorldMatrix * vec4( apertureSample, 0.0, 0.0 ) ).xyz;
|
|
321
|
+
rayDirection = focalPoint - rayOrigin;
|
|
166
322
|
|
|
323
|
+
}
|
|
167
324
|
#endif
|
|
325
|
+
rayDirection = normalize( rayDirection );
|
|
168
326
|
|
|
169
|
-
//
|
|
170
|
-
|
|
171
|
-
rayDirection = focalPoint - rayOrigin;
|
|
327
|
+
// inverse environment rotation
|
|
328
|
+
mat3 invEnvironmentRotation = inverse( environmentRotation );
|
|
172
329
|
|
|
173
|
-
//
|
|
330
|
+
// final color
|
|
174
331
|
gl_FragColor = vec4( 0.0 );
|
|
175
|
-
|
|
176
|
-
vec3 throughputColor = vec3( 1.0 );
|
|
332
|
+
gl_FragColor.a = 1.0;
|
|
177
333
|
|
|
178
334
|
// hit results
|
|
179
335
|
uvec4 faceIndices = uvec4( 0u );
|
|
@@ -181,50 +337,60 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
181
337
|
vec3 barycoord = vec3( 0.0 );
|
|
182
338
|
float side = 1.0;
|
|
183
339
|
float dist = 0.0;
|
|
340
|
+
|
|
341
|
+
// path tracing state
|
|
184
342
|
float accumulatedRoughness = 0.0;
|
|
185
|
-
|
|
343
|
+
bool transmissiveRay = true;
|
|
186
344
|
int transparentTraversals = TRANSPARENT_TRAVERSALS;
|
|
345
|
+
vec3 throughputColor = vec3( 1.0 );
|
|
346
|
+
SampleRec sampleRec;
|
|
347
|
+
int i;
|
|
348
|
+
|
|
187
349
|
for ( i = 0; i < bounces; i ++ ) {
|
|
188
350
|
|
|
189
351
|
if ( ! bvhIntersectFirstHit( bvh, rayOrigin, rayDirection, faceIndices, faceNormal, barycoord, side, dist ) ) {
|
|
190
352
|
|
|
191
|
-
|
|
353
|
+
if ( i == 0 || transmissiveRay ) {
|
|
192
354
|
|
|
193
|
-
|
|
355
|
+
gl_FragColor.rgb += sampleBackground( environmentRotation * rayDirection ) * throughputColor;
|
|
356
|
+
gl_FragColor.a = backgroundAlpha;
|
|
194
357
|
|
|
195
|
-
|
|
196
|
-
float value = ( rayDirection.y + 1.0 ) / 2.0;
|
|
358
|
+
} else {
|
|
197
359
|
|
|
198
|
-
|
|
360
|
+
#if FEATURE_MIS
|
|
199
361
|
|
|
200
|
-
|
|
201
|
-
|
|
362
|
+
// get the PDF of the hit envmap point
|
|
363
|
+
vec3 envColor;
|
|
364
|
+
float envPdf = envMapSample( environmentRotation * rayDirection, envMapInfo, envColor );
|
|
202
365
|
|
|
203
|
-
|
|
366
|
+
// and weight the contribution
|
|
367
|
+
float misWeight = misHeuristic( sampleRec.pdf, envPdf );
|
|
368
|
+
gl_FragColor.rgb += environmentIntensity * envColor * throughputColor * misWeight;
|
|
204
369
|
|
|
205
|
-
|
|
370
|
+
#else
|
|
206
371
|
|
|
207
|
-
|
|
372
|
+
gl_FragColor.rgb +=
|
|
373
|
+
environmentIntensity *
|
|
374
|
+
sampleEquirectEnvMapColor( environmentRotation * rayDirection, envMapInfo.map ) *
|
|
375
|
+
throughputColor;
|
|
208
376
|
|
|
209
|
-
|
|
377
|
+
#endif
|
|
210
378
|
|
|
211
|
-
|
|
379
|
+
}
|
|
380
|
+
break;
|
|
212
381
|
|
|
213
|
-
|
|
214
|
-
float value = ( rayDirection.y + 1.0 ) / 2.0;
|
|
215
|
-
vec3 skyColor = mix( gradientBottom, gradientTop, value );
|
|
382
|
+
}
|
|
216
383
|
|
|
217
|
-
|
|
384
|
+
uint materialIndex = uTexelFetch1D( materialIndexAttribute, faceIndices.x ).r;
|
|
385
|
+
Material material = readMaterialInfo( materials, materialIndex );
|
|
218
386
|
|
|
219
|
-
|
|
387
|
+
if ( material.matte && i == 0 ) {
|
|
220
388
|
|
|
389
|
+
gl_FragColor = vec4( 0.0 );
|
|
221
390
|
break;
|
|
222
391
|
|
|
223
392
|
}
|
|
224
393
|
|
|
225
|
-
uint materialIndex = uTexelFetch1D( materialIndexAttribute, faceIndices.x ).r;
|
|
226
|
-
Material material = materials[ materialIndex ];
|
|
227
|
-
|
|
228
394
|
vec2 uv = textureSampleBarycoord( uvAttribute, barycoord, faceIndices.xyz ).xy;
|
|
229
395
|
|
|
230
396
|
// albedo
|
|
@@ -256,7 +422,9 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
256
422
|
) {
|
|
257
423
|
|
|
258
424
|
vec3 point = rayOrigin + rayDirection * dist;
|
|
259
|
-
|
|
425
|
+
vec3 absPoint = abs( point );
|
|
426
|
+
float maxPoint = max( absPoint.x, max( absPoint.y, absPoint.z ) );
|
|
427
|
+
rayOrigin = point - ( maxPoint + 1.0 ) * faceNormal * RAY_OFFSET;
|
|
260
428
|
|
|
261
429
|
// only allow a limited number of transparency discards otherwise we could
|
|
262
430
|
// crash the context with too long a loop.
|
|
@@ -361,7 +529,7 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
361
529
|
mat3 invBasis = inverse( normalBasis );
|
|
362
530
|
|
|
363
531
|
vec3 outgoing = - normalize( invBasis * rayDirection );
|
|
364
|
-
|
|
532
|
+
sampleRec = bsdfSample( outgoing, surfaceRec );
|
|
365
533
|
|
|
366
534
|
// adjust the hit point by the surface normal by a factor of some offset and the
|
|
367
535
|
// maximum component-wise value of the current point to accommodate floating point
|
|
@@ -374,6 +542,49 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
374
542
|
bool isBelowSurface = dot( rayDirection, faceNormal ) < 0.0;
|
|
375
543
|
rayOrigin = point + faceNormal * ( maxPoint + 1.0 ) * ( isBelowSurface ? - RAY_OFFSET : RAY_OFFSET );
|
|
376
544
|
|
|
545
|
+
// direct env map sampling
|
|
546
|
+
#if FEATURE_MIS
|
|
547
|
+
{
|
|
548
|
+
|
|
549
|
+
// find a sample in the environment map to include in the contribution
|
|
550
|
+
vec3 envColor, envDirection;
|
|
551
|
+
float envPdf = randomEnvMapSample( envMapInfo, envColor, envDirection );
|
|
552
|
+
envDirection = invEnvironmentRotation * envDirection;
|
|
553
|
+
|
|
554
|
+
// this env sampling is not set up for transmissive sampling and yields overly bright
|
|
555
|
+
// results so we ignore the sample in this case.
|
|
556
|
+
// TODO: this should be improved but how? The env samples could traverse a few layers?
|
|
557
|
+
bool isSampleBelowSurface = dot( faceNormal, envDirection ) < 0.0;
|
|
558
|
+
if ( isSampleBelowSurface ) {
|
|
559
|
+
|
|
560
|
+
envPdf = 0.0;
|
|
561
|
+
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
// check if a ray could even reach the surface
|
|
565
|
+
vec3 attenuatedColor;
|
|
566
|
+
if (
|
|
567
|
+
envPdf > 0.0 &&
|
|
568
|
+
isDirectionValid( envDirection, normal, faceNormal ) &&
|
|
569
|
+
! attenuateHit( bvh, rayOrigin, envDirection, bounces - i, attenuatedColor )
|
|
570
|
+
) {
|
|
571
|
+
|
|
572
|
+
// get the material pdf
|
|
573
|
+
vec3 sampleColor;
|
|
574
|
+
float envMaterialPdf = bsdfResult( outgoing, normalize( invBasis * envDirection ), surfaceRec, sampleColor );
|
|
575
|
+
if ( envMaterialPdf > 0.0 ) {
|
|
576
|
+
|
|
577
|
+
// weight the direct light contribution
|
|
578
|
+
float misWeight = misHeuristic( envPdf, envMaterialPdf );
|
|
579
|
+
gl_FragColor.rgb += attenuatedColor * environmentIntensity * envColor * throughputColor * sampleColor * misWeight / envPdf;
|
|
580
|
+
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
}
|
|
586
|
+
#endif
|
|
587
|
+
|
|
377
588
|
// accumulate a roughness value to offset diffuse, specular, diffuse rays that have high contribution
|
|
378
589
|
// to a single pixel resulting in fireflies
|
|
379
590
|
if ( ! isBelowSurface ) {
|
|
@@ -381,6 +592,7 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
381
592
|
// determine if this is a rough normal or not by checking how far off straight up it is
|
|
382
593
|
vec3 halfVector = normalize( outgoing + sampleRec.direction );
|
|
383
594
|
accumulatedRoughness += sin( acos( halfVector.z ) );
|
|
595
|
+
transmissiveRay = false;
|
|
384
596
|
|
|
385
597
|
}
|
|
386
598
|
|
|
@@ -405,7 +617,7 @@ export class PhysicalPathTracingMaterial extends MaterialBase {
|
|
|
405
617
|
|
|
406
618
|
}
|
|
407
619
|
|
|
408
|
-
gl_FragColor.a
|
|
620
|
+
gl_FragColor.a *= opacity;
|
|
409
621
|
|
|
410
622
|
}
|
|
411
623
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
export const shaderEnvMapSampling = /* glsl */`
|
|
2
|
+
|
|
3
|
+
vec3 sampleEquirectEnvMapColor( vec3 direction, sampler2D map ) {
|
|
4
|
+
|
|
5
|
+
return texture2D( map, equirectDirectionToUv( direction ) ).rgb;
|
|
6
|
+
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
float envMapDirectionPdf( vec3 direction ) {
|
|
10
|
+
|
|
11
|
+
vec2 uv = equirectDirectionToUv( direction );
|
|
12
|
+
float theta = uv.y * PI;
|
|
13
|
+
float sinTheta = sin( theta );
|
|
14
|
+
if ( sinTheta == 0.0 ) {
|
|
15
|
+
|
|
16
|
+
return 0.0;
|
|
17
|
+
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return 1.0 / ( 2.0 * PI * PI * sinTheta );
|
|
21
|
+
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
float envMapSample( vec3 direction, EquirectHdrInfo info, out vec3 color ) {
|
|
25
|
+
|
|
26
|
+
vec2 uv = equirectDirectionToUv( direction );
|
|
27
|
+
color = texture2D( info.map, uv ).rgb;
|
|
28
|
+
|
|
29
|
+
float totalSum = texture2D( info.totalSum, vec2( 0.0 ) ).r;
|
|
30
|
+
float lum = colorToLuminance( color );
|
|
31
|
+
ivec2 resolution = textureSize( info.map, 0 );
|
|
32
|
+
float pdf = lum / totalSum;
|
|
33
|
+
|
|
34
|
+
return float( resolution.x * resolution.y ) * pdf * envMapDirectionPdf( direction );
|
|
35
|
+
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
float randomEnvMapSample( EquirectHdrInfo info, out vec3 color, out vec3 direction ) {
|
|
39
|
+
|
|
40
|
+
// sample env map cdf
|
|
41
|
+
vec2 r = rand2();
|
|
42
|
+
float v = texture2D( info.marginalWeights, vec2( r.x, 0.0 ) ).x;
|
|
43
|
+
float u = texture2D( info.conditionalWeights, vec2( r.y, v ) ).x;
|
|
44
|
+
vec2 uv = vec2( u, v );
|
|
45
|
+
|
|
46
|
+
vec3 derivedDirection = equirectUvToDirection( uv );
|
|
47
|
+
direction = derivedDirection;
|
|
48
|
+
color = texture2D( info.map, uv ).rgb;
|
|
49
|
+
|
|
50
|
+
float totalSum = texture2D( info.totalSum, vec2( 0.0 ) ).r;
|
|
51
|
+
float lum = colorToLuminance( color );
|
|
52
|
+
ivec2 resolution = textureSize( info.map, 0 );
|
|
53
|
+
float pdf = lum / totalSum;
|
|
54
|
+
|
|
55
|
+
return float( resolution.x * resolution.y ) * pdf * envMapDirectionPdf( direction );
|
|
56
|
+
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
float misHeuristic( float a, float b ) {
|
|
60
|
+
|
|
61
|
+
float aa = a * a;
|
|
62
|
+
float bb = a * b;
|
|
63
|
+
return aa / ( bb + aa );
|
|
64
|
+
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
`;
|
|
@@ -278,6 +278,13 @@ vec3 bsdfColor( vec3 wo, vec3 wi, SurfaceRec surf ) {
|
|
|
278
278
|
|
|
279
279
|
}
|
|
280
280
|
|
|
281
|
+
float bsdfResult( vec3 wo, vec3 wi, SurfaceRec surf, out vec3 color ) {
|
|
282
|
+
|
|
283
|
+
color = bsdfColor( wo, wi, surf );
|
|
284
|
+
return bsdfPdf( wo, wi, surf );
|
|
285
|
+
|
|
286
|
+
}
|
|
287
|
+
|
|
281
288
|
SampleRec bsdfSample( vec3 wo, SurfaceRec surf ) {
|
|
282
289
|
|
|
283
290
|
float ior = surf.ior;
|