完整 demo 和 lib 文件可以在 https://github.com/tengge1/webgl-guide-code 中找到。

第 2 章

第一个 WebGL 程序

function main() {
// 获取canvas元素
var canvas = document.getElementById('webgl');
// 获取webgl绘图上下文
var gl = getWebGLContext(canvas);
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
// 设置清空webgl的颜色
gl.clearColor(0.0, 0.0, 0.0, 1.0);
// 清空canvas
gl.clear(gl.COLOR_BUFFER_BIT);
}

一旦指定了背景色之后,背景色就会驻存在 WebGL 系统中,在下一次调用 gl.clearColor 方法前不会改变。

gl.clear(buffer) 用之前指定的背景色清空

buffer 指定待清空的缓冲区,可通过 | 指定多个

gl.COLOR_BUFFER_BIT 指定颜色缓存

gl.DEPTH_BUFFER_BIT 指定深度缓冲区

gl.STENCIL_BUFFER_BIT 指定模板缓冲区

清空缓冲区的默认颜色及其相关函数

缓冲区名称 默认值 相关函数
颜色缓冲区 (0.0, 0.0, 0.0, 0.0) gl.clearColor(red, green, blue, alpha)
深度缓冲区 1.0 gl.clearDepth(depth)
模板缓冲区 0 gl.clearStencil(s)

绘制一个点(版本一)

// 定点着色器程序
var VSHADER_SOURCE =
'void main() {\n' +
' gl_Position = vec4(0.0, 0.0, 0.0, 1.0);\n' + //设置坐标
' gl_PointSize = 50.0;\n' + // 设置尺寸
'}\n';
// 片元着色器程序
var FSHADER_SOURCE =
'void main() {\n' +
' gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n' + // 设置颜色
'}\n'; function main() {
// 获取canvas元素
var canvas = document.getElementById('webgl');
// 获取webgl绘图上下文
var gl = getWebGLContext(canvas);
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to initialize shaders.');
return;
}
// 设置背景色
gl.clearColor(0.0, 0.0, 1.0, 1.0);
// 清空canvas
gl.clear(gl.COLOR_BUFFER_BIT);
// 绘制一个点
gl.drawArrays(gl.POINTS, 0, 1);
}

目前可以简单的认为原点 (0.0, 0.0, 0.0) 处的点就出现在 <canvas> 的中心位置。

着色器

WebGL 依赖一种称为 着色器(shader) 的绘图机制。

定点着色器(Vertex shader) :定点着色器用来描述定点特性(如位置、颜色等)的程序。定点(vertex) 是指二维或三维空间的一个点,比如二维或三维图形的端点或交点。

片元着色器(Fragment shader):进行逐片元处理过程如光照的程序。片元(fragment)是一个 WebGL 术语,可以理解为像素。

顶点着色器的内置变量

类型 变量名 变量描述 类型描述
float gl_PointSize 表示点的尺寸(像素数) 浮点数
vec4 gl_Position 表示顶点的位置 表示由四个浮点数组成的矢量

函数 vec4(v0, v1, v2, v3) 返回一个 vec4 类型的变量。

由 4 个分量组成的矢量被称为齐次坐标。齐次坐标 (x,y,x,w) 等价于三维坐标 (x/w,y/w,z/w)

片元着色器的内置变量

类型 变量名 变量描述
vec4 gl_FragColor 指定片元颜色(RGBA格式)

gl.drawArrays(mode, first, count) 执行顶点着色器,按照 mode 参数指定的方式绘制图形。

mode 指定绘制的方式,可以接收以下常量符号

  • gl.POINTS
  • gl.LINES
  • gl.LINE_STRIP
  • gl.LINE_LOOP
  • gl.TRIANGLES
  • gl.TRIANGLE_STRIP
  • gl.TRIANGLE_FAN

first 指定从哪个点开始绘制(整型数)

count 指定绘制需要多少个顶点(整型数)

调用 gl.drawArrays(mode, first, count) 时,顶点着色器将被执行 count 次,,每次处理一个顶点。

