Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebGPURenderer: PMREMGenerator - calculate blur weights and parameters once #29900

Open
wants to merge 7 commits into
base: dev
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
145 changes: 88 additions & 57 deletions src/renderers/common/extras/PMREMGenerator.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
import NodeMaterial from '../../../materials/nodes/NodeMaterial.js';
import { getDirection, blur } from '../../../nodes/pmrem/PMREMUtils.js';
import { equirectUV } from '../../../nodes/utils/EquirectUVNode.js';
import { uniform } from '../../../nodes/core/UniformNode.js';
import { uniformArray } from '../../../nodes/accessors/UniformArrayNode.js';
import { userData } from '../../../nodes/accessors/UserDataNode.js';
import { texture } from '../../../nodes/accessors/TextureNode.js';
import { cubeTexture } from '../../../nodes/accessors/CubeTextureNode.js';
import { float, vec3 } from '../../../nodes/tsl/TSLBase.js';
import { float, int, vec3 } from '../../../nodes/tsl/TSLBase.js';
import { uv } from '../../../nodes/accessors/UV.js';
import { attribute } from '../../../nodes/core/AttributeNode.js';

Expand Down Expand Up @@ -113,6 +112,8 @@ class PMREMGenerator {
this._cubemapMaterial = null;
this._equirectMaterial = null;
this._backgroundBox = null;
this._userDataMap = new WeakMap();
this._userData = null;

}

