《WebGL 编程指南》读书笔记(2、3章)
完整 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
返回 null
,getAttribLocation
返回 -1
。
gl.uniform4f(location, v0, v1, v2, v3)
将数据
(v0,v1,v2,v3)
传给由location
参数指定的uniform
变量
location
指定将要修改的uniform
变量的存储位置
v0
,v1
,v2
填充attribute
变量的三个分量
还有类似 gl.uniform1f
,gl.uniform2f
和 gl.uniform3f
,第 2、3、4 个分量默认值分别为 0.0
,0.0
,1.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;
}
使用缓冲区对象的步骤
- 创建缓冲区对象
gl.createBuffer()
- 绑定缓冲区对象
gl.bindBuffer()
- 将数据写入缓冲区对象
gl.bufferData()
- 将缓冲区对象分配给一个
attribute
变量gl.getAttribLocation()
- 开启
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_BUFFER
或gl.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
无符号字节,Uint8Arraygl.SHORT
短整形,Int16Arraygl.UNSIGNED_SHORT
无符号短整形,Uint16Arraygl.INT
整形,Int32Arraygl.UNSIGNED_INT
无符号整形,Uint32Arraygl.FLOAT
浮点型,Float32Array
normalize
传入true
或false
,表明是否将非浮点型的数据归一化到[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_STRIP
或 gl.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
]);