Skip to content
Merged
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
3 changes: 2 additions & 1 deletion build/three.tsl.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/three.tsl.min.js

Large diffs are not rendered by default.

196 changes: 163 additions & 33 deletions build/three.webgpu.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/three.webgpu.min.js

Large diffs are not rendered by default.

196 changes: 163 additions & 33 deletions build/three.webgpu.nodes.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion build/three.webgpu.nodes.min.js

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,7 @@
"webgpu_cubemap_mix",
"webgpu_custom_fog",
"webgpu_custom_fog_background",
"webgpu_custom_fog_scattering",
"webgpu_depth_texture",
"webgpu_display_stereo",
"webgpu_equirectangular",
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion examples/webgpu_caustics.html
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( animate );
renderer.shadowMap.enabled = true;
renderer.shadowMap.colored = true;
renderer.shadowMap.transmitted = true;
renderer.inspector = new Inspector();
document.body.appendChild( renderer.domElement );

Expand Down
211 changes: 211 additions & 0 deletions examples/webgpu_custom_fog_scattering.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,211 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgpu - fog - scattering</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="example.css">
</head>
<body>

<div id="info" class="invert">
<a href="https://threejs.org/" target="_blank" rel="noopener" class="logo-link"></a>

<div class="title-wrapper">
<a href="https://threejs.org/" target="_blank" rel="noopener">three.js</a><span>Fog - Scattering</span>
</div>

<small>
The demo simulates light scattering due to fog.<br/>
</small>
</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/webgpu';
import { positionWorld, densityFogFactor, pass, vec2, uniform, mix, color } from 'three/tsl';
import { gaussianBlur } from 'three/addons/tsl/display/GaussianBlurNode.js';

import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { Inspector } from 'three/addons/inspector/Inspector.js';

let camera, scene, renderer, postProcessing, controls;

const params = {
scatteringEnabled: true
};

init();

async function init() {

camera = new THREE.PerspectiveCamera( 45, window.innerWidth / window.innerHeight, 1, 600 );
camera.position.set( 30, 10, 30 );

scene = new THREE.Scene();

// fog

scene.fog = new THREE.FogExp2( 0xcccccc, 0.014 );
scene.background = new THREE.Color( 0xcccccc );

// builds

const buildWindows = positionWorld.y.mul( 10 ).floor().mod( 4 ).sign().mix( color( 0x000066 ), color( 0xffffff ) );

const buildGeometry = new THREE.BoxGeometry( 1, 1, 1 );
const buildMaterial = new THREE.MeshPhongNodeMaterial( {
colorNode: buildWindows
} );

const buildMesh = new THREE.InstancedMesh( buildGeometry, buildMaterial, 4000 );
scene.add( buildMesh );

const dummy = new THREE.Object3D();
const center = new THREE.Vector3();

for ( let i = 0; i < buildMesh.count; i ++ ) {

const scaleY = Math.random() * 7 + .5;

dummy.position.x = Math.random() * 600 - 300;
dummy.position.z = Math.random() * 600 - 300;

const distance = Math.max( dummy.position.distanceTo( center ) * .012, 1 );

dummy.position.y = .5 * scaleY * distance;

dummy.scale.x = dummy.scale.z = Math.random() * 3 + .5;
dummy.scale.y = scaleY * distance;

dummy.updateMatrix();

buildMesh.setMatrixAt( i, dummy.matrix );

}

// lights

scene.add( new THREE.HemisphereLight( 0xf0f5f5, 0xd0dee7, 0.5 ) );

// geometry

const planeGeometry = new THREE.PlaneGeometry( 200, 200 );
const planeMaterial = new THREE.MeshPhongMaterial( {
color: 0x999999
} );

const ground = new THREE.Mesh( planeGeometry, planeMaterial );
ground.rotation.x = - Math.PI / 2;
ground.scale.multiplyScalar( 3 );
ground.castShadow = true;
ground.receiveShadow = true;
scene.add( ground );

// renderer

renderer = new THREE.WebGPURenderer( { antialias: true } );
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
renderer.setAnimationLoop( animate );
renderer.inspector = new Inspector();
document.body.appendChild( renderer.domElement );

// controls

controls = new OrbitControls( camera, renderer.domElement );
controls.target.set( 0, 2, 0 );
controls.minDistance = 7;
controls.maxDistance = 100;
controls.maxPolarAngle = Math.PI / 2;
controls.enableDamping = true;
controls.update();

//

postProcessing = new THREE.PostProcessing( renderer );

// uniforms

const density = uniform( 0.014 );
const scattering = uniform( 2 );

// scene pass

const scenePass = pass( scene, camera );

const scenePassColor = scenePass.getTextureNode( 'output' );
const scenePassViewZ = scenePass.getViewZNode();

// blur pass (always downsampled to improve performance)

const sceneColorBlurred = gaussianBlur( scenePassColor, vec2( scattering ), 4, { resolutionScale: 0.5 } );

// composite

const fogFactor = densityFogFactor( density ).context( { getViewZ: () => scenePassViewZ } );

const compositeNode = mix( scenePassColor, sceneColorBlurred, fogFactor );

postProcessing.outputNode = compositeNode;

// gui

const gui = renderer.inspector.createParameters( 'Settings' );
gui.add( density, 'value', 0.005, 0.03 ).step( 0.0001 ).name( 'fog density' ).onChange( ( value ) => {

scene.fog.density = value;

} );
gui.add( scattering, 'value', 0, 5 ).name( 'scattering factor' );
gui.add( params, 'scatteringEnabled' ).name( 'enable scattering' ).onChange( ( value ) => {

if ( value === true ) {

postProcessing.outputNode = compositeNode;

} else {

postProcessing.outputNode = scenePassColor;

}

postProcessing.needsUpdate = true;

} );

window.addEventListener( 'resize', resize );

}

