<script setup lang="ts">
import { AmbientLight, Clock, Color, Mesh, PlaneGeometry, RawShaderMaterial, Scene, Vector2 } from 'three'

const scene = new Scene()

const bgColor = new Color('#111')
scene.background = bgColor
const { createCamera } = useCamera()

const camera = createCamera(CameraType.Perspective)

camera.position.set(15, 15, 15)
scene.add(camera)

const light = new AmbientLight(0xffaaff)
light.position.set(10, 10, 10)
scene.add(light)

const shaderMaterial = new RawShaderMaterial({
  vertexShader: `
        uniform mat4 projectionMatrix;
        uniform mat4 viewMatrix;
        uniform mat4 modelMatrix;
        uniform vec2 uAmplitude;
        uniform vec2 uFrequency;
        uniform float uTime;

        attribute vec3 position;
        attribute vec2 uv;

        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;
        }
    `,
  fragmentShader: `
        precision mediump float;
        varying vec2 vUv;

        void main() {
            gl_FragColor = vec4(vUv, 0.5, 1.0);
        }
    `,
  uniforms: {
    uTime: { value: 0 },
    uAmplitude: { value: new Vector2(0.5, 0.5) },
    uFrequency: { value: new Vector2(20, 5) },
  },
  wireframe: false,
})

const plane = new Mesh(new PlaneGeometry(10, 10, 10, 10), shaderMaterial)

plane.rotation.set(-Math.PI / 2, 0, 0)

scene.add(plane)

const { pane, fpsGraph } = useTweakPane()
pane.addInput(shaderMaterial.uniforms.uAmplitude.value, 'x', {
  label: 'Amplitude X',
  min: -5,
  max: 5,
  step: 0.1,
})

pane.addInput(shaderMaterial.uniforms.uFrequency.value, 'x', {
  label: 'Frequency X',
  min: 0,
  max: 100,
  step: 0.1,
})

pane.addInput(shaderMaterial.uniforms.uAmplitude.value, 'y', {
  label: 'Amplitude y',
  min: -5,
  max: 5,
  step: 0.1,
})

pane.addInput(shaderMaterial.uniforms.uFrequency.value, 'y', {
  label: 'Frequency Y',
  min: 0,
  max: 100,
  step: 0.1,
})

pane.addInput(shaderMaterial, 'wireframe')

// Renderer

const { experience, initRenderer, updateRenderer, renderScene, updateControls, disposeRenderer } = useRenderer()

const clock = new Clock()

const loop = () => {
  // Update renderer

  fpsGraph.begin()

  const elapsedTime = clock.getElapsedTime()

  shaderMaterial.uniforms.uTime.value = elapsedTime

  updateControls()

  renderScene(scene, camera)
  fpsGraph.end()
  requestAnimationFrame(loop)
}

onMounted(() => {
  initRenderer(camera, {
    renderer: {
      antialias: true,
      alpha: true,
      preserveDrawingBuffer: true,
    },
    shadows: true,
    controls: true,
  })
  loop()
  updateRenderer()
})

onBeforeUnmount(() => {
  disposeRenderer()
})
</script>

<template>
  <canvas ref="experience"></canvas>
</template>

<route lang="yaml">
meta:
  layout: experiment
  title: Shaders
  media: '/shaders/basic.png'
  status: published
  description: 'Add a custom shader to your scene using glsl vertex and fragment shaders'
  tags: ['shader', 'glsl', 'material', 'raw', 'procedural']
</route>