顶点着色器执行完成后,片元着色器将会执行。

WebGL 的坐标系统

当你面向计算机平屏幕时,X轴时水平的(正方向为右),Y轴时垂直的(正方向为下),而Z轴垂直于屏幕(正方向为外),这套坐标系又被称为右手坐标系(right-handed coordinate system)。

WebGL 坐标和 <canvas> 坐标对应如下

  • <canvas> 的中心点 (0.0, 0.0, 0.0)
  • <canvas> 的上边缘和下边缘 (-1.0, 0.0, 0.0)(1.0, 0.0, 0.0)
  • <canvas> 的左边缘和右边缘 (0.0, -1.0, 0.0)(0.0, 1.0, 0.0)

绘制一个点(版本2)

上面的版本缺乏扩展性,现在要做的是将变量从 JavaScript 程序中传到顶点着色器。

attribute 变量 传输的是那些与顶点相关的数据。

uniform 变量 传输的是对于所有顶点都相同(或与顶点无关的)数据。

// 定点着色器程序
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'void main() {\n' +
' gl_Position = a_Position;\n' + //设置坐标
' gl_PointSize = 50.0;\n' + // 设置尺寸
'}\n';
// 片元着色器程序
var FSHADER_SOURCE =
'void main() {\n' +
' gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n' + // 设置颜色
'}\n'; function main() {
// 获取canvas元素
var canvas = document.getElementById('webgl');
// 获取webgl绘图上下文
var gl = getWebGLContext(canvas);
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
// 初始化着色器
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to initialize shaders.');
return;
}
// 获取attribute变量的存储位置
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return;
}
// 将顶点位置传输给attribute变量
gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0);
// 设置背景色
gl.clearColor(0.0, 0.0, 1.0, 1.0);
// 清空canvas
gl.clear(gl.COLOR_BUFFER_BIT);
// 绘制一个点
gl.drawArrays(gl.POINTS, 0, 1);
}

其中关键词 attribute 被称为存储限定符(storage qualifier),表示接下来的变量是一个 attribute 变量。attribute 变量必须声明成全局变量,数据将从着色器外部传给该变量。声明变量格式:<存储限定符> <类型> <变量名>

gl.getAttribLocation(program, name)

获取由 name 参数指定的 attribute 变量的存储地址

program 指定包含顶点着色器和片元着色器的着色器程序对象

name 指定想要获取其存储地址的 attribute 变量的名称

gl.vertexAttrib3f(location, v0, v1, v2)

将数据 (v0,v1,v2) 传给由 location 参数指定的 attribute 变量

location 指定将要修改的 attribute 变量的存储位置

v0, v1, v2 填充 attribute 变量的三个分量

gl.vertexAttrib3f(location, v0, v1, v2) 的同族函数

// 从 JavaScript 向顶点着色器中的 attribute 变量传值
gl.vertexAttrib1f(location, v0) // 第
gl.vertexAttrib2f(location, v0, v1)
gl.vertexAttrib3f(location, v0, v1, v2)
gl.vertexAttrib4f(location, v0, v1, v2, v3)

设置 vec4 缺省的第 2、3 个分量会被默认设置为 0.0,第 4 个分量会被设置为 1.0

通过鼠标点击绘制

