three-gpu-pathtracer 0.0.16 → 0.0.17

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 (38) hide show
  1. package/README.md +3 -1
  2. package/build/index.module.js +248 -198
  3. package/build/index.module.js.map +1 -1
  4. package/build/index.umd.cjs +245 -195
  5. package/build/index.umd.cjs.map +1 -1
  6. package/package.json +5 -6
  7. package/src/core/DynamicPathTracingSceneGenerator.js +8 -3
  8. package/src/core/PathTracingRenderer.js +8 -4
  9. package/src/core/PathTracingSceneGenerator.js +6 -1
  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 +4 -3
  15. package/src/materials/pathtracing/PhysicalPathTracingMaterial.js +61 -46
  16. package/src/materials/pathtracing/glsl/attenuateHit.glsl.js +2 -11
  17. package/src/materials/pathtracing/glsl/directLightContribution.glsl.js +10 -6
  18. package/src/materials/pathtracing/glsl/getSurfaceRecord.glsl.js +1 -1
  19. package/src/materials/pathtracing/glsl/traceScene.glsl.js +10 -12
  20. package/src/materials/surface/AmbientOcclusionMaterial.js +4 -3
  21. package/src/shader/bsdf/bsdfSampling.glsl.js +7 -7
  22. package/src/shader/common/bvhAnyHit.glsl.js +2 -2
  23. package/src/shader/common/intersectShapes.glsl.js +2 -2
  24. package/src/shader/rand/sobol.glsl.js +3 -3
  25. package/src/shader/sampling/equirectSampling.glsl.js +10 -10
  26. package/src/shader/sampling/lightSampling.glsl.js +30 -37
  27. package/src/shader/structs/fogMaterialBvh.glsl.js +2 -2
  28. package/src/shader/structs/lightsStruct.glsl.js +12 -1
  29. package/src/shader/structs/materialStruct.glsl.js +16 -15
  30. package/src/textures/ProceduralEquirectTexture.js +9 -8
  31. package/src/uniforms/EquirectHdrInfoUniform.js +18 -17
  32. package/src/uniforms/IESProfilesTexture.js +2 -2
  33. package/src/uniforms/LightsInfoUniformStruct.js +4 -2
  34. package/src/uniforms/MaterialsTexture.js +3 -1
  35. package/src/utils/BlurredEnvMapGenerator.js +4 -4
  36. package/src/utils/GeometryPreparationUtils.js +8 -2
  37. package/src/utils/IESLoader.js +7 -5
  38. package/src/utils/TextureUtils.js +15 -0
@@ -24,14 +24,14 @@ export const equirectSamplingGLSL = /* glsl */`
24
24
  }
25
25
 
26
26
  // samples the color given env map with CDF and returns the pdf of the direction
27
- float sampleEquirect( EquirectHdrInfo info, vec3 direction, out vec3 color ) {
27
+ float sampleEquirect( vec3 direction, inout vec3 color ) {
28
28
 
29
29
  vec2 uv = equirectDirectionToUv( direction );
30
- color = texture2D( info.map, uv ).rgb;
30
+ color = texture2D( envMapInfo.map, uv ).rgb;
31
31
 
32
- float totalSum = info.totalSum;
32
+ float totalSum = envMapInfo.totalSum;
33
33
  float lum = luminance( color );
34
- ivec2 resolution = textureSize( info.map, 0 );
34
+ ivec2 resolution = textureSize( envMapInfo.map, 0 );
35
35
  float pdf = lum / totalSum;
36
36
 
37
37
  return float( resolution.x * resolution.y ) * pdf * equirectDirectionPdf( direction );
@@ -39,20 +39,20 @@ export const equirectSamplingGLSL = /* glsl */`
39
39
  }
40
40
 
41
41
  // samples a direction of the envmap with color and retrieves pdf
