three-gpu-pathtracer 0.0.8 → 0.0.10
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 +1 -2
- package/build/index.module.js +643 -227
- package/build/index.module.js.map +1 -1
- package/build/index.umd.cjs +639 -224
- package/build/index.umd.cjs.map +1 -1
- package/package.json +2 -2
- package/src/core/PathTracingRenderer.js +5 -0
- package/src/materials/AmbientOcclusionMaterial.js +2 -0
- package/src/materials/PhysicalPathTracingMaterial.js +46 -37
- package/src/shader/shaderEnvMapSampling.js +4 -5
- package/src/shader/shaderGGXFunctions.js +6 -6
- package/src/shader/shaderLightSampling.js +48 -50
- package/src/shader/shaderMaterialSampling.js +9 -15
- package/src/shader/shaderRandFunctions.js +57 -0
- package/src/shader/shaderSobolSampling.js +256 -0
- package/src/shader/shaderStructs.js +6 -2
- package/src/shader/shaderUtils.js +50 -92
- package/src/uniforms/EquirectHdrInfoUniform.js +4 -4
- package/src/uniforms/LightsInfoUniformStruct.js +47 -2
- package/src/uniforms/MaterialsTexture.js +12 -6
- package/src/uniforms/utils.js +11 -2
- package/src/utils/SobolNumberMapGenerator.js +80 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
// References
|
|
2
|
+
// - https://jcgt.org/published/0009/04/01/
|
|
3
|
+
// - Code from https://www.shadertoy.com/view/WtGyDm
|
|
4
|
+
|
|
5
|
+
// functions to generate multi-dimensions variables of the same functions
|
|
6
|
+
// to support 1, 2, 3, and 4 dimensional sobol sampling.
|
|
7
|
+
function generateSobolFunctionVariants( dim = 1 ) {
|
|
8
|
+
|
|
9
|
+
let type = 'uint';
|
|
10
|
+
if ( dim > 1 ) {
|
|
11
|
+
|
|
12
|
+
type = 'uvec' + dim;
|
|
13
|
+
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
return /* glsl */`
|
|
17
|
+
${ type } sobolReverseBits( ${ type } x ) {
|
|
18
|
+
|
|
19
|
+
x = ( ( ( x & 0xaaaaaaaau ) >> 1 ) | ( ( x & 0x55555555u ) << 1 ) );
|
|
20
|
+
x = ( ( ( x & 0xccccccccu ) >> 2 ) | ( ( x & 0x33333333u ) << 2 ) );
|
|
21
|
+
x = ( ( ( x & 0xf0f0f0f0u ) >> 4 ) | ( ( x & 0x0f0f0f0fu ) << 4 ) );
|
|
22
|
+
x = ( ( ( x & 0xff00ff00u ) >> 8 ) | ( ( x & 0x00ff00ffu ) << 8 ) );
|
|
23
|
+
return ( ( x >> 16 ) | ( x << 16 ) );
|
|
24
|
+
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
${ type } sobolHashCombine( uint seed, ${ type } v ) {
|
|
28
|
+
|
|
29
|
+
return seed ^ ( v + ${ type }( ( seed << 6 ) + ( seed >> 2 ) ) );
|
|
30
|
+
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
${ type } sobolLaineKarrasPermutation( ${ type } x, ${ type } seed ) {
|
|
34
|
+
|
|
35
|
+
x += seed;
|
|
36
|
+
x ^= x * 0x6c50b47cu;
|
|
37
|
+
x ^= x * 0xb82f1e52u;
|
|
38
|
+
x ^= x * 0xc7afe638u;
|
|
39
|
+
x ^= x * 0x8d22f6e6u;
|
|
40
|
+
return x;
|
|
41
|
+
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
${ type } nestedUniformScrambleBase2( ${ type } x, ${ type } seed ) {
|
|
45
|
+
|
|
46
|
+
x = sobolLaineKarrasPermutation( x, seed );
|
|
47
|
+
x = sobolReverseBits( x );
|
|
48
|
+
return x;
|
|
49
|
+
|
|
50
|
+
}
|
|
51
|
+
`;
|
|
52
|
+
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function generateSobolSampleFunctions( dim = 1 ) {
|
|
56
|
+
|
|
57
|
+
let utype = 'uint';
|
|
58
|
+
let vtype = 'float';
|
|
59
|
+
let num = '';
|
|
60
|
+
let components = '.r';
|
|
61
|
+
let combineValues = '1u';
|
|
62
|
+
if ( dim > 1 ) {
|
|
63
|
+
|
|
64
|
+
utype = 'uvec' + dim;
|
|
65
|
+
vtype = 'vec' + dim;
|
|
66
|
+
num = dim + '';
|
|
67
|
+
if ( dim === 2 ) {
|
|
68
|
+
|
|
69
|
+
components = '.rg';
|
|
70
|
+
combineValues = 'uvec2( 1u, 2u )';
|
|
71
|
+
|
|
72
|
+
} else if ( dim === 3 ) {
|
|
73
|
+
|
|
74
|
+
components = '.rgb';
|
|
75
|
+
combineValues = 'uvec3( 1u, 2u, 3u )';
|
|
76
|
+
|
|
77
|
+
} else {
|
|
78
|
+
|
|
79
|
+
components = '';
|
|
80
|
+
combineValues = 'uvec4( 1u, 2u, 3u, 4u )';
|
|
81
|
+
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return /* glsl */`
|
|
87
|
+
|
|
88
|
+
${ vtype } sobol${ num }( int effect ) {
|
|
89
|
+
|
|
90
|
+
uint seed = sobolGetSeed( sobolBounceIndex, uint( effect ) );
|
|
91
|
+
uint index = sobolPathIndex;
|
|
92
|
+
|
|
93
|
+
uint shuffle_seed = sobolHashCombine( seed, 0u );
|
|
94
|
+
uint shuffled_index = nestedUniformScrambleBase2( sobolReverseBits( index ), shuffle_seed );
|
|
95
|
+
${ vtype } sobol_pt = sobolGetTexturePoint( shuffled_index )${ components };
|
|
96
|
+
${ utype } result = ${ utype }( sobol_pt * 16777216.0 );
|
|
97
|
+
|
|
98
|
+
${ utype } seed2 = sobolHashCombine( seed, ${ combineValues } );
|
|
99
|
+
result = nestedUniformScrambleBase2( result, seed2 );
|
|
100
|
+
|
|
101
|
+
return SOBOL_FACTOR * ${ vtype }( result >> 8 );
|
|
102
|
+
|
|
103
|
+
}
|
|
104
|
+
`;
|
|
105
|
+
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export const shaderSobolCommon = /* glsl */`
|
|
109
|
+
|
|
110
|
+
// Utils
|
|
111
|
+
const float SOBOL_FACTOR = 1.0 / 16777216.0;
|
|
112
|
+
const uint SOBOL_MAX_POINTS = 256u * 256u;
|
|
113
|
+
|
|
114
|
+
${ generateSobolFunctionVariants( 1 ) }
|
|
115
|
+
${ generateSobolFunctionVariants( 2 ) }
|
|
116
|
+
${ generateSobolFunctionVariants( 3 ) }
|
|
117
|
+
${ generateSobolFunctionVariants( 4 ) }
|
|
118
|
+
|
|
119
|
+
uint sobolHash( uint x ) {
|
|
120
|
+
|
|
121
|
+
// finalizer from murmurhash3
|
|
122
|
+
x ^= x >> 16;
|
|
123
|
+
x *= 0x85ebca6bu;
|
|
124
|
+
x ^= x >> 13;
|
|
125
|
+
x *= 0xc2b2ae35u;
|
|
126
|
+
x ^= x >> 16;
|
|
127
|
+
return x;
|
|
128
|
+
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
`;
|
|
132
|
+
|
|
133
|
+
export const shaderSobolGeneration = /* glsl */`
|
|
134
|
+
|
|
135
|
+
const uint SOBOL_DIRECTIONS_1[ 32 ] = uint[ 32 ](
|
|
136
|
+
0x80000000u, 0xc0000000u, 0xa0000000u, 0xf0000000u,
|
|
137
|
+
0x88000000u, 0xcc000000u, 0xaa000000u, 0xff000000u,
|
|
138
|
+
0x80800000u, 0xc0c00000u, 0xa0a00000u, 0xf0f00000u,
|
|
139
|
+
0x88880000u, 0xcccc0000u, 0xaaaa0000u, 0xffff0000u,
|
|
140
|
+
0x80008000u, 0xc000c000u, 0xa000a000u, 0xf000f000u,
|
|
141
|
+
0x88008800u, 0xcc00cc00u, 0xaa00aa00u, 0xff00ff00u,
|
|
142
|
+
0x80808080u, 0xc0c0c0c0u, 0xa0a0a0a0u, 0xf0f0f0f0u,
|
|
143
|
+
0x88888888u, 0xccccccccu, 0xaaaaaaaau, 0xffffffffu
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
const uint SOBOL_DIRECTIONS_2[ 32 ] = uint[ 32 ](
|
|
147
|
+
0x80000000u, 0xc0000000u, 0x60000000u, 0x90000000u,
|
|
148
|
+
0xe8000000u, 0x5c000000u, 0x8e000000u, 0xc5000000u,
|
|
149
|
+
0x68800000u, 0x9cc00000u, 0xee600000u, 0x55900000u,
|
|
150
|
+
0x80680000u, 0xc09c0000u, 0x60ee0000u, 0x90550000u,
|
|
151
|
+
0xe8808000u, 0x5cc0c000u, 0x8e606000u, 0xc5909000u,
|
|
152
|
+
0x6868e800u, 0x9c9c5c00u, 0xeeee8e00u, 0x5555c500u,
|
|
153
|
+
0x8000e880u, 0xc0005cc0u, 0x60008e60u, 0x9000c590u,
|
|
154
|
+
0xe8006868u, 0x5c009c9cu, 0x8e00eeeeu, 0xc5005555u
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
const uint SOBOL_DIRECTIONS_3[ 32 ] = uint[ 32 ](
|
|
158
|
+
0x80000000u, 0xc0000000u, 0x20000000u, 0x50000000u,
|
|
159
|
+
0xf8000000u, 0x74000000u, 0xa2000000u, 0x93000000u,
|
|
160
|
+
0xd8800000u, 0x25400000u, 0x59e00000u, 0xe6d00000u,
|
|
161
|
+
0x78080000u, 0xb40c0000u, 0x82020000u, 0xc3050000u,
|
|
162
|
+
0x208f8000u, 0x51474000u, 0xfbea2000u, 0x75d93000u,
|
|
163
|
+
0xa0858800u, 0x914e5400u, 0xdbe79e00u, 0x25db6d00u,
|
|
164
|
+
0x58800080u, 0xe54000c0u, 0x79e00020u, 0xb6d00050u,
|
|
165
|
+
0x800800f8u, 0xc00c0074u, 0x200200a2u, 0x50050093u
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
const uint SOBOL_DIRECTIONS_4[ 32 ] = uint[ 32 ](
|
|
169
|
+
0x80000000u, 0x40000000u, 0x20000000u, 0xb0000000u,
|
|
170
|
+
0xf8000000u, 0xdc000000u, 0x7a000000u, 0x9d000000u,
|
|
171
|
+
0x5a800000u, 0x2fc00000u, 0xa1600000u, 0xf0b00000u,
|
|
172
|
+
0xda880000u, 0x6fc40000u, 0x81620000u, 0x40bb0000u,
|
|
173
|
+
0x22878000u, 0xb3c9c000u, 0xfb65a000u, 0xddb2d000u,
|
|
174
|
+
0x78022800u, 0x9c0b3c00u, 0x5a0fb600u, 0x2d0ddb00u,
|
|
175
|
+
0xa2878080u, 0xf3c9c040u, 0xdb65a020u, 0x6db2d0b0u,
|
|
176
|
+
0x800228f8u, 0x400b3cdcu, 0x200fb67au, 0xb00ddb9du
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
uint getMaskedSobol( uint index, uint directions[ 32 ] ) {
|
|
180
|
+
|
|
181
|
+
uint X = 0u;
|
|
182
|
+
for ( int bit = 0; bit < 32; bit ++ ) {
|
|
183
|
+
|
|
184
|
+
uint mask = ( index >> bit ) & 1u;
|
|
185
|
+
X ^= mask * directions[ bit ];
|
|
186
|
+
|
|
187
|
+
}
|
|
188
|
+
return X;
|
|
189
|
+
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
vec4 generateSobolPoint( uint index ) {
|
|
193
|
+
|
|
194
|
+
if ( index >= SOBOL_MAX_POINTS ) {
|
|
195
|
+
|
|
196
|
+
return vec4( 0.0 );
|
|
197
|
+
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
// NOTEL this sobol "direction" is also available but we can't write out 5 components
|
|
201
|
+
// uint x = index & 0x00ffffffu;
|
|
202
|
+
uint x = sobolReverseBits( getMaskedSobol( index, SOBOL_DIRECTIONS_1 ) ) & 0x00ffffffu;
|
|
203
|
+
uint y = sobolReverseBits( getMaskedSobol( index, SOBOL_DIRECTIONS_2 ) ) & 0x00ffffffu;
|
|
204
|
+
uint z = sobolReverseBits( getMaskedSobol( index, SOBOL_DIRECTIONS_3 ) ) & 0x00ffffffu;
|
|
205
|
+
uint w = sobolReverseBits( getMaskedSobol( index, SOBOL_DIRECTIONS_4 ) ) & 0x00ffffffu;
|
|
206
|
+
|
|
207
|
+
return vec4( x, y, z, w ) * SOBOL_FACTOR;
|
|
208
|
+
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
`;
|
|
212
|
+
|
|
213
|
+
export const shaderSobolSampling = /* glsl */`
|
|
214
|
+
|
|
215
|
+
// Seeds
|
|
216
|
+
uniform sampler2D sobolTexture;
|
|
217
|
+
uint sobolPixelIndex;
|
|
218
|
+
uint sobolPathIndex;
|
|
219
|
+
uint sobolBounceIndex;
|
|
220
|
+
|
|
221
|
+
uint sobolGetSeed( uint bounce, uint effect ) {
|
|
222
|
+
|
|
223
|
+
return sobolHash(
|
|
224
|
+
sobolHashCombine(
|
|
225
|
+
sobolHashCombine(
|
|
226
|
+
sobolHash( bounce ),
|
|
227
|
+
sobolPixelIndex
|
|
228
|
+
),
|
|
229
|
+
effect
|
|
230
|
+
)
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
vec4 sobolGetTexturePoint( uint index ) {
|
|
236
|
+
|
|
237
|
+
if ( index >= SOBOL_MAX_POINTS ) {
|
|
238
|
+
|
|
239
|
+
index = index % SOBOL_MAX_POINTS;
|
|
240
|
+
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
uvec2 dim = uvec2( textureSize( sobolTexture, 0 ).xy );
|
|
244
|
+
uint y = index / dim.x;
|
|
245
|
+
uint x = index - y * dim.x;
|
|
246
|
+
vec2 uv = vec2( x, y ) / vec2( dim );
|
|
247
|
+
return texture( sobolTexture, uv );
|
|
248
|
+
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
${ generateSobolSampleFunctions( 1 ) }
|
|
252
|
+
${ generateSobolSampleFunctions( 2 ) }
|
|
253
|
+
${ generateSobolSampleFunctions( 3 ) }
|
|
254
|
+
${ generateSobolSampleFunctions( 4 ) }
|
|
255
|
+
|
|
256
|
+
`;
|
|
@@ -82,6 +82,7 @@ export const shaderMaterialStructs = /* glsl */ `
|
|
|
82
82
|
int sheenRoughnessMap;
|
|
83
83
|
|
|
84
84
|
bool vertexColors;
|
|
85
|
+
bool flatShading;
|
|
85
86
|
bool transparent;
|
|
86
87
|
|
|
87
88
|
mat3 mapTransform;
|
|
@@ -194,7 +195,8 @@ export const shaderMaterialStructs = /* glsl */ `
|
|
|
194
195
|
|
|
195
196
|
m.matte = bool( s14.r );
|
|
196
197
|
m.castShadow = ! bool( s14.g );
|
|
197
|
-
m.vertexColors = bool( s14.b );
|
|
198
|
+
m.vertexColors = bool( int( s14.b ) & 1 );
|
|
199
|
+
m.flatShading = bool( int( s14.b ) & 2 );
|
|
198
200
|
m.transparent = bool( s14.a );
|
|
199
201
|
|
|
200
202
|
uint firstTextureTransformIdx = i + 15u;
|
|
@@ -226,6 +228,8 @@ export const shaderLightStruct = /* glsl */ `
|
|
|
226
228
|
#define RECT_AREA_LIGHT_TYPE 0
|
|
227
229
|
#define CIRC_AREA_LIGHT_TYPE 1
|
|
228
230
|
#define SPOT_LIGHT_TYPE 2
|
|
231
|
+
#define DIR_LIGHT_TYPE 3
|
|
232
|
+
#define POINT_LIGHT_TYPE 4
|
|
229
233
|
|
|
230
234
|
struct LightsInfo {
|
|
231
235
|
|
|
@@ -277,7 +281,7 @@ export const shaderLightStruct = /* glsl */ `
|
|
|
277
281
|
l.v = s3.rgb;
|
|
278
282
|
l.area = s3.a;
|
|
279
283
|
|
|
280
|
-
if ( l.type == SPOT_LIGHT_TYPE ) {
|
|
284
|
+
if ( l.type == SPOT_LIGHT_TYPE || l.type == POINT_LIGHT_TYPE ) {
|
|
281
285
|
|
|
282
286
|
vec4 s4 = texelFetch1D( tex, i + 4u );
|
|
283
287
|
vec4 s5 = texelFetch1D( tex, i + 5u );
|
|
@@ -131,74 +131,7 @@ export const shaderUtils = /* glsl */`
|
|
|
131
131
|
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
|
|
135
|
-
uvec4 s0;
|
|
136
|
-
|
|
137
|
-
void rng_initialize(vec2 p, int frame) {
|
|
138
|
-
|
|
139
|
-
// white noise seed
|
|
140
|
-
s0 = uvec4( p, uint( frame ), uint( p.x ) + uint( p.y ) );
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
// https://www.pcg-random.org/
|
|
145
|
-
void pcg4d( inout uvec4 v ) {
|
|
146
|
-
|
|
147
|
-
v = v * 1664525u + 1013904223u;
|
|
148
|
-
v.x += v.y * v.w;
|
|
149
|
-
v.y += v.z * v.x;
|
|
150
|
-
v.z += v.x * v.y;
|
|
151
|
-
v.w += v.y * v.z;
|
|
152
|
-
v = v ^ ( v >> 16u );
|
|
153
|
-
v.x += v.y*v.w;
|
|
154
|
-
v.y += v.z*v.x;
|
|
155
|
-
v.z += v.x*v.y;
|
|
156
|
-
v.w += v.y*v.z;
|
|
157
|
-
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
// returns [ 0, 1 ]
|
|
161
|
-
float rand() {
|
|
162
|
-
|
|
163
|
-
pcg4d(s0);
|
|
164
|
-
return float( s0.x ) / float( 0xffffffffu );
|
|
165
|
-
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
vec2 rand2() {
|
|
169
|
-
|
|
170
|
-
pcg4d( s0 );
|
|
171
|
-
return vec2( s0.xy ) / float(0xffffffffu);
|
|
172
|
-
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
vec3 rand3() {
|
|
176
|
-
|
|
177
|
-
pcg4d(s0);
|
|
178
|
-
return vec3( s0.xyz ) / float( 0xffffffffu );
|
|
179
|
-
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
vec4 rand4() {
|
|
183
|
-
|
|
184
|
-
pcg4d(s0);
|
|
185
|
-
return vec4(s0)/float(0xffffffffu);
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
// https://github.com/mrdoob/three.js/blob/dev/src/math/Vector3.js#L724
|
|
190
|
-
vec3 randDirection() {
|
|
191
|
-
|
|
192
|
-
vec2 r = rand2();
|
|
193
|
-
float u = ( r.x - 0.5 ) * 2.0;
|
|
194
|
-
float t = r.y * PI * 2.0;
|
|
195
|
-
float f = sqrt( 1.0 - u * u );
|
|
196
|
-
|
|
197
|
-
return vec3( f * cos( t ), f * sin( t ), u );
|
|
198
|
-
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
vec2 triangleSample( vec2 a, vec2 b, vec2 c ) {
|
|
134
|
+
vec2 sampleTriangle( vec2 a, vec2 b, vec2 c, vec2 r ) {
|
|
202
135
|
|
|
203
136
|
// get the edges of the triangle and the diagonal across the
|
|
204
137
|
// center of the parallelogram
|
|
@@ -206,8 +139,7 @@ export const shaderUtils = /* glsl */`
|
|
|
206
139
|
vec2 e2 = c - b;
|
|
207
140
|
vec2 diag = normalize( e1 + e2 );
|
|
208
141
|
|
|
209
|
-
// pick
|
|
210
|
-
vec2 r = rand2();
|
|
142
|
+
// pick the point in the parallelogram
|
|
211
143
|
if ( r.x + r.y > 1.0 ) {
|
|
212
144
|
|
|
213
145
|
r = vec2( 1.0 ) - r;
|
|
@@ -218,40 +150,48 @@ export const shaderUtils = /* glsl */`
|
|
|
218
150
|
|
|
219
151
|
}
|
|
220
152
|
|
|
221
|
-
|
|
222
|
-
vec2 sampleAperture( int blades ) {
|
|
153
|
+
vec2 sampleCircle( vec2 uv ) {
|
|
223
154
|
|
|
224
|
-
|
|
155
|
+
float angle = 2.0 * PI * uv.x;
|
|
156
|
+
float radius = sqrt( uv.y );
|
|
157
|
+
return vec2( cos( angle ), sin( angle ) ) * radius;
|
|
225
158
|
|
|
226
|
-
|
|
227
|
-
float angle = 2.0 * PI * r.x;
|
|
228
|
-
float radius = sqrt( rand() );
|
|
229
|
-
return vec2( cos( angle ), sin( angle ) ) * radius;
|
|
159
|
+
}
|
|
230
160
|
|
|
231
|
-
|
|
161
|
+
vec3 sampleSphere( vec2 uv ) {
|
|
232
162
|
|
|
233
|
-
|
|
163
|
+
float u = ( uv.x - 0.5 ) * 2.0;
|
|
164
|
+
float t = uv.y * PI * 2.0;
|
|
165
|
+
float f = sqrt( 1.0 - u * u );
|
|
234
166
|
|
|
235
|
-
|
|
236
|
-
float anglePerSegment = 2.0 * PI / float( blades );
|
|
237
|
-
float segment = floor( float( blades ) * r.x );
|
|
167
|
+
return vec3( f * cos( t ), f * sin( t ), u );
|
|
238
168
|
|
|
239
|
-
|
|
240
|
-
float angle2 = angle1 + anglePerSegment;
|
|
241
|
-
vec2 a = vec2( sin( angle1 ), cos( angle1 ) );
|
|
242
|
-
vec2 b = vec2( 0.0, 0.0 );
|
|
243
|
-
vec2 c = vec2( sin( angle2 ), cos( angle2 ) );
|
|
169
|
+
}
|
|
244
170
|
|
|
245
|
-
|
|
171
|
+
vec2 sampleRegularNGon( int sides, vec3 uvw ) {
|
|
246
172
|
|
|
247
|
-
|
|
173
|
+
sides = max( sides, 3 );
|
|
174
|
+
|
|
175
|
+
vec3 r = uvw;
|
|
176
|
+
float anglePerSegment = 2.0 * PI / float( sides );
|
|
177
|
+
float segment = floor( float( sides ) * r.x );
|
|
178
|
+
|
|
179
|
+
float angle1 = anglePerSegment * segment;
|
|
180
|
+
float angle2 = angle1 + anglePerSegment;
|
|
181
|
+
vec2 a = vec2( sin( angle1 ), cos( angle1 ) );
|
|
182
|
+
vec2 b = vec2( 0.0, 0.0 );
|
|
183
|
+
vec2 c = vec2( sin( angle2 ), cos( angle2 ) );
|
|
184
|
+
|
|
185
|
+
return sampleTriangle( a, b, c, r.yz );
|
|
248
186
|
|
|
249
187
|
}
|
|
250
188
|
|
|
251
|
-
|
|
189
|
+
// samples an aperture shape with the given number of sides. 0 means circle
|
|
190
|
+
vec2 sampleAperture( int blades, vec3 uvw ) {
|
|
252
191
|
|
|
253
|
-
|
|
254
|
-
|
|
192
|
+
return blades == 0 ?
|
|
193
|
+
sampleCircle( uvw.xy ) :
|
|
194
|
+
sampleRegularNGon( blades, uvw );
|
|
255
195
|
|
|
256
196
|
}
|
|
257
197
|
|
|
@@ -332,6 +272,17 @@ export const shaderUtils = /* glsl */`
|
|
|
332
272
|
|
|
333
273
|
}
|
|
334
274
|
|
|
275
|
+
vec2 rotateVector( vec2 v, float t ) {
|
|
276
|
+
|
|
277
|
+
float ac = cos( t );
|
|
278
|
+
float as = sin( t );
|
|
279
|
+
return vec2(
|
|
280
|
+
v.x * ac - v.y * as,
|
|
281
|
+
v.x * as + v.y * ac
|
|
282
|
+
);
|
|
283
|
+
|
|
284
|
+
}
|
|
285
|
+
|
|
335
286
|
// Finds the point where the ray intersects the plane defined by u and v and checks if this point
|
|
336
287
|
// falls in the bounds of the rectangle on that same plane.
|
|
337
288
|
// Plane intersection: https://lousodrome.net/blog/light/2020/07/03/intersection-of-a-ray-and-a-plane/
|
|
@@ -400,4 +351,11 @@ export const shaderUtils = /* glsl */`
|
|
|
400
351
|
|
|
401
352
|
}
|
|
402
353
|
|
|
354
|
+
// tentFilter from Peter Shirley's 'Realistic Ray Tracing (2nd Edition)' book, pg. 60
|
|
355
|
+
// erichlof/THREE.js-PathTracing-Renderer/
|
|
356
|
+
float tentFilter( float x ) {
|
|
357
|
+
|
|
358
|
+
return x < 0.5 ? sqrt( 2.0 * x ) - 1.0 : 1.0 - sqrt( 2.0 - ( 2.0 * x ) );
|
|
359
|
+
|
|
360
|
+
}
|
|
403
361
|
`;
|
|
@@ -3,12 +3,11 @@ import { DataTexture, FloatType, RedFormat, LinearFilter, DataUtils, HalfFloatTy
|
|
|
3
3
|
function binarySearchFindClosestIndexOf( array, targetValue, offset = 0, count = array.length ) {
|
|
4
4
|
|
|
5
5
|
let lower = 0;
|
|
6
|
-
let upper = count;
|
|
6
|
+
let upper = count - 1;
|
|
7
7
|
while ( lower < upper ) {
|
|
8
8
|
|
|
9
9
|
const mid = ~ ~ ( 0.5 * upper + 0.5 * lower );
|
|
10
10
|
|
|
11
|
-
|
|
12
11
|
// check if the middle array value is above or below the target and shift
|
|
13
12
|
// which half of the array we're looking at
|
|
14
13
|
if ( array[ offset + mid ] < targetValue ) {
|
|
@@ -215,12 +214,13 @@ export class EquirectHdrInfoUniform {
|
|
|
215
214
|
const marginalDataArray = new Float32Array( height );
|
|
216
215
|
const conditionalDataArray = new Float32Array( width * height );
|
|
217
216
|
|
|
217
|
+
// we add a half texel offset so we're sampling the center of the pixel
|
|
218
218
|
for ( let i = 0; i < height; i ++ ) {
|
|
219
219
|
|
|
220
220
|
const dist = ( i + 1 ) / height;
|
|
221
221
|
const row = binarySearchFindClosestIndexOf( cdfMarginal, dist );
|
|
222
222
|
|
|
223
|
-
marginalDataArray[ i ] = row / height;
|
|
223
|
+
marginalDataArray[ i ] = ( row + 0.5 ) / height;
|
|
224
224
|
|
|
225
225
|
}
|
|
226
226
|
|
|
@@ -232,7 +232,7 @@ export class EquirectHdrInfoUniform {
|
|
|
232
232
|
const dist = ( x + 1 ) / width;
|
|
233
233
|
const col = binarySearchFindClosestIndexOf( cdfConditional, dist, y * width, width );
|
|
234
234
|
|
|
235
|
-
conditionalDataArray[ i ] = col / width;
|
|
235
|
+
conditionalDataArray[ i ] = ( col + 0.5 ) / width;
|
|
236
236
|
|
|
237
237
|
}
|
|
238
238
|
|
|
@@ -4,6 +4,8 @@ const LIGHT_PIXELS = 6;
|
|
|
4
4
|
const RECT_AREA_LIGHT = 0;
|
|
5
5
|
const CIRC_AREA_LIGHT = 1;
|
|
6
6
|
const SPOT_LIGHT = 2;
|
|
7
|
+
const DIR_LIGHT = 3;
|
|
8
|
+
const POINT_LIGHT = 4;
|
|
7
9
|
export class LightsInfoUniformStruct {
|
|
8
10
|
|
|
9
11
|
constructor() {
|
|
@@ -62,8 +64,24 @@ export class LightsInfoUniformStruct {
|
|
|
62
64
|
|
|
63
65
|
// type
|
|
64
66
|
let type = RECT_AREA_LIGHT;
|
|
65
|
-
if ( l.isRectAreaLight && l.isCircular )
|
|
66
|
-
|
|
67
|
+
if ( l.isRectAreaLight && l.isCircular ) {
|
|
68
|
+
|
|
69
|
+
type = CIRC_AREA_LIGHT;
|
|
70
|
+
|
|
71
|
+
} else if ( l.isSpotLight ) {
|
|
72
|
+
|
|
73
|
+
type = SPOT_LIGHT;
|
|
74
|
+
|
|
75
|
+
} else if ( l.isDirectionalLight ) {
|
|
76
|
+
|
|
77
|
+
type = DIR_LIGHT;
|
|
78
|
+
|
|
79
|
+
} else if ( l.isPointLight ) {
|
|
80
|
+
|
|
81
|
+
type = POINT_LIGHT;
|
|
82
|
+
|
|
83
|
+
}
|
|
84
|
+
|
|
67
85
|
floatArray[ baseIndex + ( index ++ ) ] = type;
|
|
68
86
|
|
|
69
87
|
// sample 2
|
|
@@ -150,6 +168,33 @@ export class LightsInfoUniformStruct {
|
|
|
150
168
|
// iesProfile
|
|
151
169
|
floatArray[ baseIndex + ( index ++ ) ] = iesTextures.indexOf( l.iesTexture );
|
|
152
170
|
|
|
171
|
+
} else if ( l.isPointLight ) {
|
|
172
|
+
|
|
173
|
+
const worldPosition = l.getWorldPosition( u );
|
|
174
|
+
floatArray[ baseIndex + ( index ++ ) ] = worldPosition.x;
|
|
175
|
+
floatArray[ baseIndex + ( index ++ ) ] = worldPosition.y;
|
|
176
|
+
floatArray[ baseIndex + ( index ++ ) ] = worldPosition.z;
|
|
177
|
+
index ++;
|
|
178
|
+
|
|
179
|
+
// sample 4
|
|
180
|
+
index += 4;
|
|
181
|
+
|
|
182
|
+
// sample 5
|
|
183
|
+
index += 2;
|
|
184
|
+
|
|
185
|
+
floatArray[ baseIndex + ( index ++ ) ] = l.decay;
|
|
186
|
+
floatArray[ baseIndex + ( index ++ ) ] = l.distance;
|
|
187
|
+
|
|
188
|
+
} else if ( l.isDirectionalLight ) {
|
|
189
|
+
|
|
190
|
+
const worldPosition = l.getWorldPosition( u );
|
|
191
|
+
const targetPosition = l.target.getWorldPosition( v );
|
|
192
|
+
|
|
193
|
+
target.subVectors( worldPosition, targetPosition ).normalize();
|
|
194
|
+
floatArray[ baseIndex + ( index ++ ) ] = target.x;
|
|
195
|
+
floatArray[ baseIndex + ( index ++ ) ] = target.y;
|
|
196
|
+
floatArray[ baseIndex + ( index ++ ) ] = target.z;
|
|
197
|
+
|
|
153
198
|
}
|
|
154
199
|
|
|
155
200
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DataTexture, RGBAFormat, ClampToEdgeWrapping, FloatType, FrontSide, BackSide, DoubleSide } from 'three';
|
|
2
|
-
import { reduceTexturesToUniqueSources } from './utils.js';
|
|
2
|
+
import { reduceTexturesToUniqueSources, getTextureHash } from './utils.js';
|
|
3
3
|
|
|
4
4
|
const MATERIAL_PIXELS = 45;
|
|
5
5
|
const MATERIAL_STRIDE = MATERIAL_PIXELS * 4;
|
|
@@ -61,8 +61,8 @@ export class MaterialsTexture extends DataTexture {
|
|
|
61
61
|
|
|
62
62
|
if ( key in material && material[ key ] ) {
|
|
63
63
|
|
|
64
|
-
const
|
|
65
|
-
return
|
|
64
|
+
const hash = getTextureHash( material[ key ] );
|
|
65
|
+
return uniqueTextureLookup[ hash ];
|
|
66
66
|
|
|
67
67
|
} else {
|
|
68
68
|
|
|
@@ -152,6 +152,12 @@ export class MaterialsTexture extends DataTexture {
|
|
|
152
152
|
|
|
153
153
|
// get the list of textures with unique sources
|
|
154
154
|
const uniqueTextures = reduceTexturesToUniqueSources( textures );
|
|
155
|
+
const uniqueTextureLookup = {};
|
|
156
|
+
for ( let i = 0, l = uniqueTextures.length; i < l; i ++ ) {
|
|
157
|
+
|
|
158
|
+
uniqueTextureLookup[ getTextureHash( uniqueTextures[ i ] ) ] = i;
|
|
159
|
+
|
|
160
|
+
}
|
|
155
161
|
|
|
156
162
|
if ( image.width !== dimension ) {
|
|
157
163
|
|
|
@@ -183,9 +189,9 @@ export class MaterialsTexture extends DataTexture {
|
|
|
183
189
|
// sample 1
|
|
184
190
|
// metalness & roughness
|
|
185
191
|
floatArray[ index ++ ] = getField( m, 'metalness', 0.0 );
|
|
186
|
-
floatArray[ index ++ ] =
|
|
192
|
+
floatArray[ index ++ ] = getTexture( m, 'metalnessMap' );
|
|
187
193
|
floatArray[ index ++ ] = getField( m, 'roughness', 0.0 );
|
|
188
|
-
floatArray[ index ++ ] =
|
|
194
|
+
floatArray[ index ++ ] = getTexture( m, 'roughnessMap' );
|
|
189
195
|
|
|
190
196
|
// sample 2
|
|
191
197
|
// transmission & emissiveIntensity
|
|
@@ -363,7 +369,7 @@ export class MaterialsTexture extends DataTexture {
|
|
|
363
369
|
// sample 14
|
|
364
370
|
index ++; // matte
|
|
365
371
|
index ++; // shadow
|
|
366
|
-
floatArray[ index ++ ] = Number( m.vertexColors ); // vertexColors
|
|
372
|
+
floatArray[ index ++ ] = Number( m.vertexColors ) | ( Number( m.flatShading ) << 1 ); // vertexColors & flatShading
|
|
367
373
|
floatArray[ index ++ ] = Number( m.transparent ); // transparent
|
|
368
374
|
|
|
369
375
|
// map transform 15
|
package/src/uniforms/utils.js
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
+
// we must hash the texture to determine uniqueness using the encoding, as well, because the
|
|
2
|
+
// when rendering each texture to the texture array they must have a consistent color space.
|
|
3
|
+
export function getTextureHash( t ) {
|
|
4
|
+
|
|
5
|
+
return `${ t.source.uuid }:${ t.encoding }`;
|
|
6
|
+
|
|
7
|
+
}
|
|
8
|
+
|
|
1
9
|
// reduce the set of textures to just those with a unique source while retaining
|
|
2
10
|
// the order of the textures.
|
|
3
11
|
export function reduceTexturesToUniqueSources( textures ) {
|
|
@@ -7,9 +15,10 @@ export function reduceTexturesToUniqueSources( textures ) {
|
|
|
7
15
|
for ( let i = 0, l = textures.length; i < l; i ++ ) {
|
|
8
16
|
|
|
9
17
|
const tex = textures[ i ];
|
|
10
|
-
|
|
18
|
+
const hash = getTextureHash( tex );
|
|
19
|
+
if ( ! sourceSet.has( hash ) ) {
|
|
11
20
|
|
|
12
|
-
sourceSet.add(
|
|
21
|
+
sourceSet.add( hash );
|
|
13
22
|
result.push( tex );
|
|
14
23
|
|
|
15
24
|
}
|