WebGL笔记:图形旋转的原理和实现

旋转

1 )旋转的概念

  • 三维物体的旋转要比位移复杂一点,三维物体的旋转需要满足以下条件:
    • 旋转轴
    • 旋转方向
    • 旋转角度
  • 场景举例
    • 模型站在旋转轴的起点进行旋转
    • 模型要往左转还是往右转,就是旋转的方向
    • 模型旋转的大小就是旋转角度

WebGL笔记:图形旋转的原理和实现_第1张图片

2 )旋转方向的正负

  • 在webgl中,除裁剪空间之外的大部分功能都使用了右手坐标系
  • 在webgl中,可以暂且将其当成右手坐标系, 下图就是右手坐标系

WebGL笔记:图形旋转的原理和实现_第2张图片
  • 以上图为例

    • 当物体绕 z 轴,从x轴正半轴向y轴正半轴逆时针旋转时,是正向旋转,反之为负。
    • 当物体绕 x 轴,从y轴正半轴向z轴正半轴逆时针旋转时,是正向旋转,反之为负。
    • 当物体绕 y 轴,从z轴正半轴向x轴正半轴逆时针旋转时,是正向旋转,反之为负。
  • 如下图就是正向旋转

    • 围绕z轴(骑着z轴),从x轴到y轴逆时针转动就是正向旋转

WebGL笔记:图形旋转的原理和实现_第3张图片

旋转公式

  • 如下,让顶点围绕 z 轴旋转的场景

WebGL笔记:图形旋转的原理和实现_第4张图片
  • 已知
    • 点A的位置是(ax,ay,az)
    • 点A要围绕z轴旋转β度,转到点B的位置
  • 求:点A旋转后的bx、by位置
    • 因为∠β是已知的,∠α 可以通过点 A 得出
    • 所以我们可以得出
      ∠xOB = α + β
      
    • 那我们通过三角函数就可以推出bx、by
    • 设 ∠xOB = θ,则:
      bx = cosθ * |OA|
      by = sinθ * |OA| // 注意这里因为是旋转, 所以 |OA| === |OB|, 统一用 |OA|来表示
      
    • 上面的|OA|是点O到点A的距离,可以直接用点A求出
      |OA| = Math.sqrt(ax * ax + ay * ay)
      
    • 那我们接下来只需要知道cosθ和sinθ的值即可
    • 因为:θ = α + β
    • 所以,我们可以利用和角公式求cosθ和sinθ的值
      cosθ = cos(α + β)
      cosθ = cosα * cosβ - sinα * sinβ
      
      sinθ = sin(α + β)
      sinθ = cosβ * sinα + sinβ * cosα
      
    • 所以
      bx = cosθ * |OA|
      bx = (cosα * cosβ - sinα * sinβ) * |OA|
      bx = cosα * cosβ * |OA| - sinα * sinβ * |OA|
      
      by = sinθ * |OA|
      by = (cosβ * sinα + sinβ * cosα) * |OA|
      by = cosβ * sinα * |OA| + sinβ * cosα * |OA|
      
    • 因为
      cosα * |OA| = ax
      sinα * |OA| = ay
      
    • 所以我们可以简化bx、by的公式
      bx = ax * cosβ - ay * sinβ
      by = ay * cosβ + ax * sinβ
      
    • 上面的bx、by就是我们要求的答案

在着色器中旋转

  • 可以直接在着色器里写旋转公式

    <script id="vertexShader" type="x-shader/x-vertex">
        attribute vec4 a_Position;
        float angle = radians(80.0);
        float sinB = sin(angle);
        float cosB = cos(angle);
        void main() {
            gl_Position.x = a_Position.x * cosB - a_Position.y * sinB;
            gl_Position.y = a_Position.y * cosB + a_Position.x * sinB;
            gl_Position.z = a_Position.z;
            gl_Position.w = 1.0;
        }
    script>
    
  • radians(float degree) 将角度转弧度

  • sin(float angle) 正弦

  • cos(float angle) 余弦

用js旋转图形

我们将顶点着色器里的正弦值和余弦值暴露给js,便可以用js旋转图形了

<script id="vertexShader" type="x-shader/x-vertex">
    attribute vec4 a_Position;
    uniform float u_SinB;
    uniform float u_CosB;
    void main() {
        gl_Position.x = a_Position.x * u_CosB-a_Position.y * u_SinB;
        gl_Position.y = a_Position.y * u_CosB+a_Position.x * u_SinB;
        gl_Position.z = a_Position.z;
        gl_Position.w = 1.0;
    }
script>
  • 在js 中修改uniform 变量

    const u_SinB = gl.getUniformLocation(gl.program, 'u_SinB');
    const u_CosB = gl.getUniformLocation(gl.program, 'u_CosB');
    const angle = 0.3;
    gl.uniform1f(u_SinB, Math.sin(angle));
    gl.uniform1f(u_CosB, Math.cos(angle));
    
  • 之后让图形转起来

    !(function ani() {
        angle += 0.01;
        gl.uniform1f(u_SinB, Math.sin(angle));
        gl.uniform1f(u_CosB, Math.cos(angle));
        gl.clear(gl.COLOR_BUFFER_BIT);
        gl.drawArrays(gl.TRIANGLES, 0, 3);
        requestAnimationFrame(ani);
    })()
    

完整代码

<canvas id="canvas">canvas>
<script id="vertexShader" type="x-shader/x-vertex">
  attribute vec4 a_Position;
  uniform float u_SinB;
  uniform float u_CosB;
  void main() {
      gl_Position.x = a_Position.x * u_CosB - a_Position.y * u_SinB;
      gl_Position.y = a_Position.y * u_CosB + a_Position.x * u_SinB;
      gl_Position.z = a_Position.z;
      gl_Position.w = 1.0;
  }
script>
<script id="fragmentShader" type="x-shader/x-fragment">
  void main() {
      gl_FragColor = vec4(1.0,1.0,0.0,1.0);
  }
script>
<script type="module">
  import { initShaders } from './utils.js';
  const canvas = document.getElementById('canvas');
  canvas.width = window.innerWidth;
  canvas.height = window.innerHeight;
  const gl = canvas.getContext('webgl');

  const vsSource = document.getElementById('vertexShader').innerText;
  const fsSource = document.getElementById('fragmentShader').innerText;
  initShaders(gl, vsSource, fsSource);

  const vertices = new Float32Array([
    0.0, 0.1,
    -0.1, -0.1,
    0.1, -0.1
  ]);

  const vertexBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);
  const a_Position = gl.getAttribLocation(gl.program, 'a_Position');
  gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
  gl.enableVertexAttribArray(a_Position);

  // 获取Uniform变量
  const u_SinB = gl.getUniformLocation(gl.program, 'u_SinB')
  const u_CosB = gl.getUniformLocation(gl.program, 'u_CosB')
  // 修改uniform 变量
  let angle = 0.3
  gl.uniform1f(u_SinB, Math.sin(angle))
  gl.uniform1f(u_CosB, Math.cos(angle))

  gl.clearColor(0.0, 0.0, 0.0, 1.0);
  gl.clear(gl.COLOR_BUFFER_BIT);
  gl.drawArrays(gl.TRIANGLES, 0, 3);

  !(function ani() {
    angle += 0.01;
    gl.uniform1f(u_SinB, Math.sin(angle));
    gl.uniform1f(u_CosB, Math.cos(angle));
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.drawArrays(gl.TRIANGLES, 0, 3);
    requestAnimationFrame(ani);
  })()
script>

你可能感兴趣的:(Canvas,Webgl,Three.js,webgl)