// 定点着色器程序
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'void main() {\n' +
' gl_Position = a_Position;\n' + //设置坐标
' gl_PointSize = 10.0;\n' + // 设置尺寸
'}\n';
// 片元着色器程序
var FSHADER_SOURCE =
'void main() {\n' +
' gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n' + // 设置颜色
'}\n'; function main() {
// 获取canvas元素
var canvas = document.getElementById('webgl');
// 获取webgl绘图上下文
var gl = getWebGLContext(canvas);
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
// 初始化着色器
if (!initShaders(gl, VSHADER_SOUR
CE, FSHADER_SOURCE)) {
console.log('Failed to initialize shaders.');
return;
}
// 获取attribute变量的存储位置
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return;
}
// 注册鼠标点击事件响应函数
canvas.onmousedown = function (ev) {
click(ev, gl, canvas, a_Position);
} // 将顶点位置传输给attribute变量
gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0); // 设置背景色
gl.clearColor(0.0, 0.0, 1.0, 1.0);
// 清空canvas
gl.clear(gl.COLOR_BUFFER_BIT);
} var g_points = [];
function click(ev, gl, canvas, a_Position) {
var x = ev.clientX;
var y = ev.clientY;
var rect = ev.target.getBoundingClientRect();
x = ((x - rect.left) - canvas.width / 2) / (canvas.width / 2);
y = (canvas.height / 2 - (y - rect.top)) / (canvas.height / 2);
g_points.push(x);
g_points.push(y); gl.clear(gl.COLOR_BUFFER_BIT);
var len = g_points.length;
for (var i = 0; i < len; i += 2) {
gl.vertexAttrib3f(a_Position, g_points[i], g_points[i + 1], 0.0);
gl.drawArrays(gl.POINTS, 0, 1);
}
}

这个程序书上的代码有问题,求 x 和 y 的坐标部分,已改。

把每次鼠标点击的位置都记录下来,每次点击都清空然后绘制所有的点。

如果不请清空,颜色缓冲区被重置,第一次点击后背景就变成白色了。

改变点的颜色

// 定点着色器程序
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'void main() {\n' +
' gl_Position = a_Position;\n' + //设置坐标
' gl_PointSize = 10.0;\n' + // 设置尺寸
'}\n'; // 片元着色器程序
var FSHADER_SOURCE =
'precision mediump float;\n' +
'uniform vec4 u_FragColor;\n' + // uniform变量
'void main() {\n' +
' gl_FragColor = u_FragColor;\n' + // 设置颜色
'}\n'; function main() {
// 获取canvas元素
var canvas = document.getElementById('webgl');
// 获取webgl绘图上下文
var gl = getWebGLContext(canvas);
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
// 初始化着色器
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to initialize shaders.');
return;
}
// 获取attribute变量的存储位置
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if (a_Position < 0) {
console.log('Failed to get the storage location of a_Position');
return;
}
// 获取 u_FragColor 变量的存储位置
var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');
// 注册鼠标点击事件响应函数
canvas.onmousedown = function (ev) {
click(ev, gl, canvas, a_Position, u_FragColor);
} // 将顶点位置传输给attribute变量
gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0); // 设置背景色
gl.clearColor(0.0, 0.0, 1.0, 1.0);
// 清空canvas
gl.clear(gl.COLOR_BUFFER_BIT);
} var g_points = [];
var g_colors = [];
function click(ev, gl, canvas, a_Position, u_FragColor) {
var x = ev.clientX;
var y = ev.clientY;
var rect = ev.target.getBoundingClientRect();
x = ((x - rect.left) - canvas.width / 2) / (canvas.width / 2);
y = (canvas.height / 2 - (y - rect.top)) / (canvas.height / 2);
g_points.push([x, y]);
if (x >= 0.0 && y >= 0.0) { // 第一象限
g_colors.push([1.0, 0.0, 0.0, 1.0]); // red
} else if (x < 0.0 && y < 0.0) { // 第三象限
g_colors.push([0.0, 1.0, 0.0, 1.0]); // green
} else {
g_colors.push([1.0, 1.0, 1.0, 1.0]); // white
} gl.clear(gl.COLOR_BUFFER_BIT);
var len = g_points.length;
for (var i = 0; i < len; i++) {
let xy = g_points[i];
let rgba = g_colors[i];
gl.vertexAttrib3f(a_Position, xy[0], xy[1], 0.0);
gl.uniform4f(u_FragColor, rgba[0], rgba[1], rgba[2], rgba[3]);
gl.drawArrays(gl.POINTS, 0, 1);
}
}

只有顶点着色器才可以用 attribute 变量,使用片元着色器需要使用 uniform 变量,或者使用 varying 变量。

其中 precision mediump float 使用精度限定词来指定变量的范围(最大值与最小值)和精度。

gl.getUniformLocation(program, name)