42
- float sampleEquirectProbability( EquirectHdrInfo info, vec2 r, out vec3 color, out vec3 direction ) {
42
+ float sampleEquirectProbability( vec2 r, inout vec3 color, inout vec3 direction ) {
43
43
 
44
44
  // sample env map cdf
45
- float v = texture2D( info.marginalWeights, vec2( r.x, 0.0 ) ).x;
46
- float u = texture2D( info.conditionalWeights, vec2( r.y, v ) ).x;
45
+ float v = texture2D( envMapInfo.marginalWeights, vec2( r.x, 0.0 ) ).x;
46
+ float u = texture2D( envMapInfo.conditionalWeights, vec2( r.y, v ) ).x;
47
47
  vec2 uv = vec2( u, v );
48
48
 
49
49
  vec3 derivedDirection = equirectUvToDirection( uv );
50
50
  direction = derivedDirection;
51
- color = texture2D( info.map, uv ).rgb;
51
+ color = texture2D( envMapInfo.map, uv ).rgb;
52
52
 
53
- float totalSum = info.totalSum;
53
+ float totalSum = envMapInfo.totalSum;
54
54
  float lum = luminance( color );
55
- ivec2 resolution = textureSize( info.map, 0 );
55
+ ivec2 resolution = textureSize( envMapInfo.map, 0 );
56
56
  float pdf = lum / totalSum;
57
57
 
58
58
  return float( resolution.x * resolution.y ) * pdf * equirectDirectionPdf( direction );
@@ -42,24 +42,17 @@ export const lightSamplingGLSL = /* glsl */`
42
42
 
43
43
  };
44
44
 
45
- bool lightsClosestHit( sampler2D lights, uint lightCount, vec3 rayOrigin, vec3 rayDirection, out LightRecord lightRec ) {
45
+ bool intersectLightAtIndex( sampler2D lights, vec3 rayOrigin, vec3 rayDirection, uint l, inout LightRecord lightRec ) {
46
46
 
47
47
  bool didHit = false;
48
- uint l;
49
- for ( l = 0u; l < lightCount; l ++ ) {
50
-
51
- Light light = readLightInfo( lights, l );
52
-
53
- vec3 u = light.u;
54
- vec3 v = light.v;
55
-
56
- // check for backface
57
- vec3 normal = normalize( cross( u, v ) );
58
- if ( dot( normal, rayDirection ) < 0.0 ) {
48
+ Light light = readLightInfo( lights, l );
59
49
 
60
- continue;
50
+ vec3 u = light.u;
51
+ vec3 v = light.v;
61
52
 
62
- }
53
+ // check for backface
54
+ vec3 normal = normalize( cross( u, v ) );
55
+ if ( dot( normal, rayDirection ) > 0.0 ) {
63
56
 
64
57
  u *= 1.0 / dot( u, u );
65
58
  v *= 1.0 / dot( v, v );
@@ -72,17 +65,13 @@ export const lightSamplingGLSL = /* glsl */`
72
65
  ( light.type == CIRC_AREA_LIGHT_TYPE && intersectsCircle( light.position, normal, u, v, rayOrigin, rayDirection, dist ) )
73
66
  ) {
74
67
 
75
- if ( ! didHit || dist < lightRec.dist ) {
76
-
77
- float cosTheta = dot( rayDirection, normal );
78
- didHit = true;
79
- lightRec.dist = dist;
80
- lightRec.pdf = ( dist * dist ) / ( light.area * cosTheta );
81
- lightRec.emission = light.color * light.intensity;
82
- lightRec.direction = rayDirection;
83
- lightRec.type = light.type;
84
-
85
- }
68
+ float cosTheta = dot( rayDirection, normal );
69
+ didHit = true;
70
+ lightRec.dist = dist;
71
+ lightRec.pdf = ( dist * dist ) / ( light.area * cosTheta );
72
+ lightRec.emission = light.color * light.intensity;
73
+ lightRec.direction = rayDirection;
74
+ lightRec.type = light.type;
86
75
 
87
76
  }
88
77
 
@@ -94,11 +83,6 @@ export const lightSamplingGLSL = /* glsl */`
94
83
 
95
84
  LightRecord randomAreaLightSample( Light light, vec3 rayOrigin, vec2 ruv ) {
96
85
 
97
- LightRecord lightRec;
98
- lightRec.type = light.type;
99
-
100
- lightRec.emission = light.color * light.intensity;
101
-
102
86
  vec3 randomPos;
103
87
  if( light.type == RECT_AREA_LIGHT_TYPE ) {
104
88
 
@@ -119,12 +103,17 @@ export const lightSamplingGLSL = /* glsl */`
119
103
 
120
104
  vec3 toLight = randomPos - rayOrigin;
121
105
  float lightDistSq = dot( toLight, toLight );
122
- lightRec.dist = sqrt( lightDistSq );
106
+ float dist = sqrt( lightDistSq );
107
+ vec3 direction = toLight / dist;
108
+ vec3 lightNormal = normalize( cross( light.u, light.v ) );
123
109
 
124
- vec3 direction = toLight / lightRec.dist;
110
+ LightRecord lightRec;
111
+ lightRec.type = light.type;
112
+ lightRec.emission = light.color * light.intensity;
113
+ lightRec.dist = dist;
125
114
  lightRec.direction = direction;
126
115
 
127
- vec3 lightNormal = normalize( cross( light.u, light.v ) );
116
+ // TODO: the denominator is potentially zero
128
117
  lightRec.pdf = lightDistSq / ( light.area * dot( direction, lightNormal ) );
129
118
 
130
119
  return lightRec;
@@ -172,13 +161,15 @@ export const lightSamplingGLSL = /* glsl */`
172
161
 
173
162
  LightRecord randomLightSample( sampler2D lights, sampler2DArray iesProfiles, uint lightCount, vec3 rayOrigin, vec3 ruv ) {
174
163
 
164
+ LightRecord result;
165
+
175
166
  // pick a random light
176
167
  uint l = uint( ruv.x * float( lightCount ) );
177
168
  Light light = readLightInfo( lights, l );
178
169
 
179
170
  if ( light.type == SPOT_LIGHT_TYPE ) {
180
171
 
181
- return randomSpotLightSample( light, iesProfiles, rayOrigin, ruv.yz );
172
+ result = randomSpotLightSample( light, iesProfiles, rayOrigin, ruv.yz );
182
173
 
183
174
  } else if ( light.type == POINT_LIGHT_TYPE ) {
184
175
 
@@ -198,7 +189,7 @@ export const lightSamplingGLSL = /* glsl */`
198
189
  rec.pdf = 1.0;
199
190
  rec.emission = light.color * light.intensity * distanceFalloff;
200
191
  rec.type = light.type;
201
- return rec;
192
+ result = rec;
202
193
 
203
194
  } else if ( light.type == DIR_LIGHT_TYPE ) {
204
195
 
@@ -209,15 +200,17 @@ export const lightSamplingGLSL = /* glsl */`
209
200
  rec.emission = light.color * light.intensity;
210
201
  rec.type = light.type;
211
202
 
212
- return rec;
203
+ result = rec;
213
204
 
214
205
  } else {
215
206
 
216
207
  // sample the light
217
- return randomAreaLightSample( light, rayOrigin, ruv.yz );
208
+ result = randomAreaLightSample( light, rayOrigin, ruv.yz );
218
209
 
219
210
  }
220
211
 
212
+ return result;
213
+
221
214
  }
222
215
 
223
216
  `;
@@ -15,9 +15,9 @@ bool isMaterialFogVolume( sampler2D materials, uint materialIndex ) {
15
15
 
16
16
  // returns true if we're within the first fog volume we hit
17
17
  bool bvhIntersectFogVolumeHit(
18
- BVH bvh, vec3 rayOrigin, vec3 rayDirection,
18
+ vec3 rayOrigin, vec3 rayDirection,
19
19
  usampler2D materialIndexAttribute, sampler2D materials,
20
- out Material material
20
+ inout Material material
21
21
  ) {
22
22
 
23
23
  material.fogVolume = false;
@@ -67,7 +67,18 @@ export const lightsStructGLSL = /* glsl */`
67
67
 
68
68
  l.coneCos = s5.r;
69
69
  l.penumbraCos = s5.g;
70
- l.iesProfile = int( round ( s5.b ) );
70
+ l.iesProfile = int( round( s5.b ) );
71
+
72
+ } else {
73
+
74
+ l.radius = 0.0;
75
+ l.near = 0.0;
76
+ l.decay = 0.0;
77
+ l.distance = 0.0;
78
+
79
+ l.coneCos = 0.0;
80
+ l.penumbraCos = 0.0;
81
+ l.iesProfile = - 1;
71
82
 
72
83
  }
73
84
 
@@ -184,21 +184,22 @@ export const materialStructGLSL = /* glsl */ `
184
184
 
185
185
  uint firstTextureTransformIdx = i + 15u;
186
186
 
187
- m.mapTransform = m.map == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx );
188
- m.metalnessMapTransform = m.metalnessMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 2u );
189
- m.roughnessMapTransform = m.roughnessMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 4u );
190
- m.transmissionMapTransform = m.transmissionMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 6u );
191
- m.emissiveMapTransform = m.emissiveMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 8u );
192
- m.normalMapTransform = m.normalMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 10u );
193
- m.clearcoatMapTransform = m.clearcoatMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 12u );
194
- m.clearcoatNormalMapTransform = m.clearcoatNormalMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 14u );
195
- m.clearcoatRoughnessMapTransform = m.clearcoatRoughnessMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 16u );
196
- m.sheenColorMapTransform = m.sheenColorMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 18u );
197
- m.sheenRoughnessMapTransform = m.sheenRoughnessMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 20u );
198
- m.iridescenceMapTransform = m.iridescenceMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 22u );
199
- m.iridescenceThicknessMapTransform = m.iridescenceThicknessMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 24u );
200
- m.specularColorMapTransform = m.specularColorMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 26u );
201
- m.specularIntensityMapTransform = m.specularIntensityMap == - 1 ? mat3( 0 ) : readTextureTransform( tex, firstTextureTransformIdx + 28u );
187
+ // mat3( 1.0 ) is an identity matrix
188
+ m.mapTransform = m.map == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx );
189
+ m.metalnessMapTransform = m.metalnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 2u );
190
+ m.roughnessMapTransform = m.roughnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 4u );
191
+ m.transmissionMapTransform = m.transmissionMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 6u );
192
+ m.emissiveMapTransform = m.emissiveMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 8u );
193
+ m.normalMapTransform = m.normalMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 10u );
194
+ m.clearcoatMapTransform = m.clearcoatMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 12u );
195
+ m.clearcoatNormalMapTransform = m.clearcoatNormalMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 14u );
196
+ m.clearcoatRoughnessMapTransform = m.clearcoatRoughnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 16u );
197
+ m.sheenColorMapTransform = m.sheenColorMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 18u );
198
+ m.sheenRoughnessMapTransform = m.sheenRoughnessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 20u );
199
+ m.iridescenceMapTransform = m.iridescenceMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 22u );
200
+ m.iridescenceThicknessMapTransform = m.iridescenceThicknessMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 24u );
201
+ m.specularColorMapTransform = m.specularColorMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 26u );
202
+ m.specularIntensityMapTransform = m.specularIntensityMap == - 1 ? mat3( 1.0 ) : readTextureTransform( tex, firstTextureTransformIdx + 28u );
202
203
 
