Three.js - 使用着色器材质绘制立方体(三十)

简介

着色器材质可以自定义几何体面的颜色。进一步学习后就会知道,three.js就是对GLSL语言进行了多方面的封装,下面我们就使用着色器语言来绘制一个正方体。

开始绘制

绘制一个有着色器材质的几何体

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>学习</title>
  </head>
  <body>
    <canvas id="c2d" class="c2d" width="1000" height="500"></canvas>
    <script type="module">
      import * as THREE from './file/three.js-dev/build/three.module.js'
      import { OrbitControls } from './file/three.js-dev/examples/jsm/controls/OrbitControls.js'

      const canvas = document.querySelector('#c2d')
      // 渲染器
      const renderer = new THREE.WebGLRenderer({ canvas })

      const fov = 40 // 视野范围
      const aspect = 2 // 相机默认值 画布的宽高比
      const near = 0.1 // 近平面
      const far = 10000 // 远平面

      // 透视投影相机
      const camera = new THREE.PerspectiveCamera(fov, aspect, near, far)
      camera.position.set(0, 0, 3)
      camera.lookAt(0, 0, 0)
      // 控制相机
      const controls = new OrbitControls(camera, canvas)
      controls.update()

      // 场景
      const scene = new THREE.Scene()

      // 顶点着色器
      const vertexShader = `
      varying vec2 vUv;
      varying vec3 vPosition;

      void main(){
          gl_Position = projectionMatrix*modelViewMatrix*vec4(position,1.0);
          
          vUv=uv;
          vPosition=position;
      }
      `
      // 片元着色器
      const fragmentShader = `
      uniform float uTime;
      varying vec2 vUv;

      vec3 background(vec2 uv){
          float dist=length(uv-vec2(.5));
          vec3 bg=mix(vec3(.3),vec3(.0),dist);
          return bg;
      }

      void main(){
          vec3 bg=background(vUv);
          vec3 color=bg;

          gl_FragColor=vec4(color,1.);
      }
      `

      const geometry = new THREE.BoxGeometry(1, 1, 1)
      const shaderMaterial = new THREE.ShaderMaterial({
        vertexShader: vertexShader,
        fragmentShader: fragmentShader,
        side: THREE.DoubleSide,
        uniforms: {
          uTime: {
            value: 0
          }
        }
      })

      const mesh = new THREE.Mesh(geometry, shaderMaterial)
      scene.add(mesh)

      // 渲染
      function render() {
        renderer.render(scene, camera)
        requestAnimationFrame(render)
      }
      requestAnimationFrame(render)
    </script>
  </body>
</html>

Three.js - 使用着色器材质绘制立方体(三十)_第1张图片

  1. 顶点着色器中。projectionMatrix是投影变换矩阵,modelViewMatrix是相机坐标系的变换矩阵,position顶点坐标,都是three.js定义好的。
  2. 片元着色器中。通过background函数计算出当前顶点的颜色,交由片元自动计算各个顶点之间的渐变。

绘制方块

  • 这是一个比较复杂的过程。本节只了解如何使用别人写好的公式绘制图形。
  • 下面的操作都是在片元着色器中。
  1. 首先要创建一个基本图元的距离函数。
  • 方块的距离函数。
  float sdBox(vec3 p,vec3 b){
      vec3 q=abs(p)-b;
      return length(max(q,0.))+min(max(q.x,max(q.y,q.z)),0.);
  }
  1. 有了方块,就需要一个光线步进函数来展示它。
  • 光线步进函数,计算出方块。Ray Marching(光线步进)
float rayMarch(vec3 ro,vec3 rd,float end,int maxIter){
  float d0 = 0.;
  for(int i=0;i<maxIter;i++){
      vec3 pos=ro+d0*rd;
      float ds=sdBox(pos,vec3(.3));
      d0+=ds;
      if(ds >= end || ds < 0.01){
          break;
      }
  }
  return d0;
}
  • 控制片元的颜色,来展示方块。
void main(){
    ...
    
    // 光线位置 位置
    vec3 eye = vec3(0.,0.,2.5);
    // 方向
    vec3 ray = normalize(vec3(vUv,-eye.z));
    // 最大距离
    float end = 5.;
    // 最大步数
    int maxIter=256;
    float depth = rayMarch(eye,ray,end,maxIter);
    if(depth < end){
        vec3 pos = eye + depth * ray;
        color = pos;
    }
    ...
}

Three.js - 使用着色器材质绘制立方体(三十)_第2张图片

  1. 让方块动起来。
  • 开始我们定义了uTime全局变量,通过修改它来实现旋转方块。
  • 矩阵函数。
  mat4 rotationMatrix(vec3 axis,float angle){
      axis=normalize(axis);
      float s=sin(angle);
      float c=cos(angle);
      float oc=1.-c;

      return mat4(oc*axis.x*axis.x+c,oc*axis.x*axis.y-axis.z*s,oc*axis.z*axis.x+axis.y*s,0.,
          oc*axis.x*axis.y+axis.z*s,oc*axis.y*axis.y+c,oc*axis.y*axis.z-axis.x*s,0.,
          oc*axis.z*axis.x-axis.y*s,oc*axis.y*axis.z+axis.x*s,oc*axis.z*axis.z+c,0.,
      0.,0.,0.,1.);
  }
  • 旋转函数。
  vec3 rotate(vec3 v,vec3 axis,float angle){
      mat4 m=rotationMatrix(axis,angle);
      return(m*vec4(v,1.)).xyz;
  }
  • 修改rayMarch函数,在加载方块之前先旋转方块。这里要注意rotate()函数需要在rayMarch()函数之前加载。
float rayMarch(vec3 ro,vec3 rd,float end,int maxIter){
    ...
    vec3 pos=ro+d0*rd;
    vec3 p1=rotate(pos,vec3(1.),uTime);
    float ds=sdBox(p1,vec3(.3));
    ...
}

  1. 修改方块的位置。
  • 修改法线为居中法线。
  vec2 centerUv(vec2 uv){
      uv=2.*uv-1.;
      float aspect=1;
      uv.x*=aspect;
      return uv;
  }
  • 修改光线步进的方向。
  ...

  // 方向
  vec2 cUv=centerUv(vUv);
  vec3 ray=normalize(vec3(cUv,-eye.z));
  // vec3 ray = normalize(vec3(vUv,-eye.z));
  
  ...

  • 本节是对GLSL语言的简单应用,通过各种公式使用着色器来绘制立方体。three.js的源码也是在控制着色器,所以想要深入理解three.js就必须要学会如何使用GLSL语言

你可能感兴趣的:(three.js,学习,前端,javascript,three.js)