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>
相关推荐
总要学点什么1 天前
第65篇:地图卷帘控件——点「关闭卷帘」,消失的却是右边那半张地图
信息可视化·gis·webgl·cesium·空间分析
总要学点什么2 天前
第63篇:3DTiles-大雁塔模型——190 块瓦片不是八叉树,抬高的却是球心那条法线
3d·数据挖掘·数据分析·gis·webgl
Jmyd01236 天前
3D数字协同设计实训空间技术架构解析:从三维采集到WebGL虚拟展示的全链路实现
3d·架构·webgl·虚拟实训
总要学点什么6 天前
第57篇:三维场景演示_基础版——同一秒,播过去和拖过去差出一千多米
3d·数据挖掘·数据分析·gis·webgl
总要学点什么7 天前
第55篇:空间分析-剖面分析——红线走大圆,剖面却按经纬直线采样
前端框架·gis·webgl·cesium·空间分析
:mnong7 天前
Web 三维可视化技术对比:ParaView Web vs 本项目 wasm + Three.js
前端·webgl·opengl·opencl·科学可视化
疯狂敲代码的老刘7 天前
my-fingerprint浏览器扩展:1.4K星开源指纹防护神器,Canvas/WebGL/WebGPU伪装一键开启
webgl
总要学点什么8 天前
第51篇:数据分析-热力图——笔刷画的是圆,落到地上却成了椭圆
3d·数据挖掘·数据分析·前端框架·gis·webgl·cesium
子龙_8 天前
来在网页上用threejs画个蛋白质吧
前端·webgl·three.js
总要学点什么10 天前
第47篇:三维特效-雷达波效果——波纹一圈圈往外推,一半却埋在地下
3d·前端框架·gis·webgl·cesium