WebGL系列教程七(二维及三维旋转、平移、缩放)

目录

1 前言

  上一篇我们讲了如何进行立方体的纹理贴图,为了方便立方体的贴图后效果的展示,我们对立方体进行了旋转,但代码中并没有进行体现,这一节我们来讲讲旋转、平移、缩放。实际上,在WebGL中进行旋转、平移、缩放只需要乘以相应的旋转、平移、缩放矩阵即可。

2 二维

  在二维空间中,平移、旋转和缩放是常见的几何变换。为了系统地表示这些变换,我们通常使用齐次坐标,即将二维坐标 ( x , y ) (x, y) (x,y) 扩展为三维齐次坐标 ( x , y , 1 ) (x, y, 1) (x,y,1)。这样可以统一表示线性变换和平移变换。下面分别推导这三种变换的矩阵表示。

2.1 平移

在这里插入图片描述

定义:将任意一个点 ( x , y ) (x, y) (x,y) 平移 ( t x , t y ) (t_x, t_y) (tx,ty),得到新点 ( x ′ , y ′ ) (x', y') (x,y),即

{ x ′ = x + t x y ′ = y + t y \begin{cases} x' = x + t_x \\ y' = y + t_y \end{cases} {x=x+txy=y+ty

齐次坐标表示
[ x ′ y ′ 1 ] = [ 1 0 t x 0 1 t y 0 0 1 ] [ x y 1 ] \begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix}=\begin{bmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \\ 0 & 0 & 1 \end{bmatrix}\begin{bmatrix} x \\ y \\ 1 \end{bmatrix} xy1 = 100010txty1 xy1

因此,二维平移矩阵为:

T = [ 1 0 t x 0 1 t y 0 0 1 ] T = \begin{bmatrix} 1 & 0 & t_x \\ 0 & 1 & t_y \\ 0 & 0 & 1 \end{bmatrix} T= 100010txty1

2.2 旋转

在这里插入图片描述

定义:将任意一个点 ( x , y ) (x, y) (x,y) 绕原点逆时针旋转角度 θ \theta θ,得到新点 ( x ′ , y ′ ) (x', y') (x,y),即

{ x ′ = x cos ⁡ θ − y sin ⁡ θ y ′ = x sin ⁡ θ + y cos ⁡ θ \begin{cases} x' = x \cos\theta - y \sin\theta \\ y' = x \sin\theta + y \cos\theta \end{cases} {x=xcosθysinθy=xsinθ+ycosθ

齐次坐标表示

[ x ′ y ′ 1 ] = [ cos ⁡ θ − sin ⁡ θ 0 sin ⁡ θ cos ⁡ θ 0 0 0 1 ] [ x y 1 ] \begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} =\begin{bmatrix} \cos\theta & -\sin\theta & 0 \\ \sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} xy1 = cosθsinθ0sinθcosθ0001 xy1
因此,二维旋转矩阵为:

R ( θ ) = [ cos ⁡ θ − sin ⁡ θ 0 sin ⁡ θ cos ⁡ θ 0 0 0 1 ] R(\theta) = \begin{bmatrix} \cos\theta & -\sin\theta & 0 \\ \sin\theta & \cos\theta & 0 \\ 0 & 0 & 1 \end{bmatrix} R(θ)= cosθsinθ0sinθcosθ0001

2.3 缩放

在这里插入图片描述

定义:将任意一个点 ( x , y ) (x, y) (x,y) 沿 x x x 轴缩放因子 s x s_x sx,沿 y y y 轴缩放因子 s y s_y sy,得到新点 ( x ′ , y ′ ) (x', y') (x,y),即

{ x ′ = s x ⋅ x y ′ = s y ⋅ y \begin{cases} x' = s_x \cdot x \\ y' = s_y \cdot y \end{cases} {x=sxxy=syy

齐次坐标表示

[ x ′ y ′ 1 ] = [ s x 0 0 0 s y 0 0 0 1 ] [ x y 1 ] \begin{bmatrix} x' \\ y' \\ 1 \end{bmatrix} =\begin{bmatrix} s_x & 0 & 0 \\ 0 & s_y & 0 \\ 0 & 0 & 1 \end{bmatrix} \begin{bmatrix} x \\ y \\ 1 \end{bmatrix} xy1 = sx000sy0001 xy1

因此,二维缩放矩阵为:

S = [ s x 0 0 0 s y 0 0 0 1 ] S = \begin{bmatrix} s_x & 0 & 0 \\ 0 & s_y & 0 \\ 0 & 0 & 1 \end{bmatrix} S= sx000sy0001

3 三维

  在三维空间中,平移、旋转和缩放是类似的,我们通常也使用齐次坐标,即将三维坐标 ( x , y , z ) (x, y, z) (x,y,z) 扩展为四维齐次坐标 ( x , y , z , 1 ) (x, y, z, 1) (x,y,z,1)。这样可以统一表示线性变换和平移变换。下面分别推导这三种变换的矩阵表示。

3.1 平移

定义:将任意一个点 ( x , y , z ) (x, y, z) (x,y,z) 平移 ( t x , t y , t z ) (t_x, t_y, t_z) (tx,ty,tz),得到新点 ( x ′ , y ′ , z ′ ) (x', y', z') (x,y,z),即

{ x ′ = x + t x y ′ = y + t y z ′ = z + t z \begin{cases} x' = x + t_x \\ y' = y + t_y \\ z' = z + t_z \end{cases} x=x+txy=y+tyz=z+tz

齐次坐标表示

[ x ′ y ′ z ′ 1 ] = [ 1 0 0 t x 0 1 0 t y 0 0 1 t z 0 0 0 1 ] [ x y z 1 ] \begin{bmatrix} x' \\ y' \\ z' \\ 1 \end{bmatrix}=\begin{bmatrix} 1 & 0 & 0 & t_x \\ 0 & 1 & 0 & t_y \\ 0 & 0 & 1 & t_z \\ 0 & 0 & 0 & 1 \end{bmatrix}\begin{bmatrix} x \\ y \\ z \\ 1 \end{bmatrix} xyz1 = 100001000010txtytz1 xyz1

因此,三维平移矩阵为:
T = [ 1 0 0 t x 0 1 0 t y 0 0 1 t z 0 0 0 1 ] T = \begin{bmatrix} 1 & 0 & 0 & t_x \\ 0 & 1 & 0 & t_y \\ 0 & 0 & 1 & t_z \\ 0 & 0 & 0 & 1 \end{bmatrix} T= 100001000010txtytz1

3.2 旋转

在三维空间中,旋转可以绕任意一个坐标轴进行。下面分别给出绕 X X X 轴、 Y Y Y 轴和 Z Z Z 轴旋转角度 θ \theta θ 的旋转矩阵。在三维空间中的旋转看着复杂其实很简单,因为绕X轴旋转其实X的值并不会产生变化,只有Y和Z的值会产生变化,那不就相当于在二维空间中的旋转吗?是的,我们依次来讨论。
在这里插入图片描述

3.2.1 绕 X X X 轴旋转

定义:将任意一个点 ( x , y , z ) (x, y, z) (x,y,z) X X X 轴逆时针旋转角度 θ \theta θ,得到新点 ( x ′ , y ′ , z ′ ) (x', y', z') (x,y,z),x的大小其实是不动的,y和z的值和二维平面类似,即

{ x ′ = x y ′ = y cos ⁡ θ − z sin ⁡ θ z ′ = y sin ⁡ θ + z cos ⁡ θ \begin{cases} x' = x \\ y' = y \cos\theta - z \sin\theta \\ z' = y \sin\theta + z \cos\theta \end{cases} x=xy=ycosθzsinθz=ysinθ+zcosθ

齐次坐标表示

R x ( θ ) = [ 1 0 0 0 0 cos ⁡ θ − sin ⁡ θ 0 0 sin ⁡ θ cos ⁡ θ 0 0 0 0 1 ] R_x(\theta) = \begin{bmatrix} 1 & 0 & 0 & 0 \\ 0 & \cos\theta & -\sin\theta & 0 \\ 0 & \sin\theta & \cos\theta & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} Rx(θ)= 10000cosθsinθ00sinθcosθ00001

3.2.2 绕 Y Y Y 轴旋转

定义:将任意一个点 ( x , y , z ) (x, y, z) (x,y,z) Y Y Y 轴逆时针旋转角度 θ \theta θ,得到新点 ( x ′ , y ′ , z ′ ) (x', y', z') (x,y,z),y值也是不动的,x和z的值与二维平面类似,即

{ x ′ = x cos ⁡ θ + z sin ⁡ θ y ′ = y z ′ = − x sin ⁡ θ + z cos ⁡ θ \begin{cases} x' = x \cos\theta + z \sin\theta \\ y' = y \\ z' = -x \sin\theta + z \cos\theta \end{cases} x=xcosθ+zsinθy=yz=xsinθ+zcosθ

齐次坐标表示

R y ( θ ) = [ cos ⁡ θ 0 sin ⁡ θ 0 0 1 0 0 − sin ⁡ θ 0 cos ⁡ θ 0 0 0 0 1 ] R_y(\theta) = \begin{bmatrix} \cos\theta & 0 & \sin\theta & 0 \\ 0 & 1 & 0 & 0 \\ -\sin\theta & 0 & \cos\theta & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} Ry(θ)= cosθ0sinθ00100sinθ0cosθ00001

3.2.3 绕 Z Z Z 轴旋转

定义:将一个点 ( x , y , z ) (x, y, z) (x,y,z) Z Z Z 轴逆时针旋转角度 θ \theta θ,得到新点 ( x ′ , y ′ , z ′ ) (x', y', z') (x,y,z),z值是不变的,x和y的值与二维平面类似,即

{ x ′ = x cos ⁡ θ − y sin ⁡ θ y ′ = x sin ⁡ θ + y cos ⁡ θ z ′ = z \begin{cases} x' = x \cos\theta - y \sin\theta \\ y' = x \sin\theta + y \cos\theta \\ z' = z \end{cases} x=xcosθysinθy=xsinθ+ycosθz=z

齐次坐标表示

R z ( θ ) = [ cos ⁡ θ − sin ⁡ θ 0 0 sin ⁡ θ cos ⁡ θ 0 0 0 0 1 0 0 0 0 1 ] R_z(\theta) = \begin{bmatrix} \cos\theta & -\sin\theta & 0 & 0 \\ \sin\theta & \cos\theta & 0 & 0 \\ 0 & 0 & 1 & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} Rz(θ)= cosθsinθ00sinθcosθ0000100001

因此,三维旋转矩阵可以根据旋转轴选择对应的 R x ( θ ) R_x(\theta) Rx(θ) R y ( θ ) R_y(\theta) Ry(θ) R z ( θ ) R_z(\theta) Rz(θ)

3.2.4 绕任意轴旋转

  怎么表示任意轴呢?用一个向量便可以表示任意轴。那怎么绕任意轴旋转呢?答案是先将物体平移到原点去,分解为分别绕XYZ的旋转,而绕这三个轴旋转的角度就被叫做欧拉角,旋转完毕后再平移到原来的地方就可以了。这个思想也就是著名的罗德里格斯旋转公式所采用的思想。

  设旋转轴为单位向量 u = ( u x , u y , u z ) \mathbf{u} = (u_x, u_y, u_z) u=(ux,uy,uz),旋转角度为 θ \theta θ。为了确保计算的准确性,旋转轴必须为单位向量,即:

u x 2 + u y 2 + u z 2 = 1 u_x^2 + u_y^2 + u_z^2 = 1 ux2+uy2+uz2=1
罗德里格斯旋转公式提供了一种通过旋转轴和旋转角度来计算旋转矩阵的方法。旋转矩阵 R \mathbf{R} R 可以表示为:

R = I cos ⁡ θ + ( 1 − cos ⁡ θ ) u u T + K sin ⁡ θ \mathbf{R} = \mathbf{I} \cos\theta + (1 - \cos\theta) \mathbf{u} \mathbf{u}^T + \mathbf{K} \sin\theta R=Icosθ+(1cosθ)uuT+Ksinθ

其中:

  • I \mathbf{I} I 3 × 3 3 \times 3 3×3 的单位矩阵。
  • u u T \mathbf{u} \mathbf{u}^T uuT 是旋转轴的外积矩阵。
  • K \mathbf{K} K 是旋转轴的反对称矩阵,定义为:

K = [ 0 − u z u y u z 0 − u x − u y u x 0 ] \mathbf{K} = \begin{bmatrix} 0 & -u_z & u_y \\ u_z & 0 & -u_x \\ -u_y & u_x & 0 \end{bmatrix} K= 0uzuyuz0uxuyux0
结合上述推导,绕任意单位向量 u = ( u x , u y , u z ) \mathbf{u} = (u_x, u_y, u_z) u=(ux,uy,uz) 旋转角度 θ \theta θ 的旋转矩阵 R \mathbf{R} R 为:

R = [ cos ⁡ θ + u x 2 ( 1 − cos ⁡ θ ) u x u y ( 1 − cos ⁡ θ ) − u z sin ⁡ θ u x u z ( 1 − cos ⁡ θ ) + u y sin ⁡ θ u y u x ( 1 − cos ⁡ θ ) + u z sin ⁡ θ cos ⁡ θ + u y 2 ( 1 − cos ⁡ θ ) u y u z ( 1 − cos ⁡ θ ) − u x sin ⁡ θ u z u x ( 1 − cos ⁡ θ ) − u y sin ⁡ θ u z u y ( 1 − cos ⁡ θ ) + u x sin ⁡ θ cos ⁡ θ + u z 2 ( 1 − cos ⁡ θ ) ] \mathbf{R} = \begin{bmatrix} \cos\theta + u_x^2 (1 - \cos\theta) & u_x u_y (1 - \cos\theta) - u_z \sin\theta & u_x u_z (1 - \cos\theta) + u_y \sin\theta \\ u_y u_x (1 - \cos\theta) + u_z \sin\theta & \cos\theta + u_y^2 (1 - \cos\theta) & u_y u_z (1 - \cos\theta) - u_x \sin\theta \\ u_z u_x (1 - \cos\theta) - u_y \sin\theta & u_z u_y (1 - \cos\theta) + u_x \sin\theta & \cos\theta + u_z^2 (1 - \cos\theta) \end{bmatrix} R= cosθ+ux2(1cosθ)uyux(1cosθ)+uzsinθuzux(1cosθ)uysinθuxuy(1cosθ)uzsinθcosθ+uy2(1cosθ)uzuy(1cosθ)+uxsinθuxuz(1cosθ)+uysinθuyuz(1cosθ)uxsinθcosθ+uz2(1cosθ)

3.3 缩放

定义:将一个点 ( x , y , z ) (x, y, z) (x,y,z) 沿 x x x 轴缩放因子 s x s_x sx,沿 y y y 轴缩放因子 s y s_y sy,沿 z z z 轴缩放因子 s z s_z sz,得到新点 ( x ′ , y ′ , z ′ ) (x', y', z') (x,y,z),即

{ x ′ = s x ⋅ x y ′ = s y ⋅ y z ′ = s z ⋅ z \begin{cases} x' = s_x \cdot x \\ y' = s_y \cdot y \\ z' = s_z \cdot z \end{cases} x=sxxy=syyz=szz

齐次坐标表示

S = [ s x 0 0 0 0 s y 0 0 0 0 s z 0 0 0 0 1 ] S = \begin{bmatrix} s_x & 0 & 0 & 0 \\ 0 & s_y & 0 & 0 \\ 0 & 0 & s_z & 0 \\ 0 & 0 & 0 & 1 \end{bmatrix} S= sx0000sy0000sz00001

4 WebGL中怎么实现旋转、平移、缩放

  OK.终于讲完理论了,现在我们来讲下实操。通过上面的讲解,大家应该明白了,要旋转一个立方体,只要把立方体中的每一个点都乘以一个旋转矩阵,就可以了。类似的,要实现平移,只要把立方体中的每一个点都乘以一个平移矩阵就可以了。我们以旋转立方体为例。

4.1 声明顶点着色器和片元着色器

<script id="vertex-shader" type="x-shader/x-vertex">
    attribute vec4 a_Position;
    attribute vec4 a_Color;
    uniform mat4 u_RorateMatrix;
    varying vec4 v_Color;
    void main(){
    	//给所有点都乘以一个旋转矩阵
        gl_Position =  uRotateMatrix * a_Position;
        v_Color = a_Color;
        v_TexCoord = a_TexCoord;
    }
</script>
<script id="fragment-shader" type="x-shader/x-fragment">
    precision highp float;
    varying vec4 v_Color;
    void main(){
        gl_FragColor = v_Color;
    }
</script>

4.2 计算旋转矩阵

  计算旋转矩阵的类库太多了,比如gl-Matrix、Cesiumjs、Threejs、Babylonjs都可以实现。本文中使用的是WebGL编程指南中自带的方法,只要调用时,指明要旋转的角度和要绕哪个轴旋转即可。

var modelMatrix = new Matrix4();
//绕Y轴【0,1,0】旋转60度
modelMatrix.setRotate(60,0,1,0);
//赋值给u_RorateMatrix
var u_RorateMatrix= gl.getUniformLocation(program, 'u_RorateMatrix');
//false表示不使用归一化
gl.uniformMatrix4fv(u_RorateMatrix,false,modelMatrix.elements);

旋转方法源码如下:

/**
 * Set the matrix for rotation.
 * The vector of rotation axis may not be normalized.
 * @param angle The angle of rotation (degrees)
 * @param x The X coordinate of vector of rotation axis.
 * @param y The Y coordinate of vector of rotation axis.
 * @param z The Z coordinate of vector of rotation axis.
 * @return this
 */
Matrix4.prototype.setRotate = function(angle, x, y, z) {
  var e, s, c, len, rlen, nc, xy, yz, zx, xs, ys, zs;

  angle = Math.PI * angle / 180;
  e = this.elements;

  s = Math.sin(angle);
  c = Math.cos(angle);

  if (0 !== x && 0 === y && 0 === z) {
    // Rotation around X axis
    if (x < 0) {
      s = -s;
    }
    e[0] = 1;  e[4] = 0;  e[ 8] = 0;  e[12] = 0;
    e[1] = 0;  e[5] = c;  e[ 9] =-s;  e[13] = 0;
    e[2] = 0;  e[6] = s;  e[10] = c;  e[14] = 0;
    e[3] = 0;  e[7] = 0;  e[11] = 0;  e[15] = 1;
  } else if (0 === x && 0 !== y && 0 === z) {
    // Rotation around Y axis
    if (y < 0) {
      s = -s;
    }
    e[0] = c;  e[4] = 0;  e[ 8] = s;  e[12] = 0;
    e[1] = 0;  e[5] = 1;  e[ 9] = 0;  e[13] = 0;
    e[2] =-s;  e[6] = 0;  e[10] = c;  e[14] = 0;
    e[3] = 0;  e[7] = 0;  e[11] = 0;  e[15] = 1;
  } else if (0 === x && 0 === y && 0 !== z) {
    // Rotation around Z axis
    if (z < 0) {
      s = -s;
    }
    e[0] = c;  e[4] =-s;  e[ 8] = 0;  e[12] = 0;
    e[1] = s;  e[5] = c;  e[ 9] = 0;  e[13] = 0;
    e[2] = 0;  e[6] = 0;  e[10] = 1;  e[14] = 0;
    e[3] = 0;  e[7] = 0;  e[11] = 0;  e[15] = 1;
  } else {
    // Rotation around another axis
    len = Math.sqrt(x*x + y*y + z*z);
    if (len !== 1) {
      rlen = 1 / len;
      x *= rlen;
      y *= rlen;
      z *= rlen;
    }
    nc = 1 - c;
    xy = x * y;
    yz = y * z;
    zx = z * x;
    xs = x * s;
    ys = y * s;
    zs = z * s;

    e[ 0] = x*x*nc +  c;
    e[ 1] = xy *nc + zs;
    e[ 2] = zx *nc - ys;
    e[ 3] = 0;

    e[ 4] = xy *nc - zs;
    e[ 5] = y*y*nc +  c;
    e[ 6] = yz *nc + xs;
    e[ 7] = 0;

    e[ 8] = zx *nc + ys;
    e[ 9] = yz *nc - xs;
    e[10] = z*z*nc +  c;
    e[11] = 0;

    e[12] = 0;
    e[13] = 0;
    e[14] = 0;
    e[15] = 1;
  }

  return this;
};

4.3 绘制立方体并进行旋转完整代码

const verticesColors = new Float32Array([
    // 前面
    -1.0, -1.0,  1.0,     1.0, 0.0,1.0,//v2 红色
     1.0, -1.0,  1.0,     1.0, 0.0,1.0,//v3 红色
     1.0,  1.0,  1.0,     1.0, 0.0,1.0,//v0 红色
    -1.0,  1.0,  1.0,     1.0, 0.0,1.0,//v1 红色

    // 后面
    -1.0, -1.0, -1.0,     0.0, 1.0, 0.0,//v5 绿色
     1.0, -1.0, -1.0,     0.0, 1.0, 0.0,//v4 绿色
     1.0,  1.0, -1.0,     0.0, 1.0, 0.0,//v7 绿色
    -1.0,  1.0, -1.0,     0.0, 1.0, 0.0,//v6 绿色

    // 上面
    -1.0,  1.0,  1.0,     0.0, 0.0,1.0,//v1 蓝色
     1.0,  1.0,  1.0,     0.0, 0.0,1.0,//v0 蓝色
     1.0,  1.0, -1.0,     0.0, 0.0,1.0,//v7 蓝色
    -1.0,  1.0, -1.0,     0.0, 0.0,1.0,//v6 蓝色

    // 下面
    -1.0, -1.0,  1.0,     0.0, 0.0,0.0,//v2 黑色
     1.0, -1.0,  1.0,     0.0, 0.0,0.0,//v3 黑色
     1.0, -1.0, -1.0,     0.0, 0.0,0.0,//v4 黑色
    -1.0, -1.0, -1.0,     0.0, 0.0,0.0,//v5 黑色

    // 左面
    -1.0, -1.0, -1.0,     0.0, 1.0,1.0,//v5 青色
    -1.0, -1.0,  1.0,     0.0, 1.0,1.0,//v2 青色
    -1.0,  1.0,  1.0,     0.0, 1.0,1.0,//v1 青色
    -1.0,  1.0, -1.0,     0.0, 1.0,1.0,//v6 青色

    // 右面
     1.0, -1.0,  1.0,     1.0, 1.0,1.0,//v3 白色
     1.0, -1.0, -1.0,     1.0, 1.0,1.0,//v4 白色
     1.0,  1.0, -1.0,     1.0, 1.0,1.0,//v7 白色
     1.0,  1.0,  1.0,     1.0, 1.0,1.0,//v0 白色
]);
const indices = new Uint8Array([
    0, 1, 2, 0, 2, 3, // 前面
    4, 5, 6, 4, 6, 7, // 后面
    8, 9, 10, 8, 10, 11, // 上面
    12, 13, 14, 12, 14, 15, // 下面
    16, 17, 18, 16, 18, 19, // 左面
    20, 21, 22, 20, 22, 23  // 右面
]);


//使webgl视口和canvas画板一样大
gl.viewport(0, 0, canvas.width, canvas.height);
//开启深度检测
gl.enable(gl.DEPTH_TEST);
//顶点
let vertexColorBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER,vertexColorBuffer);
gl.bufferData(gl.ARRAY_BUFFER,verticesColors,gl.STATIC_DRAW);
let FSIZE = verticesColors.BYTES_PER_ELEMENT;
let a_Position = gl.getAttribLocation(program,'a_Position');
gl.vertexAttribPointer(a_Position,3,gl.FLOAT,false,FSIZE*6,0);
gl.enableVertexAttribArray(a_Position);
//颜色
var a_Color = gl.getAttribLocation(program, 'a_Color');
gl.vertexAttribPointer(a_Color, 3, gl.FLOAT, false, FSIZE * 6, FSIZE * 3);
gl.enableVertexAttribArray(a_Color);
//旋转
var modelMatrix = new Matrix4();
modelMatrix.setRotate(60,0,1,0);

//赋值给uRotateMatrix
var u_RorateMatrix= gl.getUniformLocation(program, 'u_RorateMatrix');
gl.uniformMatrix4fv(u_RorateMatrix,false,modelMatrix .elements);
//绑定索引缓冲
let indexBuffer =  gl.createBuffer();
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER,indexBuffer);
gl.bufferData(gl.ELEMENT_ARRAY_BUFFER,indices,gl.STATIC_DRAW);
gl.drawElements(gl.TRIANGLES, 36, gl.UNSIGNED_BYTE, 0);

4.4 效果

在这里插入图片描述

5 总结

  本文我们通过梳理二维及三维的旋转、平移、缩放矩阵,搞明白了旋转、平移、缩放对应的矩阵,并在WebGL中进行了实验操作,实现了一个立方体的旋转。本文在理解上有一定的难度,尤其是矩阵的变换,需要读者具有一定的线性代数知识,希望读者仔细体会,回见~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/875558.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

基于Matlab的模拟答题卡识别阅卷可以识别指定答题卡的各个部分-界面

识别指定答题卡的各个部分-界面-如学号&#xff0c;准考证号&#xff0c;客观题答案&#xff0c;主观题分数等用户可以在Excel中自行设置标准答案&#xff0c;并对六十题客观题进行批改&#xff0c;并显示分数。 项目介绍 本项目旨在开发一个基于MATLAB的答题卡识别阅卷系统&a…

认识泛型和包装类

认识泛型和包装类 包装类基本数据类型和对应的包装类装箱和拆箱自动装箱和自动拆箱 什么是泛型引出泛型语法 泛型类的使用语法示例类型推导 裸类型(Raw Type)说明 泛型如何编译的擦除机制 泛型的上界语法示例复杂示例 泛型方法定义方法示例使用类型推导和不用类型推导静态的泛型…

IO模型---BIO、NIO、IO多路复用、AIO详解

本篇将想给详细解释一下什么是BIO、NIO、IO多路复用以及AIO~ 同步的阻塞(BIO)和非阻塞(NIO)的区别 BIO&#xff1a;线程发来IO请求后&#xff0c;一直阻塞着IO线程&#xff0c;需要缓冲区这边数据准备好之后&#xff0c;才会进行下一步的操作。 举个&#x1f330;&#xff1…

UE5学习笔记21-武器的射击功能

一、创建C类 创建武器子弹的类&#xff0c;创建生产武器子弹的类&#xff0c;创建弹壳的类&#xff0c;生产武器子弹的类的父类是武器的类 创建后如图&#xff0c;ProjectileMyWeapon类(产生子弹的类)继承自weapon类&#xff0c;Projectile(子弹的类)&#xff0c;Casing(弹壳声…

如何使用QT完成记事本程序的UI界面布局

每日QT技巧查询表-CSDN博客 会持续更新记事本编写的全部过程&#xff0c;关注不迷路 一、相关控件 ①水平和垂直布局 ②按键 ③文本框 ④水平弹簧 ⑤标签 ⑥Widget 二、控件使用方法 1、PushButton 拖出三个按键&#xff0c;并对其进行命名&#xff0c;两处地方命名可以不一…

【Echarts】vue3打开echarts的正确方式

ECharts 是一个功能强大、灵活易用的数据可视化工具&#xff0c;适用于商业报表、数据分析、科研教育等多种场景。那么该如何优雅的使用Echarts呢? 这里以vue3为例。 安装echarts pnpm i echarts封装公用方法 // ts-nocheck import * as echarts from echarts; // 我们这里借…

C++:入门基础

一.C参考文档 https://legacy.cplusplus.com/reference/ https://zh.cppreference.com/w/cpp https://en.cppreference.com/w/ 二.C的第一个程序 #include <iostream> using namespace std;int main() {cout << "Hello world!" << en…

无人机PX4飞控ROS应用层开发:MAVROS 功能包介绍与飞控消息汇总(一)

概述 这个软件包提供了针对各种自动驾驶仪(如PX4,Ardupilot)使用 MAVLink 通信协议的通信驱动程序。 此外&#xff0c;它还提供了用于地面控制站&#xff08;例如 QGroundControl&#xff09;的 UDP MAVLink 桥接功能。 通常与PX4的offboard模式联合使用 Offboard控制背后的想…

单机docker-compose部署minio

单机多副本docker-compose部署minio 简单介绍 如果服务器有限可以单机挂载多硬盘实现多副本容错&#xff08;生产不推荐&#xff09; 部署好的文件状态 有两个重要文件 docker-compose.yaml和nginx.conf docker-compose.yaml是docker部署容器的配置信息包括4个minio和1个ng…

c中 int 和 unsigned int

c语言中&#xff0c;char、short、int、int64以及unsigned char、unsigned short、unsigned int、unsigned int64等等类型都可以表示整数。但是他们表示整数的位数不同&#xff0c;比如&#xff1a;char/unisigned char表示8位整数&#xff1b; short/unsigned short表示16位整…

菜鸟入门Docker

初始Docker Docker的概念 Docker的用途 DOcke的安装 Docker架构 配置Docker镜像加速器 Docker常用命令 Docker服务相关的命令。 Docker镜像相关的命令 Docker容器相关的命令 容器的数据卷 数据卷的概念和作用 配置数据卷 Docker应用部署 Docker部署mysql Docker…

Unity同时启动多个Editor

HardLinkShellExt tool https://schinagl.priv.at/nt/hardlinkshellext/linkshellextension.html 作用&#xff1a; 1.网络Online项目方便调试&#xff0c;MMO项目 2.方便发布不同平台的包&#xff0c;快速开发测试 使用方法&#xff1a;

easy-es动态索引支持

背景 很多项目目前都引入了es&#xff0c;由于es弥补了mysql存储及搜索查询的局限性&#xff0c;随着技术的不断迭代&#xff0c;原生的es客户端使用比较繁琐不直观&#xff0c;上手代价有点大&#xff0c;所以easy-es框架就面世了&#xff0c;学习成本很低&#xff0c;有空大…

【Gateway】Gateway Filter Factories

Predicate决定了请求由哪一个路由处理,如果在请求处理前后需要加一些逻辑,这就是Filter(过滤器)的作用范围了.Filter分为两种类型:Pre类型和Post类型 滤器的两种类型 Pre 类型过滤器: 执行时机: 在请求被转发到后端服务之前执行。作用: 可以用来执行鉴权、限流、请求日志记录、…

Django笔记一:搭建Django环境与URL路径访问

博主之前学从Java后端开发&#xff0c;后面获取到读研资格&#xff0c;想着未来转算法岗&#xff0c;初学Python&#xff0c;发现Python还挺有趣的&#xff0c;由于之前所学后端缘故&#xff0c;有点后端情节&#xff0c;想学习一下Django框架&#xff08;python的web框架&…

什么是交换机级联?

在现代计算机网络中&#xff0c;交换机级联是一种广泛应用的技术&#xff0c;有助于提升网络的扩展性和灵活性。本文将深入探讨交换机级联相关知识&#xff0c;详细介绍其基本概念和连接配置方法&#xff0c;并对常见技术问题进行解答。 交换机级联概述 交换机级联是指通过将…

线性基大发现

一.构造方法 1.贪心法&#xff08;每一个数往里插入即可&#xff09; /*贪心法构造线性基的特点&#xff1a; 1.从小到大排列 2.各个基的高位可能存在重复的1 2.线性基不是唯一的&#xff0c;与原集合的元素顺序有关*/ void insert(int x){//贪心法for(int i63;i>0;i--){i…

c#中给winform定义快捷键的几种方式

快捷键的使用在日常的开发中频率比较高&#xff0c;这里总结了最常见的各种快捷键的设置方式&#xff0c;需要的时候大家直接照抄就可以了&#xff0c;不用再去查询如何实现了。 文章目录 一、按钮快捷键二、菜单快捷键三、全局快捷键1、重写ProcessCmdKey2、使用KeyPreview属…

Word使用手册

修改样式 编辑word文档时&#xff0c;标题和正文文本通常有不同的格式&#xff0c;如果能将这些格式保存为样式&#xff0c;下一次就能直接调用样式&#xff0c;而不需要重复手动设置格式。 可以将样式通常保存为不同的 样式模板.docx&#xff0c;要调用不同样式集&#xff0…

MATLAB基础应用精讲-【数模应用】卡方拟合优度检验(附MATLAB、python和R语言代码实现)

目录 前言 几个高频面试题目 卡方分布与拟合优度的卡方检验是什么关系? 算法原理 什么是卡方检验 卡方是什么? 如何使用卡方表计算器? 什么是卡方拟合优度检验 使用条件 基本原理 数学模型 卡方检验自由度 理论频数计算 需要注意的事项 卡方检验分类 1、 独…