|
|
@@ -8,16 +8,13 @@ import {
|
|
|
NeutralToneMapping,
|
|
|
CustomToneMapping,
|
|
|
SRGBTransfer,
|
|
|
- HalfFloatType,
|
|
|
- DepthStencilFormat,
|
|
|
- UnsignedInt248Type
|
|
|
+ HalfFloatType
|
|
|
} from '../../constants.js';
|
|
|
import { BufferGeometry } from '../../core/BufferGeometry.js';
|
|
|
import { Float32BufferAttribute } from '../../core/BufferAttribute.js';
|
|
|
import { RawShaderMaterial } from '../../materials/RawShaderMaterial.js';
|
|
|
import { Mesh } from '../../objects/Mesh.js';
|
|
|
import { OrthographicCamera } from '../../cameras/OrthographicCamera.js';
|
|
|
-import { DepthTexture } from '../../textures/DepthTexture.js';
|
|
|
import { WebGLRenderTarget } from '../WebGLRenderTarget.js';
|
|
|
import { ColorManagement } from '../../math/ColorManagement.js';
|
|
|
|
|
|
@@ -33,37 +30,21 @@ const toneMappingMap = {
|
|
|
|
|
|
function WebGLOutput( type, width, height, antialias, depth, stencil ) {
|
|
|
|
|
|
- // make sure depth texture matches renderer settings
|
|
|
-
|
|
|
- let depthTexture;
|
|
|
-
|
|
|
- if ( depth === true ) {
|
|
|
-
|
|
|
- depthTexture = new DepthTexture( width, height );
|
|
|
-
|
|
|
- if ( stencil === true ) {
|
|
|
-
|
|
|
- depthTexture.format = DepthStencilFormat;
|
|
|
- depthTexture.type = UnsignedInt248Type;
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- // render targets for scene and post-processing
|
|
|
- const targetA = new WebGLRenderTarget( width, height, {
|
|
|
+ // render target for the scene pass (potentially multisampled)
|
|
|
+ const targetScene = new WebGLRenderTarget( width, height, {
|
|
|
type: type,
|
|
|
depthBuffer: depth,
|
|
|
stencilBuffer: stencil,
|
|
|
samples: antialias ? 4 : 0,
|
|
|
- depthTexture: depthTexture
|
|
|
+ storeMultisampledDepthBuffer: false,
|
|
|
+ storeMultisampledStencilBuffer: false,
|
|
|
+ resolveDepthBuffer: false,
|
|
|
+ resolveStencilBuffer: false
|
|
|
} );
|
|
|
|
|
|
- const targetB = new WebGLRenderTarget( width, height, {
|
|
|
- type: HalfFloatType,
|
|
|
- depthBuffer: false,
|
|
|
- stencilBuffer: false
|
|
|
- } );
|
|
|
+ // single-sampled ping-pong buffers for post-processing effects, allocated on demand
|
|
|
+ let targetA = null;
|
|
|
+ let targetB = null;
|
|
|
|
|
|
// create fullscreen triangle geometry
|
|
|
const geometry = new BufferGeometry();
|
|
|
@@ -140,8 +121,10 @@ function WebGLOutput( type, width, height, antialias, depth, stencil ) {
|
|
|
|
|
|
this.setSize = function ( width, height ) {
|
|
|
|
|
|
- targetA.setSize( width, height );
|
|
|
- targetB.setSize( width, height );
|
|
|
+ targetScene.setSize( width, height );
|
|
|
+
|
|
|
+ if ( targetA !== null ) targetA.setSize( width, height );
|
|
|
+ if ( targetB !== null ) targetB.setSize( width, height );
|
|
|
|
|
|
for ( let i = 0; i < _effects.length; i ++ ) {
|
|
|
|
|
|
@@ -157,8 +140,15 @@ function WebGLOutput( type, width, height, antialias, depth, stencil ) {
|
|
|
_effects = effects;
|
|
|
_hasRenderPass = _effects.length > 0 && _effects[ 0 ].isRenderPass === true;
|
|
|
|
|
|
- const width = targetA.width;
|
|
|
- const height = targetA.height;
|
|
|
+ const width = targetScene.width;
|
|
|
+ const height = targetScene.height;
|
|
|
+
|
|
|
+ if ( _effects.length > 0 && targetA === null ) {
|
|
|
+
|
|
|
+ targetA = new WebGLRenderTarget( width, height, { type: HalfFloatType, depthBuffer: false, stencilBuffer: false } );
|
|
|
+ targetB = new WebGLRenderTarget( width, height, { type: HalfFloatType, depthBuffer: false, stencilBuffer: false } );
|
|
|
+
|
|
|
+ }
|
|
|
|
|
|
for ( let i = 0; i < _effects.length; i ++ ) {
|
|
|
|
|
|
@@ -184,7 +174,7 @@ function WebGLOutput( type, width, height, antialias, depth, stencil ) {
|
|
|
const width = renderTarget.width;
|
|
|
const height = renderTarget.height;
|
|
|
|
|
|
- if ( targetA.width !== width || targetA.height !== height ) {
|
|
|
+ if ( targetScene.width !== width || targetScene.height !== height ) {
|
|
|
|
|
|
this.setSize( width, height );
|
|
|
|
|
|
@@ -192,10 +182,10 @@ function WebGLOutput( type, width, height, antialias, depth, stencil ) {
|
|
|
|
|
|
}
|
|
|
|
|
|
- // if first effect is a RenderPass, it will set its own render target
|
|
|
+ // if the first effect is a RenderPass, it renders the scene itself (see end())
|
|
|
if ( _hasRenderPass === false ) {
|
|
|
|
|
|
- renderer.setRenderTarget( targetA );
|
|
|
+ renderer.setRenderTarget( targetScene );
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -220,9 +210,10 @@ function WebGLOutput( type, width, height, antialias, depth, stencil ) {
|
|
|
|
|
|
_isCompositing = true;
|
|
|
|
|
|
- // run post-processing effects
|
|
|
- let readBuffer = targetA;
|
|
|
- let writeBuffer = targetB;
|
|
|
+ // run post-processing effects - the scene lives in the multisampled targetScene,
|
|
|
+ // effects ping-pong between the single-sampled targetA and targetB
|
|
|
+ let readBuffer = targetScene;
|
|
|
+ let writeBuffer = targetA;
|
|
|
|
|
|
for ( let i = 0; i < _effects.length; i ++ ) {
|
|
|
|
|
|
@@ -234,9 +225,9 @@ function WebGLOutput( type, width, height, antialias, depth, stencil ) {
|
|
|
|
|
|
if ( effect.needsSwap !== false ) {
|
|
|
|
|
|
- const temp = readBuffer;
|
|
|
+ // keep targetScene out of the rotation so only the scene pass is multisampled
|
|
|
readBuffer = writeBuffer;
|
|
|
- writeBuffer = temp;
|
|
|
+ writeBuffer = ( writeBuffer === targetA ) ? targetB : targetA;
|
|
|
|
|
|
}
|
|
|
|
|
|
@@ -277,9 +268,11 @@ function WebGLOutput( type, width, height, antialias, depth, stencil ) {
|
|
|
|
|
|
this.dispose = function () {
|
|
|
|
|
|
- if ( targetA.depthTexture ) targetA.depthTexture.dispose();
|
|
|
- targetA.dispose();
|
|
|
- targetB.dispose();
|
|
|
+ targetScene.dispose();
|
|
|
+
|
|
|
+ if ( targetA !== null ) targetA.dispose();
|
|
|
+ if ( targetB !== null ) targetB.dispose();
|
|
|
+
|
|
|
geometry.dispose();
|
|
|
material.dispose();
|
|
|
|