14、webgl 基本概念 + 图形变换

基本概念

图形变换

平移

所有顶点移动

javascript 复制代码
<script id="vertexShader" type="x-shader/x-vertex">
  atrribute vec4 a_Position;
  uniform vec4 u_Translation;
  void main() {
    gl_Position = a_Position + u_Translation;
  }
</script>

a_Position 是原始点,属于 attribute 变量

u_Translation 是用来作为改变原始点的为一的变量

为什么 u_Translation 不是使用 attribute 来定义?

原因是 attribute 定义的变量是跟顶点强相关的,也就是只有原始顶点才用它,其他的变换位置的变量(u_Translation)都是使用 uniform 来定义。

实现三角形沿着 y 轴进行平移
html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>图形变换-平移</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
  </style>
</head>

<body>
  <canvas id="canvas"></canvas>
  
  <script id="vertexShader" type="x-shader/x-vertex">
    // 一个属性值,将会从缓冲区中获取数据
    attribute vec4 a_Position;
    attribute float a_PointSize;
    uniform vec4 u_Translation;
    // 所有着色器都有一个 main 方法
    void main() {
        // gl_Position 是一个顶点着色器主要设置的变量
        gl_Position = a_Position + u_Translation;
        gl_PointSize = a_PointSize;
    }
  </script>
 <script id="fragmentShader" type="x-shader/x-fragment">
  precision mediump float;
  uniform vec4 u_FragColor;
  void main() {
    gl_FragColor = u_FragColor;
  }
</script>

<script type="module">
    import { getPosByMouse, getInnerText, initShaderProgram } from "../utils/index.js"
    const canvas = document.querySelector('#canvas');
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    const gl = canvas.getContext('webgl');
    gl.clearColor(0, 0, 0, 1);
    const vertexStr = getInnerText("vertexShader");
    const fragmentStr = getInnerText("fragmentShader");
    const program = initShaderProgram(gl, vertexStr, fragmentStr )
    let a_Position = gl.getAttribLocation(program, 'a_Position');
    let a_PointSize = gl.getAttribLocation(program, 'a_PointSize');
    let u_FragColor = gl.getUniformLocation(program, 'u_FragColor');
    let u_Translation = gl.getUniformLocation(program, 'u_Translation');
    gl.useProgram(program);

    let vertices = [0.0, 0.5, 1, 0.5, 0, 1]
    const vertexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, 0, 0);
        
    gl.enableVertexAttribArray(a_Position);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.vertexAttrib1f(a_PointSize, 5);
    gl.uniform4fv(u_FragColor, new Float32Array([1, 0, 1, 1]));
    // 渲染
    function render() {
      gl.drawArrays(gl.TRIANGLES, 0, 6);
    }
    let y = 0.01;
    !(function ani(){
      if(y >= 1) {
        y = -1;
      }
      gl.clear(gl.COLOR_BUFFER_BIT);
      gl.uniform4fv(u_Translation, new Float32Array([0, y, 0, 1]));
      y += 0.01;
      render();
      requestAnimationFrame(ani);
    })();
 </script>
</body>
</html>

旋转

三维物体的旋转需要知道以下条件:

旋转轴

旋转方向

旋转角度

在着色器中旋转:

javascript 复制代码
<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);
  // 所有着色器都有一个 main 方法
  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 进行传值旋转
html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>图形变换-旋转</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
  </style>
</head>

<body>
  <canvas id="canvas"></canvas>
  
  <script id="vertexShader" type="x-shader/x-vertex">
    // 一个属性值,将会从缓冲区中获取数据
    attribute vec4 a_Position;
    // radians是着色器中的函数, 将角度转弧度
    // float angle = radians(30.0);
    // float sinB = sin(angle);
    // float cosB = cos(angle);
    uniform float u_CosB;
    uniform float u_SinB;
    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">
  precision mediump float;
  uniform vec4 u_FragColor;
  void main() {
    gl_FragColor = u_FragColor;
  }
</script>