获取由 name 参数指定的 attribute 变量的存储地址

program 指定包含顶点着色器和片元着色器的着色器程序对象

name 指定想要获取其存储地址的 uniform 变量的名称

未找到变量,getUniformLocation 返回 nullgetAttribLocation 返回 -1

gl.uniform4f(location, v0, v1, v2, v3)

将数据 (v0,v1,v2,v3) 传给由 location 参数指定的 uniform 变量

location 指定将要修改的 uniform 变量的存储位置

v0, v1, v2 填充 attribute 变量的三个分量

还有类似 gl.uniform1fgl.uniform2fgl.uniform3f,第 2、3、4 个分量默认值分别为 0.00.01.0

第 3 章

绘制多个点

构成三维模型的基本单位是三角形。

缓冲区对象(buffer object) 可以一次性地向着色器传入多个顶点的数据。缓冲区对象是 WebGL 的一块存储区域,可以在缓冲区中保存所有顶点的数据。

// 定点着色器程序
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'void main() {\n' +
' gl_Position = a_Position;\n' + //设置坐标
' gl_PointSize = 10.0;\n' + // 设置尺寸
'}\n'; // 片元着色器程序
var FSHADER_SOURCE =
'precision mediump float;\n' +
'uniform vec4 u_FragColor;\n' + // uniform变量
'void main() {\n' +
' gl_FragColor = u_FragColor;\n' + // 设置颜色
'}\n'; function main() {
// 获取canvas元素
var canvas = document.getElementById('webgl');
// 获取webgl绘图上下文
var gl = getWebGLContext(canvas);
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
// 初始化着色器
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to initialize shaders.');
return;
}
// 设置顶点位置
var n = initVertexBuffers(gl);
if (n < 0) {
console.log('Failed to set the positions of the vertices');
return;
}
// 设置背景色
gl.clearColor(0.0, 0.0, 1.0, 1.0);
// 清空canvas
gl.clear(gl.COLOR_BUFFER_BIT);
// 绘制三个点
gl.drawArrays(gl.POINTS, 0, n);
} function initVertexBuffers(gl) {
var vertices = new Float32Array([
0.0, 0.5, -0.5, -0.5, 0.5, -0.5
]);
var n = 3; // 点的个数
// 创建缓冲区对象
var vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.log('Failed to create the buffer object.');
return -1;
}
// 将缓冲区对象绑定到目标
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 向缓冲区对象写入数据
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
// 将缓冲区对象分配给 a_Position 变量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
// 连接 a_Position 变量与分配给它的缓冲区对象
gl.enableVertexAttribArray(a_Position); return n;
}

使用缓冲区对象的步骤

  1. 创建缓冲区对象 gl.createBuffer()
  2. 绑定缓冲区对象 gl.bindBuffer()
  3. 将数据写入缓冲区对象 gl.bufferData()
  4. 将缓冲区对象分配给一个 attribute 变量 gl.getAttribLocation()
  5. 开启 attribute 变量 gl.enableVertexAttribArray()

gl.createBuffer()

创建缓冲区对象

gl.deleteBuffer(buffer)

删除参数 buffer 表示的缓冲区对象

gl.bindBuffer(target, buffer)

允许使用 buffer 表示缓冲区对象并将其绑定到 target 表示的目标上。

target 参数可以是以下其中一个

  • gl.ARRAY_BUFFER 表示缓冲区对象中包含了顶点的数据
  • gl.ELEMENT_ARRAY_BUFFER 表示缓冲区对象中包含了顶点的索引值

    buffer 指定之前由 gl.createBuffer() 返回的待绑定的缓冲区对象

gl.bufferData(target, data, usage)

开辟存储空间,向绑定在 target 上的缓冲区对象写入数据 data

target gl.ARRAY_BUFFERgl.ELEMENT_ARRAY_BUFFER

data 写入缓冲区对象的数据

