three-gpu-pathtracer 0.0.16 → 0.0.18

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.
Files changed (52) hide show
  1. package/README.md +3 -1
  2. package/build/index.module.js +1202 -471
  3. package/build/index.module.js.map +1 -1
  4. package/build/index.umd.cjs +1200 -470
  5. package/build/index.umd.cjs.map +1 -1
  6. package/package.json +5 -6
  7. package/src/core/DynamicPathTracingSceneGenerator.js +80 -35
  8. package/src/core/PathTracingRenderer.js +36 -38
  9. package/src/core/PathTracingSceneGenerator.js +11 -55
  10. package/src/materials/debug/GraphMaterial.js +1 -1
  11. package/src/materials/fullscreen/AlphaDisplayMaterial.js +1 -1
  12. package/src/materials/fullscreen/DenoiseMaterial.js +1 -1
  13. package/src/materials/fullscreen/GradientMapMaterial.js +1 -1
  14. package/src/materials/pathtracing/LambertPathTracingMaterial.js +5 -4
  15. package/src/materials/pathtracing/PhysicalPathTracingMaterial.js +87 -50
  16. package/src/materials/pathtracing/glsl/attenuateHit.glsl.js +21 -20
  17. package/src/materials/pathtracing/glsl/cameraUtils.glsl.js +2 -2
  18. package/src/materials/pathtracing/glsl/directLightContribution.glsl.js +12 -8
  19. package/src/materials/pathtracing/glsl/getSurfaceRecord.glsl.js +3 -2
  20. package/src/materials/pathtracing/glsl/traceScene.glsl.js +11 -13
  21. package/src/materials/surface/AmbientOcclusionMaterial.js +4 -3
  22. package/src/shader/bsdf/bsdfSampling.glsl.js +21 -17
  23. package/src/shader/common/bvhAnyHit.glsl.js +2 -2
  24. package/src/shader/common/fresnel.glsl.js +15 -9
  25. package/src/shader/common/intersectShapes.glsl.js +2 -2
  26. package/src/shader/rand/pcg.glsl.js +4 -4
  27. package/src/shader/rand/sobol.glsl.js +3 -3
  28. package/src/shader/rand/stratifiedTexture.glsl.js +45 -0
  29. package/src/shader/sampling/equirectSampling.glsl.js +10 -10
  30. package/src/shader/sampling/lightSampling.glsl.js +30 -37
  31. package/src/shader/structs/fogMaterialBvh.glsl.js +2 -2
  32. package/src/shader/structs/lightsStruct.glsl.js +15 -6
  33. package/src/shader/structs/materialStruct.glsl.js +16 -15
  34. package/src/textures/BlueNoiseTexture.js +87 -0
  35. package/src/textures/ProceduralEquirectTexture.js +6 -6
  36. package/src/textures/blueNoise/BlueNoiseGenerator.js +115 -0
  37. package/src/textures/blueNoise/BlueNoiseSamples.js +214 -0
  38. package/src/textures/blueNoise/utils.js +24 -0
  39. package/src/uniforms/EquirectHdrInfoUniform.js +59 -21
  40. package/src/uniforms/IESProfilesTexture.js +2 -2
  41. package/src/uniforms/LightsInfoUniformStruct.js +15 -9
  42. package/src/uniforms/MaterialsTexture.js +3 -1
  43. package/src/uniforms/RenderTarget2DArray.js +50 -3
  44. package/src/uniforms/StratifiedSamplesTexture.js +49 -0
  45. package/src/uniforms/stratified/StratifiedSampler.js +73 -0
  46. package/src/uniforms/stratified/StratifiedSamplerCombined.js +59 -0
  47. package/src/uniforms/utils.js +1 -1
  48. package/src/utils/BlurredEnvMapGenerator.js +4 -4
  49. package/src/utils/GeometryPreparationUtils.js +8 -95
  50. package/src/utils/IESLoader.js +7 -5
  51. package/src/utils/TextureUtils.js +15 -0
  52. package/src/workers/PathTracingSceneWorker.js +18 -8
