Skip to content

Commit

Permalink
Merge pull request #20422 from higharc/defer-light-transform
Browse files Browse the repository at this point in the history
WebGLRenderer: Avoid creating per-camera render states.
  • Loading branch information
mrdoob committed Dec 8, 2020
2 parents 66e033f + 60defe3 commit b8433f3
Show file tree
Hide file tree
Showing 6 changed files with 133 additions and 73 deletions.
49 changes: 24 additions & 25 deletions src/renderers/WebGLRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,11 @@ function WebGLRenderer( parameters ) {
let currentRenderList = null;
let currentRenderState = null;

// render() can be called from within a callback triggered by another render.
// We track this so that the nested render call gets its state isolated from the parent render call.

const renderStateStack = [];

// public properties

this.domElement = _canvas;
Expand Down Expand Up @@ -133,7 +138,6 @@ function WebGLRenderer( parameters ) {
let _currentMaterialId = - 1;

let _currentCamera = null;
let _currentArrayCamera = null;

const _currentViewport = new Vector4();
const _currentScissor = new Vector4();
Expand Down Expand Up @@ -887,7 +891,7 @@ function WebGLRenderer( parameters ) {

this.compile = function ( scene, camera ) {

currentRenderState = renderStates.get( scene, camera );
currentRenderState = renderStates.get( scene );
currentRenderState.init();

scene.traverseVisible( function ( object ) {
Expand All @@ -906,7 +910,7 @@ function WebGLRenderer( parameters ) {

} );

currentRenderState.setupLights( camera );
currentRenderState.setupLights();

const compiled = new WeakMap();

Expand Down Expand Up @@ -1021,9 +1025,11 @@ function WebGLRenderer( parameters ) {
//
if ( scene.isScene === true ) scene.onBeforeRender( _this, scene, camera, renderTarget || _currentRenderTarget );

currentRenderState = renderStates.get( scene, camera );
currentRenderState = renderStates.get( scene, renderStateStack.length );
currentRenderState.init();

renderStateStack.push( currentRenderState );

_projScreenMatrix.multiplyMatrices( camera.projectionMatrix, camera.matrixWorldInverse );
_frustum.setFromProjectionMatrix( _projScreenMatrix );

Expand Down Expand Up @@ -1051,7 +1057,8 @@ function WebGLRenderer( parameters ) {

shadowMap.render( shadowsArray, scene, camera );

currentRenderState.setupLights( camera );
currentRenderState.setupLights();
currentRenderState.setupLightsView( camera );

if ( _clippingEnabled === true ) clipping.endShadows();

Expand Down Expand Up @@ -1105,8 +1112,18 @@ function WebGLRenderer( parameters ) {

// _gl.finish();

renderStateStack.pop();
if ( renderStateStack.length > 0 ) {

currentRenderState = renderStateStack[ renderStateStack.length - 1 ];

} else {

currentRenderState = null;

}

currentRenderList = null;
currentRenderState = null;

};

Expand Down Expand Up @@ -1250,8 +1267,6 @@ function WebGLRenderer( parameters ) {

if ( camera.isArrayCamera ) {

_currentArrayCamera = camera;

const cameras = camera.cameras;

for ( let j = 0, jl = cameras.length; j < jl; j ++ ) {
Expand All @@ -1262,7 +1277,7 @@ function WebGLRenderer( parameters ) {

state.viewport( _currentViewport.copy( camera2.viewport ) );

currentRenderState.setupLights( camera2 );
currentRenderState.setupLightsView( camera2 );

renderObject( object, scene, camera2, geometry, material, group );

Expand All @@ -1272,8 +1287,6 @@ function WebGLRenderer( parameters ) {

} else {

_currentArrayCamera = null;

renderObject( object, scene, camera, geometry, material, group );

}
Expand All @@ -1285,7 +1298,6 @@ function WebGLRenderer( parameters ) {
function renderObject( object, scene, camera, geometry, material, group ) {

object.onBeforeRender( _this, scene, camera, geometry, material, group );
currentRenderState = renderStates.get( scene, _currentArrayCamera || camera );

object.modelViewMatrix.multiplyMatrices( camera.matrixWorldInverse, object.matrixWorld );
object.normalMatrix.getNormalMatrix( object.modelViewMatrix );
Expand All @@ -1307,7 +1319,6 @@ function WebGLRenderer( parameters ) {
}

object.onAfterRender( _this, scene, camera, geometry, material, group );
currentRenderState = renderStates.get( scene, _currentArrayCamera || camera );

}

Expand Down Expand Up @@ -1766,18 +1777,6 @@ function WebGLRenderer( parameters ) {

};

this.getRenderState = function () {

return currentRenderState;

};

this.setRenderState = function ( renderState ) {

currentRenderState = renderState;

};

this.getRenderTarget = function () {

return _currentRenderTarget;
Expand Down
2 changes: 0 additions & 2 deletions src/renderers/webgl/WebGLCubeMaps.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,15 +42,13 @@ function WebGLCubeMaps( renderer ) {

const currentRenderList = renderer.getRenderList();
const currentRenderTarget = renderer.getRenderTarget();
const currentRenderState = renderer.getRenderState();

const renderTarget = new WebGLCubeRenderTarget( image.height / 2 );
renderTarget.fromEquirectangularTexture( renderer, texture );
cubemaps.set( texture, renderTarget );

renderer.setRenderTarget( currentRenderTarget );
renderer.setRenderList( currentRenderList );
renderer.setRenderState( currentRenderState );

texture.addEventListener( 'dispose', onTextureDispose );

Expand Down
3 changes: 2 additions & 1 deletion src/renderers/webgl/WebGLLights.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ export class WebGLLights {
};

get( light: any ): any;
setup( lights: any, shadows: any, camera: any ): void;
setup( lights: any ): void;
setupView( lights: any, camera: any ): void;

}
122 changes: 87 additions & 35 deletions src/renderers/webgl/WebGLLights.js
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ function WebGLLights( extensions, capabilities ) {
const matrix4 = new Matrix4();
const matrix42 = new Matrix4();

function setup( lights, shadows, camera ) {
function setup( lights ) {

let r = 0, g = 0, b = 0;

Expand All @@ -215,8 +215,6 @@ function WebGLLights( extensions, capabilities ) {
let numPointShadows = 0;
let numSpotShadows = 0;

const viewMatrix = camera.matrixWorldInverse;

lights.sort( shadowCastingLightsFirst );

for ( let i = 0, l = lights.length; i < l; i ++ ) {
Expand Down Expand Up @@ -248,10 +246,6 @@ function WebGLLights( extensions, capabilities ) {
const uniforms = cache.get( light );

uniforms.color.copy( light.color ).multiplyScalar( light.intensity );
uniforms.direction.setFromMatrixPosition( light.matrixWorld );
vector3.setFromMatrixPosition( light.target.matrixWorld );
uniforms.direction.sub( vector3 );
uniforms.direction.transformDirection( viewMatrix );

if ( light.castShadow ) {

Expand Down Expand Up @@ -281,16 +275,10 @@ function WebGLLights( extensions, capabilities ) {
const uniforms = cache.get( light );

uniforms.position.setFromMatrixPosition( light.matrixWorld );
uniforms.position.applyMatrix4( viewMatrix );

uniforms.color.copy( color ).multiplyScalar( intensity );
uniforms.distance = distance;

uniforms.direction.setFromMatrixPosition( light.matrixWorld );
vector3.setFromMatrixPosition( light.target.matrixWorld );
uniforms.direction.sub( vector3 );
uniforms.direction.transformDirection( viewMatrix );

uniforms.coneCos = Math.cos( light.angle );
uniforms.penumbraCos = Math.cos( light.angle * ( 1 - light.penumbra ) );
uniforms.decay = light.decay;
Expand Down Expand Up @@ -328,24 +316,9 @@ function WebGLLights( extensions, capabilities ) {
// (b) intensity is the brightness of the light
uniforms.color.copy( color ).multiplyScalar( intensity );

uniforms.position.setFromMatrixPosition( light.matrixWorld );
uniforms.position.applyMatrix4( viewMatrix );

// extract local rotation of light to derive width/height half vectors
matrix42.identity();
matrix4.copy( light.matrixWorld );
matrix4.premultiply( viewMatrix );
matrix42.extractRotation( matrix4 );

uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 );
uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 );

uniforms.halfWidth.applyMatrix4( matrix42 );
uniforms.halfHeight.applyMatrix4( matrix42 );

// TODO (abelnation): RectAreaLight distance?
// uniforms.distance = distance;

state.rectArea[ rectAreaLength ] = uniforms;

rectAreaLength ++;
Expand All @@ -354,9 +327,6 @@ function WebGLLights( extensions, capabilities ) {

const uniforms = cache.get( light );

uniforms.position.setFromMatrixPosition( light.matrixWorld );
uniforms.position.applyMatrix4( viewMatrix );

uniforms.color.copy( light.color ).multiplyScalar( light.intensity );
uniforms.distance = light.distance;
uniforms.decay = light.decay;
Expand Down Expand Up @@ -390,10 +360,6 @@ function WebGLLights( extensions, capabilities ) {

const uniforms = cache.get( light );

uniforms.direction.setFromMatrixPosition( light.matrixWorld );
uniforms.direction.transformDirection( viewMatrix );
uniforms.direction.normalize();

uniforms.skyColor.copy( light.color ).multiplyScalar( intensity );
uniforms.groundColor.copy( light.groundColor ).multiplyScalar( intensity );

Expand Down Expand Up @@ -485,8 +451,94 @@ function WebGLLights( extensions, capabilities ) {

}

function setupView( lights, camera ) {

let directionalLength = 0;
let pointLength = 0;
let spotLength = 0;
let rectAreaLength = 0;
let hemiLength = 0;

const viewMatrix = camera.matrixWorldInverse;

for ( let i = 0, l = lights.length; i < l; i ++ ) {

const light = lights[ i ];

if ( light.isDirectionalLight ) {

const uniforms = state.directional[ directionalLength ];

uniforms.direction.setFromMatrixPosition( light.matrixWorld );
vector3.setFromMatrixPosition( light.target.matrixWorld );
uniforms.direction.sub( vector3 );
uniforms.direction.transformDirection( viewMatrix );

directionalLength ++;

} else if ( light.isSpotLight ) {

const uniforms = state.spot[ spotLength ];

uniforms.position.setFromMatrixPosition( light.matrixWorld );
uniforms.position.applyMatrix4( viewMatrix );

uniforms.direction.setFromMatrixPosition( light.matrixWorld );
vector3.setFromMatrixPosition( light.target.matrixWorld );
uniforms.direction.sub( vector3 );
uniforms.direction.transformDirection( viewMatrix );

spotLength ++;

} else if ( light.isRectAreaLight ) {

const uniforms = state.rectArea[ rectAreaLength ];

uniforms.position.setFromMatrixPosition( light.matrixWorld );
uniforms.position.applyMatrix4( viewMatrix );

// extract local rotation of light to derive width/height half vectors
matrix42.identity();
matrix4.copy( light.matrixWorld );
matrix4.premultiply( viewMatrix );
matrix42.extractRotation( matrix4 );

uniforms.halfWidth.set( light.width * 0.5, 0.0, 0.0 );
uniforms.halfHeight.set( 0.0, light.height * 0.5, 0.0 );

uniforms.halfWidth.applyMatrix4( matrix42 );
uniforms.halfHeight.applyMatrix4( matrix42 );

rectAreaLength ++;

} else if ( light.isPointLight ) {

const uniforms = state.point[ pointLength ];

uniforms.position.setFromMatrixPosition( light.matrixWorld );
uniforms.position.applyMatrix4( viewMatrix );

pointLength ++;

} else if ( light.isHemisphereLight ) {

const uniforms = state.hemi[ hemiLength ];

uniforms.direction.setFromMatrixPosition( light.matrixWorld );
uniforms.direction.transformDirection( viewMatrix );
uniforms.direction.normalize();

hemiLength ++;

}

}

}

return {
setup: setup,
setupView: setupView,
state: state
};

Expand Down
7 changes: 5 additions & 2 deletions src/renderers/webgl/WebGLRenderStates.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ interface WebGLRenderState {
shadowsArray: Light[];
lights: WebGLLights;
};
setupLights( camera: Camera ): void;
setupLights(): void;
setupLightsView( camera: Camera ): void;
pushLight( light: Light ): void;
pushShadow( shadowLight: Light ): void;

Expand All @@ -24,7 +25,9 @@ export class WebGLRenderStates {

constructor( extensions: WebGLExtensions, capabilities: WebGLCapabilities );

get( scene: Scene, camera: Camera ): WebGLRenderState;
// renderCallDepth indexes start from 0.
get( scene: Scene, renderCallDepth?: number ): WebGLRenderState;

dispose(): void;

}
Loading

0 comments on commit b8433f3

Please sign in to comment.