自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

一起學 WebGL:圖形變形以及矩陣變換

開發(fā) 前端
WebGL 用的是按列主序(column major order)規(guī)則,即按列填充矩陣,從左往右,屬于主流。

之前繪制了三角形,我們現(xiàn)在給它做一個變形操作。

對一個三角形進行變形,其實就是重新這個三角形的三個頂點的位置,計算完后再繪制出來,相比原來就發(fā)生了變形。

變形常見的有位移、選擇、縮放。位移,其實就是給每個頂點的各個坐標值加上偏移量 dx、dy、dz。旋轉(zhuǎn)稍微復雜些,用到了三角函數(shù)。最后是縮放,就是簡單地各個分量乘以縮放比例系數(shù)。

這些變換可以抽象簡化成對應(yīng)的變換矩陣,方便我們用統(tǒng)一的方式作表達,并配合矩陣乘法的結(jié)合律,將多個變形矩陣合并成一個復合矩陣,減少計算量。

直接進入正題,看看怎么用 WebGL 實現(xiàn)矩陣變換。

繪制三角形

我們先繪制一個普通的沒做過變形的三角形。

demo 地址:

https://codesandbox.io/s/gbh1xf。

代碼:

/** @type {HTMLCanvasElement} */
const canvas = document.querySelector("canvas");
const gl = canvas.getContext("webgl");

const vertexShaderSrc = `
attribute vec4 a_Position;
void main() {
 gl_Position = a_Position;
 gl_PointSize = 10.0;
}
`;

const fragmentShaderSrc = `
void main() {
  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;

/**** 渲染器生成處理 ****/
// 創(chuàng)建頂點渲染器
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSrc);
gl.compileShader(vertexShader);
// 創(chuàng)建片元渲染器
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSrc);
gl.compileShader(fragmentShader);
// 程序?qū)ο?const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
gl.program = program;

// 頂點數(shù)據(jù)
const vertices = new Float32Array([
  // 第一個點
  0,
  0.5,
  // 第二個點
  -0.5,
  -0.5,
  // 第三個點
  0.5,
  -0.5
]);

// 創(chuàng)建緩存對象
const vertexBuffer = gl.createBuffer();
// 綁定緩存對象到上下文
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 向緩存區(qū)寫入數(shù)據(jù)
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

// 獲取 a_Position 變量地址
const a_Position = gl.getAttribLocation(gl.program, "a_Position");
// 將緩沖區(qū)對象分配給 a_Position 變量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);

// 允許訪問緩存區(qū)
gl.enableVertexAttribArray(a_Position);

/*** 繪制 ***/
// 清空畫布,并指定顏色
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// 繪制三角形
gl.drawArrays(gl.TRIANGLES, 0, 3);

渲染效果:

圖片

位移

位移,最簡單的方式是再聲明一個 u_Translation 向量,和 a_Position 相加就完事了:

但還是矩陣比較方便,具有可以統(tǒng)一格式,計算復合矩陣等優(yōu)勢。通常變形都是復雜的,旋轉(zhuǎn)后平移然后再縮放一套下來,矩陣還是很重要的。

頂點著色器的代碼修改為:

const vertexShaderSrc = `
attribute vec4 a_Position;
uniform mat4 u_xformMatrix;
void main() {
 gl_Position = u_xformMatrix * a_Position;
}
`;

西瓜哥在這里加多了一個 u_xformMatrix 變量。

首先用了 uniform 類型修飾符,表示這個變量不會逐頂點發(fā)生變化,是固定的。mat4 表示一個 4x4 矩陣,一個有 16 個浮點數(shù)的一維數(shù)組。

來看看通過矩陣進行位移的實現(xiàn)。位移矩陣如下:

對應(yīng)的 Float32Array 數(shù)組為:

/****** 位移矩陣 ****/
const dx = 0.5; // 向右移動
const dy = -0.3; // 向下移動
// z 先不管,沒用到透視矩陣,設(shè)置值也看不到效果

const xformMatrix = new Float32Array([
  1, 0, 0, 0,

  0, 1, 0, 0,

  0, 0, 1, 0,

  dx, dy, 0, 1
]);

WebGL 用的是按列主序(column major order)規(guī)則,即按列填充矩陣,從左往右,屬于主流。

還有一種是按行主序(row major order)的,也就是將遍歷數(shù)組一行行填充到矩陣,從上往下。比較少見。

接著把這個數(shù)組懟到前面頂點著色器聲明的 u_xformMatrix 變量中。

const u_xformMatrix = gl.getUniformLocation(gl.program, "u_xformMatrix");
gl.uniformMatrix4fv(u_xformMatrix, false, xformMatrix);

這里是用 gl.uniformMatrix4fv 來設(shè)置 4x4 矩陣的值。

完整代碼:

/** @type {HTMLCanvasElement} */
const canvas = document.querySelector("canvas");
const gl = canvas.getContext("webgl");

const vertexShaderSrc = `
attribute vec4 a_Position;
uniform mat4 u_xformMatrix;
void main() {
 gl_Position = u_xformMatrix * a_Position;
}
`;

const fragmentShaderSrc = `
void main() {
  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;

/**** 渲染器生成處理 ****/
// 創(chuàng)建頂點渲染器
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSrc);
gl.compileShader(vertexShader);
// 創(chuàng)建片元渲染器
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSrc);
gl.compileShader(fragmentShader);
// 程序?qū)ο?const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
gl.program = program;

