diff --git a/docs/api/en/renderers/WebGLMultisampleRenderTarget.html b/docs/api/en/renderers/WebGLMultisampleRenderTarget.html new file mode 100644 index 0000000000000000000000000000000000000000..be52eed4f47e7a1d62a9af34243907065cc680b2 --- /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 0000000000000000000000000000000000000000..b0cda86fc81061661f460dbad6abfc24ea4f58d4 --- /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 8b81eee0fc52ffc9070162936d2c05d6b5ae4eef..8e56f65219fb48cb5eafce03db9c4c299801faeb 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 3af33c6c008c38d2fb721b46252b21130e0fbc9b..c6588e734e2bff4a6f950a182eddd5c3914161e1 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 0000000000000000000000000000000000000000..8b8f0b97e3eb280de505415baaaef2110609a87b --- /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 52b7935fbf984b53039adaf542a6798cdb47d408..c5472752417a4764efeaf5dd3be84f3c13bcd015 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 33260f24923ccd6d3a7621260d3363683686d86f..bff7cf76960e20ee3de59e985deda4de1ed6efbb 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 0000000000000000000000000000000000000000..1cc028184cd1a0fac43af33fa957c8b9b9af2ee5 --- /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 f684902b8c5f8944691e68bf9e45f03e7b4afdc7..89d47718a4f7852cbcba79b38a97e8521c25dc58 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 ccb3624b40c67284245db9d690063e3dab311bf0..4eafd2132f9ce2ed650c59fbc35611190a4c0bdd 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 b1f04eec08a4a90f57080af5cb5733c4ed21709c..1d85b6d327aba3e521a9eb2a663c988fa7962c6e 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; }