Expand Down Expand Up @@ -156,6 +157,8 @@ class PMREMGenerator {
_oldActiveCubeFace = this._renderer.getActiveCubeFace();
_oldActiveMipmapLevel = this._renderer.getActiveMipmapLevel();

if ( this._userData === null ) this._userData = { longitudinal: [], latitudinal: [] };

const cubeUVRenderTarget = renderTarget || this._allocateTargets();
cubeUVRenderTarget.depthBuffer = true;

Expand Down Expand Up @@ -356,6 +359,17 @@ class PMREMGenerator {

this._setSizeFromTexture( texture );

let userData = this._userDataMap.get( texture );

if ( userData === undefined ) {

userData = { longitudinal: [], latitudinal: [] };
this._userDataMap.set( texture, userData );

}

this._userData = userData;

_oldTarget = this._renderer.getRenderTarget();
_oldActiveCubeFace = this._renderer.getActiveCubeFace();
_oldActiveMipmapLevel = this._renderer.getActiveMipmapLevel();
Expand Down Expand Up @@ -559,11 +573,7 @@ class PMREMGenerator {

for ( let i = 1; i < n; i ++ ) {

const sigma = Math.sqrt( this._sigmas[ i ] * this._sigmas[ i ] - this._sigmas[ i - 1 ] * this._sigmas[ i - 1 ] );

const poleAxis = _axisDirections[ ( n - i - 1 ) % _axisDirections.length ];

this._blur( cubeUVRenderTarget, i - 1, i, sigma, poleAxis );
this._blur( cubeUVRenderTarget, i - 1, i );

}

Expand All @@ -584,7 +594,7 @@ class PMREMGenerator {
* @param {Number} sigma - The blur radius in radians.
* @param {Vector3} [poleAxis] - The pole axis.
*/
_blur( cubeUVRenderTarget, lodIn, lodOut, sigma, poleAxis ) {
_blur( cubeUVRenderTarget, lodIn, lodOut ) {

const pingPongRenderTarget = this._pingPongRenderTarget;

Expand All @@ -593,39 +603,65 @@ class PMREMGenerator {
pingPongRenderTarget,
lodIn,
lodOut,
sigma,
'latitudinal',
poleAxis );
'latitudinal' );

this._halfBlur(
pingPongRenderTarget,
cubeUVRenderTarget,
lodOut,
lodOut,
sigma,
'longitudinal',
poleAxis );
'longitudinal' );

}

_halfBlur( targetIn, targetOut, lodIn, lodOut, sigmaRadians, direction, poleAxis ) {
_halfBlur( targetIn, targetOut, lodIn, lodOut, direction ) {

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

if ( direction !== 'latitudinal' && direction !== 'longitudinal' ) {
const blurMesh = this._lodMeshes[ lodOut ];

blurMesh.material = blurMaterial;
blurMesh.userData = this._getUserDataBlur( targetIn, lodIn, lodOut, direction );

targetIn.texture.frame = ( targetIn.texture.frame || 0 ) + 1;

blurMaterial._envMap.value = targetIn.texture;

const { _lodMax } = this;

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 );
renderer.render( blurMesh, _flatCamera );

}

_getUserDataBlur( targetIn, lodIn, lodOut, direction ) {

const cache = this._userData[ direction ];

if ( cache === undefined ) {

console.error( 'blur direction must be either latitudinal or longitudinal!' );

}

// Number of standard deviations at which to cut off the discrete approximation.
const STANDARD_DEVIATIONS = 3;
const u = cache[ lodIn ];

const blurMesh = this._lodMeshes[ lodOut ];
blurMesh.material = blurMaterial;
if ( u !== undefined ) return u;

// populate data for this pass

const blurUniforms = blurMaterial.uniforms;
const { _lodMax } = this;

const sigmaRadians = Math.sqrt( this._sigmas[ lodOut ] * this._sigmas[ lodOut ] - this._sigmas[ lodOut - 1 ] * this._sigmas[ lodOut - 1 ] );
Copy link
Contributor

@elalish elalish Dec 18, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm - is this right? The cache is based on lodIn, but the sigmas we're saving are based on lodOut. Sometimes they are the same and sometimes they are not, based on how halfblur is called. And does this all continue to work when a PMREM is generated with a different size?


// Number of standard deviations at which to cut off the discrete approximation.
const STANDARD_DEVIATIONS = 3;

const pixels = this._sizeLods[ lodIn ] - 1;
const radiansPerPixel = isFinite( sigmaRadians ) ? Math.PI / ( 2 * pixels ) : 2 * Math.PI / ( 2 * MAX_SAMPLES - 1 );
Expand All @@ -640,14 +676,15 @@ class PMREMGenerator {

}

const weights = [];
const weights = new Array( MAX_SAMPLES ).fill( 0 );

let sum = 0;

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

const x = i / sigmaPixels;
const weight = Math.exp( - x * x / 2 );
weights.push( weight );
weights[ i ] = weight;

if ( i === 0 ) {

Expand All @@ -667,30 +704,23 @@ class PMREMGenerator {

}

targetIn.texture.frame = ( targetIn.texture.frame || 0 ) + 1;

blurUniforms.envMap.value = targetIn.texture;
blurUniforms.samples.value = samples;
blurUniforms.weights.array = weights;
blurUniforms.latitudinal.value = direction === 'latitudinal' ? 1 : 0;

if ( poleAxis ) {

blurUniforms.poleAxis.value = poleAxis;

}

const { _lodMax } = this;
blurUniforms.dTheta.value = radiansPerPixel;
blurUniforms.mipInt.value = _lodMax - lodIn;
const n = this._lodPlanes.length;
const poleAxis = _axisDirections[ ( n - lodOut - 1 ) % _axisDirections.length ];

const userData = {
latitudinal: direction === 'latitudinal' ? 1 : 0,
weights,
poleAxis,
outputDirection,
dTheta: radiansPerPixel,
samples,
envMap: targetIn.texture,
mipInt: _lodMax - lodIn
};

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

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

}

Expand Down Expand Up @@ -812,18 +842,19 @@ function _getMaterial( type ) {

function _getBlurShader( lodMax, width, height ) {

const weights = uniformArray( new Array( MAX_SAMPLES ).fill( 0 ) );
const poleAxis = uniform( new Vector3( 0, 1, 0 ) );
const dTheta = uniform( 0 );
const weights = userData( 'weights', 'float' );
const poleAxis = userData( 'poleAxis', 'vec3' );
const dTheta = userData( 'dTheta', 'float' );
const n = float( MAX_SAMPLES );
const latitudinal = uniform( 0 ); // false, bool
const samples = uniform( 1 ); // int
const envMap = texture( null );
const mipInt = uniform( 0 ); // int
const latitudinal = userData( 'latitudinal', 'int' ); // bool
const samples = userData( 'samples', 'int' );
const mipInt = userData( 'mipInt', 'int' );
const CUBEUV_TEXEL_WIDTH = float( 1 / width );
const CUBEUV_TEXEL_HEIGHT = float( 1 / height );
const CUBEUV_MAX_MIP = float( lodMax );

const material = _getMaterial( 'blur' );

const materialUniforms = {
n,
latitudinal,
Expand All @@ -832,16 +863,16 @@ function _getBlurShader( lodMax, width, height ) {
outputDirection,
dTheta,
samples,
envMap,
envMap: texture( null ),
mipInt,
CUBEUV_TEXEL_WIDTH,
CUBEUV_TEXEL_HEIGHT,
CUBEUV_MAX_MIP
};

const material = _getMaterial( 'blur' );
material.uniforms = materialUniforms; // TODO: Move to outside of the material
material.fragmentNode = blur( { ...materialUniforms, latitudinal: latitudinal.equal( 1 ) } );
material._envMap = materialUniforms.envMap;

material.fragmentNode = blur( { ...materialUniforms, latitudinal: latitudinal.equal( int( 1 ) ) } );

return material;

Expand Down
Loading