// 頂點數(shù)據(jù)
const vertices = new Float32Array([
  // 第一個點
  0,
  0.5,
  // 第二個點
  -0.5,
  -0.5,
  // 第三個點
  0.5,
  -0.5
]);

// 創(chuàng)建緩存對象
const vertexBuffer = gl.createBuffer();
// 綁定緩存對象到上下文
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 向緩存區(qū)寫入數(shù)據(jù)
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

// 獲取 a_Position 變量地址
const a_Position = gl.getAttribLocation(gl.program, "a_Position");
// 將緩沖區(qū)對象分配給 a_Position 變量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);

// 允許訪問緩存區(qū)
gl.enableVertexAttribArray(a_Position);

/****** 位移矩陣 ****/
const dx = 0.5; // 向右移動
const dy = -0.3; // 向下移動
// z 先不管,沒用到透視矩陣,設(shè)置值也看不到效果

const xformMatrix = new Float32Array([
  1,
  0,
  0,
  0,

  0,
  1,
  0,
  0,

  0,
  0,
  1,
  0,

  dx,
  dy,
  0,
  1
]);
const u_xformMatrix = gl.getUniformLocation(gl.program, "u_xformMatrix");
gl.uniformMatrix4fv(u_xformMatrix, false, xformMatrix);

/*** 繪制 ***/
// 清空畫布,并指定顏色
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// 繪制三角形
gl.drawArrays(gl.TRIANGLES, 0, 3);

demo 地址:

https://codesandbox.io/s/09ujp5?file=/index.js。

看看效果:

圖片

成功向右并向下移動了一段距離。

旋轉(zhuǎn)

頂點著色器的代碼不用改,這次我們傳一個旋轉(zhuǎn)矩陣進去,就逆時針旋轉(zhuǎn) 90 度吧,沿著 z 軸作旋轉(zhuǎn)。

公式為:

這個公示是幾何數(shù)學推導出來的。

數(shù)組數(shù)據(jù)為:

/****** 旋轉(zhuǎn)矩陣 ****/
const angle = 90;
const radian = (angle * Math.PI) / 180;
const cos = Math.cos(radian);
const sin = Math.sin(radian);

const xformMatrix = new Float32Array([
  cos, sin, 0, 0,

  -sin, cos, 0, 0,

  0, 0, 1, 0,

  0, 0, 0, 1
]);

因為很多 API 只支持弧度制,所以我們需要將角度轉(zhuǎn)弧度。

然后是旋轉(zhuǎn)方向,提供一個正數(shù),WebGL 是沿著逆時針旋轉(zhuǎn)的。順帶一提, Canvas 2D 是順時針旋轉(zhuǎn)的。

完整代碼:

/** @type {HTMLCanvasElement} */
const canvas = document.querySelector("canvas");
const gl = canvas.getContext("webgl");

const vertexShaderSrc = `
attribute vec4 a_Position;
uniform mat4 u_xformMatrix;
void main() {
 gl_Position = u_xformMatrix * a_Position;
}
`;

