Skip to content

Commit

Permalink
WebGPURenderer: Fix Material Arrays and Geometry Groups (mrdoob#29278)
Browse files Browse the repository at this point in the history
* WebGPURenderer: Fix Material Arrays and Geometry Groups

* rename to materials

* update screenshot name too
  • Loading branch information
RenaudRohlinger authored Aug 31, 2024
1 parent 701c101 commit 8ca1d4d
Show file tree
Hide file tree
Showing 7 changed files with 247 additions and 13 deletions.
1 change: 1 addition & 0 deletions examples/files.json
Original file line number Diff line number Diff line change
Expand Up @@ -345,6 +345,7 @@
"webgpu_loader_gltf_transmission",
"webgpu_loader_materialx",
"webgpu_materials",
"webgpu_materials_arrays",
"webgpu_materials_basic",
"webgpu_materials_displacementmap",
"webgpu_materials_envmaps",
Expand Down
Binary file added examples/screenshots/webgpu_materials_arrays.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions examples/tags.json
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,7 @@
"webgpu_materials_lightmap": [ "shadow" ],
"webgpu_materials_sss": [ "subsurface scattering", "derivatives", "translucency" ],
"webgpu_materials_transmission": [ "alpha" ],
"webgpu_materials_arrays": [ "webgpu", "geometry", "groups", "array" ],
"webgpu_mirror": [ "reflection" ],
"webgpu_multiple_rendertargets": [ "mrt" ],
"webgpu_multiple_rendertargets_readback": [ "mrt" ],
Expand Down
173 changes: 173 additions & 0 deletions examples/webgpu_materials_arrays.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>three.js webgpu - materials arrays and geometry groups</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>
<div id="info">
<a href="https://threejs.org" target="_blank" rel="noopener">three.js</a> - WebGPU Materials Arrays and Geometry Groups<br />
</div>

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

<script type="module">

import * as THREE from 'three';

import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

let renderer, scene, camera, controls;
let planeMesh, boxMesh, boxMeshWireframe, planeMeshWireframe;
let materials;

const api = {
webgpu: true
};


init( ! api.webgpu );

function init( forceWebGL = false ) {

if ( renderer ) {

renderer.dispose();
controls.dispose();
document.body.removeChild( renderer.domElement );

}

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

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

// camera
camera = new THREE.PerspectiveCamera( 40, window.innerWidth / window.innerHeight, 1, 100 );
camera.position.set( 0, 0, 10 );

// controls
controls = new OrbitControls( camera, renderer.domElement );

// materials
materials = [
new THREE.MeshBasicMaterial( { color: 0xff1493, side: THREE.DoubleSide } ),
new THREE.MeshBasicMaterial( { color: 0x0000ff, side: THREE.DoubleSide } ),
new THREE.MeshBasicMaterial( { color: 0x00ff00, side: THREE.DoubleSide } ),
];

// plane geometry
const planeGeometry = new THREE.PlaneGeometry( 1, 1, 4, 4 );

planeGeometry.clearGroups();
const numFacesPerRow = 4; // Number of faces in a row (since each face is made of 2 triangles)

planeGeometry.addGroup( 0, 6 * numFacesPerRow, 0 );
planeGeometry.addGroup( 6 * numFacesPerRow, 6 * numFacesPerRow, 1 );
planeGeometry.addGroup( 12 * numFacesPerRow, 6 * numFacesPerRow, 2 );

// box geometry
const boxGeometry = new THREE.BoxGeometry( .75, .75, .75 );

boxGeometry.clearGroups();
boxGeometry.addGroup( 0, 6, 0 ); // front face
boxGeometry.addGroup( 6, 6, 0 ); // back face
boxGeometry.addGroup( 12, 6, 2 ); // top face
boxGeometry.addGroup( 18, 6, 2 ); // bottom face
boxGeometry.addGroup( 24, 6, 1 ); // left face
boxGeometry.addGroup( 30, 6, 1 ); // right face

scene.background = forceWebGL ? new THREE.Color( 0x000000 ) : new THREE.Color( 0x222222 );

// meshes
planeMesh = new THREE.Mesh( planeGeometry, materials );

const materialsWireframe = [];

for ( let index = 0; index < materials.length; index ++ ) {

const material = new THREE.MeshBasicMaterial( { color: materials[ index ].color, side: THREE.DoubleSide, wireframe: true } );
materialsWireframe.push( material );

}

planeMeshWireframe = new THREE.Mesh( planeGeometry, materialsWireframe );
boxMeshWireframe = new THREE.Mesh( boxGeometry, materialsWireframe );

boxMesh = new THREE.Mesh( boxGeometry, materials );

planeMesh.position.set( - 1.5, - 1, 0 );
boxMesh.position.set( 1.5, - 0.75, 0 );
boxMesh.rotation.set( - Math.PI / 8, Math.PI / 4, Math.PI / 4 );

planeMeshWireframe.position.set( - 1.5, 1, 0 );
boxMeshWireframe.position.set( 1.5, 1.25, 0 );
boxMeshWireframe.rotation.set( - Math.PI / 8, Math.PI / 4, Math.PI / 4 );

scene.add( planeMesh, planeMeshWireframe );
scene.add( boxMesh, boxMeshWireframe );

}

function animate() {

boxMesh.rotation.y += 0.005;
boxMesh.rotation.x += 0.005;
boxMeshWireframe.rotation.y += 0.005;
boxMeshWireframe.rotation.x += 0.005;
renderer.render( scene, camera );

}


// gui

const gui = new GUI();

gui.add( api, 'webgpu' ).onChange( () => {

init( ! api.webgpu );

} );

// listeners

window.addEventListener( 'resize', onWindowResize );

function onWindowResize() {

const width = window.innerWidth;
const height = window.innerHeight;

camera.aspect = width / height;
camera.updateProjectionMatrix();

renderer.setSize( width, height );

}

</script>

</body>
</html>
3 changes: 2 additions & 1 deletion src/renderers/common/Renderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -1570,7 +1570,8 @@ class Renderer {
_renderObjectDirect( object, material, scene, camera, lightsNode, group, passId ) {

const renderObject = this._objects.get( object, material, scene, camera, lightsNode, this._currentRenderContext, passId );
renderObject.drawRange = group || object.geometry.drawRange;
renderObject.drawRange = object.geometry.drawRange;
renderObject.group = group;

//

Expand Down
37 changes: 31 additions & 6 deletions src/renderers/webgl-fallback/WebGLBackend.js
Original file line number Diff line number Diff line change
Expand Up @@ -637,7 +637,6 @@ class WebGLBackend extends Backend {

const geometry = renderObject.geometry;
const drawRange = renderObject.drawRange;
const firstVertex = drawRange.start;

//

Expand Down Expand Up @@ -669,6 +668,7 @@ class WebGLBackend extends Backend {
}

//
let rangeFactor = 1;

const renderer = this.bufferRenderer;

Expand All @@ -683,6 +683,8 @@ class WebGLBackend extends Backend {
state.setLineWidth( material.wireframeLinewidth * this.renderer.getPixelRatio() );
renderer.mode = gl.LINES;

rangeFactor = 2;

} else {

renderer.mode = gl.TRIANGLES;
Expand All @@ -693,28 +695,51 @@ class WebGLBackend extends Backend {

//

const group = renderObject.group;

let count;

renderer.object = object;


let firstVertex = drawRange.start * rangeFactor;
let lastVertex = ( drawRange.start + drawRange.count ) * rangeFactor;

if ( group !== null ) {

firstVertex = Math.max( firstVertex, group.start * rangeFactor );
lastVertex = Math.min( lastVertex, ( group.start + group.count ) * rangeFactor );

}

if ( index !== null ) {

const indexData = this.get( index );
const indexCount = ( drawRange.count !== Infinity ) ? drawRange.count : index.count;
const indexCount = index.count;

renderer.index = index.count;
renderer.type = indexData.type;

count = indexCount;
firstVertex = Math.max( firstVertex, 0 );
lastVertex = Math.min( lastVertex, indexCount );

} else {

renderer.index = 0;

const vertexCount = ( drawRange.count !== Infinity ) ? drawRange.count : geometry.attributes.position.count;
const vertexCount = geometry.attributes.position.count;

firstVertex = Math.max( firstVertex, 0 );
lastVertex = Math.min( lastVertex, vertexCount );

}

const count = lastVertex - firstVertex;

if ( count < 0 || count === Infinity ) return;

if ( index !== null ) {

count = vertexCount;
firstVertex *= index.array.BYTES_PER_ELEMENT;

}

Expand Down
45 changes: 39 additions & 6 deletions src/renderers/webgpu/WebGPUBackend.js
Original file line number Diff line number Diff line change
Expand Up @@ -829,7 +829,7 @@ class WebGPUBackend extends Backend {

draw( renderObject, info ) {

const { object, geometry, context, pipeline } = renderObject;
const { object, material, geometry, context, pipeline, group } = renderObject;
const bindings = renderObject.getBindings();
const renderContextData = this.get( context );
const pipelineGPU = this.get( pipeline ).pipeline;
Expand Down Expand Up @@ -930,7 +930,26 @@ class WebGPUBackend extends Backend {
// draw

const drawRange = renderObject.drawRange;
const firstVertex = drawRange.start;

let rangeFactor = 1;

if ( material.wireframe === true && ! object.isPoints && ! object.isLineSegments && ! object.isLine && ! object.isLineLoop ) {

rangeFactor = 2;

}

let firstVertex = drawRange.start * rangeFactor;
let lastVertex = ( drawRange.start + drawRange.count ) * rangeFactor;

if ( group !== null ) {

firstVertex = Math.max( firstVertex, group.start * rangeFactor );
lastVertex = Math.min( lastVertex, ( group.start + group.count ) * rangeFactor );

}



const instanceCount = this.getInstanceCount( renderObject );
if ( instanceCount === 0 ) return;
Expand All @@ -955,18 +974,32 @@ class WebGPUBackend extends Backend {

} else if ( hasIndex === true ) {

const indexCount = ( drawRange.count !== Infinity ) ? drawRange.count : index.count;
const indexCount = index.count;

firstVertex = Math.max( firstVertex, 0 );
lastVertex = Math.min( lastVertex, indexCount );

const count = lastVertex - firstVertex;

passEncoderGPU.drawIndexed( indexCount, instanceCount, firstVertex, 0, 0 );
if ( count < 0 || count === Infinity ) return;

passEncoderGPU.drawIndexed( count, instanceCount, firstVertex, 0, 0 );

info.update( object, indexCount, instanceCount );

} else {

const positionAttribute = geometry.attributes.position;
const vertexCount = ( drawRange.count !== Infinity ) ? drawRange.count : positionAttribute.count;
const vertexCount = positionAttribute.count;

firstVertex = Math.max( firstVertex, 0 );
lastVertex = Math.min( lastVertex, vertexCount );

const count = lastVertex - firstVertex;

if ( count < 0 || count === Infinity ) return;

passEncoderGPU.draw( vertexCount, instanceCount, firstVertex, 0 );
passEncoderGPU.draw( count, instanceCount, firstVertex, 0 );

info.update( object, vertexCount, instanceCount );

Expand Down

0 comments on commit 8ca1d4d

Please sign in to comment.