Skip to content

first pass at occlusion in gpurenderer #31207

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

Draft
wants to merge 1 commit into
base: dev
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -464,6 +464,7 @@
"webgpu_water",
"webgpu_xr_rollercoaster",
"webgpu_xr_cubes",
"webgpu_xr_occluded_cubes",
"webgpu_xr_native_layers"
],
"webaudio": [
Expand Down
Binary file added examples/screenshots/webgpu_xr_occluded_cubes.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
279 changes: 279 additions & 0 deletions examples/webgpu_xr_occluded_cubes.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,279 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js xr - cubes</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<link type="text/css" rel="stylesheet" href="main.css">
</head>
<body>

<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> xr - interactive cubes
</div>

<script type="importmap">
{
"imports": {
"three": "../build/three.webgpu.js",
"three/webgpu": "../build/three.webgpu.js",
"three/tsl": "../build/three.tsl.js",
"three/addons/": "./jsm/"
}
}
</script>

<script type="module">

import * as THREE from 'three';

import { XRButton } from 'three/addons/webxr/XRButton.js';
import { XRControllerModelFactory } from 'three/addons/webxr/XRControllerModelFactory.js';

const clock = new THREE.Clock();

let container;
let camera, scene, raycaster, renderer;

let room;

let controller, controllerGrip;
let INTERSECTED;

init();

function init() {

container = document.createElement( 'div' );
document.body.appendChild( container );

scene = new THREE.Scene();
scene.background = new THREE.Color( 0x505050 );

camera = new THREE.PerspectiveCamera( 50, window.innerWidth / window.innerHeight, 0.1, 10 );
camera.position.set( 0, 1.6, 3 );
scene.add( camera );

room = new THREE.Object3D();
scene.add( room );

scene.add( new THREE.HemisphereLight( 0xa5a5a5, 0x898989, 3 ) );

const light = new THREE.DirectionalLight( 0xffffff, 3 );
light.position.set( 1, 1, 1 ).normalize();
scene.add( light );

const geometry = new THREE.BoxGeometry( 0.15, 0.15, 0.15 );

for ( let i = 0; i < 200; i ++ ) {

const object = new THREE.Mesh( geometry, new THREE.MeshLambertMaterial( { color: Math.random() * 0xffffff } ) );

object.position.x = Math.random() * 4 - 2;
object.position.y = Math.random() * 4;
object.position.z = Math.random() * 4 - 2;

object.rotation.x = Math.random() * 2 * Math.PI;
object.rotation.y = Math.random() * 2 * Math.PI;
object.rotation.z = Math.random() * 2 * Math.PI;

object.scale.x = Math.random() + 0.5;
object.scale.y = Math.random() + 0.5;
object.scale.z = Math.random() + 0.5;

object.userData.velocity = new THREE.Vector3();
object.userData.velocity.x = Math.random() * 0.01 - 0.005;
object.userData.velocity.y = Math.random() * 0.01 - 0.005;
object.userData.velocity.z = Math.random() * 0.01 - 0.005;

room.add( object );

}

raycaster = new THREE.Raycaster();

renderer = new THREE.WebGPURenderer( { antialias: true, forceWebGL: true, colorBufferType: THREE.UnsignedByteType, multiview: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( animate );
renderer.xr.enabled = true;
container.appendChild( renderer.domElement );

//

function onSelectStart() {

this.userData.isSelecting = true;

}

function onSelectEnd() {

this.userData.isSelecting = false;

}

controller = renderer.xr.getController( 0 );
controller.addEventListener( 'selectstart', onSelectStart );
controller.addEventListener( 'selectend', onSelectEnd );
controller.addEventListener( 'connected', function ( event ) {

const targetRayMode = event.data.targetRayMode;

if ( targetRayMode === 'tracked-pointer' || targetRayMode === 'gaze' ) {

this.add( buildController( event.data ) );

}

} );
controller.addEventListener( 'disconnected', function () {

this.remove( this.children[ 0 ] );

} );
scene.add( controller );

const controllerModelFactory = new XRControllerModelFactory();

controllerGrip = renderer.xr.getControllerGrip( 0 );
controllerGrip.add( controllerModelFactory.createControllerModel( controllerGrip ) );
scene.add( controllerGrip );

window.addEventListener( 'resize', onWindowResize );

//

document.body.appendChild( XRButton.createButton( renderer , {
'optionalFeatures': [ 'depth-sensing' ],
'depthSensing': { 'usagePreference': [ 'gpu-optimized' ], 'dataFormatPreference': [] } } ) );

}

function buildController( data ) {

let geometry, material;

switch ( data.targetRayMode ) {

case 'tracked-pointer':

geometry = new THREE.BufferGeometry();
geometry.setAttribute( 'position', new THREE.Float32BufferAttribute( [ 0, 0, 0, 0, 0, - 1 ], 3 ) );
geometry.setAttribute( 'color', new THREE.Float32BufferAttribute( [ 0.5, 0.5, 0.5, 0, 0, 0 ], 3 ) );

material = new THREE.LineBasicMaterial( { vertexColors: true, blending: THREE.AdditiveBlending } );

return new THREE.Line( geometry, material );

case 'gaze':

geometry = new THREE.RingGeometry( 0.02, 0.04, 32 ).translate( 0, 0, - 1 );
material = new THREE.MeshBasicMaterial( { opacity: 0.5, transparent: true } );
return new THREE.Mesh( geometry, material );

}

}

function onWindowResize() {

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

renderer.setSize( window.innerWidth, window.innerHeight );

}

//

function animate() {

const delta = clock.getDelta() * 60;

renderer.xr.applyOcclusion( scene );

if ( controller.userData.isSelecting === true ) {

const cube = room.children[ 0 ];
room.remove( cube );

cube.position.copy( controller.position );
cube.userData.velocity.x = ( Math.random() - 0.5 ) * 0.02 * delta;
cube.userData.velocity.y = ( Math.random() - 0.5 ) * 0.02 * delta;
cube.userData.velocity.z = ( Math.random() * 0.01 - 0.05 ) * delta;
cube.userData.velocity.applyQuaternion( controller.quaternion );
room.add( cube );

}

// find intersections

raycaster.setFromXRController( controller );

const intersects = raycaster.intersectObjects( room.children, false );

if ( intersects.length > 0 ) {

if ( INTERSECTED != intersects[ 0 ].object ) {

if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );

INTERSECTED = intersects[ 0 ].object;
INTERSECTED.currentHex = INTERSECTED.material.emissive.getHex();
INTERSECTED.material.emissive.setHex( 0xff0000 );

}

} else {

if ( INTERSECTED ) INTERSECTED.material.emissive.setHex( INTERSECTED.currentHex );

INTERSECTED = undefined;

}

// Keep cubes inside room

for ( let i = 0; i < room.children.length; i ++ ) {

const cube = room.children[ i ];

cube.userData.velocity.multiplyScalar( 1 - ( 0.001 * delta ) );

cube.position.add( cube.userData.velocity );

if ( cube.position.x < - 3 || cube.position.x > 3 ) {

cube.position.x = THREE.MathUtils.clamp( cube.position.x, - 3, 3 );
cube.userData.velocity.x = - cube.userData.velocity.x;

}

if ( cube.position.y < 0 || cube.position.y > 6 ) {

cube.position.y = THREE.MathUtils.clamp( cube.position.y, 0, 6 );
cube.userData.velocity.y = - cube.userData.velocity.y;

}

if ( cube.position.z < - 3 || cube.position.z > 3 ) {

cube.position.z = THREE.MathUtils.clamp( cube.position.z, - 3, 3 );
cube.userData.velocity.z = - cube.userData.velocity.z;

}

cube.rotation.x += cube.userData.velocity.x * 2 * delta;
cube.rotation.y += cube.userData.velocity.y * 2 * delta;
cube.rotation.z += cube.userData.velocity.z * 2 * delta;

}

renderer.render( scene, camera );

}

</script>
</body>
</html>
2 changes: 2 additions & 0 deletions src/Three.TSL.js
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ export const bool = TSL.bool;
export const buffer = TSL.buffer;
export const bufferAttribute = TSL.bufferAttribute;
export const bumpMap = TSL.bumpMap;
export const builtin = TSL.builtin;
export const burn = TSL.burn;
export const bvec2 = TSL.bvec2;
export const bvec3 = TSL.bvec3;
Expand Down Expand Up @@ -423,6 +424,7 @@ export const samplerComparison = TSL.samplerComparison;
export const saturate = TSL.saturate;
export const saturation = TSL.saturation;
export const screen = TSL.screen;
export const screenRaw = TSL.screenRaw;
export const screenCoordinate = TSL.screenCoordinate;
export const screenSize = TSL.screenSize;
export const screenUV = TSL.screenUV;
Expand Down
1 change: 1 addition & 0 deletions src/nodes/TSL.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export * from './tsl/TSLBase.js';
export * from './accessors/AccessorsUtils.js';
export * from './accessors/Arrays.js';
export * from './accessors/UniformArrayNode.js';
export * from './accessors/BuiltinNode.js';
export * from './accessors/Bitangent.js';
export * from './accessors/BufferAttributeNode.js';
export * from './accessors/BufferNode.js';
Expand Down
6 changes: 6 additions & 0 deletions src/nodes/core/NodeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -979,6 +979,12 @@ class NodeBuilder {

}

getFrag() {

console.warn( 'Abstract function.' );

}

/**
* Whether to flip texture data along its vertical axis or not. WebGL needs
* this method evaluate to `true`, WebGPU to `false`.
Expand Down
19 changes: 15 additions & 4 deletions src/nodes/display/ScreenNode.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Node from '../core/Node.js';
import { NodeUpdateType } from '../core/constants.js';
import { uniform } from '../core/UniformNode.js';
import { Fn, nodeImmutable, vec2 } from '../tsl/TSLBase.js';
import { Fn, nodeImmutable, vec2, vec4 } from '../tsl/TSLBase.js';

import { Vector2 } from '../../math/Vector2.js';
import { Vector4 } from '../../math/Vector4.js';
Expand All @@ -26,7 +26,7 @@ class ScreenNode extends Node {
/**
* Constructs a new screen node.
*
* @param {('coordinate'|'viewport'|'size'|'uv')} scope - The node's scope.
* @param {('coordinate'|'viewport'|'size'|'uv'|'raw')} scope - The node's scope.
*/
constructor( scope ) {

Expand Down Expand Up @@ -62,7 +62,7 @@ class ScreenNode extends Node {
*/
getNodeType() {

if ( this.scope === ScreenNode.VIEWPORT ) return 'vec4';
if ( this.scope === ScreenNode.VIEWPORT || this.scope === ScreenNode.RAW ) return 'vec4';
else return 'vec2';

}
Expand Down Expand Up @@ -143,6 +143,10 @@ class ScreenNode extends Node {

output = uniform( viewportVec || ( viewportVec = new Vector4() ) );

} else if ( scope === ScreenNode.RAW ) {

output = vec4( screenRaw.div( screenSize ) );

} else {

output = vec2( screenCoordinate.div( screenSize ) );
Expand All @@ -155,7 +159,11 @@ class ScreenNode extends Node {

generate( builder ) {

if ( this.scope === ScreenNode.COORDINATE ) {
if ( this.scope === ScreenNode.RAW ) {

return builder.getFrag();

} else if ( this.scope === ScreenNode.COORDINATE ) {

let coord = builder.getFragCoord();

Expand Down Expand Up @@ -183,6 +191,7 @@ ScreenNode.COORDINATE = 'coordinate';
ScreenNode.VIEWPORT = 'viewport';
ScreenNode.SIZE = 'size';
ScreenNode.UV = 'uv';
ScreenNode.RAW = 'raw';

export default ScreenNode;

Expand Down Expand Up @@ -230,6 +239,8 @@ export const viewport = /*@__PURE__*/ nodeImmutable( ScreenNode, ScreenNode.VIEW
*/
export const viewportSize = viewport.zw;

export const screenRaw = /*@__PURE__*/ nodeImmutable( ScreenNode, ScreenNode.RAW );//.div( vec4( viewport.z, viewport.w, 1, 1 ) );

/**
* TSL object that represents the current `x`/`y` pixel position on the viewport in physical pixel units.
*
Expand Down
Loading
Loading