Skip to content

Shaders

Diese Anleitung wird dir helfen, deine ersten Schritte mit Shadern in TresJS zu machen.

Wir werden eine einfache Szene mit einem Blob erstellen. Anschließend animieren wir ihn, um ihn sanft zu verzerren.

WARNING

Es sind Grundkenntnisse über Shader erforderlich

Einrichten der Szene (optional)

Wir importieren alle Module, die wir benötigen. Zusätzlich können wir die Orbit-Controls von Cientos verwenden. Siehe hier, wie das geht.

Nun positionieren wir unsere Kamera an der Position [11,11,11].

Um uns bei der Positionierung zu helfen, fügen wir zum Schluß eine einfache Ebene mit den Maßen [10, 10] hinzu, die um die X-Achse gedreht ist.

vue
<script setup lang="ts">
import { TresCanvas } from '@tresjs/core'
import { OrbitControls } from '@tresjs/cientos'
</script>

<template>
  <TresCanvas
    clear-color="#111"
    window-size
  >
    <OrbitControls />
    <TresPerspectiveCamera :position="[11, 11, 11]" />

    <TresMesh :rotation="[-Math.PI / 2, 0, 0]">
      <TresPlaneGeometry :args="[10, 10]" />
      <TresMeshBasicMaterial color="#444" />
    </TresMesh>
  </TresCanvas>
</template>

ShaderMaterial

Wie du weißt, ist jede Instanz aus ThreeJs in TresJs verfügbar, also können wir auch ShaderMaterial verwenden. Wir müssen lediglich das Präfix Tres hinzufügen, um es zu nutzen.

Für unseren Blob können wir eine einfache SphereGeometry verwenden. Durch das Hinzufügen von widthSegments und heightSegments erzielen wir einen sanften Effekt. Wir platzieren unseren Blob 4 Einheiten entlang der positiven Y-Achse.

html
<TresMesh :position="[0, 4, 0]">
  <TresSphereGeometry :args="[2, 32, 32]" />
  <TresShaderMaterial />
</TresMesh>

Das ShaderMaterial akzeptiert spezielle Props wie uniforms, vertexShader und fragmentShader. Wir können diese Objekte in unserem Skriptbereich erstellen und später der Komponente übergeben.

Für dieses Beispiel sehen unsere Uniforms so aus:

ts
import { Vector2 } from 'three'

//...
const uniforms = {
  uTime: { value: 0 },
  uAmplitude: { value: new Vector2(0.1, 0.1) },
  uFrequency: { value: new Vector2(20, 5) },
}
//..

Unser Fragment-Shader sieht so aus:

ts
//...
const fragmentShader = `
precision mediump float;
varying vec2 vUv;

void main() {
    gl_FragColor = vec4(1.0, vUv.y, 0.5, 1.0);
}
`
//..

Und schließlich unser vertexShader:

ts
const vertexShader = `
uniform vec2 uAmplitude;
uniform vec2 uFrequency;
uniform float uTime;

varying vec2 vUv;

void main() {
    vec4 modelPosition = modelMatrix * vec4(position, 1.0);
    modelPosition.y += sin(modelPosition.x * uFrequency.x - uTime) * uAmplitude.x;
    modelPosition.x += cos(modelPosition.y * uFrequency.y - uTime) * uAmplitude.y;

    vec4 viewPosition = viewMatrix * modelPosition;
    gl_Position = projectionMatrix * viewPosition;
    vUv = uv;
}
`
//..

Animieren des Blobs

Ähnlich wie wir im Beispiel Grundlegende Animationen gelernt haben, beginnen wir, indem wir unseren Blob mit einer Template-Ref referenzieren.

vue
<script setup lang="ts">
import { shallowRef } from 'vue'
import { TresCanvas } from '@tresjs/core'
import { OrbitControls } from '@tresjs/cientos'

const blobRef = shallowRef(null)
//...
</script>

<template>
  <TresCanvas
    clear-color="#111"
    window-size
  >
    <OrbitControls />
    <TresPerspectiveCamera :position="[11, 11, 11]" />
    <TresMesh
      ref="blobRef"
      :position="[0, 4, 0]"
    >
      <TresSphereGeometry :args="[2, 32, 32]" />
      <TresShaderMaterial
        :vertex-shader="vertexShader"
        :fragment-shader="fragmentShader"
        :uniforms="uniforms"
      />
    </TresMesh>
  </TresCanvas>
</template>

Nun können wir den onLoop-Callback nutzen, um uTime zu animieren.

ts
import { TresCanvas, useRenderLoop } from '@tresjs/core'

//...
const { onLoop } = useRenderLoop()

onLoop(({ elapsed }) => {
  if (blobRef.value) {
    blobRef.value.material.uniforms.uTime.value = elapsed
  }
})
//...

Somit haben unseren ersten grundlegenden Shader zum Laufen gebracht!

Verwendung des GLSL vite-plugins (optional)

Dieser Schritt ist vollständig optional und liegt außerhalb des Scopes des TresJs-Teams

Wenn du nicht immer deine Shader inline definieren möchtest, kannst du vite-plugin-glsl nutzen, um GLSL-Code in separate Dateien auszulagern.

Dann könnte man den Code zum Beispiel so organisieren:

├── src/
│   ├── myTresJsComponent.vue
│   ├── shaders/
│       ├── vertexShader.glsl
│       ├── fragmentShader.glsl