Skip to content

Commit d2e6cfc

Browse files
authored
docs: Small @typegpu/three tutorial (#2050)
1 parent b2f7db5 commit d2e6cfc

7 files changed

Lines changed: 253 additions & 101 deletions

File tree

apps/typegpu-docs/src/content/docs/ecosystem/typegpu-three.mdx

Lines changed: 0 additions & 101 deletions
This file was deleted.
84 KB
Loading
21.9 KB
Loading
21.5 KB
Loading
92 KB
Loading
75.3 KB
Loading
Lines changed: 253 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,253 @@
1+
---
2+
title: "@typegpu/three"
3+
---
4+
5+
import { Image } from 'astro:assets';
6+
import banner from './banner.webp';
7+
8+
<Image src={banner} alt="A banner image showcasing a snippet of code using @typegpu/three" />
9+
10+
[TSL (Three.js Shading Language)](https://github.com/mrdoob/three.js/wiki/Three.js-Shading-Language#function) is a node-based shader composition system for Three.js. Shader logic and control flow is built up by composing special functions,
11+
with a focus on composability, intuitive sharing of logic across modules and customizability. TypeGPU fits naturally into this system thanks to the `@typegpu/three` package. You can choose to write your TSL building blocks in TypeGPU, which has a few benefits:
12+
- Control-flow like `if` statements and `for` loops makes use of familiar JavaScript syntax instead of special functions.
13+
- The code you write is semantically valid JavaScript, with types flowing through each expression.
14+
- Unit-testability, since you can call these functions on the CPU
15+
16+
You can see a [direct comparison between TSL and TypeGPU here](#differences-between-tsl-and-typegpu).
17+
18+
## Setup
19+
20+
import { Tabs, TabItem } from '@astrojs/starlight/components';
21+
22+
Refer to [TypeGPU's installation guide](/TypeGPU/getting-started/) for setting up TypeGPU if you haven't already.
23+
After that, install Three.js and @typegpu/three using the package manager of your choice.
24+
25+
<Tabs syncKey="package-manager">
26+
<TabItem label="npm" icon="seti:npm">
27+
```sh frame=none
28+
npm install three @typegpu/three
29+
```
30+
</TabItem>
31+
<TabItem label="pnpm" icon="pnpm">
32+
```sh frame=none
33+
pnpm add three @typegpu/three
34+
```
35+
</TabItem>
36+
<TabItem label="yarn" icon="seti:yarn">
37+
```sh frame=none
38+
yarn add three @typegpu/three
39+
```
40+
</TabItem>
41+
</Tabs>
42+
43+
44+
## Using TypeGPU with TSL
45+
46+
import cube1 from './cube1.png';
47+
import cube2 from './cube2.png';
48+
import cube3 from './cube3.png';
49+
import cube4 from './cube4.png';
50+
51+
Calling `t3.toTSL` with a TypeGPU function will return a TSL node, which can then be plugged into material properties, or used by other nodes.
52+
53+
<div class="flex flex-col md:flex-row gap-4 items-stretch md:items-center justify-between overflow-hidden">
54+
<div class="flex-1 w-auto min-w-[0]">
55+
56+
```ts twoslash
57+
import tgpu from 'typegpu';
58+
import * as d from 'typegpu/data';
59+
// ---cut---
60+
import * as THREE from 'three/webgpu';
61+
import * as t3 from '@typegpu/three';
62+
63+
const material = new THREE.MeshBasicNodeMaterial();
64+
65+
material.colorNode = t3.toTSL(() => {
66+
'use gpu';
67+
return d.vec4f(1, 0, 0, 1); // just red
68+
});
69+
```
70+
71+
</div>
72+
<div class="flex-none">
73+
<Image src={cube1} alt="A red cube" class="mb-4 max-w-[16rem]" />
74+
</div>
75+
</div>
76+
77+
We can use other TSL nodes within TypeGPU functions with `t3.fromTSL`:
78+
79+
<div class="flex flex-col md:flex-row gap-4 items-stretch md:items-center justify-between overflow-hidden">
80+
<div class="flex-1 w-auto min-w-[0]">
81+
82+
```diff lang="ts"
83+
import * as THREE from 'three/webgpu';
84+
+import * as TSL from 'three/tsl';
85+
import * as t3 from '@typegpu/three';
86+
87+
const material = new THREE.MeshBasicNodeMaterial();
88+
89+
+const albedo = TSL.color('magenta').mul(0.5);
90+
+const albedoAccess = t3.fromTSL(albedo, d.vec3f);
91+
92+
material.colorNode = t3.toTSL(() => {
93+
'use gpu';
94+
+ return d.vec4f(albedoAccess.$, 1);
95+
});
96+
```
97+
98+
</div>
99+
<div class="flex-none">
100+
<Image src={cube2} alt="A magenta cube" class="mb-4 max-w-[16rem]" />
101+
</div>
102+
</div>
103+
104+
There are a handful of builtin TSL node accessors in the `t3` namespace:
105+
106+
<div class="flex flex-col md:flex-row gap-4 items-stretch md:items-center justify-between overflow-hidden">
107+
<div class="flex-1 w-auto min-w-[0]">
108+
109+
```ts twoslash
110+
import tgpu from 'typegpu';
111+
import * as d from 'typegpu/data';
112+
// ---cut---
113+
import * as THREE from 'three/webgpu';
114+
import * as t3 from '@typegpu/three';
115+
116+
const material = new THREE.MeshBasicNodeMaterial();
117+
118+
material.colorNode = t3.toTSL(() => {
119+
'use gpu';
120+
const uv = t3.uv().$;
121+
return d.vec4f(uv, 0, 1);
122+
});
123+
```
124+
125+
</div>
126+
<div class="flex-none">
127+
<Image src={cube3} alt="A UV cube" class="mb-4 max-w-[16rem]" />
128+
</div>
129+
</div>
130+
131+
Other TypeGPU functions (user-defined or from libraries) can be called to achieve more complex effects.
132+
133+
<div class="flex flex-col md:flex-row gap-4 items-stretch md:items-center justify-between overflow-hidden">
134+
<div class="flex-1 w-auto min-w-[0]">
135+
136+
```ts twoslash
137+
import tgpu from 'typegpu';
138+
import * as d from 'typegpu/data';
139+
import * as std from 'typegpu/std';
140+
// ---cut---
141+
import { perlin3d } from '@typegpu/noise';
142+
import * as THREE from 'three/webgpu';
143+
import * as t3 from '@typegpu/three';
144+
145+
const material = new THREE.MeshBasicNodeMaterial();
146+
147+
material.colorNode = t3.toTSL(() => {
148+
'use gpu';
149+
const coords = t3.uv().$.mul(2);
150+
const pattern = perlin3d.sample(d.vec3f(coords, t3.time.$ * 0.2));
151+
return d.vec4f(std.tanh(pattern * 5), 0.2, 0.4, 1);
152+
});
153+
```
154+
155+
</div>
156+
<div class="flex-none">
157+
<Image src={cube4} alt="A funky cube" class="mb-4 max-w-[16rem]" />
158+
</div>
159+
</div>
160+
161+
The code and interactive preview of this example [can be found here](/TypeGPU/examples#example=threejs--simple).
162+
163+
## Differences between TSL and TypeGPU
164+
165+
Below are a select few cases comparing TSL and TypeGPU:
166+
167+
### Node definition
168+
169+
TSL:
170+
```ts
171+
const simulate = Fn(() => {
172+
//
173+
// ... TSL code ...
174+
//
175+
});
176+
```
177+
178+
TypeGPU:
179+
```ts twoslash
180+
import * as t3 from '@typegpu/three';
181+
// ---cut---
182+
const simulate = t3.toTSL(() => {
183+
'use gpu';
184+
//
185+
// ... TypeGPU code ...
186+
//
187+
});
188+
```
189+
190+
### Function definition
191+
192+
TSL:
193+
```ts
194+
const oscSine = Fn(([t = time]) => {
195+
return t.add(0.75).mul(Math.PI * 2).sin().mul(0.5).add(0.5);
196+
});
197+
```
198+
199+
TypeGPU:
200+
```ts twoslash
201+
import * as std from 'typegpu/std';
202+
// ---cut---
203+
const oscSine = (t: number) => {
204+
'use gpu';
205+
return std.sin((t + 0.75) * Math.PI * 2) * 0.5 + 0.5;
206+
};
207+
```
208+
209+
### If statements
210+
211+
TSL:
212+
```ts
213+
If(instanceIndex.greaterThanEqual(uint(vertexCount)), () => {
214+
Return();
215+
});
216+
```
217+
218+
TypeGPU:
219+
```ts twoslash
220+
import * as t3 from '@typegpu/three';
221+
declare const vertexCount: number;
222+
const some = () => {
223+
// ---cut-before---
224+
if (t3.instanceIndex.$ >= vertexCount) {
225+
return;
226+
}
227+
// ---cut-after---
228+
};
229+
```
230+
231+
### For loops
232+
233+
TSL:
234+
```ts
235+
Loop({ start: ptrStart, end: ptrEnd, type: 'uint', condition: '<' }, ({ i }) => {
236+
const springId = springListBuffer.element( i ).toVar( 'springId' );
237+
const springForce = springForceBuffer.element( springId );
238+
const springVertexIds = springVertexIdBuffer.element( springId );
239+
const factor = select( springVertexIds.x.equal( instanceIndex ), 1.0, - 1.0 );
240+
force.addAssign( springForce.mul( factor ) );
241+
});
242+
```
243+
244+
TypeGPU:
245+
```ts
246+
for (let i = ptrStart; i < ptrEnd; i++) {
247+
const springId = springListBuffer.$[i];
248+
const springForce = springForceBuffer.$[springId];
249+
const springVertexIds = springVertexIdBuffer.$[springId];
250+
const factor = std.select(-1, 1, springVertexIds.x === idx);
251+
force = force.add(springForce.mul(d.f32(factor)));
252+
}
253+
```

0 commit comments

Comments
 (0)