|
@@ -25,8 +25,6 @@ import { BoxGeometry } from '../geometries/BoxGeometry.js';
|
|
|
import { BackSide } from '../constants.js';
|
|
|
|
|
|
const LOD_MIN = 4;
|
|
|
-const LOD_MAX = 8;
|
|
|
-const SIZE_MAX = Math.pow( 2, LOD_MAX );
|
|
|
|
|
|
// The standard deviations (radians) associated with the extra mips. These are
|
|
|
// chosen to approximate a Trowbridge-Reitz distribution function times the
|
|
@@ -34,14 +32,11 @@ const SIZE_MAX = Math.pow( 2, LOD_MAX );
|
|
|
// variance #defines in cube_uv_reflection_fragment.glsl.js.
|
|
|
const EXTRA_LOD_SIGMA = [ 0.125, 0.215, 0.35, 0.446, 0.526, 0.582 ];
|
|
|
|
|
|
-const TOTAL_LODS = LOD_MAX - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length;
|
|
|
-
|
|
|
// The maximum length of the blur for loop. Smaller sigmas will use fewer
|
|
|
// samples and exit early, but not recompile the shader.
|
|
|
const MAX_SAMPLES = 20;
|
|
|
|
|
|
const _flatCamera = /*@__PURE__*/ new OrthographicCamera();
|
|
|
-const { _lodPlanes, _sizeLods, _sigmas } = /*@__PURE__*/ _createPlanes();
|
|
|
const _clearColor = /*@__PURE__*/ new Color();
|
|
|
let _oldTarget = null;
|
|
|
|
|
@@ -85,7 +80,13 @@ class PMREMGenerator {
|
|
|
this._renderer = renderer;
|
|
|
this._pingPongRenderTarget = null;
|
|
|
|
|
|
- this._blurMaterial = _getBlurShader( MAX_SAMPLES );
|
|
|
+ this._lodMax = 0;
|
|
|
+ this._cubeSize = 0;
|
|
|
+ this._lodPlanes = [];
|
|
|
+ this._sizeLods = [];
|
|
|
+ this._sigmas = [];
|
|
|
+
|
|
|
+ this._blurMaterial = null;
|
|
|
this._equirectShader = null;
|
|
|
this._cubemapShader = null;
|
|
|
|
|
@@ -103,7 +104,10 @@ class PMREMGenerator {
|
|
|
fromScene( scene, sigma = 0, near = 0.1, far = 100 ) {
|
|
|
|
|
|
_oldTarget = this._renderer.getRenderTarget();
|
|
|
+
|
|
|
+ this._setSize( 256 );
|
|
|
const cubeUVRenderTarget = this._allocateTargets();
|
|
|
+ cubeUVRenderTarget.depthBuffer = true;
|
|
|
|
|
|
this._sceneToCubeUV( scene, near, far, cubeUVRenderTarget );
|
|
|
if ( sigma > 0 ) {
|
|
@@ -178,22 +182,35 @@ class PMREMGenerator {
|
|
|
*/
|
|
|
dispose() {
|
|
|
|
|
|
- this._blurMaterial.dispose();
|
|
|
-
|
|
|
- if ( this._pingPongRenderTarget !== null ) this._pingPongRenderTarget.dispose();
|
|
|
+ this._dispose();
|
|
|
|
|
|
if ( this._cubemapShader !== null ) this._cubemapShader.dispose();
|
|
|
if ( this._equirectShader !== null ) this._equirectShader.dispose();
|
|
|
|
|
|
- for ( let i = 0; i < _lodPlanes.length; i ++ ) {
|
|
|
+ }
|
|
|
|
|
|
- _lodPlanes[ i ].dispose();
|
|
|
+ // private interface
|
|
|
|
|
|
- }
|
|
|
+ _setSize( cubeSize ) {
|
|
|
+
|
|
|
+ this._lodMax = Math.floor( Math.log2( cubeSize ) );
|
|
|
+ this._cubeSize = Math.pow( 2, this._lodMax );
|
|
|
|
|
|
}
|
|
|
|
|
|
- // private interface
|
|
|
+ _dispose() {
|
|
|
+
|
|
|
+ this._blurMaterial.dispose();
|
|
|
+
|
|
|
+ if ( this._pingPongRenderTarget !== null ) this._pingPongRenderTarget.dispose();
|
|
|
+
|
|
|
+ for ( let i = 0; i < this._lodPlanes.length; i ++ ) {
|
|
|
+
|
|
|
+ this._lodPlanes[ i ].dispose();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
_cleanup( outputTarget ) {
|
|
|
|
|
@@ -205,8 +222,18 @@ class PMREMGenerator {
|
|
|
|
|
|
_fromTexture( texture, renderTarget ) {
|
|
|
|
|
|
+ if ( texture.mapping === CubeReflectionMapping || texture.mapping === CubeRefractionMapping ) {
|
|
|
+
|
|
|
+ this._setSize( texture.image.length === 0 ? 16 : texture.image[ 0 ].width ?? texture.image[ 0 ].image.width );
|
|
|
+
|
|
|
+ } else { // Equirectangular
|
|
|
+
|
|
|
+ this._setSize( texture.image.width / 4 ?? 256 );
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
_oldTarget = this._renderer.getRenderTarget();
|
|
|
- const cubeUVRenderTarget = renderTarget || this._allocateTargets( texture );
|
|
|
+ const cubeUVRenderTarget = renderTarget || this._allocateTargets();
|
|
|
this._textureToCubeUV( texture, cubeUVRenderTarget );
|
|
|
this._applyPMREM( cubeUVRenderTarget );
|
|
|
this._cleanup( cubeUVRenderTarget );
|
|
@@ -215,7 +242,10 @@ class PMREMGenerator {
|
|
|
|
|
|
}
|
|
|
|
|
|
- _allocateTargets( texture ) { // warning: null texture is valid
|
|
|
+ _allocateTargets() {
|
|
|
+
|
|
|
+ const width = 3 * Math.max( this._cubeSize, 16 * 7 );
|
|
|
+ const height = 4 * this._cubeSize - 32;
|
|
|
|
|
|
const params = {
|
|
|
magFilter: LinearFilter,
|
|
@@ -227,12 +257,23 @@ class PMREMGenerator {
|
|
|
depthBuffer: false
|
|
|
};
|
|
|
|
|
|
- const cubeUVRenderTarget = _createRenderTarget( params );
|
|
|
- cubeUVRenderTarget.depthBuffer = texture ? false : true;
|
|
|
+ const cubeUVRenderTarget = _createRenderTarget( width, height, params );
|
|
|
+ cubeUVRenderTarget.texture.image = { width, height };
|
|
|
+
|
|
|
+ if ( this._pingPongRenderTarget === null || this._pingPongRenderTarget.width !== width ) {
|
|
|
|
|
|
- if ( this._pingPongRenderTarget === null ) {
|
|
|
+ if ( this._pingPongRenderTarget !== null ) {
|
|
|
|
|
|
- this._pingPongRenderTarget = _createRenderTarget( params );
|
|
|
+ this._dispose();
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ this._pingPongRenderTarget = _createRenderTarget( width, height, params );
|
|
|
+
|
|
|
+ const { _lodMax } = this;
|
|
|
+ ( { sizeLods: this._sizeLods, lodPlanes: this._lodPlanes, sigmas: this._sigmas } = _createPlanes( _lodMax ) );
|
|
|
+
|
|
|
+ this._blurMaterial = _getBlurShader( _lodMax, width, height );
|
|
|
|
|
|
}
|
|
|
|
|
@@ -242,7 +283,7 @@ class PMREMGenerator {
|
|
|
|
|
|
_compileMaterial( material ) {
|
|
|
|
|
|
- const tmpMesh = new Mesh( _lodPlanes[ 0 ], material );
|
|
|
+ const tmpMesh = new Mesh( this._lodPlanes[ 0 ], material );
|
|
|
this._renderer.compile( tmpMesh, _flatCamera );
|
|
|
|
|
|
}
|
|
@@ -312,8 +353,8 @@ class PMREMGenerator {
|
|
|
|
|
|
}
|
|
|
|
|
|
- _setViewport( cubeUVRenderTarget,
|
|
|
- col * SIZE_MAX, i > 2 ? SIZE_MAX : 0, SIZE_MAX, SIZE_MAX );
|
|
|
+ const size = this._cubeSize;
|
|
|
+ _setViewport( cubeUVRenderTarget, col * size, i > 2 ? size : 0, size, size );
|
|
|
renderer.setRenderTarget( cubeUVRenderTarget );
|
|
|
|
|
|
if ( useSolidColor ) {
|
|
@@ -362,7 +403,7 @@ class PMREMGenerator {
|
|
|
}
|
|
|
|
|
|
const material = isCubeTexture ? this._cubemapShader : this._equirectShader;
|
|
|
- const mesh = new Mesh( _lodPlanes[ 0 ], material );
|
|
|
+ const mesh = new Mesh( this._lodPlanes[ 0 ], material );
|
|
|
|
|
|
const uniforms = material.uniforms;
|
|
|
|
|
@@ -374,7 +415,8 @@ class PMREMGenerator {
|
|
|
|
|
|
}
|
|
|
|
|
|
- _setViewport( cubeUVRenderTarget, 0, 0, 3 * SIZE_MAX, 2 * SIZE_MAX );
|
|
|
+ const size = this._cubeSize;
|
|
|
+ _setViewport( cubeUVRenderTarget, 0, 0, 3 * size, 2 * size );
|
|
|
|
|
|
renderer.setRenderTarget( cubeUVRenderTarget );
|
|
|
renderer.render( mesh, _flatCamera );
|
|
@@ -387,9 +429,9 @@ class PMREMGenerator {
|
|
|
const autoClear = renderer.autoClear;
|
|
|
renderer.autoClear = false;
|
|
|
|
|
|
- for ( let i = 1; i < TOTAL_LODS; i ++ ) {
|
|
|
+ for ( let i = 1; i < this._lodPlanes.length; i ++ ) {
|
|
|
|
|
|
- const sigma = Math.sqrt( _sigmas[ i ] * _sigmas[ i ] - _sigmas[ i - 1 ] * _sigmas[ i - 1 ] );
|
|
|
+ const sigma = Math.sqrt( this._sigmas[ i ] * this._sigmas[ i ] - this._sigmas[ i - 1 ] * this._sigmas[ i - 1 ] );
|
|
|
|
|
|
const poleAxis = _axisDirections[ ( i - 1 ) % _axisDirections.length ];
|
|
|
|
|
@@ -447,10 +489,10 @@ class PMREMGenerator {
|
|
|
// Number of standard deviations at which to cut off the discrete approximation.
|
|
|
const STANDARD_DEVIATIONS = 3;
|
|
|
|
|
|
- const blurMesh = new Mesh( _lodPlanes[ lodOut ], blurMaterial );
|
|
|
+ const blurMesh = new Mesh( this._lodPlanes[ lodOut ], blurMaterial );
|
|
|
const blurUniforms = blurMaterial.uniforms;
|
|
|
|
|
|
- const pixels = _sizeLods[ lodIn ] - 1;
|
|
|
+ const pixels = this._sizeLods[ lodIn ] - 1;
|
|
|
const radiansPerPixel = isFinite( sigmaRadians ) ? Math.PI / ( 2 * pixels ) : 2 * Math.PI / ( 2 * MAX_SAMPLES - 1 );
|
|
|
const sigmaPixels = sigmaRadians / radiansPerPixel;
|
|
|
const samples = isFinite( sigmaRadians ) ? 1 + Math.floor( STANDARD_DEVIATIONS * sigmaPixels ) : MAX_SAMPLES;
|
|
@@ -501,12 +543,13 @@ class PMREMGenerator {
|
|
|
|
|
|
}
|
|
|
|
|
|
+ const { _lodMax } = this;
|
|
|
blurUniforms[ 'dTheta' ].value = radiansPerPixel;
|
|
|
- blurUniforms[ 'mipInt' ].value = LOD_MAX - lodIn;
|
|
|
+ blurUniforms[ 'mipInt' ].value = _lodMax - lodIn;
|
|
|
|
|
|
- const outputSize = _sizeLods[ lodOut ];
|
|
|
- const x = 3 * Math.max( 0, SIZE_MAX - 2 * outputSize );
|
|
|
- const y = ( lodOut === 0 ? 0 : 2 * SIZE_MAX ) + 2 * outputSize * ( lodOut > LOD_MAX - LOD_MIN ? lodOut - LOD_MAX + LOD_MIN : 0 );
|
|
|
+ const outputSize = this._sizeLods[ lodOut ];
|
|
|
+ const x = 3 * outputSize * ( lodOut > _lodMax - LOD_MIN ? lodOut - _lodMax + LOD_MIN : 0 );
|
|
|
+ const y = 4 * ( this._cubeSize - outputSize );
|
|
|
|
|
|
_setViewport( targetOut, x, y, 3 * outputSize, 2 * outputSize );
|
|
|
renderer.setRenderTarget( targetOut );
|
|
@@ -516,23 +559,27 @@ class PMREMGenerator {
|
|
|
|
|
|
}
|
|
|
|
|
|
-function _createPlanes() {
|
|
|
|
|
|
- const _lodPlanes = [];
|
|
|
- const _sizeLods = [];
|
|
|
- const _sigmas = [];
|
|
|
|
|
|
- let lod = LOD_MAX;
|
|
|
+function _createPlanes( lodMax ) {
|
|
|
|
|
|
- for ( let i = 0; i < TOTAL_LODS; i ++ ) {
|
|
|
+ const lodPlanes = [];
|
|
|
+ const sizeLods = [];
|
|
|
+ const sigmas = [];
|
|
|
+
|
|
|
+ let lod = lodMax;
|
|
|
+
|
|
|
+ const totalLods = lodMax - LOD_MIN + 1 + EXTRA_LOD_SIGMA.length;
|
|
|
+
|
|
|
+ for ( let i = 0; i < totalLods; i ++ ) {
|
|
|
|
|
|
const sizeLod = Math.pow( 2, lod );
|
|
|
- _sizeLods.push( sizeLod );
|
|
|
+ sizeLods.push( sizeLod );
|
|
|
let sigma = 1.0 / sizeLod;
|
|
|
|
|
|
- if ( i > LOD_MAX - LOD_MIN ) {
|
|
|
+ if ( i > lodMax - LOD_MIN ) {
|
|
|
|
|
|
- sigma = EXTRA_LOD_SIGMA[ i - LOD_MAX + LOD_MIN - 1 ];
|
|
|
+ sigma = EXTRA_LOD_SIGMA[ i - lodMax + LOD_MIN - 1 ];
|
|
|
|
|
|
} else if ( i === 0 ) {
|
|
|
|
|
@@ -540,7 +587,7 @@ function _createPlanes() {
|
|
|
|
|
|
}
|
|
|
|
|
|
- _sigmas.push( sigma );
|
|
|
+ sigmas.push( sigma );
|
|
|
|
|
|
const texelSize = 1.0 / ( sizeLod - 1 );
|
|
|
const min = - texelSize / 2;
|
|
@@ -580,7 +627,7 @@ function _createPlanes() {
|
|
|
planes.setAttribute( 'position', new BufferAttribute( position, positionSize ) );
|
|
|
planes.setAttribute( 'uv', new BufferAttribute( uv, uvSize ) );
|
|
|
planes.setAttribute( 'faceIndex', new BufferAttribute( faceIndex, faceIndexSize ) );
|
|
|
- _lodPlanes.push( planes );
|
|
|
+ lodPlanes.push( planes );
|
|
|
|
|
|
if ( lod > LOD_MIN ) {
|
|
|
|
|
@@ -590,13 +637,13 @@ function _createPlanes() {
|
|
|
|
|
|
}
|
|
|
|
|
|
- return { _lodPlanes, _sizeLods, _sigmas };
|
|
|
+ return { lodPlanes, sizeLods, sigmas };
|
|
|
|
|
|
}
|
|
|
|
|
|
-function _createRenderTarget( params ) {
|
|
|
+function _createRenderTarget( width, height, params ) {
|
|
|
|
|
|
- const cubeUVRenderTarget = new WebGLRenderTarget( 3 * SIZE_MAX, 3 * SIZE_MAX, params );
|
|
|
+ const cubeUVRenderTarget = new WebGLRenderTarget( width, height, params );
|
|
|
cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping;
|
|
|
cubeUVRenderTarget.texture.name = 'PMREM.cubeUv';
|
|
|
cubeUVRenderTarget.scissorTest = true;
|
|
@@ -611,15 +658,20 @@ function _setViewport( target, x, y, width, height ) {
|
|
|
|
|
|
}
|
|
|
|
|
|
-function _getBlurShader( maxSamples ) {
|
|
|
+function _getBlurShader( lodMax, width, height ) {
|
|
|
|
|
|
- const weights = new Float32Array( maxSamples );
|
|
|
+ const weights = new Float32Array( MAX_SAMPLES );
|
|
|
const poleAxis = new Vector3( 0, 1, 0 );
|
|
|
const shaderMaterial = new RawShaderMaterial( {
|
|
|
|
|
|
name: 'SphericalGaussianBlur',
|
|
|
|
|
|
- defines: { 'n': maxSamples },
|
|
|
+ defines: {
|
|
|
+ 'n': MAX_SAMPLES,
|
|
|
+ 'CUBEUV_TEXEL_WIDTH': 1.0 / width,
|
|
|
+ 'CUBEUV_TEXEL_HEIGHT': 1.0 / height,
|
|
|
+ 'CUBEUV_MAX_MIP': `${lodMax}.0`,
|
|
|
+ },
|
|
|
|
|
|
uniforms: {
|
|
|
'envMap': { value: null },
|