未验证 提交 25106099 编写于 作者: T Takahiro 提交者: GitHub

MRT Support (#16390)

* MRT Support

* Add MRT example

* Rename AttachnemtsNum to NumAttachments

* Reflect the review comments to webgl2_mrt.html

* Define texture parameter via WebGLMultiRenderTarget constructor in MRT example

* Minor clean up

* Fix setupFrameBufferTextre(). Use texture.format/type instead of renderTarget.texture's

* Use module in MRT example

* Replace var with let or const

* Replace ShaderMaterial with RawShaderMaterial for MRT example

* Fix formatting

* Fix for lint

* Add MRT example screenshot

* Reflect the review comments for WebGLMultiRenderTarget

* Rename .textures to .texture in WebGLMultiRenderTarget

* WebGLMultiRenderTarget: Remove setCount()

* WebGLMultiRenderTarget: Method chain

* WebGLMultiRenderTarget: More proper setTexture() and copy()

* WebGLCapabilities: Use extensions.has() instead of .get()

* WebGLMultiRenderTarget: Remove setTexture()

* Rename WebGLMultiRenderTarget to WebGLMultipleRenderTargets
上级 d9e89711
......@@ -309,6 +309,7 @@
"webgl2_materials_texture2darray",
"webgl2_materials_texture3d",
"webgl2_materials_texture3d_partialupdate",
"webgl2_mrt",
"webgl2_multisampled_renderbuffers",
"webgl2_rendertarget_texture2darray",
"webgl2_volume_cloud",
......
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - Multiple Render Targets</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<style>
body {
font-family: Monospace;
background-color: #000;
color: #fff;
margin: 0px;
overflow: hidden;
}
#info {
color: #fff;
position: absolute;
top: 10px;
width: 100%;
text-align: center;
display:block;
}
#info a, .button { color: #f00; font-weight: bold; text-decoration: underline; cursor: pointer }
</style>
<!-- Write to G-Buffer -->
<script id="gbuffer-vert" type="x-shader/x-vertex">
in vec3 position;
in vec3 normal;
in vec2 uv;
out vec3 vNormal;
out vec2 vUv;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
uniform mat3 normalMatrix;
void main() {
vUv = uv;
// get smooth normals
vec4 mvPosition = modelViewMatrix * vec4( position, 1.0 );
vec3 transformedNormal = normalMatrix * normal;
vNormal = normalize( transformedNormal );
gl_Position = projectionMatrix * mvPosition;
}
</script>
<script id="gbuffer-frag" type="x-shader/x-fragment">
precision highp float;
precision highp int;
layout(location = 0) out vec4 gColor;
layout(location = 1) out vec4 gNormal;
uniform sampler2D tDiffuse;
uniform vec2 repeat;
in vec3 vNormal;
in vec2 vUv;
void main() {
// write color to G-Buffer
gColor = texture( tDiffuse, vUv * repeat );
// write normals to G-Buffer
gNormal = vec4( normalize( vNormal ), 0.0 );
}
</script>
<!-- Read G-Buffer and render to screen -->
<script id="render-vert" type="x-shader/x-vertex">
in vec3 position;
in vec2 uv;
out vec2 vUv;
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;
void main() {
vUv = uv;
gl_Position = projectionMatrix * modelViewMatrix * vec4( position, 1.0 );
}
</script>
<script id="render-frag" type="x-shader/x-fragment">
precision highp float;
precision highp int;
layout(location = 0) out vec4 pc_FragColor;
in vec2 vUv;
uniform sampler2D tDiffuse;
uniform sampler2D tNormal;
void main() {
vec3 diffuse = texture( tDiffuse, vUv ).rgb;
vec3 normal = texture( tNormal, vUv ).rgb;
pc_FragColor.rgb = mix( diffuse, normal, step( 0.5, vUv.x ) );
pc_FragColor.a = 1.0;
}
</script>
</head>
<body>
<div id="info">
<a href="http://threejs.org" target="_blank">threejs</a> - WebGL - Multiple Render Targets<br/>
Renders geometry into a G-Buffer.<br/>
Visualized here is the color and normal data from the G-Buffer.<br/>
Created by <a href="http://twitter.com/mattdesl" target="_blank">@mattdesl</a>.
</div>
<script type="module">
import * as THREE from '../build/three.module.js';
import { WEBGL } from './jsm/WebGL.js';
import { OrbitControls } from './jsm/controls/OrbitControls.js';
let container;
let camera, scene, renderer, controls;
let renderTarget;
let postScene, postCamera;
init();
function init() {
if ( WEBGL.isWebGL2Available() === false ) {
document.body.appendChild( WEBGL.getWebGL2ErrorMessage() );
return;
}
container = document.createElement( 'div' );
document.body.appendChild( container );
const canvas = document.createElement( 'canvas' );
const context = canvas.getContext( 'webgl2' );
renderer = new THREE.WebGLRenderer( {
canvas: canvas,
context: context
} );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
container.appendChild( renderer.domElement );
// Create a multi render target with Float buffers
renderTarget = new THREE.WebGLMultipleRenderTargets(
window.innerWidth * window.devicePixelRatio,
window.innerHeight * window.devicePixelRatio,
2
);
for ( let i = 0, il = renderTarget.texture.length; i < il; i ++ ) {
renderTarget.texture[ i ].minFilter = THREE.NearestFilter;
renderTarget.texture[ i ].magFilter = THREE.NearestFilter;
renderTarget.texture[ i ].type = THREE.FloatType;
}
// Name our G-Buffer attachments for debugging
renderTarget.texture[ 0 ].name = 'diffuse';
renderTarget.texture[ 1 ].name = 'normal';
// Scene setup
scene = new THREE.Scene();
camera = new THREE.PerspectiveCamera( 70, window.innerWidth / window.innerHeight, 1, 10 );
camera.position.z = 4;
const diffuse = new THREE.TextureLoader().load(
'textures/brick_diffuse.jpg',
function () {
// ready to render
render();
}
);
diffuse.wrapS = diffuse.wrapT = THREE.RepeatWrapping;
scene.add( new THREE.Mesh(
new THREE.TorusKnotGeometry( 1, 0.3, 128, 64 ),
new THREE.RawShaderMaterial( {
vertexShader: document.querySelector( '#gbuffer-vert' ).textContent.trim(),
fragmentShader: document.querySelector( '#gbuffer-frag' ).textContent.trim(),
uniforms: {
tDiffuse: { value: diffuse },
repeat: { value: new THREE.Vector2( 5, 0.5 ) }
},
glslVersion: THREE.GLSL3
} )
) );
// PostProcessing setup
postScene = new THREE.Scene();
postCamera = new THREE.OrthographicCamera( - 1, 1, 1, - 1, 0, 1 );
postScene.add( new THREE.Mesh(
new THREE.PlaneGeometry( 2, 2 ),
new THREE.RawShaderMaterial( {
vertexShader: document.querySelector( '#render-vert' ).textContent.trim(),
fragmentShader: document.querySelector( '#render-frag' ).textContent.trim(),
uniforms: {
tDiffuse: { value: renderTarget.texture[ 0 ] },
tNormal: { value: renderTarget.texture[ 1 ] },
},
glslVersion: THREE.GLSL3
} )
) );
// Controls
controls = new OrbitControls( camera, renderer.domElement );
controls.addEventListener( 'change', render );
controls.enableZoom = false;
controls.screenSpacePanning = true;
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();
renderer.setSize( window.innerWidth, window.innerHeight );
const dpr = renderer.getPixelRatio();
renderTarget.setSize( window.innerWidth * dpr, window.innerHeight * dpr );
render();
}
function render() {
// render scene into target
renderer.setRenderTarget( renderTarget );
renderer.render( scene, camera );
// render post FX
renderer.setRenderTarget( null );
renderer.render( postScene, postCamera );
}
</script>
</body>
</html>
import { REVISION } from './constants.js';
export { WebGLMultipleRenderTargets } from './renderers/WebGLMultipleRenderTargets.js';
export { WebGLMultisampleRenderTarget } from './renderers/WebGLMultisampleRenderTarget.js';
export { WebGLCubeRenderTarget } from './renderers/WebGLCubeRenderTarget.js';
export { WebGLRenderTarget } from './renderers/WebGLRenderTarget.js';
......
import { WebGLRenderTarget } from './WebGLRenderTarget.js';
class WebGLMultipleRenderTargets extends WebGLRenderTarget {
constructor( width, height, count ) {
super( width, height );
const texture = this.texture;
this.texture = [];
for ( let i = 0; i < count; i ++ ) {
this.texture[ i ] = texture.clone();
}
}
setSize( width, height, depth = 1 ) {
if ( this.width !== width || this.height !== height || this.depth !== depth ) {
this.width = width;
this.height = height;
this.depth = depth;
for ( let i = 0, il = this.texture.length; i < il; i ++ ) {
this.texture[ i ].image.width = width;
this.texture[ i ].image.height = height;
this.texture[ i ].image.depth = depth;
}
this.dispose();
}
this.viewport.set( 0, 0, width, height );
this.scissor.set( 0, 0, width, height );
return this;
}
copy( source ) {
this.dispose();
this.width = source.width;
this.height = source.height;
this.depth = source.depth;
this.viewport.set( 0, 0, this.width, this.height );
this.scissor.set( 0, 0, this.width, this.height );
this.depthBuffer = source.depthBuffer;
this.stencilBuffer = source.stencilBuffer;
this.depthTexture = source.depthTexture;
this.texture.length = 0;
for ( let i = 0, il = source.texture.length; i < il; i ++ ) {
this.texture[ i ] = source.texture[ i ].clone();
}
return this;
}
}
WebGLMultipleRenderTargets.prototype.isWebGLMultipleRenderTargets = true;
export { WebGLMultipleRenderTargets };
......@@ -149,6 +149,10 @@ function WebGLRenderer( parameters ) {
const _scissor = new Vector4( 0, 0, _width, _height );
let _scissorTest = false;
//
const _currentDrawBuffers = [];
// frustum
const _frustum = new Frustum();
......@@ -275,6 +279,8 @@ function WebGLRenderer( parameters ) {
state = new WebGLState( _gl, extensions, capabilities );
_currentDrawBuffers[ 0 ] = _gl.BACK;
info = new WebGLInfo( _gl );
properties = new WebGLProperties();
textures = new WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info );
......@@ -1846,7 +1852,62 @@ function WebGLRenderer( parameters ) {
}
state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer );
if ( state.bindFramebuffer( _gl.FRAMEBUFFER, framebuffer ) && capabilities.multiRenderTarget ) {
let needsUpdate = false;
if ( renderTarget && renderTarget.isWebGLMultipleRenderTargets ) {
if ( _currentDrawBuffers.length !== renderTarget.texture.length || _currentDrawBuffers[ 0 ] !== _gl.COLOR_ATTACHMENT0 ) {
for ( let i = 0, il = renderTarget.texture.length; i < il; i ++ ) {
_currentDrawBuffers[ i ] = _gl.COLOR_ATTACHMENT0 + i;
}
_currentDrawBuffers.length = renderTarget.texture.length;
needsUpdate = true;
}
} else if ( renderTarget ) {
if ( _currentDrawBuffers.length !== 1 || _currentDrawBuffers[ 0 ] !== _gl.COLOR_ATTACHMENT0 ) {
_currentDrawBuffers[ 0 ] = _gl.COLOR_ATTACHMENT0;
_currentDrawBuffers.length = 1;
needsUpdate = true;
}
} else {
if ( _currentDrawBuffers.length !== 1 || _currentDrawBuffers[ 0 ] !== _gl.BACK ) {
_currentDrawBuffers[ 0 ] = _gl.BACK;
_currentDrawBuffers.length = 1;
needsUpdate = true;
}
}
if ( needsUpdate ) {
if ( capabilities.isWebGL2 ) {
_gl.drawBuffers( _currentDrawBuffers );
} else {
extensions.get( 'WEBGL_draw_buffers' ).drawBuffersWEBGL( _currentDrawBuffers );
}
}
}
state.viewport( _currentViewport );
state.scissor( _currentScissor );
......
......@@ -84,6 +84,7 @@ function WebGLCapabilities( gl, extensions, parameters ) {
const floatVertexTextures = vertexTextures && floatFragmentTextures;
const maxSamples = isWebGL2 ? gl.getParameter( gl.MAX_SAMPLES ) : 0;
const multiRenderTarget = isWebGL2 || extensions.has( 'WEBGL_draw_buffers' );
return {
......@@ -109,7 +110,8 @@ function WebGLCapabilities( gl, extensions, parameters ) {
floatFragmentTextures: floatFragmentTextures,
floatVertexTextures: floatVertexTextures,
maxSamples: maxSamples
maxSamples: maxSamples,
multiRenderTarget: multiRenderTarget
};
......
......@@ -465,8 +465,12 @@ function WebGLState( gl, extensions, capabilities ) {
}
return true;
}
return false;
}
function useProgram( program ) {
......
......@@ -223,8 +223,6 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
deallocateRenderTarget( renderTarget );
info.memory.textures --;
}
//
......@@ -254,6 +252,8 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
_gl.deleteTexture( textureProperties.__webglTexture );
info.memory.textures --;
}
if ( renderTarget.depthTexture ) {
......@@ -281,6 +281,26 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
}
if ( renderTarget.isWebGLMultipleRenderTargets ) {
for ( let i = 0, il = texture.length; i < il; i ++ ) {
const attachmentProperties = properties.get( texture[ i ] );
if ( attachmentProperties.__webglTexture ) {
_gl.deleteTexture( attachmentProperties.__webglTexture );
info.memory.textures --;
}
properties.remove( texture[ i ] );
}
}
properties.remove( texture );
properties.remove( renderTarget );
......@@ -833,9 +853,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
// Render targets
// Setup storage for target texture and bind it to correct framebuffer
function setupFrameBufferTexture( framebuffer, renderTarget, attachment, textureTarget ) {
const texture = renderTarget.texture;
function setupFrameBufferTexture( framebuffer, renderTarget, texture, attachment, textureTarget ) {
const glFormat = utils.convert( texture.format );
const glType = utils.convert( texture.type );
......@@ -915,7 +933,8 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
} else {
const texture = renderTarget.texture;
// Use the first texture for MRT so far
const texture = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture[ 0 ] : renderTarget.texture;
const glFormat = utils.convert( texture.format );
const glType = utils.convert( texture.type );
......@@ -1035,12 +1054,16 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
renderTarget.addEventListener( 'dispose', onRenderTargetDispose );
textureProperties.__webglTexture = _gl.createTexture();
textureProperties.__version = texture.version;
if ( renderTarget.isWebGLMultipleRenderTargets !== true ) {
info.memory.textures ++;
textureProperties.__webglTexture = _gl.createTexture();
textureProperties.__version = texture.version;
info.memory.textures ++;
}
const isCube = ( renderTarget.isWebGLCubeRenderTarget === true );
const isMultiRenderTarget = ( renderTarget.isWebGLMultipleRenderTargets === true );
const isMultisample = ( renderTarget.isWebGLMultisampleRenderTarget === true );
const isRenderTarget3D = texture.isDataTexture3D || texture.isDataTexture2DArray;
const supportsMips = isPowerOfTwo( renderTarget ) || isWebGL2;
......@@ -1071,7 +1094,31 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer();
if ( isMultisample ) {
if ( isMultiRenderTarget ) {
if ( capabilities.multiRenderTarget ) {
for ( let i = 0, il = renderTarget.texture.length; i < il; i ++ ) {
const attachmentProperties = properties.get( renderTarget.texture[ i ] );
if ( attachmentProperties.__webglTexture === undefined ) {
attachmentProperties.__webglTexture = _gl.createTexture();
info.memory.textures ++;
}
}
} else {
console.warn( 'THREE.WebGLRenderer: WebGLMultipleRenderTargets can only be used with WebGL2 or WEBGL_draw_buffers extension.' );
}
} else if ( isMultisample ) {
if ( isWebGL2 ) {
......@@ -1119,7 +1166,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
for ( let i = 0; i < 6; i ++ ) {
setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i );
setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer[ i ], renderTarget, texture, _gl.COLOR_ATTACHMENT0, _gl.TEXTURE_CUBE_MAP_POSITIVE_X + i );
}
......@@ -1131,6 +1178,29 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
state.bindTexture( _gl.TEXTURE_CUBE_MAP, null );
} else if ( isMultiRenderTarget ) {
const texture = renderTarget.texture;
for ( let i = 0, il = texture.length; i < il; i ++ ) {
const attachment = texture[ i ];
const attachmentProperties = properties.get( attachment );
state.bindTexture( _gl.TEXTURE_2D, attachmentProperties.__webglTexture );
setTextureParameters( _gl.TEXTURE_2D, attachment, supportsMips );
setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, attachment, _gl.COLOR_ATTACHMENT0 + i, _gl.TEXTURE_2D );
if ( textureNeedsGenerateMipmaps( attachment, supportsMips ) ) {
generateMipmap( _gl.TEXTURE_2D, attachment, renderTarget.width, renderTarget.height );
}
}
state.bindTexture( _gl.TEXTURE_2D, null );
} else {
let glTextureType = _gl.TEXTURE_2D;
......@@ -1154,7 +1224,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
state.bindTexture( glTextureType, textureProperties.__webglTexture );
setTextureParameters( glTextureType, texture, supportsMips );
setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, _gl.COLOR_ATTACHMENT0, glTextureType );
setupFrameBufferTexture( renderTargetProperties.__webglFramebuffer, renderTarget, texture, _gl.COLOR_ATTACHMENT0, glTextureType );
if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) {
......@@ -1178,18 +1248,24 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils,
function updateRenderTargetMipmap( renderTarget ) {
const texture = renderTarget.texture;
const supportsMips = isPowerOfTwo( renderTarget ) || isWebGL2;
if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) {
const textures = renderTarget.isWebGLMultipleRenderTargets === true ? renderTarget.texture : [ renderTarget.texture ];
for ( let i = 0, il = textures.length; i < il; i ++ ) {
const texture = textures[ i ];
const target = renderTarget.isWebGLCubeRenderTarget ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D;
const webglTexture = properties.get( texture ).__webglTexture;
if ( textureNeedsGenerateMipmaps( texture, supportsMips ) ) {
const target = renderTarget.isWebGLCubeRenderTarget ? _gl.TEXTURE_CUBE_MAP : _gl.TEXTURE_2D;
const webglTexture = properties.get( texture ).__webglTexture;
state.bindTexture( target, webglTexture );
generateMipmap( target, texture, renderTarget.width, renderTarget.height );
state.bindTexture( target, null );
state.bindTexture( target, webglTexture );
generateMipmap( target, texture, renderTarget.width, renderTarget.height );
state.bindTexture( target, null );
}
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册