const fragmentShaderSrc = `
void main() {
  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;

/**** 渲染器生成處理 ****/
// 創(chuàng)建頂點渲染器
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSrc);
gl.compileShader(vertexShader);
// 創(chuàng)建片元渲染器
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSrc);
gl.compileShader(fragmentShader);
// 程序?qū)ο?const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
gl.program = program;

// 頂點數(shù)據(jù)
const vertices = new Float32Array([
  // 第一個點
  0,
  0.5,
  // 第二個點
  -0.5,
  -0.5,
  // 第三個點
  0.5,
  -0.5
]);

// 創(chuàng)建緩存對象
const vertexBuffer = gl.createBuffer();
// 綁定緩存對象到上下文
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 向緩存區(qū)寫入數(shù)據(jù)
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

// 獲取 a_Position 變量地址
const a_Position = gl.getAttribLocation(gl.program, "a_Position");
// 將緩沖區(qū)對象分配給 a_Position 變量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);

// 允許訪問緩存區(qū)
gl.enableVertexAttribArray(a_Position);

/****** 旋轉(zhuǎn)矩陣 ****/
const angle = 90;
const radian = (angle * Math.PI) / 180;
const cos = Math.cos(radian);
const sin = Math.sin(radian);

const xformMatrix = new Float32Array([
  cos,
  sin,
  0,
  0,

  -sin,
  cos,
  0,
  0,

  0,
  0,
  1,
  0,

  0,
  0,
  0,
  1
]);
const u_xformMatrix = gl.getUniformLocation(gl.program, "u_xformMatrix");
gl.uniformMatrix4fv(u_xformMatrix, false, xformMatrix);

/*** 繪制 ***/
// 清空畫布,并指定顏色
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// 繪制三角形
gl.drawArrays(gl.TRIANGLES, 0, 3);

demo 地址:

https://codesandbox.io/s/wx44l0?file=/index.js。

渲染效果:

圖片

縮放

縮放公式為:

數(shù)組為:

/****** 縮放矩陣 ****/
const sx = 2;
const sy = 2;
const sz = 1;
// sz 先不管,沒用到透視矩陣,設(shè)置了值也看不到效果

const xformMatrix = new Float32Array([
  sx, 0, 0, 0,

  0, sy, 0, 0,

  0, 0, sz, 0,

  0, 0, 0, 1
]);

完整代碼:

/** @type {HTMLCanvasElement} */
const canvas = document.querySelector("canvas");
const gl = canvas.getContext("webgl");

const vertexShaderSrc = `
attribute vec4 a_Position;
uniform mat4 u_xformMatrix;
void main() {
 gl_Position = u_xformMatrix * a_Position;
}
`;

const fragmentShaderSrc = `
void main() {
  gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`;

/**** 渲染器生成處理 ****/
// 創(chuàng)建頂點渲染器
const vertexShader = gl.createShader(gl.VERTEX_SHADER);
gl.shaderSource(vertexShader, vertexShaderSrc);
gl.compileShader(vertexShader);
// 創(chuàng)建片元渲染器
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER);
gl.shaderSource(fragmentShader, fragmentShaderSrc);
gl.compileShader(fragmentShader);
// 程序?qū)ο?const program = gl.createProgram();
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
gl.useProgram(program);
gl.program = program;

// 頂點數(shù)據(jù)
const vertices = new Float32Array([
  // 第一個點
  0,
  0.5,
  // 第二個點
  -0.5,
  -0.5,
  // 第三個點
  0.5,
  -0.5
]);

// 創(chuàng)建緩存對象
const vertexBuffer = gl.createBuffer();
// 綁定緩存對象到上下文
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 向緩存區(qū)寫入數(shù)據(jù)
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW);

// 獲取 a_Position 變量地址
const a_Position = gl.getAttribLocation(gl.program, "a_Position");
// 將緩沖區(qū)對象分配給 a_Position 變量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);

// 允許訪問緩存區(qū)
gl.enableVertexAttribArray(a_Position);

/****** 縮放矩陣 ****/
const sx = 2;
const sy = 2;
const sz = 2;
// z 先不管,沒用到透視矩陣,設(shè)置了值也看不到效果

const xformMatrix = new Float32Array([
  sx,
  0,
  0,
  0,

  0,
  sy,
  0,
  0,

  0,
  0,
  sz,
  0,

  0,
  0,
  0,
  1
]);
const u_xformMatrix = gl.getUniformLocation(gl.program, "u_xformMatrix");
gl.uniformMatrix4fv(u_xformMatrix, false, xformMatrix);

/*** 繪制 ***/
// 清空畫布,并指定顏色
gl.clearColor(0, 0, 0, 1);
gl.clear(gl.COLOR_BUFFER_BIT);
// 繪制三角形
gl.drawArrays(gl.TRIANGLES, 0, 3);

demo 地址:

https://codesandbox.io/s/jsfdtr?file=/index.js。

繪制效果:

圖片

結(jié)尾

矩陣變換是 WebGL 非常重要的一部分。

本節(jié)介紹了三種常見的變形矩陣,并展示了各自的效果,下節(jié)我們講多個矩陣的組合,復合矩陣。

責任編輯:姜華 來源: 前端西瓜哥
相關(guān)推薦

2023-05-04 08:48:42

WebGL復合矩陣

2023-04-12 07:46:24

JavaScriptWebGL

2023-03-29 07:31:09

WebGL坐標系

2023-04-26 07:42:16

WebGL圖元的類型

2023-06-26 15:14:19

WebGL紋理對象學習

2023-05-08 07:29:48

WebGL視圖矩陣

2023-05-31 20:10:03

WebGL繪制立方體

2023-05-16 07:44:03

紋理映射WebGL

2023-04-13 07:45:15

WebGL片元著色器

2023-04-11 07:48:32

WebGLCanvas

2023-05-17 08:28:55

2023-04-17 09:01:01

WebGL繪制三角形

2023-02-23 08:40:09

Pixijs修改圖形屬性

2023-02-22 09:27:31

CanvasWebGL

2022-11-29 16:35:02

Tetris鴻蒙

2022-12-02 14:20:09

Tetris鴻蒙

2022-11-14 17:01:34

游戲開發(fā)畫布功能

2023-03-30 09:32:27

2023-05-06 07:23:57

2024-02-28 12:12:20

Pipeline數(shù)據(jù)機制
點贊
收藏

51CTO技術(shù)棧公眾號