uasge 表示程序将如何使用存储在缓冲区对象的数据。

  • gl.STATIC_DRAW 只会向缓冲区对象中写入一次数据,但需要绘制很多次
  • gl.STREAM_DRAW 只会向缓冲区对象中写入一次数据,然后绘制若干次
  • gl.DYNAMIC_DRAW 会向缓冲区对象中多次写入数据,并绘制很多次

类型化数组

WebGL 通常需要同时处理大量相同类型的数据,所以为每种数据类型引入了一种特殊的数组(类型化数组)。

WebGL 使用的各种类型化数组

数组类型 每个元素所占字节数 描述(C语言中的数据类型)
Int8Array 1 8位整型数(signed char)
UInt8Array 1 8位无符号整型数(unsigned char)
Int16Array 2 16位整型数(signed short)
UInt32Array 2 16位无符号整型数(unsigned char)
Int32Array 4 32位整型数(signed int)
UInt32Array 4 32位无符号整型数(unsigned int)
Float32Array 4 单精度32位浮点数(float)
Float64Array 8 双精度64位浮点数(double)

类型化数组的方法和属性

方法、属性和常量 描述
get(index) 获取第 index 个元素值
set(index, value) 设置第 index 个元素值为 value
set(array, offset) 从第 offset 个元素开始将数组 array 中的值填充进去
length 数组的长度
BYTES_PER_ELEMENT 数组中每个元素所占的字节数

类型化数组需要通过 new 创建,不能使用 [] 运算符。

gl.vertexAttribPointer(location, size, normalized, stride, offset)

将绑定到 gl.ARRAY_BUFFER 的缓冲区对象分配给由 location 指定的 attribute 变量。

location 指定待分配 attribue 变量的存储位置

size 指定缓冲区每个顶点的分量个数(1到4),

type 用以下类型之一来指定数据格式:

  • gl.UNSIGNED_BYTE 无符号字节,Uint8Array
  • gl.SHORT 短整形,Int16Array
  • gl.UNSIGNED_SHORT 无符号短整形,Uint16Array
  • gl.INT 整形,Int32Array
  • gl.UNSIGNED_INT 无符号整形,Uint32Array
  • gl.FLOAT 浮点型,Float32Array

    normalize 传入 truefalse,表明是否将非浮点型的数据归一化到 [0,1][-1,1] 区间。

    stride 指定相邻两个顶点间的字节数,默认为 0。

    offset 指定缓冲区对象中偏移量(以字节为单位)。

gl.enableVertexAttribArray(location)

开启 location 指定的 attribute 变量。为了使顶点着色器能够访问缓冲区内的数据。

location 指定 attribue 变量的存储位置

gl.disableVertexAttribArray(location)

开启 location 指定的 attribute 变量。为了使顶点着色器能够访问缓冲区内的数据。

location 指定 attribue 变量的存储位置

Hello Triangle (绘制三角形)

// 定点着色器程序
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'void main() {\n' +
' gl_Position = a_Position;\n' + //设置坐标
// ' gl_PointSize = 10.0;\n' + // 设置尺寸
'}\n'; // 片元着色器程序
var FSHADER_SOURCE =
'precision mediump float;\n' +
'void main() {\n' +
' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + // 设置颜色
'}\n'; function main() {
// 获取canvas元素
var canvas = document.getElementById('webgl');
// 获取webgl绘图上下文
var gl = getWebGLContext(canvas);
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
// 初始化着色器
if (!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)) {
console.log('Failed to initialize shaders.');
return;
}
// 设置顶点位置
var n = initVertexBuffers(gl);
if (n < 0) {
console.log('Failed to set the positions of the vertices');
return;
}
// 设置背景色
gl.clearColor(1.0, 1.0, 1.0, 1.0);
// 清空canvas
gl.clear(gl.COLOR_BUFFER_BIT);
// 绘制三个点
gl.drawArrays(gl.TRIANGLES, 0, n);
} function initVertexBuffers(gl) {
var vertices = new Float32Array([
0.0, 0.5, -0.5, -0.5, 0.5, -0.5
]);
var n = 3; // 点的个数
// 创建缓冲区对象
var vertexBuffer = gl.createBuffer();
if (!vertexBuffer) {
console.log('Failed to create the buffer object.');
return -1;
}
// 将缓冲区对象绑定到目标
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
// 向缓冲区对象写入数据
gl.bufferData(gl.ARRAY_BUFFER, vertices, gl.STATIC_DRAW); var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
// 将缓冲区对象分配给 a_Position 变量
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0);
// 连接 a_Position 变量与分配给它的缓冲区对象
gl.enableVertexAttribArray(a_Position); return n;
}

