Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Examples: Depth peeling example #24227

Closed
wants to merge 7 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -223,7 +223,8 @@
"webgl_video_kinect",
"webgl_video_panorama_equirectangular",
"webgl_water",
"webgl_water_flowmap"
"webgl_water_flowmap",
"webgl_depth_peeling"
],
"webgl / nodes": [
"webgl_materials_instance_uniform_nodes",
Expand Down
Binary file added examples/screenshots/webgl_depth_peeling.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
305 changes: 305 additions & 0 deletions examples/webgl_depth_peeling.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,305 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgl - geometry - cube</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, user-scalable=no, minimum-scale=1.0, maximum-scale=1.0">
<link type="text/css" rel="stylesheet" href="main.css">
</head>
<body>

<!-- Import maps polyfill -->
<!-- Remove this when import maps will be widely supported -->
<script async src="https://unpkg.com/[email protected]/dist/es-module-shims.js"></script>

<script type="importmap">
{
"imports": {
"three": "../build/three.module.js"
}
}
</script>

<script type="module">

import * as THREE from 'three';
import { OrbitControls } from './jsm/controls/OrbitControls.js';
import { FullScreenQuad } from './jsm/postprocessing/Pass.js';
import { CopyShader } from './jsm/shaders/CopyShader.js';
import { GUI } from './jsm/libs/lil-gui.module.min.js';

export class DepthPeeling {

globalUniforms = {
uPrevDepthTexture: { value: null },
uReciprocalScreenSize: { value: new THREE.Vector2( 1, 1 ) },
};

layers = [];
depth = 3;
one = new THREE.DataTexture( new Uint8Array( [ 1, 1, 1, 1 ] ), 1, 1 );
quad = new FullScreenQuad(
new THREE.ShaderMaterial( {
...CopyShader,
transparent: true,
depthTest: false,
depthWrite: false,
} )
);
screenSize = new THREE.Vector2();
pixelRatio = 1;
originalClearColor = new THREE.Color();
ownScene = new THREE.Scene();
constructor( p ) {

this.setScreenSize( p.width, p.height, p.pixelRatio );
this.setDepth( p.depth );

}

add( sceneGraph ) {

const clonedScene = sceneGraph.clone( true );
clonedScene.traverse( ( obj ) => {

if ( obj instanceof THREE.Mesh && obj.material instanceof THREE.Material ) {

const clonedMaterial = obj.material.clone();
clonedMaterial.blending = THREE.NoBlending;
clonedMaterial.onBeforeCompile = ( shader ) => {

shader.uniforms.uReciprocalScreenSize =
this.globalUniforms.uReciprocalScreenSize;
shader.uniforms.uPrevDepthTexture =
this.globalUniforms.uPrevDepthTexture;
shader.fragmentShader = `
// --- DEPTH PEELING SHADER CHUNK (START) (uniform definition)
uniform vec2 uReciprocalScreenSize;
uniform sampler2D uPrevDepthTexture;
// --- DEPTH PEELING SHADER CHUNK (END)
${shader.fragmentShader}
`;
//peel depth
shader.fragmentShader = shader.fragmentShader.replace(
/}$/gm,
`
// --- DEPTH PEELING SHADER CHUNK (START) (peeling)
vec2 screenPos = gl_FragCoord.xy * uReciprocalScreenSize;
float prevDepth = texture2D(uPrevDepthTexture,screenPos).x;
if( prevDepth >= gl_FragCoord.z )
discard;
// --- DEPTH PEELING SHADER CHUNK (END)
}
`
);

};

obj.material = clonedMaterial;
obj.material.needsUpdate = true;

}

} );
this.ownScene.add( clonedScene );

}

render( renderer, camera ) {

const originalRenderTarget = renderer.getRenderTarget();
const originalAutoClear = renderer.autoClear;
renderer.autoClear = false;

renderer.getClearColor( this.originalClearColor );
renderer.setClearColor( 0x000000 );

this.layers.reduceRight( ( prevDepth, layer ) => {

this.globalUniforms.uPrevDepthTexture.value = prevDepth;
renderer.setRenderTarget( layer );
renderer.clear();
renderer.render( this.ownScene, camera );
return layer.depthTexture;

}, this.one );

renderer.setRenderTarget( originalRenderTarget );
renderer.setClearColor( this.originalClearColor );
renderer.clear();

for ( const layer of this.layers ) {

this.quad.material.uniforms.tDiffuse.value =
layer.texture;
this.quad.material.needsUpdate = true;
this.quad.render( renderer );

}

renderer.autoClear = originalAutoClear;

}
getDepth() {

return this.depth;

}
setDepth( depth ) {

while ( depth < this.layers.length ) this.layers.pop()?.dispose();

const w = this.screenSize.width * this.pixelRatio;
const h = this.screenSize.height * this.pixelRatio;
while ( this.layers.length < depth )
this.layers.push(
new THREE.WebGLRenderTarget( w, h, {
depthTexture: new THREE.DepthTexture( w, h ),
// samples: 2,
} )
);

this.depth = depth;

}
setScreenSize( width, height, pixelRatio ) {

this.screenSize.set( width, height );
this.pixelRatio = pixelRatio;
const w = width * pixelRatio;
const h = height * pixelRatio;
this.globalUniforms.uReciprocalScreenSize.value.set(
1 / w,
1 / h
);

this.layers.forEach( ( rt ) => {

rt.setSize( w, h );
rt.depthTexture.dispose();
rt.depthTexture = new THREE.DepthTexture( w, h );

} );

}

}



