From 3ef857bb14d9da68811ec5c6ff2238f2644ec841 Mon Sep 17 00:00:00 2001 From: Mugen87 Date: Mon, 7 Jan 2019 20:29:58 +0100 Subject: [PATCH] WebGL2: Added Support for Multisampled Renderbuffers. --- .../WebGLMultisampleRenderTarget.html | 48 +++++ .../WebGLMultisampleRenderTarget.html | 46 +++++ docs/list.js | 2 + examples/files.js | 1 + .../webgl2_multisampled_renderbuffers.html | 189 ++++++++++++++++++ rollup.config.js | 10 +- src/Three.js | 1 + src/renderers/WebGLMultisampleRenderTarget.js | 35 ++++ src/renderers/WebGLRenderer.js | 12 +- src/renderers/webgl/WebGLCapabilities.js | 6 +- src/renderers/webgl/WebGLTextures.js | 104 +++++++++- 11 files changed, 444 insertions(+), 10 deletions(-) create mode 100644 docs/api/en/renderers/WebGLMultisampleRenderTarget.html create mode 100644 docs/api/zh/renderers/WebGLMultisampleRenderTarget.html create mode 100644 examples/webgl2_multisampled_renderbuffers.html create mode 100644 src/renderers/WebGLMultisampleRenderTarget.js diff --git a/docs/api/en/renderers/WebGLMultisampleRenderTarget.html b/docs/api/en/renderers/WebGLMultisampleRenderTarget.html new file mode 100644 index 0000000000..be52eed4f4 --- /dev/null +++ b/docs/api/en/renderers/WebGLMultisampleRenderTarget.html @@ -0,0 +1,48 @@ + + + + + + + + + + +

[name]

+ +

+ A special render target that can be used to utilize multi-sampled renderbuffers. +

+ + +

Constructor

+ + +

[name]([param:Number width], [param:Number height], [param:Object options])

+ +

+ [page:Float width] - The width of the render target.
+ [page:Float height] - The height of the render target.
+ [page:Object options] - (optional) object that holds texture parameters for an auto-generated target + texture and depthBuffer/stencilBuffer booleans. +

+ +

Properties

+ +

[property:number samples]

+

+ Specifies the number of samples to be used for the renderbuffer storage. However, the maximum supported + size for multisampling is platform dependent and defined via *gl.MAX_SAMPLES*. +

+ +

[page:WebGLRenderTarget WebGLRenderTarget] properties are available on this class.

+ +

Methods

+ +

[page:WebGLRenderTarget WebGLRenderTarget] methods are available on this class.

+ +

Source

+ + [link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js] + + diff --git a/docs/api/zh/renderers/WebGLMultisampleRenderTarget.html b/docs/api/zh/renderers/WebGLMultisampleRenderTarget.html new file mode 100644 index 0000000000..b0cda86fc8 --- /dev/null +++ b/docs/api/zh/renderers/WebGLMultisampleRenderTarget.html @@ -0,0 +1,46 @@ + + + + + + + + + + +

[name]

+ +

+ TODO +

+ + +

Constructor

+ + +

[name]([param:Number width], [param:Number height], [param:Object options])

+ +

+ [page:Float width] - TODO
+ [page:Float height] - TODO
+ [page:Object options] - TODO +

+ +

Properties

+ +

[property:Number samples]

+

+ TODO +

+ +

[page:WebGLRenderTarget WebGLRenderTarget] TODO

+ +

Methods

+ +

[page:WebGLRenderTarget WebGLRenderTarget] TODO

+ +

Source

