提交 bb5600fd 编写于 作者: B Ben Houston (Clara.io) 提交者: Mr.doob

add unbiased mode to the MSAA render pass. (#8930)

* add unbiased mode to the MSAA render pass.

* clearer code.

* better comment.

* cleanup code.  fix bug of using "camera", instead of "this.camera"

* fix more places were "camera" is used instad of "this.camera" -- it worked because example declared "camera" as a global.

* reduce light intensity.

* add PerspectiveCamera.clearViewOffset() per @mrdoob's recommendation.
上级 60ef1a65
......@@ -189,6 +189,7 @@ var files = {
"webgl_postprocessing_godrays",
"webgl_postprocessing_masking",
"webgl_postprocessing_msaa",
"webgl_postprocessing_msaa_unbiased",
"webgl_postprocessing_nodes",
"webgl_postprocessing_procedural",
"webgl_postprocessing_smaa",
......
......@@ -18,6 +18,7 @@ THREE.ManualMSAARenderPass = function ( scene, camera ) {
this.camera = camera;
this.sampleLevel = 4; // specified as n, where the number of samples is 2^n, so sampleLevel = 4, is 2^4 samples, 16.
this.unbiased = true;
if ( THREE.CopyShader === undefined ) console.error( "THREE.ManualMSAARenderPass relies on THREE.CopyShader" );
......@@ -77,27 +78,39 @@ Object.assign( THREE.ManualMSAARenderPass.prototype, {
var autoClear = renderer.autoClear;
renderer.autoClear = false;
this.copyUniforms[ "opacity" ].value = 1.0 / jitterOffsets.length;
var baseSampleWeight = 1.0 / jitterOffsets.length;
var roundingRange = 1 / 32;
this.copyUniforms[ "tDiffuse" ].value = this.sampleRenderTarget.texture;
var width = readBuffer.width, height = readBuffer.height;
// render the scene multiple times, each slightly jitter offset from the last and accumulate the results.
for ( var i = 0; i < jitterOffsets.length; i ++ ) {
// only jitters perspective cameras. TODO: add support for jittering orthogonal cameras
var jitterOffset = jitterOffsets[i];
if ( camera.setViewOffset ) {
camera.setViewOffset( readBuffer.width, readBuffer.height,
if ( this.camera.setViewOffset ) {
this.camera.setViewOffset( width, height,
jitterOffset[ 0 ] * 0.0625, jitterOffset[ 1 ] * 0.0625, // 0.0625 = 1 / 16
readBuffer.width, readBuffer.height );
width, height );
}
var sampleWeight = baseSampleWeight;
if( this.unbiased ) {
// the theory is that equal weights for each sample lead to an accumulation of rounding errors.
// The following equation varies the sampleWeight per sample so that it is uniformly distributed
// across a range of values whose rounding errors cancel each other out.
var uniformCenteredDistribution = ( -0.5 + ( i + 0.5 ) / jitterOffsets.length );
sampleWeight += roundingRange * uniformCenteredDistribution;
}
this.copyUniforms[ "opacity" ].value = sampleWeight;
renderer.render( this.scene, this.camera, this.sampleRenderTarget, true );
renderer.render( this.scene2, this.camera2, writeBuffer, (i === 0) );
}
// reset jitter to nothing. TODO: add support for orthogonal cameras
if ( camera.setViewOffset ) camera.setViewOffset( undefined, undefined, undefined, undefined, undefined, undefined );
if ( this.camera.clearViewOffset ) this.camera.clearViewOffset();
renderer.autoClear = autoClear;
......
......@@ -45,8 +45,6 @@ Object.assign( THREE.TAARenderPass.prototype, {
var jitterOffsets = THREE.TAARenderPass.JitterVectors[ 5 ];
var camera = ( this.camera || this.scene.camera );
if ( ! this.sampleRenderTarget ) {
this.sampleRenderTarget = new THREE.WebGLRenderTarget( readBuffer.width, readBuffer.height, this.params );
......@@ -82,10 +80,9 @@ Object.assign( THREE.TAARenderPass.prototype, {
for ( var i = 0; i < numSamplesPerFrame; i ++ ) {
var j = this.accumulateIndex;
// only jitters perspective cameras. TODO: add support for jittering orthogonal cameras
var jitterOffset = jitterOffsets[j];
if ( camera.setViewOffset ) {
camera.setViewOffset( readBuffer.width, readBuffer.height,
if ( this.camera.setViewOffset ) {
this.camera.setViewOffset( readBuffer.width, readBuffer.height,
jitterOffset[ 0 ] * 0.0625, jitterOffset[ 1 ] * 0.0625, // 0.0625 = 1 / 16
readBuffer.width, readBuffer.height );
}
......@@ -97,9 +94,8 @@ Object.assign( THREE.TAARenderPass.prototype, {
if( this.accumulateIndex >= jitterOffsets.length ) break;
}
// reset jitter to nothing. TODO: add support for orthogonal cameras
if ( camera.setViewOffset ) camera.setViewOffset( undefined, undefined, undefined, undefined, undefined, undefined );
if ( this.camera.clearViewOffset ) this.camera.clearViewOffset();
}
var accumulationWeight = this.accumulateIndex * sampleWeight;
......
......@@ -53,7 +53,9 @@
var camera, scene, renderer, composer, copyPass, msaaRenderPass;
var gui, stats, texture;
var param = { MSAASampleLevel: 2 };
var param = {
sampleLevel: 2
};
init();
animate();
......@@ -66,19 +68,13 @@
gui = new dat.GUI();
var example = gui.add( param, 'MSAASampleLevel', {
gui.add( param, 'sampleLevel', {
'Level 0: 1 Sample': 0,
'Level 1: 2 Samples': 1,
'Level 2: 4 Samples': 2,
'Level 3: 8 Samples': 3,
'Level 4: 16 Samples': 4,
'Level 5: 32 Samples': 5
} ).onFinishChange( function() {
if( msaaRenderPass ) {
msaaRenderPass.sampleLevel = param.MSAASampleLevel;
}
} );
gui.open();
......@@ -128,7 +124,7 @@
composer = new THREE.EffectComposer( renderer );
msaaRenderPass = new THREE.ManualMSAARenderPass( scene, camera );
msaaRenderPass.sampleLevel = param.MSAASampleLevel;
msaaRenderPass.unbiased = false;
composer.addPass( msaaRenderPass );
copyPass = new THREE.ShaderPass( THREE.CopyShader );
......@@ -171,6 +167,8 @@
}
msaaRenderPass.sampleLevel = param.sampleLevel;
composer.render();
stats.end();
......
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - postprocessing manual msaa</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 {
margin: 0px;
background-color: #000;
overflow: hidden;
font-family:Monospace;
font-size:13px;
margin: 0px;
text-align:center;
overflow: hidden;
}
#info {
color: #fff;
position: absolute;
top: 10px;
width: 100%;
text-align: center;
display:block;
}
</style>
</head>
<body>
<div id="info">
<a href="http://threejs.org" target="_blank">three.js</a> - Unbiased Manual Multi-Sample Anti-Aliasing (MSAA) pass by <a href="https://clara.io" target="_blank">Ben Houston</a><br/><br/>
This example shows how to unbias the rounding errors accumulated using high number of MSAA samples on a 8-bit per channel buffer.<br/><br/>
Turn off the "unbiased" feature to see the banding that results from accumulated rounding errors.
</div>
<div id="container"></div>
<script src="../build/three.js"></script>
<script src="js/libs/stats.min.js"></script>
<script src="js/libs/dat.gui.min.js"></script>
<script src="js/shaders/CopyShader.js"></script>
<script src="js/postprocessing/EffectComposer.js"></script>
<script src="js/postprocessing/ManualMSAARenderPass.js"></script>
<script src="js/postprocessing/RenderPass.js"></script>
<script src="js/postprocessing/MaskPass.js"></script>
<script src="js/postprocessing/ShaderPass.js"></script>
<script>
var camera, scene, renderer, composer, copyPass, msaaRenderPass;
var gui, stats, texture;
var param = {
sampleLevel: 4,
unbiased: true
};
init();
animate();
clearGui();
function clearGui() {
if ( gui ) gui.destroy();
gui = new dat.GUI();
gui.add( param, "unbiased" );
gui.add( param, 'sampleLevel', {
'Level 0: 1 Sample': 0,
'Level 1: 2 Samples': 1,
'Level 2: 4 Samples': 2,
'Level 3: 8 Samples': 3,
'Level 4: 16 Samples': 4,
'Level 5: 32 Samples': 5
} );
gui.open();
}
function init() {
container = document.getElementById( "container" );
var width = window.innerWidth || 1;
var height = window.innerHeight || 1;
var devicePixelRatio = window.devicePixelRatio || 1;
renderer = new THREE.WebGLRenderer( { antialias: false } );
renderer.setPixelRatio( devicePixelRatio );
renderer.setSize( width, height );
document.body.appendChild( renderer.domElement );
stats = new Stats();
container.appendChild( stats.dom );
//
camera = new THREE.PerspectiveCamera( 65, width / height, 3, 10 );
camera.position.z = 7;
scene = new THREE.Scene();
group = new THREE.Object3D();
scene.add( group );
var light = new THREE.PointLight( 0xddffdd, 1.0 );
light.position.z = 70;
light.position.y = -70;
light.position.x = -70;
scene.add( light );
var light2 = new THREE.PointLight( 0xffdddd, 1.0 );
light2.position.z = 70;
light2.position.x = -70;
light2.position.y = 70;
scene.add( light2 );
var light3 = new THREE.PointLight( 0xddddff, 1.0 );
light3.position.z = 70;
light3.position.x = 70;
light3.position.y = -70;
scene.add( light3 );
var light3 = new THREE.AmbientLight( 0xffffff, 0.05 );
scene.add( light3 );
var geometry = new THREE.SphereBufferGeometry( 3, 48, 24 );
for ( var i = 0; i < 120; i ++ ) {
var material = new THREE.MeshStandardMaterial();
material.roughness = 0.5 * Math.random() + 0.25;
material.metalness = 0;
material.color.setHSL( Math.random(), 1.0, 0.3 );
var mesh = new THREE.Mesh( geometry, material );
mesh.position.x = Math.random() * 4 - 2;
mesh.position.y = Math.random() * 4 - 2;
mesh.position.z = Math.random() * 4 - 2;
mesh.rotation.x = Math.random();
mesh.rotation.y = Math.random();
mesh.rotation.z = Math.random();
mesh.scale.x = mesh.scale.y = mesh.scale.z = Math.random() * 0.2 + 0.05;
group.add( mesh );
}
// postprocessing
composer = new THREE.EffectComposer( renderer );
msaaRenderPass = new THREE.ManualMSAARenderPass( scene, camera );
composer.addPass( msaaRenderPass );
copyPass = new THREE.ShaderPass( THREE.CopyShader );
copyPass.renderToScreen = true;
composer.addPass( copyPass );
window.addEventListener( 'resize', onWindowResize, false );
}
function onWindowResize() {
var width = window.innerWidth;
var height = window.innerHeight;
camera.aspect = width / height;
camera.updateProjectionMatrix();
renderer.setSize( width, height );
var pixelRatio = renderer.getPixelRatio();
var newWidth = Math.floor( width / pixelRatio ) || 1;
var newHeight = Math.floor( height / pixelRatio ) || 1;
composer.setSize( newWidth, newHeight );
}
function animate() {
requestAnimationFrame( animate );
stats.begin();
for ( var i = 0; i < scene.children.length; i ++ ) {
var child = scene.children[ i ];
child.rotation.x += 0.005;
child.rotation.y += 0.01;
}
msaaRenderPass.sampleLevel = param.sampleLevel;
msaaRenderPass.unbiased = param.unbiased;
composer.render();
stats.end();
}
</script>
<div>
</body>
</html>
......@@ -144,6 +144,7 @@
composer = new THREE.EffectComposer( renderer );
taaRenderPass = new THREE.TAARenderPass( scene, camera );
taaRenderPass.unbiased = false;
composer.addPass( taaRenderPass );
renderPass = new THREE.RenderPass( scene, camera );
......
......@@ -155,6 +155,13 @@ THREE.PerspectiveCamera.prototype = Object.assign( Object.create( THREE.Camera.p
},
clearViewOffset: function() {
this.view = null;
this.updateProjectionMatrix();
},
updateProjectionMatrix: function () {
var near = this.near,
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册