<!-- SOLIDIVISM · part 410 -->
just wrote

		_setViewport( cubeUVRenderTarget, x, y, 3 * outputSize, 2 * outputSize );
		renderer.setRenderTarget( cubeUVRenderTarget );
		renderer.render( ggxMesh, _flatCamera );

	}

	/**
	 * This is a two-pass Gaussian blur for a cubemap. Each pass importance-samples
	 * the Gaussian along a spiral kernel (Golden Angle), which distributes samples
	 * isotropically on the sphere (no pole artifacts).
	 *
	 * Used for initial scene blur in fromScene() method when sigma > 0.
	 *
	 * @private
	 * @param {WebGLRenderTarget} cubeUVRenderTarget
	 * @param {number} lodIn
	 * @param {number} lodOut
	 * @param {number} sigma
	 */
	_blur( cubeUVRenderTarget, lodIn, lodOut, sigma ) {

		const pingPongRenderTarget = this._pingPongRenderTarget;

		// Two passes of sigma / sqrt( 2 ) compose to a blur of sigma while squaring
		// the effective sample count. Sigmas beyond PI are visually indistinguishable
		// from a uniform blur, so clamp to keep the shader math finite.
		const blurSigma = Math.min( sigma, Math.PI ) / Math.SQRT2;

		this._blurPass( cubeUVRenderTarget, pingPongRenderTarget, lodIn, lodOut, blurSigma );
		this._blurPass( pingPongRenderTarget, cubeUVRenderTarget, lodOut, lodOut, blurSigma );

	}

	_blurPass( targetIn, targetOut, lodIn, lodOut, sigmaRadians ) {

		const renderer = this._renderer;
		const blurMaterial = this._blurMaterial;

		const blurMesh = this._lodMeshes[ lodOut ];
		blurMesh.material = blurMaterial;

		const blurUniforms = blurMaterial.uniforms;

		blurUniforms[ 'envMap' ].value = targetIn.texture;
		blurUniforms[ 'sigma' ].value = sigmaRadians;
		blurUniforms[ 'mipInt' ].value = this._lodMax - lodIn;

		const outputSize = this._sizeLods[ lodOut ];
		const x = 3 * outputSize * ( lodOut > this._lodMax - LOD_MIN ? lodOut - this._lodMax + LOD_MIN : 0 );
		const y = 4 * ( this._cubeSize - outputSize );

		_setViewport( targetOut, x, y, 3 * outputSize, 2 * outputSize );
		renderer.setRenderTarget( targetOut );
		renderer.render( blurMesh, _flatCamera );

	}

}



function _createPlanes( lodMax ) {

	const sizeLods = [];
	const lodMeshes = [];

	let lod = lodMax;

	const totalLods = lodMax - LOD_MIN + 1 + EXTRA_LODS;

	for ( let i = 0; i < totalLods; i ++ ) {

		const sizeLod = Math.pow( 2, lod );
		sizeLods.push( sizeLod );

		// UVs overshoot the face by one texel to bake the CubeUV border into the directions.
		const texelSize = 1.0 / ( sizeLod - 2 );
		const min = - texelSize;
		const max = 1 + texelSize;
		const uv1 = [ min, min, max, min, max, max, min, min, max, max, min, max ];

		const cubeFaces = 6;
		const vertices = 6;
		const positionSize = 3;

		const position = new Float32Array( positionSize * vertices * cubeFaces );
		const outputDirection = new Float32Array( positionSize * vertices * cubeFaces );

		for ( let face = 0; face < cubeFaces; face ++ ) {

			const x = ( face % 3 ) * 2 / 3 - 1;
			const y = face > 2 ? 0 : -1;
			const coordinates = [
				x, y, 0,
				x + 2 / 3, y, 0,
				x + 2 / 3, y + 1, 0,
				x, y, 0,
				x + 2 / 3, y + 1, 0,
				x, y + 1, 0
			];
			position.set( coordinates, positionSize * vertices * face );

			for ( let vertex = 0; vertex < vertices; vertex ++ ) {

				const u = uv1[ vertex * 2 ] * 2 - 1;
				const v = uv1[ vertex * 2 + 1 ] * 2 - 1;

				// RH coordinate system; PMREM face-indexing convention
				if ( face === 0 ) {

					_direction.set( 1, v, u ); // pos x

				} else if ( face === 1 ) {

					_direction.set( - u, 1, - v ); // pos y

				} else if ( face === 2 ) {

					_direction.set( - u, v, 1 ); // pos z

				} else if ( face === 3 ) {

					_direction.set( -1, v, - u ); // neg x

				} else if ( face === 4 ) {

					_direction.set( - u, -1, v ); // neg y

				} else {

					_direction.set( u, v, -1 ); // neg z

				}

				_direction.toArray( outputDirection, ( face * vertices + vertex ) * positionSize );

			}

		}

		const planes = new BufferGeometry();
		planes.setAttribute( 'position', new BufferAttribute( position, positionSize ) );
		planes.setAttribute( 'outputDirection', new BufferAttribute( outputDirection, positionSize ) );
		lodMeshes.push( new Mesh( planes, null ) );

		if ( lod > LOD_MIN ) {

			lod --;

		}

	}

	return { lodMeshes, sizeLods };

}

function _createRenderTarget( width, height, params ) {

	const cubeUVRenderTarget = new WebGLRenderTarget( width, height, params );
	cubeUVRenderTarget.texture.mapping = CubeUVReflectionMapping;
	cubeUVRenderTarget.texture.name = 'PMREM.cubeUv';
	cubeUVRenderTarget.scissorTest = true;
	return cubeUVRenderTarget;

}

function _setViewport( target, x, y, width, height ) {

	target.viewport.set( x, y, width, height );
	target.scissor.set( x, y, width, height );

}

function _getGGXShader( lodMax, width, height ) {

	const shaderMaterial = new ShaderMaterial( {

		name: 'PMREMGGXConvolution',

		defines: {
			'GGX_SAMPLES': GGX_SAMPLES,
			'CUBEUV_TEXEL_WIDTH': 1.0 / width,
			'CUBEUV_TEXEL_HEIGHT': 1.0 / height,
			'CUBEUV_MAX_MIP': `${lodMax}.0`,
		}