Skip to content

Commit

Permalink
Add support for WebGPURenderer and TSL example (#5655)
Browse files Browse the repository at this point in the history
* rename isNode to isANode

* add 'npm run start:webgpu' command to use WebGPURenderer

* import UniformsUtils and UniformsLib, those are not exposed on the three/webgpu build

* renderer.info.programs is undefined with three/webgpu

* add empty tsl example

* add galaxy example

* add link to tsl example

* remove mesh and gui in remove

* change title

* rename directory tsl to webgpu

* reformat with 120 chars, single quote and no trailing comma

* reformat to 120 chars

* Update index.html

* Update index.html

---------

Co-authored-by: Diego Marcos <[email protected]>
  • Loading branch information
vincentfretin and dmarcos authored Feb 14, 2025
1 parent fc8a5c6 commit 536adc0
Show file tree
Hide file tree
Showing 13 changed files with 154 additions and 18 deletions.
1 change: 1 addition & 0 deletions examples/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ <h2>Examples</h2>
<li><a href="mixed-reality/anchor/">Anchor (Mixed Reality)</a></li>
<li><a href="mixed-reality/real-world-meshing/">Real World Meshing (Mixed Reality)</a></li>
<li><a href="boilerplate/importmap/">Importmap (import teapot geometry from three/addons)</a></li>
<li><a href="showcase/webgpu/">WebGPU renderer and TSL</a></li>
</ul>

<h2>Examples from Documentation</h2>
Expand Down
107 changes: 107 additions & 0 deletions examples/showcase/webgpu/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
<!doctype html>
<html>
<head>
<meta charset="utf-8" />
<title>WebGPURenderer and TSL • A-Frame</title>
<meta name="description" content="WebGPU renderer and TSL • A-Frame" />
<script type="importmap">
{
"imports": {
"aframe": "../../../dist/aframe-master.module.min.js",
"three": "../../../super-three-package/build/three.webgpu.js",
"three/webgpu": "../../../super-three-package/build/three.webgpu.js",
"three/tsl": "../../../super-three-package/build/three.tsl.js",
"three/addons/": "../../../super-three-package/examples/jsm/"
}
}
</script>
<script type="module">
import AFRAME from 'aframe';
import { color, cos, float, mix, range, sin, time, uniform, uv, vec3, vec4, PI2 } from 'three/tsl';
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';

AFRAME.registerComponent('galaxy', {
init() {
const material = new THREE.SpriteNodeMaterial({
transparent: true,
depthWrite: false,
blending: THREE.AdditiveBlending
});

const size = uniform(0.08);
material.scaleNode = range(0, 1).mul(size);

const radiusRatio = range(0, 1);
const radius = radiusRatio.pow(1.5).mul(5).toVar();

const branches = 3;
const branchAngle = range(0, branches).floor().mul(PI2.div(branches));
const angle = branchAngle.add(time.mul(radiusRatio.oneMinus()));

const position = vec3(cos(angle), 0, sin(angle)).mul(radius);

const randomOffset = range(vec3(-1), vec3(1)).pow(3).mul(radiusRatio).add(0.2);

material.positionNode = position.add(randomOffset);

const colorInside = uniform(color('#ffa575'));
const colorOutside = uniform(color('#311599'));
const colorFinal = mix(colorInside, colorOutside, radiusRatio.oneMinus().pow(2).oneMinus());
const alpha = float(0.1).div(uv().sub(0.5).length()).sub(0.2);
material.colorNode = vec4(colorFinal, alpha);

const mesh = new THREE.InstancedMesh(new THREE.PlaneGeometry(1, 1), material, 20000);
this.el.setObject3D('mesh', mesh);

// debug

const gui = new GUI();
this.gui = gui;

gui.add(size, 'value', 0, 1, 0.001).name('size');

gui
.addColor({ color: colorInside.value.getHex(THREE.SRGBColorSpace) }, 'color')
.name('colorInside')
.onChange(function (value) {
colorInside.value.set(value);
});

gui
.addColor({ color: colorOutside.value.getHex(THREE.SRGBColorSpace) }, 'color')
.name('colorOutside')
.onChange(function (value) {
colorOutside.value.set(value);
});
},
remove() {
const mesh = this.el.getObject3D('mesh');
if (mesh) {
mesh.material.dispose();
mesh.geometry.dispose();
this.el.removeObject3D('mesh');
this.gui.destroy();
}
}
});
</script>
<script
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/aframe-orbit-controls.min.js"
type="module"
></script>
<script src="../../js/info-message.js" type="module"></script>
</head>
<body>
<a-scene background="color: #201919" info-message="htmlSrc: #messageText">
<a-assets>
<a-asset-item id="messageText" src="message.html"></a-asset-item>
</a-assets>
<a-entity galaxy></a-entity>
<a-entity
camera="fov: 50; near: 0.1; far: 100"
look-controls="enabled: false"
orbit-controls="target: 0 0 0; minDistance: 0.1; maxDistance: 50; initialPosition: 4 2 5; enableDamping: true; rotateSpeed: 1"
></a-entity>
</a-scene>
</body>
</html>
7 changes: 7 additions & 0 deletions examples/showcase/webgpu/message.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<p>
Galaxy
<br />
Based on
<a href="https://threejs-journey.com/lessons/animated-galaxy" target="_blank" rel="noopener">Three.js Journey</a>
lessons
</p>
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"prerelease": "node scripts/release.js 1.5.0 1.6.0",
"start": "npm run dev",
"start:https": "npm run dev -- --server-type https",
"start:webgpu": "cross-env WEBGPU=true npm run dev -- --server-type https",
"test": "karma start ./tests/karma.conf.js",
"test:docs": "node scripts/docsLint.js",
"test:firefox": "npm test -- --browsers Firefox",
Expand Down
8 changes: 4 additions & 4 deletions src/core/a-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ export var knownTags = {
'a-entity': true
};

function isNode (node) {
return node.tagName.toLowerCase() in knownTags || node.isNode;
function isANode (node) {
return node.tagName.toLowerCase() in knownTags || node.isANode;
}

/**
Expand All @@ -29,7 +29,7 @@ export class ANode extends HTMLElement {
super();
this.computedMixinStr = '';
this.hasLoaded = false;
this.isNode = true;
this.isANode = true;
this.mixinEls = [];
}

Expand Down Expand Up @@ -119,7 +119,7 @@ export class ANode extends HTMLElement {
if (this.hasLoaded) { return; }

// Default to waiting for all nodes.
childFilter = childFilter || isNode;
childFilter = childFilter || isANode;
// Wait for children to load (if any), then load.
children = this.getChildren();
childrenLoaded = children.filter(childFilter).map(function (child) {
Expand Down
5 changes: 4 additions & 1 deletion src/core/scene/a-scene.js
Original file line number Diff line number Diff line change
Expand Up @@ -547,7 +547,10 @@ export class AScene extends AEntity {
};
}

renderer = this.renderer = new THREE.WebGLRenderer(rendererConfig);
renderer = this.renderer = new (THREE.WebGLRenderer || THREE.WebGPURenderer)(rendererConfig);
if (!renderer.xr.setPoseTarget) {
renderer.xr.setPoseTarget = function () {};
}
renderer.setPixelRatio(window.devicePixelRatio);

if (this.camera) { renderer.xr.setPoseTarget(this.camera.el.object3D); }
Expand Down
6 changes: 4 additions & 2 deletions src/shaders/msdf.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { registerShader } from '../core/shader.js';
import THREE from '../lib/three.js';
import { UniformsUtils } from 'three/src/renderers/shaders/UniformsUtils.js';
import { UniformsLib } from 'three/src/renderers/shaders/UniformsLib.js';

var VERTEX_SHADER = [
'#include <common>',
Expand Down Expand Up @@ -84,8 +86,8 @@ export var Shader = registerShader('msdf', {
fragmentShader: FRAGMENT_SHADER,

init: function () {
this.uniforms = THREE.UniformsUtils.merge([
THREE.UniformsLib.fog,
this.uniforms = UniformsUtils.merge([
UniformsLib.fog,
this.initUniforms()
]);
this.material = new THREE.ShaderMaterial({
Expand Down
6 changes: 4 additions & 2 deletions src/shaders/sdf.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { registerShader } from '../core/shader.js';
import THREE from '../lib/three.js';
import { UniformsUtils } from 'three/src/renderers/shaders/UniformsUtils.js';
import { UniformsLib } from 'three/src/renderers/shaders/UniformsLib.js';

var VERTEX_SHADER = [
'#include <common>',
Expand Down Expand Up @@ -94,8 +96,8 @@ export var Shader = registerShader('sdf', {
fragmentShader: FRAGMENT_SHADER,

init: function () {
this.uniforms = THREE.UniformsUtils.merge([
THREE.UniformsLib.fog,
this.uniforms = UniformsUtils.merge([
UniformsLib.fog,
this.initUniforms()
]);
this.material = new THREE.ShaderMaterial({
Expand Down
2 changes: 1 addition & 1 deletion src/systems/camera.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ export var System = registerSystem('camera', {
});

// Load camera and wait for camera to initialize.
if (cameraEls[i].isNode) {
if (cameraEls[i].isANode) {
cameraEls[i].load();
} else {
cameraEls[i].addEventListener('nodeready', function () {
Expand Down
10 changes: 5 additions & 5 deletions tests/core/a-entity.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ suite('a-entity', function () {
});

test('createdCallback', function () {
assert.ok(el.isNode);
assert.ok(el.isANode);
assert.ok(el.isEntity);
});

Expand Down Expand Up @@ -669,9 +669,9 @@ suite('a-entity', function () {
el.appendChild(a);
el.appendChild(b);
setTimeout(function () {
a.isNode = false;
a.isANode = false;
a.hasLoaded = false;
b.isNode = false;
b.isANode = false;
b.hasLoaded = false;
el.hasLoaded = false;
el.addEventListener('loaded', function () {
Expand All @@ -697,9 +697,9 @@ suite('a-entity', function () {
el.appendChild(a);
el.appendChild(b);
setTimeout(function () {
a.isNode = false;
a.isANode = false;
a.hasLoaded = false;
b.isNode = false;
b.isANode = false;
b.hasLoaded = false;
el.hasLoaded = false;
el.addEventListener('loaded', function () {
Expand Down
4 changes: 3 additions & 1 deletion tests/core/scene/a-scene.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ suite('a-scene (without renderer) - WebXR', function () {
// Mock renderer.
assert.ok(sceneEl.renderer);
// Mock renderer is not a real WebGLRenderer.
assert.notOk(sceneEl.renderer instanceof THREE.WebGLRenderer);
assert.notOk(
(THREE.WebGLRenderer && sceneEl.renderer instanceof THREE.WebGLRenderer) ||
(THREE.WebGPURenderer && sceneEl.renderer instanceof THREE.WebGPURenderer));
});
});

Expand Down
2 changes: 1 addition & 1 deletion vendor/rStats.extras.js
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ window.threeStats = function ( renderer ) {
function _update () {

_rS( 'renderer.info.memory.geometries' ).set( renderer.info.memory.geometries );
_rS( 'renderer.info.programs' ).set( renderer.info.programs.length );
_rS( 'renderer.info.programs' ).set( renderer.info.programs?.length ?? NaN );
_rS( 'renderer.info.memory.textures' ).set( renderer.info.memory.textures );
_rS( 'renderer.info.render.calls' ).set( renderer.info.render.calls );
_rS( 'renderer.info.render.triangles' ).set( renderer.info.render.triangles );
Expand Down
13 changes: 12 additions & 1 deletion webpack.common.cjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
var path = require('path');
var webpack = require('webpack');

module.exports = {
var config = {
entry: './src/index.js',
devtool: 'source-map',
plugins: [
Expand Down Expand Up @@ -28,3 +29,13 @@ module.exports = {
]
}
};

if (process.env.WEBGPU === 'true') {
config.resolve = {
alias: {
'three$': path.resolve(__dirname, 'node_modules/three/build/three.webgpu.js')
}
};
}

module.exports = config;

0 comments on commit 536adc0

Please sign in to comment.