203
204
  return m;
204
205
 
@@ -2,8 +2,9 @@ import {
2
2
  ClampToEdgeWrapping,
3
3
  Color,
4
4
  DataTexture,
5
+ DataUtils,
5
6
  EquirectangularReflectionMapping,
6
- FloatType,
7
+ HalfFloatType,
7
8
  LinearFilter,
8
9
  RepeatWrapping,
9
10
  RGBAFormat,
@@ -17,11 +18,11 @@ const _polar = new Spherical();
17
18
  const _color = new Color();
18
19
  export class ProceduralEquirectTexture extends DataTexture {
19
20
 
20
- constructor( width, height ) {
21
+ constructor( width = 512, height = 512 ) {
21
22
 
22
23
  super(
23
- new Float32Array( width * height * 4 ),
24
- width, height, RGBAFormat, FloatType, EquirectangularReflectionMapping,
24
+ new Uint16Array( width * height * 4 ),
25
+ width, height, RGBAFormat, HalfFloatType, EquirectangularReflectionMapping,
25
26
  RepeatWrapping, ClampToEdgeWrapping, LinearFilter, LinearFilter,
26
27
  );
27
28
 
@@ -53,10 +54,10 @@ export class ProceduralEquirectTexture extends DataTexture {
53
54
 
54
55
  const i = y * width + x;
55
56
  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;
57
+ data[ i4 + 0 ] = DataUtils.toHalfFloat( _color.r );
58
+ data[ i4 + 1 ] = DataUtils.toHalfFloat( _color.g );
59
+ data[ i4 + 2 ] = DataUtils.toHalfFloat( _color.b );
60
+ data[ i4 + 3 ] = DataUtils.toHalfFloat( 1.0 );
60
61
 
61
62
  }
62
63
 
@@ -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 } from 'three';
2
+ import { toHalfFloatArray } from '../utils/TextureUtils.js';
2
3
 
3
4
  function binarySearchFindClosestIndexOf( array, targetValue, offset = 0, count = array.length ) {
4
5
 
@@ -49,15 +50,15 @@ function preprocessEnvMap( envMap ) {
49
50
  let newData = data;
50
51
  if ( map.type === HalfFloatType ) {
51
52
 
52
- newData = new Float32Array( data.length );
53
+ newData = new Uint16Array( data.length );
53
54
  for ( const i in data ) {
54
55
 
55
- newData[ i ] = DataUtils.fromHalfFloat( data[ i ] );
56
+ newData[ i ] = data[ i ];
56
57
 
57
58
  }
58
59
 
59
60
  map.image.data = newData;
60
- map.type = FloatType;
61
+ map.type = HalfFloatType;
61
62
 
62
63
  }
63
64
 
@@ -98,8 +99,8 @@ export class EquirectHdrInfoUniform {
98
99
 
99
100
  // Default to a white texture and associated weights so we don't
100
101
  // just render black initially.
101
- const whiteTex = new DataTexture( new Float32Array( [ 1, 1, 1, 1 ] ), 1, 1 );
102
- whiteTex.type = FloatType;
102
+ const whiteTex = new DataTexture( toHalfFloatArray( new Float32Array( [ 1, 1, 1, 1 ] ) ), 1, 1 );
103
+ whiteTex.type = HalfFloatType;
103
104
  whiteTex.format = RGBAFormat;
104
105
  whiteTex.minFilter = LinearFilter;
105
106
  whiteTex.magFilter = LinearFilter;
@@ -110,8 +111,8 @@ export class EquirectHdrInfoUniform {
110
111
 
111
112
  // Stores a map of [0, 1] value -> cumulative importance row & pdf
112
113
  // 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;
114
+ const marginalWeights = new DataTexture( toHalfFloatArray( new Float32Array( [ 0, 1 ] ) ), 1, 2 );
115
+ marginalWeights.type = HalfFloatType;
115
116
  marginalWeights.format = RedFormat;
116
117
  marginalWeights.minFilter = LinearFilter;
117
118
  marginalWeights.magFilter = LinearFilter;
@@ -120,8 +121,8 @@ export class EquirectHdrInfoUniform {
120
121
 
121
122
  // Stores a map of [0, 1] value -> cumulative importance column & pdf
122
123
  // 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;
124
+ const conditionalWeights = new DataTexture( toHalfFloatArray( new Float32Array( [ 0, 0, 1, 1 ] ) ), 2, 2 );
125
+ conditionalWeights.type = HalfFloatType;
125
126
  conditionalWeights.format = RedFormat;
126
127
  conditionalWeights.minFilter = LinearFilter;
127
128
  conditionalWeights.magFilter = LinearFilter;
@@ -171,9 +172,9 @@ export class EquirectHdrInfoUniform {
171
172
  for ( let x = 0; x < width; x ++ ) {
172
173
 
173
174
  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 ];
175
+ const r = DataUtils.fromHalfFloat( data[ 4 * i + 0 ] );
176
+ const g = DataUtils.fromHalfFloat( data[ 4 * i + 1 ] );
177
+ const b = DataUtils.fromHalfFloat( data[ 4 * i + 2 ] );
177
178
 
178
179
  // the probability of the pixel being selected in this row is the
179
180
  // scale of the luminance relative to the rest of the pixels.
@@ -225,8 +226,8 @@ export class EquirectHdrInfoUniform {
225
226
  // the marginal and conditional data. These will be used to sample with a random number
226
227
  // to retrieve a uv value to sample in the environment map.
227
228
  // 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 );
229
+ const marginalDataArray = new Uint16Array( height );
230
+ const conditionalDataArray = new Uint16Array( width * height );
230
231
 
231
232
  // we add a half texel offset so we're sampling the center of the pixel
232
233
  for ( let i = 0; i < height; i ++ ) {
@@ -234,7 +235,7 @@ export class EquirectHdrInfoUniform {
234
235
  const dist = ( i + 1 ) / height;
235
236
  const row = binarySearchFindClosestIndexOf( cdfMarginal, dist );
236
237
 
237
- marginalDataArray[ i ] = ( row + 0.5 ) / height;
238
+ marginalDataArray[ i ] = DataUtils.toHalfFloat( ( row + 0.5 ) / height );
238
239
 
239
240
  }
240
241
 
@@ -246,7 +247,7 @@ export class EquirectHdrInfoUniform {
246
247
  const dist = ( x + 1 ) / width;
247
248
  const col = binarySearchFindClosestIndexOf( cdfConditional, dist, y * width, width );
248
249
 
249
- conditionalDataArray[ i ] = ( col + 0.5 ) / width;
250
+ conditionalDataArray[ i ] = DataUtils.toHalfFloat( ( col + 0.5 ) / width );
250
251
 
251
252
  }
252
253
 
@@ -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
 
@@ -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();
@@ -1,4 +1,4 @@
1
- import { WebGLRenderTarget, RGBAFormat, FloatType, PMREMGenerator, DataTexture, EquirectangularReflectionMapping } from 'three';
1
+ import { WebGLRenderTarget, RGBAFormat, HalfFloatType, PMREMGenerator, DataTexture, EquirectangularReflectionMapping } from 'three';
2
2
  import { FullScreenQuad } from 'three/examples/jsm/postprocessing/Pass.js';
3
3
  import { MaterialBase } from '../materials/MaterialBase.js';
4
4
  import { utilsGLSL } from '../shader/common/utils.glsl.js';
@@ -58,7 +58,7 @@ export class BlurredEnvMapGenerator {
58
58
  this.renderer = renderer;
59
59
  this.pmremGenerator = new PMREMGenerator( renderer );
60
60
  this.copyQuad = new FullScreenQuad( new PMREMCopyMaterial() );
61
- this.renderTarget = new WebGLRenderTarget( 1, 1, { type: FloatType, format: RGBAFormat } );
61
+ this.renderTarget = new WebGLRenderTarget( 1, 1, { type: HalfFloatType, format: RGBAFormat } );
62
62
 
63
63
  }
64
64
 
@@ -95,10 +95,10 @@ export class BlurredEnvMapGenerator {
95
95
  renderer.autoClear = prevClear;
96
96
 
97
97
  // read the data back
98
- const buffer = new Float32Array( width * height * 4 );
98
+ const buffer = new Uint16Array( width * height * 4 );
99
99
  renderer.readRenderTargetPixels( renderTarget, 0, 0, width, height, buffer );
100
100
 
101
- const result = new DataTexture( buffer, width, height, RGBAFormat, FloatType );
101
+ const result = new DataTexture( buffer, width, height, RGBAFormat, HalfFloatType );
102
102
  result.minFilter = texture.minFilter;
103
103
  result.magFilter = texture.magFilter;
104
104
  result.wrapS = texture.wrapS;
@@ -1,5 +1,5 @@
1
1
  import { BufferAttribute } from 'three';
2
- import { mergeBufferGeometries, mergeVertices } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
2
+ import { mergeGeometries, mergeVertices } from 'three/examples/jsm/utils/BufferGeometryUtils.js';
3
3
  export function getGroupMaterialIndicesAttribute( geometry, materials, allMaterials ) {
4
4
 
5
5
  const indexAttr = geometry.index;
@@ -176,6 +176,12 @@ export function mergeMeshes( meshes, options = {} ) {
176
176
  const geometry = options.cloneGeometry ? originalGeometry.clone() : originalGeometry;
177
177
  geometry.applyMatrix4( mesh.matrixWorld );
178
178
 
179
+ if ( mesh.matrixWorld.determinant() < 0 ) {
180
+
181
+ geometry.index.array.reverse();
182
+
183
+ }
184
+
179
185
  // ensure our geometry has common attributes
180
186
  setCommonAttributes( geometry, {
181
187
  attributes: options.attributes,
@@ -207,7 +213,7 @@ export function mergeMeshes( meshes, options = {} ) {
207
213
 
208
214
  } );
209
215
 
210
- const geometry = mergeBufferGeometries( transformedGeometry, false );
216
+ const geometry = mergeGeometries( transformedGeometry, false );
211
217
  const textures = Array.from( textureSet );
212
218
  return { geometry, materials, textures };
213
219
 
@@ -1,13 +1,15 @@
1
1
  import {
2
2
  DataTexture,
3
3
  FileLoader,
4
- FloatType,
4
+ HalfFloatType,
5
5
  LinearFilter,
6
6
  RedFormat,
7
7
  MathUtils,
8
8
  Loader,
9
9
  } from 'three';
10
10
 
11
+ import { toHalfFloatArray } from './TextureUtils.js';
12
+
11
13
  function IESLamp( text ) {
12
14
 
13
15
  const _self = this;
@@ -286,7 +288,7 @@ export class IESLoader extends Loader {
286
288
  loader.setPath( this.path );
287
289
  loader.setRequestHeader( this.requestHeader );
288
290
 
289
- const texture = new DataTexture( null, 360, 180, RedFormat, FloatType );
291
+ const texture = new DataTexture( null, 360, 180, RedFormat, HalfFloatType );
290
292
  texture.minFilter = LinearFilter;
291
293
  texture.magFilter = LinearFilter;
292
294
 
@@ -294,7 +296,7 @@ export class IESLoader extends Loader {
294
296
 
295
297
  const iesLamp = new IESLamp( text );
296
298
 
297
- texture.image.data = this._getIESValues( iesLamp );
299
+ texture.image.data = toHalfFloatArray( this._getIESValues( iesLamp ) );
298
300
  texture.needsUpdate = true;
299
301
 
300
302
  if ( onLoad !== undefined ) {
@@ -312,10 +314,10 @@ export class IESLoader extends Loader {
312
314
  parse( text ) {
313
315
 
314
316
  const iesLamp = new IESLamp( text );
315
- const texture = new DataTexture( null, 360, 180, RedFormat, FloatType );
317
+ const texture = new DataTexture( null, 360, 180, RedFormat, HalfFloatType );
316
318
  texture.minFilter = LinearFilter;
317
319
  texture.magFilter = LinearFilter;
318
- texture.image.data = this._getIESValues( iesLamp );
320
+ texture.image.data = toHalfFloatArray( this._getIESValues( iesLamp ) );
319
321
  texture.needsUpdate = true;
320
322
 
321
323
  return texture;
@@ -0,0 +1,15 @@
1
+ import { DataUtils } from 'three';
2
+
3
+
4
+ export function toHalfFloatArray( f32Array ) {
5
+
6
+ const f16Array = new Uint16Array( f32Array.length );
7
+ for ( let i = 0, n = f32Array.length; i < n; ++ i ) {
8
+
9
+ f16Array[ i ] = DataUtils.toHalfFloat( f32Array[ i ] );
10
+
11
+ }
12
+
13
+ return f16Array;
14
+
15
+ }