function resize() {

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

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

}

function animate() {

controls.update();

postProcessing.render();

}

</script>
</body>
</html>
2 changes: 1 addition & 1 deletion examples/webgpu_shadowmap_opacity.html
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@
renderer.toneMapping = THREE.AgXToneMapping;
renderer.toneMappingExposure = 1.5;
renderer.shadowMap.enabled = true;
renderer.shadowMap.colored = true;
renderer.shadowMap.transmitted = true;
renderer.inspector = new Inspector();
container.appendChild( renderer.domElement );

Expand Down
2 changes: 1 addition & 1 deletion examples/webgpu_volume_caustics.html
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@

renderer = new THREE.WebGPURenderer( { antialias: true } );
renderer.shadowMap.enabled = true;
renderer.shadowMap.colored = true;
renderer.shadowMap.transmitted = true;
renderer.inspector = new Inspector();
renderer.setPixelRatio( window.devicePixelRatio );
renderer.setSize( window.innerWidth, window.innerHeight );
Expand Down
2 changes: 1 addition & 1 deletion src/nodes/accessors/BufferAttributeNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -366,7 +366,7 @@ function createBufferAttribute( array, type = null, stride = 0, offset = 0, usag

}

return new BufferAttributeNode( array, type, stride, offset );
return new BufferAttributeNode( array, type, stride, offset ).setUsage( usage );

}

Expand Down
17 changes: 10 additions & 7 deletions src/nodes/accessors/InstanceNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ class InstanceNode extends Node {

if ( instanceMatrixNode === null ) {

instanceMatrixNode = this._createInstanceMatrixNode( true );
instanceMatrixNode = this._createInstanceMatrixNode( true, builder );

this.instanceMatrixNode = instanceMatrixNode;

Expand Down Expand Up @@ -229,7 +229,7 @@ class InstanceNode extends Node {

// update version if necessary

if ( this.instanceMatrix.usage !== DynamicDrawUsage && this.instanceMatrix.version !== this.buffer.version ) {
if ( this.instanceMatrix.version !== this.buffer.version ) {

this.buffer.version = this.instanceMatrix.version;

Expand All @@ -242,7 +242,7 @@ class InstanceNode extends Node {
this.bufferColor.clearUpdateRanges();
this.bufferColor.updateRanges.push( ... this.instanceColor.updateRanges );

if ( this.instanceColor.usage !== DynamicDrawUsage && this.instanceColor.version !== this.bufferColor.version ) {
if ( this.instanceColor.version !== this.bufferColor.version ) {

this.bufferColor.version = this.instanceColor.version;

Expand Down Expand Up @@ -272,7 +272,7 @@ class InstanceNode extends Node {

instancedMesh.previousInstanceMatrix = this.instanceMatrix.clone();

this.previousInstanceMatrixNode = this._createInstanceMatrixNode( false );
this.previousInstanceMatrixNode = this._createInstanceMatrixNode( false, builder );

}

Expand All @@ -285,9 +285,10 @@ class InstanceNode extends Node {
*
* @private
* @param {boolean} assignBuffer - Whether the created interleaved buffer should be assigned to the `buffer` member or not.
* @param {NodeBuilder} builder - A reference to the current node builder.
* @return {Node} The instance matrix node.
*/
_createInstanceMatrixNode( assignBuffer ) {
_createInstanceMatrixNode( assignBuffer, builder ) {

let instanceMatrixNode;

Expand All @@ -300,9 +301,11 @@ class InstanceNode extends Node {

} else {

// Both backends have ~64kb UBO limit; fallback to attributes above 1000 matrices.
// WebGPU has a 64kb UBO limit, WebGL 2 ensures only 16KB; fallback to attributes if a certain count is exceeded

if ( count <= 1000 ) {
const limit = ( builder.renderer.backend.isWebGPUBackend === true ) ? 1000 : 250;

if ( count <= limit ) {

instanceMatrixNode = buffer( instanceMatrix.array, 'mat4', Math.max( count, 1 ) ).element( instanceIndex );

Expand Down
2 changes: 1 addition & 1 deletion src/nodes/lighting/ShadowNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -518,7 +518,7 @@ class ShadowNode extends ShadowBaseNode {

let shadowColor;

if ( renderer.shadowMap.colored === true ) {
if ( renderer.shadowMap.transmitted === true ) {

if ( shadowMap.texture.isCubeTexture ) {

Expand Down
8 changes: 4 additions & 4 deletions src/renderers/common/Renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -658,7 +658,7 @@ class Renderer {
* Shadow map configuration
* @typedef {Object} ShadowMapConfig
* @property {boolean} enabled - Whether to globally enable shadows or not.
* @property {boolean} colored - Whether shadows can have a custom color or not.
* @property {boolean} transmitted - Whether to enable light transmission through non-opaque materials.
* @property {number} type - The shadow map type.
*/

Expand All @@ -669,7 +669,7 @@ class Renderer {
*/
this.shadowMap = {
enabled: false,
colored: false,
transmitted: false,
type: PCFShadowMap
};

Expand Down Expand Up @@ -3053,9 +3053,9 @@ class Renderer {
shadowRGB = material.castShadowNode.rgb;
shadowAlpha = material.castShadowNode.a;

if ( this.shadowMap.colored !== true ) {
if ( this.shadowMap.transmitted !== true ) {

warnOnce( 'Renderer: `shadowMap.colored` needs to be set to `true` when using `material.castShadowNode`.' );
warnOnce( 'Renderer: `shadowMap.transmitted` needs to be set to `true` when using `material.castShadowNode`.' );

}

Expand Down
Loading