@@ -3,12 +3,12 @@ import {
3
3
  Color,
4
4
  DataTexture,
5
5
  EquirectangularReflectionMapping,
6
- FloatType,
7
6
  LinearFilter,
8
7
  RepeatWrapping,
9
8
  RGBAFormat,
10
9
  Spherical,
11
10
  Vector2,
11
+ FloatType
12
12
  } from 'three';
13
13
 
14
14
  const _uv = new Vector2();
@@ -17,7 +17,7 @@ const _polar = new Spherical();
17
17
  const _color = new Color();
18
18
  export class ProceduralEquirectTexture extends DataTexture {
19
19
 
20
- constructor( width, height ) {
20
+ constructor( width = 512, height = 512 ) {
21
21
 
22
22
  super(
23
23
  new Float32Array( width * height * 4 ),
@@ -53,10 +53,10 @@ export class ProceduralEquirectTexture extends DataTexture {
53
53
 
54
54
  const i = y * width + x;
55
55
  const i4 = 4 * i;
56
- data[ i4 + 0 ] = _color.r;
57
- data[ i4 + 1 ] = _color.g;
58
- data[ i4 + 2 ] = _color.b;
59
- data[ i4 + 3 ] = 1.0;
56
+ data[ i4 + 0 ] = ( _color.r );
57
+ data[ i4 + 1 ] = ( _color.g );
58
+ data[ i4 + 2 ] = ( _color.b );
59
+ data[ i4 + 3 ] = ( 1.0 );
60
60
 
61
61
  }
62
62
 
@@ -0,0 +1,115 @@
1
+ import { shuffleArray, fillWithOnes } from './utils.js';
2
+ import { BlueNoiseSamples } from './BlueNoiseSamples.js';
3
+
4
+ export class BlueNoiseGenerator {
5
+
6
+ constructor() {
7
+
8
+ this.random = Math.random;
9
+ this.sigma = 1.5;
10
+ this.size = 64;
11
+ this.majorityPointsRatio = 0.1;
12
+
13
+ this.samples = new BlueNoiseSamples( 1 );
14
+ this.savedSamples = new BlueNoiseSamples( 1 );
15
+
16
+ }
17
+
18
+ generate() {
19
+
20
+ // http://cv.ulichney.com/papers/1993-void-cluster.pdf
21
+
22
+ const {
23
+ samples,
24
+ savedSamples,
25
+ sigma,
26
+ majorityPointsRatio,
27
+ size,
28
+ } = this;
29
+
30
+ samples.resize( size );
31
+ samples.setSigma( sigma );
32
+
33
+ // 1. Randomly place the minority points.
34
+ const pointCount = Math.floor( size * size * majorityPointsRatio );
35
+ const initialSamples = samples.binaryPattern;
36
+
37
+ fillWithOnes( initialSamples, pointCount );
38
+ shuffleArray( initialSamples, this.random );
39
+
40
+ for ( let i = 0, l = initialSamples.length; i < l; i ++ ) {
41
+
42
+ if ( initialSamples[ i ] === 1 ) {
43
+
44
+ samples.addPointIndex( i );
45
+
46
+ }
47
+
48
+ }
49
+
50
+ // 2. Remove minority point that is in densest cluster and place it in the largest void.
51
+ while ( true ) {
52
+
53
+ const clusterIndex = samples.findCluster();
54
+ samples.removePointIndex( clusterIndex );
55
+
56
+ const voidIndex = samples.findVoid();
57
+ if ( clusterIndex === voidIndex ) {
58
+
59
+ samples.addPointIndex( clusterIndex );
60
+ break;
61
+
62
+ }
63
+
64
+ samples.addPointIndex( voidIndex );
65
+
66
+ }
67
+
68
+ // 3. PHASE I: Assign a rank to each progressively less dense cluster point and put it
69
+ // in the dither array.
70
+ const ditherArray = new Uint32Array( size * size );
71
+ savedSamples.copy( samples );
72
+
73
+ let rank;
74
+ rank = samples.count - 1;
75
+ while ( rank >= 0 ) {
76
+
77
+ const clusterIndex = samples.findCluster();
78
+ samples.removePointIndex( clusterIndex );
79
+
80
+ ditherArray[ clusterIndex ] = rank;
81
+ rank --;
82
+
83
+ }
84
+
85
+ // 4. PHASE II: Do the same thing for the largest voids up to half of the total pixels using
86
+ // the initial binary pattern.
87
+ const totalSize = size * size;
88
+ rank = savedSamples.count;
89
+ while ( rank < totalSize / 2 ) {
90
+
91
+ const voidIndex = savedSamples.findVoid();
92
+ savedSamples.addPointIndex( voidIndex );
93
+ ditherArray[ voidIndex ] = rank;
94
+ rank ++;
95
+
96
+ }
97
+
98
+ // 5. PHASE III: Invert the pattern and finish out by assigning a rank to the remaining
99
+ // and iteratively removing them.
100
+ savedSamples.invert();
101
+
102
+ while ( rank < totalSize ) {
103
+
104
+ const clusterIndex = savedSamples.findCluster();
105
+ savedSamples.removePointIndex( clusterIndex );
106
+ ditherArray[ clusterIndex ] = rank;
107
+ rank ++;
108
+
109
+ }
110
+
111
+ return { data: ditherArray, maxValue: totalSize };
112
+
113
+ }
114
+
115
+ }
@@ -0,0 +1,214 @@
1
+ export class BlueNoiseSamples {
2
+
3
+ constructor( size ) {
4
+
5
+ this.count = 0;
6
+ this.size = - 1;
7
+ this.sigma = - 1;
8
+ this.radius = - 1;
9
+ this.lookupTable = null;
10
+ this.score = null;
11
+ this.binaryPattern = null;
12
+
13
+ this.resize( size );
14
+ this.setSigma( 1.5 );
15
+
16
+ }
17
+
18
+ findVoid() {
19
+
20
+ const { score, binaryPattern } = this;
21
+
22
+ let currValue = Infinity;
23
+ let currIndex = - 1;
24
+ for ( let i = 0, l = binaryPattern.length; i < l; i ++ ) {
25
+
26
+ if ( binaryPattern[ i ] !== 0 ) {
27
+
28
+ continue;
29
+
30
+ }
31
+
32
+ const pScore = score[ i ];
33
+ if ( pScore < currValue ) {
34
+
35
+ currValue = pScore;
36
+ currIndex = i;
37
+
38
+ }
39
+
40
+ }
41
+
42
+ return currIndex;
43
+
44
+ }
45
+
46
+ findCluster() {
47
+
48
+ const { score, binaryPattern } = this;
49
+
50
+ let currValue = - Infinity;
51
+ let currIndex = - 1;
52
+ for ( let i = 0, l = binaryPattern.length; i < l; i ++ ) {
53
+
54
+ if ( binaryPattern[ i ] !== 1 ) {
55
+
56
+ continue;
57
+
58
+ }
59
+
60
+ const pScore = score[ i ];
61
+ if ( pScore > currValue ) {
62
+
63
+ currValue = pScore;
64
+ currIndex = i;
65
+
66
+ }
67
+
68
+ }
69
+
70
+ return currIndex;
71
+
72
+ }
73
+
74
+ setSigma( sigma ) {
75
+
76
+ if ( sigma === this.sigma ) {
77
+
78
+ return;
79
+
80
+ }
81
+
82
+ // generate a radius in which the score will be updated under the
83
+ // assumption that e^-10 is insignificant enough to be the border at
84
+ // which we drop off.
85
+ const radius = ~ ~ ( Math.sqrt( 10 * 2 * ( sigma ** 2 ) ) + 1 );
86
+ const lookupWidth = 2 * radius + 1;
87
+ const lookupTable = new Float32Array( lookupWidth * lookupWidth );
88
+ const sigma2 = sigma * sigma;
89
+ for ( let x = - radius; x <= radius; x ++ ) {
90
+
91
+ for ( let y = - radius; y <= radius; y ++ ) {
92
+
93
+ const index = ( radius + y ) * lookupWidth + x + radius;
94
+ const dist2 = x * x + y * y;
95
+ lookupTable[ index ] = Math.E ** ( - dist2 / ( 2 * sigma2 ) );
96
+
97
+ }
98
+
99
+ }
100
+
101
+ this.lookupTable = lookupTable;
102
+ this.sigma = sigma;
103
+ this.radius = radius;
104
+
105
+ }
106
+
107
+ resize( size ) {
108
+
109
+ if ( this.size !== size ) {
110
+
111
+ this.size = size;
112
+ this.score = new Float32Array( size * size );
113
+ this.binaryPattern = new Uint8Array( size * size );
114
+
115
+ }
116
+
117
+
118
+ }
119
+
120
+ invert() {
121
+
122
+ const { binaryPattern, score, size } = this;
123
+
124
+ score.fill( 0 );
125
+
126
+ for ( let i = 0, l = binaryPattern.length; i < l; i ++ ) {
127
+
128
+ if ( binaryPattern[ i ] === 0 ) {
129
+
130
+ const y = ~ ~ ( i / size );
131
+ const x = i - y * size;
132
+ this.updateScore( x, y, 1 );
133
+ binaryPattern[ i ] = 1;
134
+
135
+ } else {
136
+
137
+ binaryPattern[ i ] = 0;
138
+
139
+ }
140
+
141
+ }
142
+
143
+ }
144
+
145
+ updateScore( x, y, multiplier ) {
146
+
147
+ // TODO: Is there a way to keep track of the highest and lowest scores here to avoid have to search over
148
+ // everything in the buffer?
149
+ const { size, score, lookupTable } = this;
150
+
151
+ // const sigma2 = sigma * sigma;
152
+ // const radius = Math.floor( size / 2 );
153
+ const radius = this.radius;
154
+ const lookupWidth = 2 * radius + 1;
155
+ for ( let px = - radius; px <= radius; px ++ ) {
156
+
157
+ for ( let py = - radius; py <= radius; py ++ ) {
158
+
159
+ // const dist2 = px * px + py * py;
160
+ // const value = Math.E ** ( - dist2 / ( 2 * sigma2 ) );
161
+
162
+ const lookupIndex = ( radius + py ) * lookupWidth + px + radius;
163
+ const value = lookupTable[ lookupIndex ];
164
+
165
+ let sx = ( x + px );
166
+ sx = sx < 0 ? size + sx : sx % size;
167
+
168
+ let sy = ( y + py );
169
+ sy = sy < 0 ? size + sy : sy % size;
170
+
171
+ const sindex = sy * size + sx;
172
+ score[ sindex ] += multiplier * value;
173
+
174
+ }
175
+
176
+ }
177
+
178
+ }
179
+
180
+ addPointIndex( index ) {
181
+
182
+ this.binaryPattern[ index ] = 1;
183
+
184
+ const size = this.size;
185
+ const y = ~ ~ ( index / size );
186
+ const x = index - y * size;
187
+ this.updateScore( x, y, 1 );
188
+ this.count ++;
189
+
190
+ }
191
+
192
+ removePointIndex( index ) {
193
+
194
+ this.binaryPattern[ index ] = 0;
195
+
196
+ const size = this.size;
197
+ const y = ~ ~ ( index / size );
198
+ const x = index - y * size;
199
+ this.updateScore( x, y, - 1 );
200
+ this.count --;
201
+
202
+ }
203
+
204
+ copy( source ) {
205
+
206
+ this.resize( source.size );
207
+ this.score.set( source.score );
208
+ this.binaryPattern.set( source.binaryPattern );
209
+ this.setSigma( source.sigma );
210
+ this.count = source.count;
211
+
212
+ }
213
+
214
+ }
@@ -0,0 +1,24 @@
1
+ export function shuffleArray( array, random = Math.random ) {
2
+
3
+ for ( let i = array.length - 1; i > 0; i -- ) {
4
+
5
+ const replaceIndex = ~ ~ ( ( random() - 1e-6 ) * i );
6
+ const tmp = array[ i ];
7
+ array[ i ] = array[ replaceIndex ];
8
+ array[ replaceIndex ] = tmp;
9
+
10
+ }
11
+
12
+ }
13
+
14
+ export function fillWithOnes( array, count ) {
15
+
16
+ array.fill( 0 );
17
+
18
+ for ( let i = 0; i < count; i ++ ) {
19
+
20
+ array[ i ] = 1;
21
+
22
+ }
23
+
24
+ }
@@ -1,4 +1,5 @@
1
- import { DataTexture, FloatType, RedFormat, LinearFilter, DataUtils, HalfFloatType, Source, RepeatWrapping, RGBAFormat } from 'three';
1
+ import { DataTexture, RedFormat, LinearFilter, DataUtils, HalfFloatType, Source, RepeatWrapping, RGBAFormat, FloatType, ClampToEdgeWrapping } from 'three';
2
+ import { toHalfFloatArray } from '../utils/TextureUtils.js';
2
3
 
3
4
  function binarySearchFindClosestIndexOf( array, targetValue, offset = 0, count = array.length ) {
4
5
 
@@ -38,7 +39,7 @@ function colorToLuminance( r, g, b ) {
38
39
  }
39
40
 
40
41
  // ensures the data is all floating point values and flipY is false
41
- function preprocessEnvMap( envMap ) {
42
+ function preprocessEnvMap( envMap, targetType = HalfFloatType ) {
42
43
 
43
44
  const map = envMap.clone();
44
45
  map.source = new Source( { ...map.image } );
@@ -47,17 +48,54 @@ function preprocessEnvMap( envMap ) {
47
48
  // TODO: is there a simple way to avoid cloning and adjusting the env map data here?
48
49
  // convert the data from half float uint 16 arrays to float arrays for cdf computation
49
50
  let newData = data;
50
- if ( map.type === HalfFloatType ) {
51
+ if ( map.type !== targetType ) {
51
52
 
52
- newData = new Float32Array( data.length );
53
- for ( const i in data ) {
53
+ if ( targetType === HalfFloatType ) {
54
54
 
55
- newData[ i ] = DataUtils.fromHalfFloat( data[ i ] );
55
+ newData = new Uint16Array( data.length );
56
+
57
+ } else {
58
+
59
+ newData = new Float32Array( data.length );
60
+
61
+ }
62
+
63
+ let maxIntValue;
64
+ if ( data instanceof Int8Array || data instanceof Int16Array || data instanceof Int32Array ) {
65
+
66
+ maxIntValue = 2 ** ( 8 * data.BYTES_PER_ELEMENT - 1 ) - 1;
67
+
68
+ } else {
69
+
70
+ maxIntValue = 2 ** ( 8 * data.BYTES_PER_ELEMENT ) - 1;
71
+
72
+ }
73
+
74
+ for ( let i = 0, l = data.length; i < l; i ++ ) {
75
+
76
+ let v = data[ i ];
77
+ if ( map.type === HalfFloatType ) {
78
+
79
+ v = DataUtils.fromHalfFloat( data[ i ] );
80
+
81
+ }
82
+
83
+ if ( map.type !== FloatType && map.type !== HalfFloatType ) {
84
+
85
+ v /= maxIntValue;
86
+
87
+ }
88
+
89
+ if ( targetType === HalfFloatType ) {
90
+
91
+ newData[ i ] = DataUtils.toHalfFloat( v );
92
+
93
+ }
56
94
 
57
95
  }
58
96
 
59
97
  map.image.data = newData;
60
- map.type = FloatType;
98
+ map.type = targetType;
61
99
 
62
100
  }
63
101
 
@@ -98,8 +136,8 @@ export class EquirectHdrInfoUniform {
98
136
 
99
137
  // Default to a white texture and associated weights so we don't
100
138
  // just render black initially.
101
- const whiteTex = new DataTexture( new Float32Array( [ 1, 1, 1, 1 ] ), 1, 1 );
102
- whiteTex.type = FloatType;
139
+ const whiteTex = new DataTexture( toHalfFloatArray( new Float32Array( [ 1, 1, 1, 1 ] ) ), 1, 1 );
140
+ whiteTex.type = HalfFloatType;
103
141
  whiteTex.format = RGBAFormat;
104
142
  whiteTex.minFilter = LinearFilter;
105
143
  whiteTex.magFilter = LinearFilter;
@@ -110,8 +148,8 @@ export class EquirectHdrInfoUniform {
110
148
 
111
149
  // Stores a map of [0, 1] value -> cumulative importance row & pdf
112
150
  // used to sampling a random value to a relevant row to sample from
113
- const marginalWeights = new DataTexture( new Float32Array( [ 0, 1 ] ), 1, 2 );
114
- marginalWeights.type = FloatType;
151
+ const marginalWeights = new DataTexture( toHalfFloatArray( new Float32Array( [ 0, 1 ] ) ), 1, 2 );
152
+ marginalWeights.type = HalfFloatType;
115
153
  marginalWeights.format = RedFormat;
116
154
  marginalWeights.minFilter = LinearFilter;
117
155
  marginalWeights.magFilter = LinearFilter;
@@ -120,8 +158,8 @@ export class EquirectHdrInfoUniform {
120
158
 
121
159
  // Stores a map of [0, 1] value -> cumulative importance column & pdf
122
160
  // used to sampling a random value to a relevant pixel to sample from
123
- const conditionalWeights = new DataTexture( new Float32Array( [ 0, 0, 1, 1 ] ), 2, 2 );
124
- conditionalWeights.type = FloatType;
161
+ const conditionalWeights = new DataTexture( toHalfFloatArray( new Float32Array( [ 0, 0, 1, 1 ] ) ), 2, 2 );
162
+ conditionalWeights.type = HalfFloatType;
125
163
  conditionalWeights.format = RedFormat;
126
164
  conditionalWeights.minFilter = LinearFilter;
127
165
  conditionalWeights.magFilter = LinearFilter;
@@ -149,7 +187,7 @@ export class EquirectHdrInfoUniform {
149
187
  // https://pbr-book.org/3ed-2018/Light_Transport_I_Surface_Reflection/Sampling_Light_Sources#InfiniteAreaLights
150
188
  const map = preprocessEnvMap( hdr );
151
189
  map.wrapS = RepeatWrapping;
152
- map.wrapT = RepeatWrapping;
190
+ map.wrapT = ClampToEdgeWrapping;
153
191
 
154
192
  const { width, height, data } = map.image;
155
193
 
@@ -171,9 +209,9 @@ export class EquirectHdrInfoUniform {
171
209
  for ( let x = 0; x < width; x ++ ) {
172
210
 
173
211
  const i = y * width + x;
174
- const r = data[ 4 * i + 0 ];
175
- const g = data[ 4 * i + 1 ];
176
- const b = data[ 4 * i + 2 ];
212
+ const r = DataUtils.fromHalfFloat( data[ 4 * i + 0 ] );
213
+ const g = DataUtils.fromHalfFloat( data[ 4 * i + 1 ] );
214
+ const b = DataUtils.fromHalfFloat( data[ 4 * i + 2 ] );
177
215
 
178
216
  // the probability of the pixel being selected in this row is the
179
217
  // scale of the luminance relative to the rest of the pixels.
@@ -225,8 +263,8 @@ export class EquirectHdrInfoUniform {
225
263
  // the marginal and conditional data. These will be used to sample with a random number
226
264
  // to retrieve a uv value to sample in the environment map.
227
265
  // These values continually increase so it's okay to interpolate between them.
228
- const marginalDataArray = new Float32Array( height );
229
- const conditionalDataArray = new Float32Array( width * height );
266
+ const marginalDataArray = new Uint16Array( height );
267
+ const conditionalDataArray = new Uint16Array( width * height );
230
268
 
231
269
  // we add a half texel offset so we're sampling the center of the pixel
232
270
  for ( let i = 0; i < height; i ++ ) {
@@ -234,7 +272,7 @@ export class EquirectHdrInfoUniform {
234
272
  const dist = ( i + 1 ) / height;
235
273
  const row = binarySearchFindClosestIndexOf( cdfMarginal, dist );
236
274
 
237
- marginalDataArray[ i ] = ( row + 0.5 ) / height;
275
+ marginalDataArray[ i ] = DataUtils.toHalfFloat( ( row + 0.5 ) / height );
238
276
 
239
277
  }
240
278
 
@@ -246,7 +284,7 @@ export class EquirectHdrInfoUniform {
246
284
  const dist = ( x + 1 ) / width;
247
285
  const col = binarySearchFindClosestIndexOf( cdfConditional, dist, y * width, width );
248
286
 
249
- conditionalDataArray[ i ] = ( col + 0.5 ) / width;
287
+ conditionalDataArray[ i ] = DataUtils.toHalfFloat( ( col + 0.5 ) / width );
250
288
 
251
289
  }
252
290
 
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  ClampToEdgeWrapping,
3
3
  Color,
4
- FloatType,
4
+ HalfFloatType,
5
5
  LinearFilter,
6
6
  MeshBasicMaterial,
7
7
  NoToneMapping,
@@ -20,7 +20,7 @@ export class IESProfilesTexture extends WebGLArrayRenderTarget {
20
20
 
21
21
  const tex = this.texture;
22
22
  tex.format = RGBAFormat;
23
- tex.type = FloatType;
23
+ tex.type = HalfFloatType;
24
24
  tex.minFilter = LinearFilter;
25
25
  tex.magFilter = LinearFilter;
26
26
  tex.wrapS = ClampToEdgeWrapping;
@@ -1,4 +1,4 @@
1
- import { DataTexture, RGBAFormat, ClampToEdgeWrapping, FloatType, Vector3, Quaternion, Matrix4 } from 'three';
1
+ import { DataTexture, RGBAFormat, ClampToEdgeWrapping, FloatType, Vector3, Quaternion, Matrix4, NearestFilter } from 'three';
2
2
 
3
3
  const LIGHT_PIXELS = 6;
4
4
  const RECT_AREA_LIGHT = 0;
@@ -16,6 +16,8 @@ export class LightsInfoUniformStruct {
16
16
  tex.wrapS = ClampToEdgeWrapping;
17
17
  tex.wrapT = ClampToEdgeWrapping;
18
18
  tex.generateMipmaps = false;
19
+ tex.minFilter = NearestFilter;
20
+ tex.magFilter = NearestFilter;
19
21
 
20
22
  this.tex = tex;
21
23
  this.count = 0;
@@ -46,7 +48,7 @@ export class LightsInfoUniformStruct {
46
48
  const worldQuaternion = new Quaternion();
47
49
  const eye = new Vector3();
48
50
  const target = new Vector3();
49
- const up = new Vector3();
51
+ const up = new Vector3( 0, 1, 0 );
50
52
 
51
53
  for ( let i = 0, l = lights.length; i < l; i ++ ) {
52
54
 
@@ -55,6 +57,13 @@ export class LightsInfoUniformStruct {
55
57
  const baseIndex = i * LIGHT_PIXELS * 4;
56
58
  let index = 0;
57
59
 
60
+ // initialize to 0
61
+ for ( let p = 0; p < LIGHT_PIXELS; p ++ ) {
62
+
63
+ floatArray[ baseIndex + p ] = 0;
64
+
65
+ }
66
+
58
67
  // sample 1
59
68
  // position
60
69
  l.getWorldPosition( v );
@@ -119,7 +128,7 @@ export class LightsInfoUniformStruct {
119
128
 
120
129
  } else if ( l.isSpotLight ) {
121
130
 
122
- const radius = l.radius;
131
+ const radius = l.radius || 0;
123
132
  eye.setFromMatrixPosition( l.matrixWorld );
124
133
  target.setFromMatrixPosition( l.target.matrixWorld );
125
134
  m.lookAt( eye, target, up );
@@ -149,24 +158,21 @@ export class LightsInfoUniformStruct {
149
158
  // radius
150
159
  floatArray[ baseIndex + ( index ++ ) ] = radius;
151
160
 
152
- // near
153
- floatArray[ baseIndex + ( index ++ ) ] = l.shadow.camera.near;
154
-
155
161
  // decay
156
162
  floatArray[ baseIndex + ( index ++ ) ] = l.decay;
157
163
 
158
164
  // distance
159
165
  floatArray[ baseIndex + ( index ++ ) ] = l.distance;
160
166
 
161
- // sample 6
162
167
  // coneCos
163
168
  floatArray[ baseIndex + ( index ++ ) ] = Math.cos( l.angle );
164
169
 
170
+ // sample 6
165
171
  // penumbraCos
166
172
  floatArray[ baseIndex + ( index ++ ) ] = Math.cos( l.angle * ( 1 - l.penumbra ) );
167
173
 
168
174
  // iesProfile
169
- floatArray[ baseIndex + ( index ++ ) ] = iesTextures.indexOf( l.iesTexture );
175
+ floatArray[ baseIndex + ( index ++ ) ] = l.iesTexture ? iesTextures.indexOf( l.iesTexture ) : - 1;
170
176
 
171
177
  } else if ( l.isPointLight ) {
172
178
 
@@ -183,7 +189,7 @@ export class LightsInfoUniformStruct {
183
189
  index += 4;
184
190
 
185
191
  // sample 5
186
- index += 2;
192
+ index += 1;
187
193
 
188
194
  floatArray[ baseIndex + ( index ++ ) ] = l.decay;
189
195
  floatArray[ baseIndex + ( index ++ ) ] = l.distance;
@@ -1,4 +1,4 @@
1
- import { DataTexture, RGBAFormat, ClampToEdgeWrapping, FloatType, FrontSide, BackSide, DoubleSide } from 'three';
1
+ import { DataTexture, RGBAFormat, ClampToEdgeWrapping, FloatType, FrontSide, BackSide, DoubleSide, NearestFilter } from 'three';
2
2
  import { reduceTexturesToUniqueSources, getTextureHash } from './utils.js';
3
3
 
4
4
  const MATERIAL_PIXELS = 45;
@@ -53,6 +53,8 @@ export class MaterialsTexture extends DataTexture {
53
53
  this.type = FloatType;
54
54
  this.wrapS = ClampToEdgeWrapping;
55
55
  this.wrapT = ClampToEdgeWrapping;
56
+ this.minFilter = NearestFilter;
57
+ this.magFilter = NearestFilter;
56
58
  this.generateMipmaps = false;
57
59
  this.threeCompatibilityTransforms = false;
58
60
  this.features = new MaterialFeatures();