gl_PointSize = 10.0; 该语句只有在绘制单点的时候才能使用,

gl.drawArrays(mode, first, count) 执行顶点着色器,按照 mode 参数指定的方式绘制图形。

mode 指定绘制的方式,可以接收以下常量符号

  • gl.POINTS 一系列点
  • gl.LINES 一系列单独的线段 绘制在 (v0,v1)、(v2,v3)、(v4,v5)……处,奇数最后一个点会被忽略
  • gl.LINE_STRIP 一系列连接的点,最后一个点是最后一条线段的终点
  • gl.LINE_LOOP 一系列连接的点,最后一个点会和第一个点连接起来
  • gl.TRIANGLES 一系列单独的三角形 (v0,v1,v2) (v3,v4,v5) 这样绘制一系列三角形,不是3的倍数最后剩下一个或两个点会被忽略
  • gl.TRIANGLE_STRIP 一系列带状三角形 (v0,v1,v2) (v2,v1,v3) (v2,v3,v4)
  • gl.TRIANGLE_FAN 三角扇 以v0为中心,分别和每两个点组成三角形 (v0,v1,v2) (v0,v2,v3) (v0,v3,v4)

first 指定从哪个点开始绘制(整型数)

count 指定绘制需要多少个顶点(整型数)

绘制正方形

就是绘制两个相邻的三角形。可以通过 gl.TRIANGLE_STRIPgl.TRIANGLE_FAN 实现。(gl.TRIANGLES 也可以,但是需要指定更多的点。)

var vertices = new Float32Array([
-0.5, 0.5, -0.5, -0.5, 0.5, -0.5, 0.5, 0.5
]);
var n = 4; // 点的个数 gl.drawArrays(gl.TRIANGLE_FAN, 0, n);

var vertices = new Float32Array([
-0.5, 0.5, -0.5, -0.5, 0.5, 0.5, 0.5, -0.5
]);
var n = 4; // 点的个数 gl.drawArrays(gl.TRIANGLE_STRIP, 0, n);

移动

// 定点着色器程序
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'uniform vec4 u_Translation;\n' +
'void main() {\n' +
' gl_Position = a_Position + u_Translation;\n' + //设置坐标
'}\n';
// ...
// 在 x,y,z 方向上平移的距离
var Tx = 0.5, Ty = 0.5, Tz = 0.0;
// ...
// 设置顶点位置
var n = initVertexBuffers(gl);
// 将传输距离传输给顶点着色器
var u_Translation = gl.getUniformLocation(gl.program, 'u_Translation');
gl.uniform4f(u_Translation, Tx, Ty, Tz, 0.0);

旋转

为了描述一个旋转,必须指明:旋转轴、旋转方向、旋转角度。

旋转角度,逆时针为正。(右手法则旋转)

// 定点着色器程序
var VSHADER_SOURCE =
// x' = x cosb - y sinb
// y' = x sinb + y cosb
// z' = z
'attribute vec4 a_Position;\n' +
'uniform float u_CosB, u_SinB;\n' +
'void main() {\n' +
' gl_Position.x = a_Position.x * u_CosB - a_Position.y * u_SinB;\n' +
' gl_Position.y = a_Position.x * u_SinB + a_Position.y * u_CosB;\n' +
' gl_Position.z = a_Position.z;\n' +
' gl_Position.w = 1.0;\n' +
'}\n'; // 旋转角度
var ANGLE = 90.0; var n = initVertexBuffers(gl); // 将旋转图形所需数据传输给顶点着色器
var radian = Math.PI * ANGLE / 180.0; // 转为弧度制
var cosB = Math.cos(radian);
var sinB = Math.sin(radian); var u_CosB = gl.getUniformLocation(gl.program, 'u_CosB');
var u_SinB = gl.getUniformLocation(gl.program, 'u_SinB'); gl.uniform1f(u_CosB, cosB);
gl.uniform1f(u_SinB, sinB);

