three
Version:
JavaScript 3D library
381 lines (215 loc) • 7.7 kB
JavaScript
import TempNode from '../core/TempNode.js';
import { default as TextureNode/*, texture*/ } from '../accessors/TextureNode.js';
import { NodeUpdateType } from '../core/constants.js';
import { nodeObject } from '../tsl/TSLBase.js';
import { uniform } from '../core/UniformNode.js';
import { viewZToOrthographicDepth, perspectiveDepthToViewZ } from './ViewportDepthNode.js';
import { HalfFloatType/*, FloatType*/ } from '../../constants.js';
import { Vector2 } from '../../math/Vector2.js';
import { DepthTexture } from '../../textures/DepthTexture.js';
import { RenderTarget } from '../../core/RenderTarget.js';
const _size = /*@__PURE__*/ new Vector2();
class PassTextureNode extends TextureNode {
static get type() {
return 'PassTextureNode';
}
constructor( passNode, texture ) {
super( texture );
this.passNode = passNode;
this.setUpdateMatrix( false );
}
setup( builder ) {
if ( builder.object.isQuadMesh ) this.passNode.build( builder );
return super.setup( builder );
}
clone() {
return new this.constructor( this.passNode, this.value );
}
}
class PassMultipleTextureNode extends PassTextureNode {
static get type() {
return 'PassMultipleTextureNode';
}
constructor( passNode, textureName, previousTexture = false ) {
super( passNode, null );
this.textureName = textureName;
this.previousTexture = previousTexture;
}
updateTexture() {
this.value = this.previousTexture ? this.passNode.getPreviousTexture( this.textureName ) : this.passNode.getTexture( this.textureName );
}
setup( builder ) {
this.updateTexture();
return super.setup( builder );
}
clone() {
return new this.constructor( this.passNode, this.textureName, this.previousTexture );
}
}
class PassNode extends TempNode {
static get type() {
return 'PassNode';
}
constructor( scope, scene, camera, options = {} ) {
super( 'vec4' );
this.scope = scope;
this.scene = scene;
this.camera = camera;
this.options = options;
this._pixelRatio = 1;
this._width = 1;
this._height = 1;
const depthTexture = new DepthTexture();
depthTexture.isRenderTargetTexture = true;
//depthTexture.type = FloatType;
depthTexture.name = 'depth';
const renderTarget = new RenderTarget( this._width * this._pixelRatio, this._height * this._pixelRatio, { type: HalfFloatType, ...options, } );
renderTarget.texture.name = 'output';
renderTarget.depthTexture = depthTexture;
this.renderTarget = renderTarget;
this.updateBeforeType = NodeUpdateType.FRAME;
this._textures = {
output: renderTarget.texture,
depth: depthTexture
};
this._textureNodes = {};
this._linearDepthNodes = {};
this._viewZNodes = {};
this._previousTextures = {};
this._previousTextureNodes = {};
this._cameraNear = uniform( 0 );
this._cameraFar = uniform( 0 );
this._mrt = null;
this.isPassNode = true;
}
setMRT( mrt ) {
this._mrt = mrt;
return this;
}
getMRT() {
return this._mrt;
}
isGlobal() {
return true;
}
getTexture( name ) {
let texture = this._textures[ name ];
if ( texture === undefined ) {
const refTexture = this.renderTarget.texture;
texture = refTexture.clone();
texture.isRenderTargetTexture = true;
texture.name = name;
this._textures[ name ] = texture;
this.renderTarget.textures.push( texture );
}
return texture;
}
getPreviousTexture( name ) {
let texture = this._previousTextures[ name ];
if ( texture === undefined ) {
texture = this.getTexture( name ).clone();
texture.isRenderTargetTexture = true;
this._previousTextures[ name ] = texture;
}
return texture;
}
toggleTexture( name ) {
const prevTexture = this._previousTextures[ name ];
if ( prevTexture !== undefined ) {
const texture = this._textures[ name ];
const index = this.renderTarget.textures.indexOf( texture );
this.renderTarget.textures[ index ] = prevTexture;
this._textures[ name ] = prevTexture;
this._previousTextures[ name ] = texture;
this._textureNodes[ name ].updateTexture();
this._previousTextureNodes[ name ].updateTexture();
}
}
getTextureNode( name = 'output' ) {
let textureNode = this._textureNodes[ name ];
if ( textureNode === undefined ) {
textureNode = nodeObject( new PassMultipleTextureNode( this, name ) );
textureNode.updateTexture();
this._textureNodes[ name ] = textureNode;
}
return textureNode;
}
getPreviousTextureNode( name = 'output' ) {
let textureNode = this._previousTextureNodes[ name ];
if ( textureNode === undefined ) {
if ( this._textureNodes[ name ] === undefined ) this.getTextureNode( name );
textureNode = nodeObject( new PassMultipleTextureNode( this, name, true ) );
textureNode.updateTexture();
this._previousTextureNodes[ name ] = textureNode;
}
return textureNode;
}
getViewZNode( name = 'depth' ) {
let viewZNode = this._viewZNodes[ name ];
if ( viewZNode === undefined ) {
const cameraNear = this._cameraNear;
const cameraFar = this._cameraFar;
this._viewZNodes[ name ] = viewZNode = perspectiveDepthToViewZ( this.getTextureNode( name ), cameraNear, cameraFar );
}
return viewZNode;
}
getLinearDepthNode( name = 'depth' ) {
let linearDepthNode = this._linearDepthNodes[ name ];
if ( linearDepthNode === undefined ) {
const cameraNear = this._cameraNear;
const cameraFar = this._cameraFar;
const viewZNode = this.getViewZNode( name );
// TODO: just if ( builder.camera.isPerspectiveCamera )
this._linearDepthNodes[ name ] = linearDepthNode = viewZToOrthographicDepth( viewZNode, cameraNear, cameraFar );
}
return linearDepthNode;
}
setup( { renderer } ) {
this.renderTarget.samples = this.options.samples === undefined ? renderer.samples : this.options.samples;
// Disable MSAA for WebGL backend for now
if ( renderer.backend.isWebGLBackend === true ) {
this.renderTarget.samples = 0;
}
this.renderTarget.depthTexture.isMultisampleRenderTargetTexture = this.renderTarget.samples > 1;
return this.scope === PassNode.COLOR ? this.getTextureNode() : this.getLinearDepthNode();
}
updateBefore( frame ) {
const { renderer } = frame;
const { scene, camera } = this;
this._pixelRatio = renderer.getPixelRatio();
const size = renderer.getSize( _size );
this.setSize( size.width, size.height );
const currentRenderTarget = renderer.getRenderTarget();
const currentMRT = renderer.getMRT();
this._cameraNear.value = camera.near;
this._cameraFar.value = camera.far;
for ( const name in this._previousTextures ) {
this.toggleTexture( name );
}
renderer.setRenderTarget( this.renderTarget );
renderer.setMRT( this._mrt );
renderer.render( scene, camera );
renderer.setRenderTarget( currentRenderTarget );
renderer.setMRT( currentMRT );
}
setSize( width, height ) {
this._width = width;
this._height = height;
const effectiveWidth = this._width * this._pixelRatio;
const effectiveHeight = this._height * this._pixelRatio;
this.renderTarget.setSize( effectiveWidth, effectiveHeight );
}
setPixelRatio( pixelRatio ) {
this._pixelRatio = pixelRatio;
this.setSize( this._width, this._height );
}
dispose() {
this.renderTarget.dispose();
}
}
PassNode.COLOR = 'color';
PassNode.DEPTH = 'depth';
export default PassNode;
export const pass = ( scene, camera, options ) => nodeObject( new PassNode( PassNode.COLOR, scene, camera, options ) );
export const passTexture = ( pass, texture ) => nodeObject( new PassTextureNode( pass, texture ) );
export const depthPass = ( scene, camera ) => nodeObject( new PassNode( PassNode.DEPTH, scene, camera ) );