Skip to content

Commit 536adc0

Browse files
Add support for WebGPURenderer and TSL example (#5655)
* 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]>
1 parent fc8a5c6 commit 536adc0

File tree

13 files changed

+154
-18
lines changed

13 files changed

+154
-18
lines changed

examples/index.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,7 @@ <h2>Examples</h2>
158158
<li><a href="mixed-reality/anchor/">Anchor (Mixed Reality)</a></li>
159159
<li><a href="mixed-reality/real-world-meshing/">Real World Meshing (Mixed Reality)</a></li>
160160
<li><a href="boilerplate/importmap/">Importmap (import teapot geometry from three/addons)</a></li>
161+
<li><a href="showcase/webgpu/">WebGPU renderer and TSL</a></li>
161162
</ul>
162163

163164
<h2>Examples from Documentation</h2>

examples/showcase/webgpu/index.html

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,107 @@
1+
<!doctype html>
2+
<html>
3+
<head>
4+
<meta charset="utf-8" />
5+
<title>WebGPURenderer and TSL • A-Frame</title>
6+
<meta name="description" content="WebGPU renderer and TSL • A-Frame" />
7+
<script type="importmap">
8+
{
9+
"imports": {
10+
"aframe": "../../../dist/aframe-master.module.min.js",
11+
"three": "../../../super-three-package/build/three.webgpu.js",
12+
"three/webgpu": "../../../super-three-package/build/three.webgpu.js",
13+
"three/tsl": "../../../super-three-package/build/three.tsl.js",
14+
"three/addons/": "../../../super-three-package/examples/jsm/"
15+
}
16+
}
17+
</script>
18+
<script type="module">
19+
import AFRAME from 'aframe';
20+
import { color, cos, float, mix, range, sin, time, uniform, uv, vec3, vec4, PI2 } from 'three/tsl';
21+
import { GUI } from 'three/addons/libs/lil-gui.module.min.js';
22+
23+
AFRAME.registerComponent('galaxy', {
24+
init() {
25+
const material = new THREE.SpriteNodeMaterial({
26+
transparent: true,
27+
depthWrite: false,
28+
blending: THREE.AdditiveBlending
29+
});
30+
31+
const size = uniform(0.08);
32+
material.scaleNode = range(0, 1).mul(size);
33+
34+
const radiusRatio = range(0, 1);
35+
const radius = radiusRatio.pow(1.5).mul(5).toVar();
36+
37+
const branches = 3;
38+
const branchAngle = range(0, branches).floor().mul(PI2.div(branches));
39+
const angle = branchAngle.add(time.mul(radiusRatio.oneMinus()));
40+
41+
const position = vec3(cos(angle), 0, sin(angle)).mul(radius);
42+
43+
const randomOffset = range(vec3(-1), vec3(1)).pow(3).mul(radiusRatio).add(0.2);
44+
45+
material.positionNode = position.add(randomOffset);
46+
47+
const colorInside = uniform(color('#ffa575'));
48+
const colorOutside = uniform(color('#311599'));
49+
const colorFinal = mix(colorInside, colorOutside, radiusRatio.oneMinus().pow(2).oneMinus());
50+
const alpha = float(0.1).div(uv().sub(0.5).length()).sub(0.2);
51+
material.colorNode = vec4(colorFinal, alpha);
52+
53+
const mesh = new THREE.InstancedMesh(new THREE.PlaneGeometry(1, 1), material, 20000);
54+
this.el.setObject3D('mesh', mesh);
55+
56+
// debug
57+
58+
const gui = new GUI();
59+
this.gui = gui;
60+
61+
gui.add(size, 'value', 0, 1, 0.001).name('size');
62+
63+
gui
64+
.addColor({ color: colorInside.value.getHex(THREE.SRGBColorSpace) }, 'color')
65+
.name('colorInside')
66+
.onChange(function (value) {
67+
colorInside.value.set(value);
68+
});
69+
70+
gui
71+
.addColor({ color: colorOutside.value.getHex(THREE.SRGBColorSpace) }, 'color')
72+
.name('colorOutside')
73+
.onChange(function (value) {
74+
colorOutside.value.set(value);
75+
});
76+
},
77+
remove() {
78+
const mesh = this.el.getObject3D('mesh');
79+
if (mesh) {
80+
mesh.material.dispose();
81+
mesh.geometry.dispose();
82+
this.el.removeObject3D('mesh');
83+
this.gui.destroy();
84+
}
85+
}
86+
});
87+
</script>
88+
<script
89+
src="https://cdn.jsdelivr.net/npm/[email protected]/dist/aframe-orbit-controls.min.js"
90+
type="module"
91+
></script>
92+
<script src="../../js/info-message.js" type="module"></script>
93+
</head>
94+
<body>
95+
<a-scene background="color: #201919" info-message="htmlSrc: #messageText">
96+
<a-assets>
97+
<a-asset-item id="messageText" src="message.html"></a-asset-item>
98+
</a-assets>
99+
<a-entity galaxy></a-entity>
100+
<a-entity
101+
camera="fov: 50; near: 0.1; far: 100"
102+
look-controls="enabled: false"
103+
orbit-controls="target: 0 0 0; minDistance: 0.1; maxDistance: 50; initialPosition: 4 2 5; enableDamping: true; rotateSpeed: 1"
104+
></a-entity>
105+
</a-scene>
106+
</body>
107+
</html>