变换矩阵(Transformation matrix)

矩阵旋转

// 定点着色器程序
var VSHADER_SOURCE =
'attribute vec4 a_Position;\n' +
'uniform mat4 u_xformMatrix;\n' +
'void main() {\n' +
' gl_Position = u_xformMatrix * a_Position;\n' +
'}\n'; // 将旋转图形所需数据传输给顶点着色器
var radian = Math.PI * ANGLE / 180.0; // 转为弧度制
var cosB = Math.cos(radian);
var sinB = Math.sin(radian); var xformMatrix = new Float32Array([
cosB, sinB, 0.0, 0.0,
-sinB, cosB, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
0.0, 0.0, 0.0, 1.0
]); var u_xformMarix = gl.getUniformLocation(gl.program, 'u_xformMatrix'); gl.uniformMatrix4fv(u_xformMarix, false, xformMatrix);

在 WebGL 中,是 按列主序(column major order) 在数组中存储矩阵元素。

gl.uniformMatrix4fv(location, transpose, array)

array 表示的 4*4 的矩阵分配给由 location 指定的 uniform 变量。

location uniform 变量的存储位置

transpose 表示是否转置矩阵,在 WebGL 中必须设置为 false

array 待传输的类型化数组,4*4矩阵按列主序存储在其中

矩阵平移

将上面程序的矩阵改为:

var Tx = 0.5, Ty = 0.5, Tz = 0.0;
var xformMatrix = new Float32Array([
1.0, 0.0, 0.0, 0.0,
0.0, 1.0, 0.0, 0.0,
0.0, 0.0, 1.0, 0.0,
Tx, Ty, Tz, 1.0
]);

矩阵缩放

将上面程序的矩阵改为:

var Sx = 1.0, Sy = 1.5, Sz = 1.0; // x,y,z轴的缩放因子
var xformMatrix = new Float32Array([
Sx, 0.0, 0.0, 0.0,
0.0, Sy, 0.0, 0.0,
0.0, 0.0, Sz, 0.0,
0.0, 0.0, 0.0, 1.0
]);

《WebGL 编程指南》读书笔记(2、3章)的更多相关文章

  1. Android权威编程指南读书笔记(1-2章)

    第一章 Android应用初体验 1.4用户界面设计 <?xml version="1.0" encoding="utf-8"?> ADT21开发版 ...

  2. 《Linux多线程服务器端编程》读书笔记第3章

    <Linux多线程服务器端编程>第3章主要讲的是多线程服务器的适用场合与常用的编程模型. 1.进程和线程 一个进程是"内存中正在运行的程序“.每个进程都有自己独立的地址空间(ad ...

  3. css权威指南读书笔记-第10章浮动和定位

    这一章看了之后真是豁然开朗,之前虽然写了圣杯布局和双飞翼布局,有些地方也是模糊的,现在打算总结之后再写一遍. 以下都是从<css权威指南>中摘抄的我认为很有用的说明. 浮动元素 一个元素浮 ...

  4. JavaScript权威指南读书笔记【第一章】

    第一章 JavaScript概述 前端三大技能: HTML: 描述网页内容 CSS: 描述网页样式 JavaScript: 描述网页行为 特点:动态.弱类型.适合面向对象和函数式编程的风格 语法源自J ...

  5. 《Unix环境高级编程》读书笔记 第13章-守护进程

    1. 引言 守护进程是生存期长的一种进程.它们常常在系统引导装入时启动,仅在系统关闭时才终止.它们没有控制终端,在后台运行. 本章说明守护进程结构.如何编写守护进程程序.守护进程如何报告出错情况. 2 ...

  6. 《python核心编程》读书笔记--第16章 网络编程

    在进行网络编程之前,先对网络以及互联网协议做一个了解. 推荐阮一峰的博客:(感谢) http://www.ruanyifeng.com/blog/2012/05/internet_protocol_s ...

  7. 《python核心编程》--读书笔记 第21章 数据库编程

    准备:今天拿笔记本装了mysql,这样就能在不同地方用其他电脑远程访问同一个数据库了. python安装MySQLdb模块:http://www.codegood.com/downloads. 21. ...

  8. 《python核心编程》读书笔记--第18章 多线程编程

    18.1引言 在多线程(multithreaded,MT)出现之前,电脑程序的运行由一个执行序列组成.多线程对某些任务来说是最理想的.这些任务有以下特点:它们本质上就是异步的,需要多个并发事务,各个事 ...

  9. 《Unix环境高级编程》读书笔记 第11章-线程

    1. 引言 了解如何使用多个控制线程在单进程环境中执行多个任务. 不管在什么情况下,只要单个资源需要在多个用户键共享,就必须处理一致性问题. 2. 线程概念 典型的Unix进程可以看成只有一个控制线程 ...

  10. 《Unix环境高级编程》读书笔记 第5章-标准I/O流

    1. 引言 标准I/O库由ISO C标准说明,由各个操作系统实现 标准I/O库处理很多细节,如缓冲区分配.以优化的块长度执行I/O等.这些处理使用户不必担心如何使用正确的块长度,这使得它便于用于使用, ...