<script type="module">
    import { getPosByMouse, getInnerText, initShaderProgram } from "../utils/index.js"
    const canvas = document.querySelector('#canvas');
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    const gl = canvas.getContext('webgl');
    const vertexStr = getInnerText("vertexShader");
    const fragmentStr = getInnerText("fragmentShader");
    const program = initShaderProgram(gl, vertexStr, fragmentStr )
    let a_Position = gl.getAttribLocation(program, 'a_Position');
    let u_FragColor = gl.getUniformLocation(program, 'u_FragColor');
    let u_CosB = gl.getUniformLocation(program, 'u_CosB');
    let u_SinB = gl.getUniformLocation(program, 'u_SinB');
    gl.useProgram(program);
    let vertices = [0.0, 0.5, 1, 0.5, 0, 1]
    const vertexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(a_Position);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.uniform4fv(u_FragColor, new Float32Array([1, 0, 1, 1]));
    gl.clearColor(0, 0, 0, 1);
    // 渲染
    function render() {
      gl.drawArrays(gl.TRIANGLES, 0, 6);
    }
    let angle = 3.0;
    !(function ani(){
      angle += 0.02
      gl.clear(gl.COLOR_BUFFER_BIT);
      gl.uniform1f(u_CosB, Math.cos(angle));
      gl.uniform1f(u_SinB, Math.sin(angle));
      render();
      requestAnimationFrame(ani);
    })()
 </script>
</body>
</html>

缩放

可以理解为对向量长度的改变,或者对向量坐标分量的同步缩放

在着色器中缩放

1、对 gl_Position 的 x、y、z依次缩放

javascript 复制代码
<script id="vertexShader" type="x-shader/x-vertex">
    // 一个属性值,将会从缓冲区中获取数据
  attribute vec4 a_Position;
  float scale = 1.2;
  void main() {
      gl_Position.x = a_Position.x * scale;
      gl_Position.y = a_Position.y * scale;
      gl_Position.z = a_Position.z * scale;
      gl_Position.w = 1.0;
  }
</script>

2、对 a_Position 中 抽离出由 x、y、z组成的三维向量,对其一次性缩放

javascript 复制代码
<script id="vertexShader" type="x-shader/x-vertex">
    // 一个属性值,将会从缓冲区中获取数据
  attribute vec4 a_Position;
  float scale = 1.2;
  void main() {
      gl_Position = vec4(vec3(a_Position) * scale, 1.0);
  }
</script>
实现图形缩放,直接传参缩放值
html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>图形变换-缩放</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
  </style>
</head>

<body>
  <canvas id="canvas"></canvas>
  
  <script id="vertexShader" type="x-shader/x-vertex">
    // 一个属性值,将会从缓冲区中获取数据
    attribute vec4 a_Position;
    uniform float U_Scale;
    void main() {
        gl_Position.x = a_Position.x * U_Scale;
        gl_Position.y = a_Position.y * U_Scale;
        gl_Position.z = a_Position.z * U_Scale;
        gl_Position.w = 1.0;
    }
  </script>
 <script id="fragmentShader" type="x-shader/x-fragment">
  precision mediump float;
  uniform vec4 u_FragColor;
  void main() {
    gl_FragColor = u_FragColor;
  }
</script>

<script type="module">
    import { getPosByMouse, getInnerText, initShaderProgram } from "../utils/index.js"
    const canvas = document.querySelector('#canvas');
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    const gl = canvas.getContext('webgl');
    const vertexStr = getInnerText("vertexShader");
    const fragmentStr = getInnerText("fragmentShader");
    const program = initShaderProgram(gl, vertexStr, fragmentStr )
    let a_Position = gl.getAttribLocation(program, 'a_Position');
    let u_FragColor = gl.getUniformLocation(program, 'u_FragColor');
    let U_Scale = gl.getUniformLocation(program, 'U_Scale');
    gl.useProgram(program);
    let vertices = [0.0, 0.5, 1, 0.5, 0, 1]
    const vertexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(a_Position);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.uniform4fv(u_FragColor, new Float32Array([1, 0, 1, 1]));
    gl.clearColor(0, 0, 0, 1);
    // 渲染
    function render() {
      gl.drawArrays(gl.TRIANGLES, 0, 6);
    }
    let scale = 20.0;
    !(function ani(){
      if(scale <= 0.01 ) {
        scale = 20.0
      }
      scale -= 0.01
      gl.clear(gl.COLOR_BUFFER_BIT);
      gl.uniform1f(U_Scale, scale);
      render();
      requestAnimationFrame(ani);
    })()
 </script>
