Skip to content

What Do We Have

Video Lecture

Section Video Links
What Do We Have What Do We Have

Description

Note

In this lesson, we also introduce,

  • the renderer.debug.getShaderAsync() method

  • the TSL named imports of color, texture, convertColorSpace and positionLocal

View The WebGL/WebGPU Shader Source

The TSL code in the Getting Started example was very minimal.

const material = new THREE.NodeMaterial() // comes from three/webgpu
material.fragmentNode = color('crimson') // color comes from three/tsl

When our code was executed, the TSL interpreted our code at runtime and output a WebGL or WebGPU version of our code depending on the capabilities our own browser.

We can read that output shader code.

Add this extra highlighted code to your main.ts script, after the line where the mesh is added to the scene.

./src/main.ts

// ...
scene.add(mesh)

renderer.debug.getShaderAsync(scene, camera, mesh).then((e) => {
  //console.log(e.vertexShader)
  console.log(e.fragmentShader)
})

// ...

Then save and view the browsers console output.

FragmentNode

As we can see, we've set the FragmentNode to be a colour. The FragmentNode is a property of a NodeMaterial.

The NodeMaterial was then used as a material for a classic Threejs PlaneGeometry.

// ...
import { color } from 'three/tsl'

// ...

const material = new THREE.NodeMaterial()
material.fragmentNode = color('crimson')

const mesh = new THREE.Mesh(new THREE.PlaneGeometry(), material)
scene.add(mesh)

// ...

We could instead use a texture.

// ...
import { texture } from 'three/tsl'

// ...

const material = new THREE.NodeMaterial()
//material.fragmentNode = texture('crimson')
material.fragmentNode = texture(
  new THREE.TextureLoader().load('https://sbcode.net/img/grid.png')
)

const mesh = new THREE.Mesh(new THREE.PlaneGeometry(), material)
scene.add(mesh)

// ...

The texture colours seem a bit dulled out. We can make them more vivid for our monitor by changing the colorspace.

// ...
import { texture, convertColorSpace } from 'three/tsl'

// ...

const material = new THREE.NodeMaterial()
//material.fragmentNode = texture('crimson')
material.fragmentNode = convertColorSpace(
  texture(new THREE.TextureLoader().load('https://sbcode.net/img/grid.png')),
  THREE.SRGBColorSpace,
  THREE.LinearSRGBColorSpace
)

const mesh = new THREE.Mesh(new THREE.PlaneGeometry(), material)
scene.add(mesh)

// ...

When just using colors or textures in your TSL and not doing much else, you can just stick with the classic Threejs materials. This saves importing extra code, and the time delay when TSL interprets the source code.

In this course though, we will be doing more complicated things than that. We will be experimenting a lot with positional coordinates to customise the output.

So next, we will use the TSL named import of positionLocal to access the fragment shader positional coordinates and use that to decide the colours.

// ...
import { positionLocal } from 'three/tsl'

// ...

const material = new THREE.NodeMaterial()
//material.fragmentNode = texture('crimson')
// material.fragmentNode = convertColorSpace(
//   texture(new THREE.TextureLoader().load('https://sbcode.net/img/grid.png')),
//   THREE.SRGBColorSpace,
//   THREE.LinearSRGBColorSpace
// )
material.fragmentNode = positionLocal

const mesh = new THREE.Mesh(new THREE.PlaneGeometry(), material)
scene.add(mesh)

// ...

Final Script

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
import './style.css'
import * as THREE from 'three/webgpu'
import { positionLocal } from 'three/tsl'
import { OrbitControls } from 'three/addons/controls/OrbitControls.js'

const scene = new THREE.Scene()

const camera = new THREE.PerspectiveCamera(
  75,
  window.innerWidth / window.innerHeight,
  0.1,
  10
)
camera.position.z = 1

const renderer = new THREE.WebGPURenderer()
renderer.setSize(window.innerWidth, window.innerHeight)
document.body.appendChild(renderer.domElement)
renderer.setAnimationLoop(animate)

window.addEventListener('resize', function () {
  camera.aspect = window.innerWidth / window.innerHeight
  camera.updateProjectionMatrix()
  renderer.setSize(window.innerWidth, window.innerHeight)
})

const controls = new OrbitControls(camera, renderer.domElement)
controls.enableDamping = true

const material = new THREE.NodeMaterial()
// material.fragmentNode = color('crimson')
// material.fragmentNode = convertColorSpace(
//   texture(new THREE.TextureLoader().load('https://sbcode.net/img/grid.png')),
//   THREE.SRGBColorSpace,
//   THREE.LinearSRGBColorSpace
// )
material.fragmentNode = positionLocal

const mesh = new THREE.Mesh(new THREE.PlaneGeometry(), material)
scene.add(mesh)

renderer.debug.getShaderAsync(scene, camera, mesh).then((e) => {
  //console.log(e.vertexShader)
  console.log(e.fragmentShader)
})

function animate() {
  controls.update()

  renderer.render(scene, camera)
}

Working Example

<>