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;
}