+ + [link:https://github.com/mrdoob/three.js/blob/master/src/[path].js src/[path].js] + + diff --git a/docs/list.js b/docs/list.js index 8b81eee0fc..8e56f65219 100644 --- a/docs/list.js +++ b/docs/list.js @@ -309,6 +309,7 @@ var list = { }, "Renderers": { + "WebGLMultisampleRenderTarget": "api/en/renderers/WebGLMultisampleRenderTarget", "WebGLRenderer": "api/en/renderers/WebGLRenderer", "WebGLRenderTarget": "api/en/renderers/WebGLRenderTarget", "WebGLRenderTargetCube": "api/en/renderers/WebGLRenderTargetCube" @@ -733,6 +734,7 @@ var list = { }, "渲染器": { + "WebGLMultisampleRenderTarget": "api/zh/renderers/WebGLMultisampleRenderTarget", "WebGLRenderer": "api/zh/renderers/WebGLRenderer", "WebGLRenderTarget": "api/zh/renderers/WebGLRenderTarget", "WebGLRenderTargetCube": "api/zh/renderers/WebGLRenderTargetCube" diff --git a/examples/files.js b/examples/files.js index 3af33c6c00..c6588e734e 100644 --- a/examples/files.js +++ b/examples/files.js @@ -316,6 +316,7 @@ var files = { "webgl2": [ "webgl2_materials_texture3d", "webgl2_materials_texture3d_volume", + "webgl2_multisampled_renderbuffers", "webgl2_sandbox" ], "webaudio": [ diff --git a/examples/webgl2_multisampled_renderbuffers.html b/examples/webgl2_multisampled_renderbuffers.html new file mode 100644 index 0000000000..8b8f0b97e3 --- /dev/null +++ b/examples/webgl2_multisampled_renderbuffers.html @@ -0,0 +1,189 @@ + + + + three.js WebGL 2 - Multisampled Renderbuffers + + + + + + + +
+ three.js - Multisampled Renderbuffers
+ Left scene is multi-sampled, right scene is rendered without anti-aliasing. +
+
+
+ + + + + + + + + + + + + diff --git a/rollup.config.js b/rollup.config.js index 52b7935fbf..c547275241 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -129,7 +129,10 @@ function glconstants() { MAX_VARYING_VECTORS: 36348, MAX_FRAGMENT_UNIFORM_VECTORS: 36349, UNPACK_FLIP_Y_WEBGL: 37440, - UNPACK_PREMULTIPLY_ALPHA_WEBGL: 37441 + UNPACK_PREMULTIPLY_ALPHA_WEBGL: 37441, + MAX_SAMPLES: 36183, + READ_FRAMEBUFFER: 36008, + DRAW_FRAMEBUFFER: 36009 }; return { @@ -147,10 +150,11 @@ function glconstants() { return { code: code, map: { mappings: '' } - } + }; + } - } + }; } diff --git a/src/Three.js b/src/Three.js index 33260f2492..bff7cf7696 100644 --- a/src/Three.js +++ b/src/Three.js @@ -1,5 +1,6 @@ import './polyfills.js'; +export { WebGLMultisampleRenderTarget } from './renderers/WebGLMultisampleRenderTarget.js'; export { WebGLRenderTargetCube } from './renderers/WebGLRenderTargetCube.js'; export { WebGLRenderTarget } from './renderers/WebGLRenderTarget.js'; export { WebGLRenderer } from './renderers/WebGLRenderer.js'; diff --git a/src/renderers/WebGLMultisampleRenderTarget.js b/src/renderers/WebGLMultisampleRenderTarget.js new file mode 100644 index 0000000000..1cc028184c --- /dev/null +++ b/src/renderers/WebGLMultisampleRenderTarget.js @@ -0,0 +1,35 @@ +import { WebGLRenderTarget } from './WebGLRenderTarget.js'; + +/** + * @author Mugen87 / https://github.com/Mugen87 + * @author Matt DesLauriers / @mattdesl + */ + +function WebGLMultisampleRenderTarget( width, height, options ) { + + WebGLRenderTarget.call( this, width, height, options ); + + this.samples = 4; + +} + +WebGLMultisampleRenderTarget.prototype = Object.assign( Object.create( WebGLRenderTarget.prototype ), { + + constructor: WebGLMultisampleRenderTarget, + + isWebGLMultisampleRenderTarget: true, + + copy: function ( source ) { + + WebGLRenderTarget.prototype.copy.call( this, source ); + + this.samples = source.samples; + + return this; + + } + +} ); + + +export { WebGLMultisampleRenderTarget }; diff --git a/src/renderers/WebGLRenderer.js b/src/renderers/WebGLRenderer.js index f684902b8c..89d47718a4 100644 --- a/src/renderers/WebGLRenderer.js +++ b/src/renderers/WebGLRenderer.js @@ -1133,12 +1133,18 @@ function WebGLRenderer( parameters ) { } - // Generate mipmap if we're using any kind of mipmap filtering + // if ( renderTarget ) { + // Generate mipmap if we're using any kind of mipmap filtering + textures.updateRenderTargetMipmap( renderTarget ); + // resolve multisample renderbuffers to to single-sample texture if necessary + + textures.updateMultisampleRenderTarget( renderTarget ); + } // Ensure depth buffer writing is enabled so it can be cleared on next render @@ -2494,6 +2500,10 @@ function WebGLRenderer( parameters ) { framebuffer = __webglFramebuffer[ renderTarget.activeCubeFace ]; isCube = true; + } else if ( renderTarget.isWebGLMultisampleRenderTarget ) { + + framebuffer = properties.get( renderTarget ).__webglMultisampledFramebuffer; + } else { framebuffer = __webglFramebuffer; diff --git a/src/renderers/webgl/WebGLCapabilities.js b/src/renderers/webgl/WebGLCapabilities.js index ccb3624b40..4eafd2132f 100644 --- a/src/renderers/webgl/WebGLCapabilities.js +++ b/src/renderers/webgl/WebGLCapabilities.js @@ -84,6 +84,8 @@ function WebGLCapabilities( gl, extensions, parameters ) { var floatFragmentTextures = isWebGL2 || !! extensions.get( 'OES_texture_float' ); var floatVertexTextures = vertexTextures && floatFragmentTextures; + var maxSamples = isWebGL2 ? gl.getParameter( gl.MAX_SAMPLES ) : 0; + return { isWebGL2: isWebGL2, @@ -106,7 +108,9 @@ function WebGLCapabilities( gl, extensions, parameters ) { vertexTextures: vertexTextures, floatFragmentTextures: floatFragmentTextures, - floatVertexTextures: floatVertexTextures + floatVertexTextures: floatVertexTextures, + + maxSamples: maxSamples }; diff --git a/src/renderers/webgl/WebGLTextures.js b/src/renderers/webgl/WebGLTextures.js index b1f04eec08..1d85b6d327 100644 --- a/src/renderers/webgl/WebGLTextures.js +++ b/src/renderers/webgl/WebGLTextures.js @@ -723,24 +723,60 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, } // Setup storage for internal depth/stencil buffers and bind to correct framebuffer - function setupRenderBufferStorage( renderbuffer, renderTarget ) { + function setupRenderBufferStorage( renderbuffer, renderTarget, isMultisample ) { _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderbuffer ); if ( renderTarget.depthBuffer && ! renderTarget.stencilBuffer ) { - _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); + if ( isMultisample ) { + + var samples = getRenderTargetSamples( renderTarget ); + + _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); + + } else { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_COMPONENT16, renderTarget.width, renderTarget.height ); + + } + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); } else if ( renderTarget.depthBuffer && renderTarget.stencilBuffer ) { - _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); + if ( isMultisample ) { + + var samples = getRenderTargetSamples( renderTarget ); + + _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); + + } else { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.DEPTH_STENCIL, renderTarget.width, renderTarget.height ); + + } + + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.DEPTH_STENCIL_ATTACHMENT, _gl.RENDERBUFFER, renderbuffer ); } else { - // FIXME: We don't support !depth !stencil - _gl.renderbufferStorage( _gl.RENDERBUFFER, _gl.RGBA4, renderTarget.width, renderTarget.height ); + var glFormat = utils.convert( renderTarget.texture.format ); + var glType = utils.convert( renderTarget.texture.type ); + var glInternalFormat = getInternalFormat( glFormat, glType ); + + if ( isMultisample ) { + + var samples = getRenderTargetSamples( renderTarget ); + + _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); + + } else { + + _gl.renderbufferStorage( _gl.RENDERBUFFER, glInternalFormat, renderTarget.width, renderTarget.height ); + + } } @@ -847,6 +883,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, info.memory.textures ++; var isCube = ( renderTarget.isWebGLRenderTargetCube === true ); + var isMultisample = ( renderTarget.isWebGLMultisampleRenderTarget === true ); var isTargetPowerOfTwo = isPowerOfTwo( renderTarget ); // Setup framebuffer @@ -865,6 +902,33 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, renderTargetProperties.__webglFramebuffer = _gl.createFramebuffer(); + if ( isMultisample ) { + + renderTargetProperties.__webglMultisampledFramebuffer = _gl.createFramebuffer(); + renderTargetProperties.__webglColorRenderbuffer = _gl.createRenderbuffer(); + + _gl.bindRenderbuffer( _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer ); + var glFormat = utils.convert( renderTarget.texture.format ); + var glType = utils.convert( renderTarget.texture.type ); + var glInternalFormat = getInternalFormat( glFormat, glType ); + var samples = getRenderTargetSamples( renderTarget ); + _gl.renderbufferStorageMultisample( _gl.RENDERBUFFER, samples, glInternalFormat, renderTarget.width, renderTarget.height ); + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer ); + _gl.framebufferRenderbuffer( _gl.FRAMEBUFFER, _gl.COLOR_ATTACHMENT0, _gl.RENDERBUFFER, renderTargetProperties.__webglColorRenderbuffer ); + _gl.bindRenderbuffer( _gl.RENDERBUFFER, null ); + + if ( renderTarget.depthBuffer ) { + + renderTargetProperties.__webglDepthRenderbuffer = _gl.createRenderbuffer(); + setupRenderBufferStorage( renderTargetProperties.__webglDepthRenderbuffer, renderTarget, true ); + + } + + _gl.bindFramebuffer( _gl.FRAMEBUFFER, null ); + + } + } // Setup color buffer @@ -932,6 +996,35 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, } + function updateMultisampleRenderTarget( renderTarget ) { + + if ( renderTarget.isWebGLMultisampleRenderTarget ) { + + var renderTargetProperties = properties.get( renderTarget ); + + _gl.bindFramebuffer( _gl.READ_FRAMEBUFFER, renderTargetProperties.__webglMultisampledFramebuffer ); + _gl.bindFramebuffer( _gl.DRAW_FRAMEBUFFER, renderTargetProperties.__webglFramebuffer ); + + var width = renderTarget.width; + var height = renderTarget.height; + var mask = _gl.COLOR_BUFFER_BIT; + + if ( renderTarget.depthBuffer ) mask |= _gl.DEPTH_BUFFER_BIT; + if ( renderTarget.stencilBuffer ) mask |= _gl.STENCIL_BUFFER_BIT; + + _gl.blitFramebuffer( 0, 0, width, height, 0, 0, width, height, mask, _gl.NEAREST ); + + } + + } + + function getRenderTargetSamples( renderTarget ) { + + return ( capabilities.isWebGL2 && renderTarget.isWebGLMultisampleRenderTarget ) ? + Math.min( capabilities.maxSamples, renderTarget.samples ) : 0; + + } + function updateVideoTexture( texture ) { var id = texture.id; @@ -954,6 +1047,7 @@ function WebGLTextures( _gl, extensions, state, properties, capabilities, utils, this.setTextureCubeDynamic = setTextureCubeDynamic; this.setupRenderTarget = setupRenderTarget; this.updateRenderTargetMipmap = updateRenderTargetMipmap; + this.updateMultisampleRenderTarget = updateMultisampleRenderTarget; } -- GitLab