examples/showcase/webgpu/message.html

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
<p>
2+
Galaxy
3+
<br />
4+
Based on
5+
<a href="https://threejs-journey.com/lessons/animated-galaxy" target="_blank" rel="noopener">Three.js Journey</a>
6+
lessons
7+
</p>

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
"prerelease": "node scripts/release.js 1.5.0 1.6.0",
2828
"start": "npm run dev",
2929
"start:https": "npm run dev -- --server-type https",
30+
"start:webgpu": "cross-env WEBGPU=true npm run dev -- --server-type https",
3031
"test": "karma start ./tests/karma.conf.js",
3132
"test:docs": "node scripts/docsLint.js",
3233
"test:firefox": "npm test -- --browsers Firefox",

src/core/a-node.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ export var knownTags = {
1414
'a-entity': true
1515
};
1616

17-
function isNode (node) {
18-
return node.tagName.toLowerCase() in knownTags || node.isNode;
17+
function isANode (node) {
18+
return node.tagName.toLowerCase() in knownTags || node.isANode;
1919
}
2020

2121
/**
@@ -29,7 +29,7 @@ export class ANode extends HTMLElement {
2929
super();
3030
this.computedMixinStr = '';
3131
this.hasLoaded = false;
32-
this.isNode = true;
32+
this.isANode = true;
3333
this.mixinEls = [];
3434
}
3535

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

121121
// Default to waiting for all nodes.
122-
childFilter = childFilter || isNode;
122+
childFilter = childFilter || isANode;
123123
// Wait for children to load (if any), then load.
124124
children = this.getChildren();
125125
childrenLoaded = children.filter(childFilter).map(function (child) {

src/core/scene/a-scene.js

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -547,7 +547,10 @@ export class AScene extends AEntity {
547547
};
548548
}
549549

550-
renderer = this.renderer = new THREE.WebGLRenderer(rendererConfig);
550+
renderer = this.renderer = new (THREE.WebGLRenderer || THREE.WebGPURenderer)(rendererConfig);
551+
if (!renderer.xr.setPoseTarget) {
552+
renderer.xr.setPoseTarget = function () {};
553+
}
551554
renderer.setPixelRatio(window.devicePixelRatio);
552555

553556
if (this.camera) { renderer.xr.setPoseTarget(this.camera.el.object3D); }

src/shaders/msdf.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { registerShader } from '../core/shader.js';
22
import THREE from '../lib/three.js';
3+
import { UniformsUtils } from 'three/src/renderers/shaders/UniformsUtils.js';
4+
import { UniformsLib } from 'three/src/renderers/shaders/UniformsLib.js';
35

46
var VERTEX_SHADER = [
57
'#include <common>',
@@ -84,8 +86,8 @@ export var Shader = registerShader('msdf', {
8486
fragmentShader: FRAGMENT_SHADER,
8587

8688
init: function () {
87-
this.uniforms = THREE.UniformsUtils.merge([
88-
THREE.UniformsLib.fog,
89+
this.uniforms = UniformsUtils.merge([
90+
UniformsLib.fog,
8991
this.initUniforms()
9092
]);
9193
this.material = new THREE.ShaderMaterial({

src/shaders/sdf.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { registerShader } from '../core/shader.js';
22
import THREE from '../lib/three.js';
3+
import { UniformsUtils } from 'three/src/renderers/shaders/UniformsUtils.js';
4+
import { UniformsLib } from 'three/src/renderers/shaders/UniformsLib.js';
35

46
var VERTEX_SHADER = [
57
'#include <common>',
@@ -94,8 +96,8 @@ export var Shader = registerShader('sdf', {
9496
fragmentShader: FRAGMENT_SHADER,
9597

9698
init: function () {
97-
this.uniforms = THREE.UniformsUtils.merge([
98-
THREE.UniformsLib.fog,
99+
this.uniforms = UniformsUtils.merge([
100+
UniformsLib.fog,
99101
this.initUniforms()
100102
]);
101103
this.material = new THREE.ShaderMaterial({

src/systems/camera.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ export var System = registerSystem('camera', {
5959
});
6060

6161
// Load camera and wait for camera to initialize.
62-
if (cameraEls[i].isNode) {
62+
if (cameraEls[i].isANode) {
6363
cameraEls[i].load();
6464
} else {
6565
cameraEls[i].addEventListener('nodeready', function () {

tests/core/a-entity.test.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ suite('a-entity', function () {
3737
});
3838

3939
test('createdCallback', function () {
40-
assert.ok(el.isNode);
40+
assert.ok(el.isANode);
4141
assert.ok(el.isEntity);
4242
});
4343

@@ -669,9 +669,9 @@ suite('a-entity', function () {
669669
el.appendChild(a);
670670
el.appendChild(b);
671671
setTimeout(function () {
672-
a.isNode = false;
672+
a.isANode = false;
673673
a.hasLoaded = false;
674-
b.isNode = false;
674+
b.isANode = false;
675675
b.hasLoaded = false;
676676
el.hasLoaded = false;
677677
el.addEventListener('loaded', function () {
@@ -697,9 +697,9 @@ suite('a-entity', function () {
697697
el.appendChild(a);
698698
el.appendChild(b);
699699
setTimeout(function () {
700-
a.isNode = false;
700+
a.isANode = false;
701701
a.hasLoaded = false;
702-
b.isNode = false;
702+
b.isANode = false;
703703
b.hasLoaded = false;
704704
el.hasLoaded = false;
705705
el.addEventListener('loaded', function () {

tests/core/scene/a-scene.test.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,9 @@ suite('a-scene (without renderer) - WebXR', function () {
7676
// Mock renderer.
7777
assert.ok(sceneEl.renderer);
7878
// Mock renderer is not a real WebGLRenderer.
79-
assert.notOk(sceneEl.renderer instanceof THREE.WebGLRenderer);
79+
assert.notOk(
80+
(THREE.WebGLRenderer && sceneEl.renderer instanceof THREE.WebGLRenderer) ||
81+
(THREE.WebGPURenderer && sceneEl.renderer instanceof THREE.WebGPURenderer));
8082
});
8183
});
8284

vendor/rStats.extras.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -138,7 +138,7 @@ window.threeStats = function ( renderer ) {
138138
function _update () {
139139

140140
_rS( 'renderer.info.memory.geometries' ).set( renderer.info.memory.geometries );
141-
_rS( 'renderer.info.programs' ).set( renderer.info.programs.length );
141+
_rS( 'renderer.info.programs' ).set( renderer.info.programs?.length ?? NaN );
142142
_rS( 'renderer.info.memory.textures' ).set( renderer.info.memory.textures );
143143
_rS( 'renderer.info.render.calls' ).set( renderer.info.render.calls );
144144
_rS( 'renderer.info.render.triangles' ).set( renderer.info.render.triangles );

webpack.common.cjs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
1+
var path = require('path');
12
var webpack = require('webpack');
23

3-
module.exports = {
4+
var config = {
45
entry: './src/index.js',
56
devtool: 'source-map',
67
plugins: [
@@ -28,3 +29,13 @@ module.exports = {
2829
]
2930
}
3031
};
32+
33+
if (process.env.WEBGPU === 'true') {
34+
config.resolve = {
35+
alias: {
36+
'three$': path.resolve(__dirname, 'node_modules/three/build/three.webgpu.js')
37+
}
38+
};
39+
}
40+
41+
module.exports = config;

0 commit comments

Comments
 (0)