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>
相关推荐
总要学点什么7 小时前
第97篇:三维数据加载-城市建筑白模自定义着色器——让一条光带沿高度扫上去,快慢却由帧率说了算
前端·gis·webgl·cesium·着色器·空间分析·白模
总要学点什么1 天前
第98篇:三维特效-材质线效果——给一条线换十种材质,虚线的图案只填了一半
开发语言·前端·javascript·ecmascript·gis·webgl·材质
总要学点什么8 天前
第92篇:三维特效-圆(逐渐消逝)——从圆心开始,让一个洞把整片光啃到只剩外圈
gis·webgl·cesium·空间分析·1024程序员节
总要学点什么9 天前
第89篇:三维特效-螺旋圆效果——让螺旋臂卷起一圈圈外扩的涟漪
开发语言·javascript·ecmascript·webgl·cesium·空间分析·1024程序员节
GoldenSpider.AI9 天前
一张户型图做成浏览器 3D 漫游:house.json 统一模型、1.2 m 剖切图纸与分阶段验收
ai编程·webgl·three.js·3d漫游·claude code
Jmyd012312 天前
WebGL 三维党史馆怎么扛住 720 度全景与移动端弱网?
webgl·三维数字化
limit007514 天前
三维 WebGIS 工具选型:加载、样式、分析、交付,一套界面能做完什么
android·前端·web3·安卓·webgl
总要学点什么15 天前
第79篇:数据可视化-模拟迁徙效果——把三十条航线画成会飞的弧
3d·信息可视化·数据挖掘·数据分析·gis·webgl·cesium
limit007515 天前
三维模型悬空与切片偏移的排查顺序
低代码·3d·arcgis·web3·安卓·webgl
Sakura_j_h_16 天前
Three.js 单文件离线机械臂工作台:爆炸图、透明外壳与剖面
前端·机械臂·webgl·three.js·1024程序员节·esbuild