美文网首页
three.js - Raging-sea

three.js - Raging-sea

作者: 闪电西兰花 | 来源:发表于2024-08-25 21:12 被阅读0次
    • We're going to create raging sea only by using shaders
    • Set up
    <script setup>
      import * as THREE from 'three'
      import {OrbitControls} from 'three/addons/controls/OrbitControls.js'
      import * as dat from 'dat.gui'
    
      /**
       * scene
      */
      const scene = new THREE.Scene()
    
      /**
       * mesh
      */
      const plane = new THREE.Mesh(
        new THREE.PlaneGeometry(2, 2, 128, 128),
        new THREE.MeshBasicMaterial({
          color: '#fff'
        })
      )
      plane.rotation.x = - Math.PI * 0.5
      scene.add(plane)
    
      /**
       * camera
      */
      const camera = new THREE.PerspectiveCamera(
        75,
        window.innerWidth / window.innerHeight,
        0.1,
        100
      )
      camera.position.set(1, 1, 1)
    
      /**
       * renderer
      */
      const renderer = new THREE.WebGLRenderer()
      renderer.setSize(window.innerWidth, window.innerHeight)
      document.body.appendChild(renderer.domElement)
    
      window.addEventListener('resize', () => {
        camera.aspect = window.innerWidth / window.innerHeight
        camera.updateProjectionMatrix()
    
        renderer.setSize(window.innerWidth, window.innerHeight)
        renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2))
      })
    
      /**
       * axesHelper
      */
      const axesHelper = new THREE.AxesHelper(5)
      scene.add(axesHelper)
    
      /**
       * controls
      */
      const controls = new OrbitControls(camera, renderer.domElement)
      controls.enableDamping = true
    
      /**
       * tick
      */
      const clock = new THREE.Clock()
      const tick = () => {
        const elapsedTime = clock.getElapsedTime()
    
        controls.update()
    
        requestAnimationFrame(tick)
    
        renderer.render(scene, camera)
      }
      tick()
    
      /**
       * gui
      */
      const gui = new dat.GUI()
    </script>
    
    Set up.png
    • Replace the mesh and replace the material with shaderMaterial
      import vertexShader from './raging-sea/vertex.glsl'
      import fragmentShader from './raging-sea/fragment.glsl'
    
      /**
       * water
      */
      const waterGeometry = new THREE.PlaneGeometry(2, 2, 512, 512)
      const waterMaterial = new THREE.ShaderMaterial({
        vertexShader,
        fragmentShader,
        side: THREE.DoubleSide
      })
      const water = new THREE.Mesh(waterGeometry, waterMaterial)
      water.rotation.x = - Math.PI * 0.5
      scene.add(water)
    
    // vertex.glsl
    void main () {
      // 模型矩阵 - mesh
      vec4 modelPosition = modelMatrix * vec4(position, 1.0);
      // 视图矩阵 - camera
      vec4 viewPosition = viewMatrix * modelPosition;
      // 投影矩阵 - 坐标
      vec4 projectedPosition = projectionMatrix * viewPosition;
    
      gl_Position = projectedPosition;
    }
    
    // fragment.glsl
    void main () {
      gl_FragColor = vec4(0.5, 0.8, 1.0, 1.0);
    }
    
    shaderMaterial.png
    • Big waves
      • uBigWavesElevation 波浪的高度
      const waterMaterial = new THREE.ShaderMaterial({
        ...
        uniforms: {
          uBigWavesElevation: {value: 0.2},  // 高度
        },
        side: THREE.DoubleSide
      })
      
      gui.add(waterMaterial.uniforms.uBigWavesElevation, 'value')
        .min(0)
        .max(1)
        .step(0.001)
        .name('uBigWavesElevation')
      
      // vertex.glsl
      uniform float uBigWavesElevation;
      
      void main () {
        // 模型矩阵 - mesh
        vec4 modelPosition = modelMatrix * vec4(position, 1.0);
        float elevation = sin(modelPosition.x) * uBigWavesElevation;
        modelPosition.y += elevation;
      
        // 视图矩阵 - camera
        vec4 viewPosition = viewMatrix * modelPosition;
      
        // 投影矩阵 - 坐标
        vec4 projectedPosition = projectionMatrix * viewPosition;
      
        gl_Position = projectedPosition;
      }
      
      uBigWavesElevation.png
      • 当前我们只使用了x值在y轴上产生了波动,那么下一步想要实现在2个轴上同时波动(使用z值在y轴上波动)
      const waterMaterial = new THREE.ShaderMaterial({
        uniforms: {
          uBigWavesElevation: {value: 0.2},  // 高度
          uBigWavesFrequency: {value: new THREE.Vector2(4, 1.5)},  // 频率,折叠次数
        },
        side: THREE.DoubleSide
      })
      
      gui.add(waterMaterial.uniforms.uBigWavesFrequency.value, 'x')
        .min(0)
        .max(10)
        .step(0.001)
        .name('uBigWavesFrequencyX')
      gui.add(waterMaterial.uniforms.uBigWavesFrequency.value, 'y')
        .min(0)
        .max(10)
        .step(0.001)
        .name('uBigWavesFrequencyY')
      
      // vertex.glsl
      uniform float uBigWavesElevation;
      uniform vec2 uBigWavesFrequency;  // 使用该变量时取得是x、y的值,但实际使用是发生在x、z轴上
      
      void main () {
        // 模型矩阵 - mesh
        vec4 modelPosition = modelMatrix * vec4(position, 1.0);
        float elevation = sin(modelPosition.x * uBigWavesFrequency.x) * 
                          sin(modelPosition.z * uBigWavesFrequency.y) *
                          uBigWavesElevation;
        modelPosition.y += elevation;
      
        // 视图矩阵 - camera
        vec4 viewPosition = viewMatrix * modelPosition;
      
        // 投影矩阵 - 坐标
        vec4 projectedPosition = projectionMatrix * viewPosition;
      
        gl_Position = projectedPosition;
      }
      
      uBigWavesFrequency.png
    • Animate, we use the elapsedTime
      const waterMaterial = new THREE.ShaderMaterial({
        uniforms: {
          uTime: {value: 0},
          
          uBigWavesElevation: {value: 0.2},  // 高度
          uBigWavesFrequency: {value: new THREE.Vector2(4, 1.5)},  // 频率,折叠次数
        },
        side: THREE.DoubleSide
      })
    
      /**
       * tick
      */
      const clock = new THREE.Clock()
      const tick = () => {
        const elapsedTime = clock.getElapsedTime()
    
        // update water
        waterMaterial.uniforms.uTime.value = elapsedTime
    
        controls.update()
    
        requestAnimationFrame(tick)
    
        renderer.render(scene, camera)
      }
      tick()
    
      // vertex.glsl
      uniform float uTime;
      ...
    
      void main () {
        // 模型矩阵 - mesh
        vec4 modelPosition = modelMatrix * vec4(position, 1.0);
        float elevation = sin(modelPosition.x * uBigWavesFrequency.x + uTime) * 
                          sin(modelPosition.z * uBigWavesFrequency.y + uTime) *
                          uBigWavesElevation;
        modelPosition.y += elevation;
        ....
        ...
      } 
    
    • We can control the speed of the waves, we're going to use the same speed for both axes
      const waterMaterial = new THREE.ShaderMaterial({
        vertexShader,
        fragmentShader,
        uniforms: {
          uTime: {value: 0},
    
          uBigWavesElevation: {value: 0.2},  // 高度
          uBigWavesFrequency: {value: new THREE.Vector2(4, 1.5)},  // 频率,折叠次数
          uBigWaveSpeed: {value: 0.75},  // 速度
        },
        side: THREE.DoubleSide
      })
    
      gui.add(waterMaterial.uniforms.uBigWaveSpeed, 'value')
        .min(0)
        .max(4)
        .step(0.001)
        .name('uBigWaveSpeed')
    
    // vertex.glsl
    ...
    uniform float uBigWaveSpeed;
    
    void main () {
      // 模型矩阵 - mesh
      vec4 modelPosition = modelMatrix * vec4(position, 1.0);
      float elevation = sin(modelPosition.x * uBigWavesFrequency.x + uTime * uBigWaveSpeed) * 
                        sin(modelPosition.z * uBigWavesFrequency.y + uTime * uBigWaveSpeed) * 
                        uBigWavesElevation;
      modelPosition.y += elevation;
    
      // 视图矩阵 - camera
      vec4 viewPosition = viewMatrix * modelPosition;
    
      // 投影矩阵 - 坐标
      vec4 projectedPosition = projectionMatrix * viewPosition;
    
      gl_Position = projectedPosition;
    }
    
    • Color
      • we're going to use a different color for the depth and for the surface
      • we want to be able to tweak the color, so we must to create debugObject
      /**
       * water
      */
      ...
    
      const debugObject = {}
      debugObject.depthColor = '#186691'
      debugObject.surfaceColor = '#9bd8ff'
    
      const waterMaterial = new THREE.ShaderMaterial({
        ...
        uniforms: {
          ...
          ...
          uDepthColor: {value: new THREE.Color(debugObject.depthColor)},
          uSurfaceColor: {value: new THREE.Color(debugObject.surfaceColor)},
        },
        side: THREE.DoubleSide
      })
    
      gui.addColor(debugObject, 'depthColor')
        .name('depthColor')
        .onChange(() => {
          waterMaterial.uniforms.uDepthColor.value.set(debugObject.depthColor) // 同步更新uniform
        })
      gui.addColor(debugObject, 'surfaceColor')
        .name('surfaceColor')
        .onChange(() => {
          waterMaterial.uniforms.uSurfaceColor.value.set(debugObject.surfaceColor) // 同步更新uniform
        })
    
    // fragment.glsl
    uniform vec3 uDepthColor;
    uniform vec3 uSurfaceColor;
    
    void main () {
      gl_FragColor = vec4(uDepthColor, 1.0);
      // gl_FragColor = vec4(uSurfaceColor, 1.0);
    }
    
    color.png
    • we're going to create a mix between the uDepthColor with the uSurfaceColor according to the wave elevation, but the vElevation goes from - 0.2 to + 0.2, so we add uColorOffset and uColorMultiplier
      const waterMaterial = new THREE.ShaderMaterial({
        vertexShader,
        fragmentShader,
        uniforms: {
          ...
          ...
          uDepthColor: {value: new THREE.Color(debugObject.depthColor)},
          uSurfaceColor: {value: new THREE.Color(debugObject.surfaceColor)},
          uColorOffset: {value: 0.08},  // 颜色偏移量
          uColorMultiplier: {value: 5},
        },
        side: THREE.DoubleSide
      })
    
      gui.add(waterMaterial.uniforms.uColorOffset, 'value')
        .min(0)
        .max(1)
        .step(0.001)
        .name('uColorOffset')
      gui.add(waterMaterial.uniforms.uColorMultiplier, 'value')
        .min(0)
        .max(10)
        .name('uColorMultiplier')
    
    // vertex.glsl
    ...
    ...
    varying float vElevation;
    
    void main () {
      ...
      float elevation = sin(modelPosition.x * uBigWavesFrequency.x + uTime * uBigWaveSpeed) * 
                        sin(modelPosition.z * uBigWavesFrequency.y + uTime * uBigWaveSpeed) * 
                        uBigWavesElevation;
      ...
      ...
      // Varyings
      vElevation = elevation;
    }
    
    • create a mixStrength variable and use it in the mix()
    // fragment.glsl
    uniform vec3 uDepthColor;
    uniform vec3 uSurfaceColor;
    uniform float uColorOffset;
    uniform float uColorMultiplier;
    
    varying float vElevation;
    
    void main () {
      float mixStrength = (vElevation + uColorOffset) * uColorMultiplier;
      // mix() 
      // 前2个参数,类型必须相同
      // 第三个值为0,则是第一种颜色
      // 第三个值为1,则是第二种颜色
      // 第三个值在0和1之间,即参数和颜色的结合
      // vElevation 根据前面vertex的计算,该值的范围是[-0.2, 0.2]
      vec3 color = mix(uDepthColor, uSurfaceColor, mixStrength);  // 根据起伏高度混合颜色
      gl_FragColor = vec4(color, 1.0);
    }
    
    mixColor.png
    • Small waves
      • we're going to use a 3D perlin noise to make the waves change in time, add it before your main function
      • 在当前波浪的线条上再添加小波浪
      • 使波浪呈现波峰、波谷,呈现混乱、不规律
      const waterMaterial = new THREE.ShaderMaterial({
        vertexShader,
        fragmentShader,
        uniforms: {
          uTime: {value: 0},
    
          uBigWavesElevation: {value: 0.2},  // 高度
          uBigWavesFrequency: {value: new THREE.Vector2(4, 1.5)},  // 频率,折叠次数
          uBigWaveSpeed: {value: 0.75},  // 速度
    
          uSmallWavesElevation: {value: 0.15},
          uSmallWavesFrequency: {value: 3},
          uSmallWavesSpeed: {value: 0.2},
          uSmallIterations: {value: 4.0},
    
          uDepthColor: {value: new THREE.Color(debugObject.depthColor)},
          uSurfaceColor: {value: new THREE.Color(debugObject.surfaceColor)},
          uColorOffset: {value: 0.08},  // 颜色偏移量
          uColorMultiplier: {value: 5},
        },
        side: THREE.DoubleSide
      })
    
      gui.add(waterMaterial.uniforms.uSmallWavesElevation, 'value')
        .min(0)
        .max(1)
        .step(0.001)
        .name('uSmallWavesElevation')
      gui.add(waterMaterial.uniforms.uSmallWavesFrequency, 'value')
        .min(0)
        .max(30)
        .step(0.001)
        .name('uSmallWavesFrequency')
      gui.add(waterMaterial.uniforms.uSmallWavesSpeed, 'value')
        .min(0)
        .max(4)
        .step(0.001)
        .name('uSmallWavesSpeed')
    gui.add(waterMaterial.uniforms.uSmallIterations, 'value')
        .min(0)
        .max(5)
        .step(1)
        .name('uSmallIterations')
    
    // vertex.glsl
    uniform float uTime;
    uniform float uBigWavesElevation;
    uniform vec2 uBigWavesFrequency;  // 使用该变量时取得是x、y的值,但实际使用是发生在x、z轴上
    uniform float uBigWaveSpeed;
    
    uniform float uSmallWavesElevation;
    uniform float uSmallWavesFrequency;
    uniform float uSmallWavesSpeed;
    uniform float uSmallIterations;
    
    varying float vElevation;
    
    //  Classic Perlin 3D Noise 
    //  by Stefan Gustavson
    //
    vec4 permute(vec4 x){return mod(((x*34.0)+1.0)*x, 289.0);}
    vec4 taylorInvSqrt(vec4 r){return 1.79284291400159 - 0.85373472095314 * r;}
    vec3 fade(vec3 t) {return t*t*t*(t*(t*6.0-15.0)+10.0);}
    
    float cnoise(vec3 P){
      vec3 Pi0 = floor(P); // Integer part for indexing
      vec3 Pi1 = Pi0 + vec3(1.0); // Integer part + 1
      Pi0 = mod(Pi0, 289.0);
      Pi1 = mod(Pi1, 289.0);
      vec3 Pf0 = fract(P); // Fractional part for interpolation
      vec3 Pf1 = Pf0 - vec3(1.0); // Fractional part - 1.0
      vec4 ix = vec4(Pi0.x, Pi1.x, Pi0.x, Pi1.x);
      vec4 iy = vec4(Pi0.yy, Pi1.yy);
      vec4 iz0 = Pi0.zzzz;
      vec4 iz1 = Pi1.zzzz;
    
      vec4 ixy = permute(permute(ix) + iy);
      vec4 ixy0 = permute(ixy + iz0);
      vec4 ixy1 = permute(ixy + iz1);
    
      vec4 gx0 = ixy0 / 7.0;
      vec4 gy0 = fract(floor(gx0) / 7.0) - 0.5;
      gx0 = fract(gx0);
      vec4 gz0 = vec4(0.5) - abs(gx0) - abs(gy0);
      vec4 sz0 = step(gz0, vec4(0.0));
      gx0 -= sz0 * (step(0.0, gx0) - 0.5);
      gy0 -= sz0 * (step(0.0, gy0) - 0.5);
    
      vec4 gx1 = ixy1 / 7.0;
      vec4 gy1 = fract(floor(gx1) / 7.0) - 0.5;
      gx1 = fract(gx1);
      vec4 gz1 = vec4(0.5) - abs(gx1) - abs(gy1);
      vec4 sz1 = step(gz1, vec4(0.0));
      gx1 -= sz1 * (step(0.0, gx1) - 0.5);
      gy1 -= sz1 * (step(0.0, gy1) - 0.5);
    
      vec3 g000 = vec3(gx0.x,gy0.x,gz0.x);
      vec3 g100 = vec3(gx0.y,gy0.y,gz0.y);
      vec3 g010 = vec3(gx0.z,gy0.z,gz0.z);
      vec3 g110 = vec3(gx0.w,gy0.w,gz0.w);
      vec3 g001 = vec3(gx1.x,gy1.x,gz1.x);
      vec3 g101 = vec3(gx1.y,gy1.y,gz1.y);
      vec3 g011 = vec3(gx1.z,gy1.z,gz1.z);
      vec3 g111 = vec3(gx1.w,gy1.w,gz1.w);
    
      vec4 norm0 = taylorInvSqrt(vec4(dot(g000, g000), dot(g010, g010), dot(g100, g100), dot(g110, g110)));
      g000 *= norm0.x;
      g010 *= norm0.y;
      g100 *= norm0.z;
      g110 *= norm0.w;
      vec4 norm1 = taylorInvSqrt(vec4(dot(g001, g001), dot(g011, g011), dot(g101, g101), dot(g111, g111)));
      g001 *= norm1.x;
      g011 *= norm1.y;
      g101 *= norm1.z;
      g111 *= norm1.w;
    
      float n000 = dot(g000, Pf0);
      float n100 = dot(g100, vec3(Pf1.x, Pf0.yz));
      float n010 = dot(g010, vec3(Pf0.x, Pf1.y, Pf0.z));
      float n110 = dot(g110, vec3(Pf1.xy, Pf0.z));
      float n001 = dot(g001, vec3(Pf0.xy, Pf1.z));
      float n101 = dot(g101, vec3(Pf1.x, Pf0.y, Pf1.z));
      float n011 = dot(g011, vec3(Pf0.x, Pf1.yz));
      float n111 = dot(g111, Pf1);
    
      vec3 fade_xyz = fade(Pf0);
      vec4 n_z = mix(vec4(n000, n100, n010, n110), vec4(n001, n101, n011, n111), fade_xyz.z);
      vec2 n_yz = mix(n_z.xy, n_z.zw, fade_xyz.y);
      float n_xyz = mix(n_yz.x, n_yz.y, fade_xyz.x); 
      return 2.2 * n_xyz;
    }
    
    void main () {
      // 模型矩阵 - mesh
      vec4 modelPosition = modelMatrix * vec4(position, 1.0);
      float elevation = sin(modelPosition.x * uBigWavesFrequency.x + uTime * uBigWaveSpeed) * 
                        sin(modelPosition.z * uBigWavesFrequency.y + uTime * uBigWaveSpeed) * 
                        uBigWavesElevation;
      for (float i = 1.0; i <= uSmallIterations; i++) {
        elevation -= abs(cnoise(vec3(modelPosition.xz * uSmallWavesFrequency * i, uTime * uSmallWavesSpeed)) * uBigWavesElevation / i);
      }
      modelPosition.y += elevation;
    
      // 视图矩阵 - camera
      vec4 viewPosition = viewMatrix * modelPosition;
    
      // 投影矩阵 - 坐标
      vec4 projectedPosition = projectionMatrix * viewPosition;
    
      gl_Position = projectedPosition;
    
      // Varyings
      vElevation = elevation;
    }
    
    waves.png

    相关文章

      网友评论

          本文标题:three.js - Raging-sea

          本文链接:https://www.haomeiwen.com/subject/xyvzadtx.html