</body>
</html>
实现图形缩放,利用三角函数中的正弦进行缩放
html 复制代码
<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>图形变换-缩放</title>
  <style>
    * {
      margin: 0;
      padding: 0;
    }
  </style>
</head>

<body>
  <canvas id="canvas"></canvas>
  
  <script id="vertexShader" type="x-shader/x-vertex">
    // 一个属性值,将会从缓冲区中获取数据
    attribute vec4 a_Position;
    uniform float U_Scale;
    void main() {
        gl_Position.x = a_Position.x * U_Scale;
        gl_Position.y = a_Position.y * U_Scale;
        gl_Position.z = a_Position.z * U_Scale;
        gl_Position.w = 1.0;
    }
  </script>
 <script id="fragmentShader" type="x-shader/x-fragment">
  precision mediump float;
  uniform vec4 u_FragColor;
  void main() {
    gl_FragColor = u_FragColor;
  }
</script>

<script type="module">
    import { getPosByMouse, getInnerText, initShaderProgram } from "../utils/index.js"
    const canvas = document.querySelector('#canvas');
    canvas.width = window.innerWidth;
    canvas.height = window.innerHeight;
    const gl = canvas.getContext('webgl');
    const vertexStr = getInnerText("vertexShader");
    const fragmentStr = getInnerText("fragmentShader");
    const program = initShaderProgram(gl, vertexStr, fragmentStr )
    let a_Position = gl.getAttribLocation(program, 'a_Position');
    let u_FragColor = gl.getUniformLocation(program, 'u_FragColor');
    let U_Scale = gl.getUniformLocation(program, 'U_Scale');
    gl.useProgram(program);
    let vertices = [0.0, 0.5, 1, 0.5, 0.5, 0]
    const vertexBuffer = gl.createBuffer();
    gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
    gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW);
    gl.vertexAttribPointer(a_Position, 3, gl.FLOAT, false, 0, 0);
    gl.enableVertexAttribArray(a_Position);
    gl.clear(gl.COLOR_BUFFER_BIT);
    gl.uniform4fv(u_FragColor, new Float32Array([1, 0, 1, 1]));
    gl.clearColor(0, 0, 0, 1);
    // 渲染
    function render() {
      gl.drawArrays(gl.TRIANGLES, 0, 3);
    }
    let angle = 0;
    !(function ani(){
      angle += 0.05;
      const scale = Math.sin(angle) + 1;
      gl.uniform1f(U_Scale, scale);
      gl.clear(gl.COLOR_BUFFER_BIT);
      render();
      requestAnimationFrame(ani);
    })()
 </script>
</body>
</html>
相关推荐
李伟_Li慢慢2 天前
ShaderToy-山峦+蓝天+白云
前端·webgl
爱看书的小沐3 天前
【小沐杂货铺】基于Three.js绘制三维艺术画廊3DArtGallery (Three.js,WebGL)
javascript·3d·webgl·three.js·babylon.js·三维画廊
爱看书的小沐3 天前
【小沐学GIS】基于C++渲染三维飞行仿真Flight Simulation(OpenGL )第十三期
c++·qt·webgl·opengl·飞行仿真·flight
千鼎数字孪生-可视化7 天前
webGPU即将到来,和原生GPU有啥区别呢?
webgl·网页3d
cxxcode10 天前
认识 WebGL:基本用法与核心 API
webgl
threelab13 天前
从工厂模式到简化封装:三维引擎架构演进之路 threejs设计
javascript·3d·架构·webgl
qiao若huan喜13 天前
13、webgl基本概念 + 绘制狮子座星空
前端·javascript·信息可视化·webgl
qiao若huan喜19 天前
12、webgl 基本概念 +满天星星眨眼睛
前端·信息可视化·webgl
threelab20 天前
Vue3 + Trilab:打造高扩展性三维可视化插件化框架实战指南
javascript·3d·webgl