《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.POINTSgl.LINESgl.LINE_STRIPgl.LINE_LOOPgl.TRIANGLESgl.TRIANGLE_STRIPgl.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。
targetgl.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变量。
locationuniform变量的存储位置
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章)的更多相关文章
- Android权威编程指南读书笔记(1-2章)
第一章 Android应用初体验 1.4用户界面设计 <?xml version="1.0" encoding="utf-8"?> ADT21开发版 ...
- 《Linux多线程服务器端编程》读书笔记第3章
<Linux多线程服务器端编程>第3章主要讲的是多线程服务器的适用场合与常用的编程模型. 1.进程和线程 一个进程是"内存中正在运行的程序“.每个进程都有自己独立的地址空间(ad ...
- css权威指南读书笔记-第10章浮动和定位
这一章看了之后真是豁然开朗,之前虽然写了圣杯布局和双飞翼布局,有些地方也是模糊的,现在打算总结之后再写一遍. 以下都是从<css权威指南>中摘抄的我认为很有用的说明. 浮动元素 一个元素浮 ...
- JavaScript权威指南读书笔记【第一章】
第一章 JavaScript概述 前端三大技能: HTML: 描述网页内容 CSS: 描述网页样式 JavaScript: 描述网页行为 特点:动态.弱类型.适合面向对象和函数式编程的风格 语法源自J ...
- 《Unix环境高级编程》读书笔记 第13章-守护进程
1. 引言 守护进程是生存期长的一种进程.它们常常在系统引导装入时启动,仅在系统关闭时才终止.它们没有控制终端,在后台运行. 本章说明守护进程结构.如何编写守护进程程序.守护进程如何报告出错情况. 2 ...
- 《python核心编程》读书笔记--第16章 网络编程
在进行网络编程之前,先对网络以及互联网协议做一个了解. 推荐阮一峰的博客:(感谢) http://www.ruanyifeng.com/blog/2012/05/internet_protocol_s ...
- 《python核心编程》--读书笔记 第21章 数据库编程
准备:今天拿笔记本装了mysql,这样就能在不同地方用其他电脑远程访问同一个数据库了. python安装MySQLdb模块:http://www.codegood.com/downloads. 21. ...
- 《python核心编程》读书笔记--第18章 多线程编程
18.1引言 在多线程(multithreaded,MT)出现之前,电脑程序的运行由一个执行序列组成.多线程对某些任务来说是最理想的.这些任务有以下特点:它们本质上就是异步的,需要多个并发事务,各个事 ...
- 《Unix环境高级编程》读书笔记 第11章-线程
1. 引言 了解如何使用多个控制线程在单进程环境中执行多个任务. 不管在什么情况下,只要单个资源需要在多个用户键共享,就必须处理一致性问题. 2. 线程概念 典型的Unix进程可以看成只有一个控制线程 ...
- 《Unix环境高级编程》读书笔记 第5章-标准I/O流
1. 引言 标准I/O库由ISO C标准说明,由各个操作系统实现 标准I/O库处理很多细节,如缓冲区分配.以优化的块长度执行I/O等.这些处理使用户不必担心如何使用正确的块长度,这使得它便于用于使用, ...
随机推荐
- 题解:CF1984B Large Addition
题解:CF1984B Large Addition 题意 判断 \(n\) 是否是两个位数相同的 \(large\) 数的和. 思路 有以下三种证明方法: 最高位为 \(1\),因为两个 \(larg ...
- 使用 useNuxtData 进行高效的数据获取与管理
title: 使用 useNuxtData 进行高效的数据获取与管理 date: 2024/7/22 updated: 2024/7/22 author: cmdragon excerpt: 深入讲解 ...
- 把python中的列表转化为字符串
怎么把python中的列表转换为字符串: 1,列表中非字符串的元素的转换 方法一: 使用列表推导式进行转换 1 list=['hello',6,9,'beizhi'] 2 list=[str(i) f ...
- idea使用git管理项目(Mac版)
1.本地安装git mac版 breaw install git 查看是否安装成功 git --version 这样就成功了,一般是自带的有 windows版 https://www.cnblogs. ...
- 【WebSocket】一个简单的前后端交互Demo
WebSocket资料参考: https://www.jianshu.com/p/d79bf8174196 使用SpringBoot整合参考: https://blog.csdn.net/KeepSt ...
- 2018年视频,路径规划:层次化路径规划系统——hierarchical pathfinding system —— Hierarchical Dynamic Pathfinding for Large Voxel Worlds (续)
前文: 2018年视频,路径规划:层次化路径规划系统--hierarchical pathfinding system -- Hierarchical Dynamic Pathfinding for ...
- Jax框架:通过显存分析判断操作是否进行jit编译
相关: https://jax.readthedocs.io/en/latest/device_memory_profiling.html 代码: import jax import jax.nump ...
- 【转载】 Python格式化字符串f-string概览
版权声明:本文为CSDN博主「sunxb10」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明.原文链接:https://blog.csdn.net/sunxb10/a ...
- Ubuntu18.04环境下 以太坊Geth的安装
ubuntu18.04系统下安装: sudo apt-get install software-properties-common sudo add-apt-repository -y ppa:eth ...
- 凸优化: 回溯线搜索(Backtracking line search)
声明: 本文大量摘录 https://www.cnblogs.com/kemaswill/p/3416231.html 内容. ==================================== ...