随机推荐

  1. Windows版本免费PyMol的安装

    技术背景 在前面一篇博客中,我们介绍过在Linux平台下安装和使用免费版本的PyMol.其实同样的这个免费版在Windows平台上(这里以win11为例)也是支持的. 安装流程 这个免费版本的PyMo ...

  2. 微服务:nacos服务注册与发现

    服务治理的三个角色: 服务提供者:订阅服务 服务消费者:注册服务 注册中心:记录与监控服务状态,推送服务变更信息.提供者定时发送心跳检测,心跳检测失败,就会向消费者推送变更 提供者通过负载均衡的算法选 ...

  3. macOS 常用键盘快捷键

    macOS 常用键盘快捷键大全 - 最值得你记住的 Mac 常用快捷键组合 Pertim 与 Windows 的差异 一切开始前,我们先来认识一下苹果 Mac 键盘上几个陌生的按键,比如 ⌘ (Com ...

  4. 学术写作: These authors contributed equally to this work. —— 共同一作

    早些年很少看到论文里面有: These authors contributed equally to this work. 不过现在这种方法在论文中出现的还是比较多的,说白了,这种共同一作的声明其实是 ...

  5. Jax框架:通过显存分析判断操作是否进行jit编译

    相关: https://jax.readthedocs.io/en/latest/device_memory_profiling.html 代码: import jax import jax.nump ...

  6. 编程语言mojo报错:error: cannot call function that may raise in a context that cannot raise

    代码: from python import Python fn main(): # fn main() raises: # This is equivalent to Python's `impor ...

  7. Mybatis-Plus系统化学习之环境准备与简单使用

    1.背景 平时在开发中会经常用到单表的CRUD操作 其实,这些单表的CRUD,完全不需要我们写sql,可以使用mybatis-plus自动生成,不但高效而且不容用出错! 2.mybatis-plus的 ...

  8. dsu on tree 模板

    dsu on tree模板运用 例题以及代码: U41492 树上数颜色 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn) 记录详情 - 洛谷 | 计算机科学教育新生态 (luogu. ...

  9. 第 358 场周赛 - 力扣(LeetCode)

    第 358 场周赛 - 力扣(LeetCode) 2815. 数组中的最大数对和 - 力扣(LeetCode) 双for遍历即可 class Solution { public: int maxSum ...

  10. USACO 2024Feb Silver

    https://usaco.org/index.php?page=feb24results 话说 usaco 赛后怎么看成绩啊.为啥 submission 只有代码没有评测结果 T3 交了巨大多次才过 ...