const AppState = {
Enabled: true,
Depth: 3,
};
init();
async function init() {

const renderer = new THREE.WebGLRenderer();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
document.body.appendChild( renderer.domElement );

const scene = new THREE.Scene();
const width = window.innerWidth, height = window.innerHeight;
const camera = new THREE.PerspectiveCamera( 75, width / height, 0.1, 1000 );
camera.position.z = 5;

renderer.shadowMap.enabled = true;

const dirLight = new THREE.DirectionalLight();
dirLight.position.set( 0, 3, 0 );
dirLight.castShadow = true;
scene.add( dirLight );
scene.add( new THREE.AmbientLight( undefined, 0.5 ) );

const sphere = new THREE.Mesh(
new THREE.SphereBufferGeometry(),
new THREE.MeshStandardMaterial()
);
sphere.translateY( 3 ).translateX( 1.5 );
sphere.castShadow = true;
scene.add( sphere );

const knot = new THREE.Mesh(
new THREE.TorusKnotBufferGeometry( undefined, undefined, 128, 32 ),
new THREE.MeshStandardMaterial( {
transparent: true,
opacity: 0.7,
side: THREE.DoubleSide,
} )
);
knot.receiveShadow = true;
scene.add( knot );

scene.add(
new THREE.Mesh(
new THREE.BoxBufferGeometry(),
new THREE.MeshBasicMaterial( { color: 0xf0a000 } )
)
);

const texture = await new THREE.TextureLoader().loadAsync(
'textures/sprite0.png'
);
const plane = new THREE.Mesh(
new THREE.PlaneBufferGeometry( 3, 3 ),
new THREE.MeshStandardMaterial( { map: texture, side: THREE.DoubleSide } )
);
plane.translateX( - 1.6 );
plane.translateY( 1.5 );
scene.add( plane );

const plane2 = new THREE.Mesh(
new THREE.PlaneBufferGeometry( 3, 3 ),
new THREE.MeshStandardMaterial( {
map: texture,
transparent: true,
side: THREE.DoubleSide,
} )
);
plane2
.translateX( - 1.2 )
.translateY( - 1.5 )
.translateZ( 0 )
.rotateY( - 2 * Math.PI * ( 1 / 10 ) );
scene.add( plane2 );

const dp = new DepthPeeling( { depth: AppState.Depth, width: window.innerWidth, height: window.innerHeight, pixelRatio: renderer.getPixelRatio() } );
dp.add( scene );
Mugen87 marked this conversation as resolved.
Show resolved Hide resolved
const animate = () =>
requestAnimationFrame( () =>
AppState.Enabled ? dp.render( renderer, camera ) : renderer.render( scene, camera )
);


const orbit = new OrbitControls( camera, renderer.domElement );
orbit.update();
orbit.addEventListener( 'change', animate );


animate();

const gui = new GUI();
gui.add( AppState, 'Enabled' ).onChange( animate );
gui.add( AppState, 'Depth' ).min( 1 ).max( 5 ).step( 1 ).onChange( v => {

dp.setDepth( v );
animate();

} );

window.addEventListener( 'resize', () => {

camera.aspect = window.innerWidth / window.innerHeight;
camera.updateProjectionMatrix();

renderer.setSize( window.innerWidth, window.innerHeight );
dp.setScreenSize( window.innerWidth, window.innerHeight, renderer.getPixelRatio() );

} );

}

</script>
</body>
</html>