WebGL学习笔记

WebGL学习笔记

入门

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var canvas = document.getElementById('webgl');
var webgl = canvas.getContext('webgl'); // 获取webGL的context

// 清除背景并填充颜色
webgl.clearColor(0, 0, 0, 0.6);
webgl.clear(webgl.COLOR_BUFFER_BIT);

// 顶点着色器,主要用于描述顶点属性(大小,位置) GLSL ES语言
var VSHADER_SOURCE =
`void main() {
gl_Position = vec4(0.5, 0.5, 0, 1);
gl_PointSize = 10.0;
}`;

// 片元着色器,决定片元的颜色等,GLSL ES语言
var FSHADER_SOURCE =
`void main() {
gl_FragColor = vec4(1, 0, 0, 1);
}`;

//初始化着色器,initShaders函数并非自带函数,需要调用createShader,compileShader等函数
// initShaders详见:https://github.com/MrZJD/webgl/blob/master/lib/base.js
initShaders( webgl, VSHADER_SOURCE, FSHADER_SOURCE)

//画点,0表示从第0个点开始绘制,1表示绘制1个点
webgl.drawArrays(webgl.POINTS, 0, 1);

使用attribute

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
// 顶点着色器程序
var VSHADER_SOURCE =
`attribute vec4 a_Position; //声明attribute变量,存储限定符storage qualifier,必须声明成全局变量,从外部传入
attribute float a_PointSize;// 限定符 数据类型 变量名
void main() {
gl_Position = a_Position;
gl_PointSize = a_PointSize;
}`;

// 片元着色器程序
var FSHADER_SOURCE =
`void main() {
gl_FragColor = vec4(1, 0, 0, 1);
}`;

var canvas = document.getElementById('webgl');
var webgl = canvas.getContext('webgl');
initShaders( webgl, VSHADER_SOURCE, FSHADER_SOURCE)
// 获取attribute变量的地址
var a_Position = webgl.getAttribLocation(webgl.program, 'a_Position');
var a_PointSize = webgl.getAttribLocation(webgl.program, 'a_PointSize');

// 根据地址给attribute变量赋值
webgl.vertexAttrib3f(a_Position, 0.5, 0.5, 0.0); // 省略第四个参数,会默认赋值1.0
webgl.vertexAttrib1f(a_PointSize, 10.0);
// 还有vertexAttrib2f, vertexAttrib4f等函数
// 也可以用矢量版本:
// var position = new Float32Array([1.0, 2.0, 3.0, 1.0]);
// gl.vertextAttrib4fv(a_Position, position)

// 清空颜色缓冲区
webgl.clearColor(0, 0, 0, 0.6);
webgl.clear(webgl.COLOR_BUFFER_BIT);

// 画点
webgl.drawArrays(webgl.POINTS, 0, 1);

使用Uniform

只有顶点着色器才能用attribute变量,在片元着色器中,需要用uniform变量(当然也可以用varying)。

uniform变量用来从JavaScript程序向顶点着色器和片元着色器传输“一致的”(不变的)数据。

1
2
3
4
5
6
7
8
9
10
11
12
13
// 片元着色器程序
var FSHADER_SOURCE =
`precision mediump float;// 精度限定
uniform vec4 u_FragColor;
void main() {
gl_FragColor = u_FragColor;
}`;

// 获取uniform变量的地址
var u_FragColor = webgl.getUniformLocation(webgl.program, 'u_FragColor');

// 根据地址给uniform变量赋值
webgl.uniform4f(u_FragColor, 1.0, 1.0, 1.0, 1.0);

三角形

缓冲区

利用缓冲区对象可以向顶点着色器传入多个顶点。

分为五步:

  1. 创建缓冲区对象(gl.createBuffer())。
  2. 绑定缓冲区对象(gl.bindBuffer())。
  3. 将数据写入缓冲区(gl.bufferData())。
  4. 将缓冲区对象分配给一个attribute变量(gl.vertextAttribPointer())。
  5. 开启attribute变量(gl.enableVertextAttribArray())。
1557306698055
1557306698055
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// 顶点数据,类型化数组,处理效率更高,但不支持push,pop;
// 可以通过一个数组,或者一个数(也就是数组长度)来初始化
var points = new Float32Array([0.0, 0.5, -0.5, -0.5, 0.5, -0.5]);
var n = 3;

// 创建缓冲区对象,利用deleteBuffer进行删除
var vertexBuffer = webgl.createBuffer();

// 绑定对象到缓冲区指针(ARRAY_BUFFER)上
webgl.bindBuffer(webgl.ARRAY_BUFFER, vertexBuffer);

/** gl.bufferData(target, data, usage);
* 写入数据到缓冲区
* @param target gl.ARRAY_BUFFER 或 gl.ELEMENT_ARRAY_BUFFER
* @param data
* @param usage gl.STATIC_DRAW(只会向缓冲区对象写入一次数据,但是要绘制很多次);gl.STREAM_DRAW(只会向缓冲区对象写入一次数据,然后绘制若干次);gl.DYNAMIC_DRAW(多次写入数据,多次绘制)
*/
webgl.bufferData(webgl.ARRAY_BUFFER, points, webgl.STATIC_DRAW);

/** gl.vertexAttribPointer(location, size, type, normalized, stride, offset);
* 指定attribute变量解析规则
* @param location 指定待分配的attribute变量
* @param size 指定每个顶点的数据数量(1-4个)
* @param type 数据类型(gl.SHORT, gl.INT, gl.FLOAT等等)
* @param normalized 是否需要归一化
* @param stride 指定相邻两个顶点间的字节数,默认为0
* @param offset 指定缓冲区对象中的偏移量(以字节为单位),即attribute变量从缓冲区中的何处开始读取。如果是从起始位置开始的,offset设为0
*/
webgl.vertexAttribPointer(a_Position, 2, webgl.FLOAT, false, 0, 0);

// 启用attribute变量 => 即链接缓冲区到attribute变量上
webgl.enableVertexAttribArray(a_Position);

drawArrays的第一个参数

基本图形 参数mode 描述
gl.POINTS 一系列点
线段 gl.LINES 一系列单独的线段((v0,v1),(v2,v3),(v4,v5),...)
线条 gl.LINE_STRIP 一系列连接的线段((v0,v1),(v1,v2),(v2,v3),...)
回路 gl.LINE_LOOP 首尾相连的一系列线段((v0,v1),(v1,v2),...,(vn,v0))
三角形 gl.TRIANGLES 一系列单独的三角形((v0,v1,v2),(v3,v4,v5),...)
三角带 gl.TRIANGLE_STRIP 一系列条带状的三角形((v0,v1,v2),(v2,v1,v3),(v2,v3,v4),...)
三角扇 gl.TRIANGLE_FAN 一系列扇状的三角形((v0,v1,v2),(v0,v2,v3),(v0,v3,v4),...)
1557313632070
1557313632070

平移

利用uniform变量来对图形进行平移:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// 顶点着色器程序
var VSHADER_SOURCE =
`attribute vec4 a_Position;
uniform vec4 u_Translation; //平移变换矢量
void main() {
gl_Position = a_Position + u_Translation;
}`;

// 片元着色器程序
var FSHADER_SOURCE =
`void main() {
gl_FragColor = vec4(1.0, 0, 0, 1.0);
}`;

var canvas = document.getElementById('webgl');
var webgl = canvas.getContext('webgl');

initShaders( webgl, VSHADER_SOURCE, FSHADER_SOURCE)

// 获取attribute变量的地址
var a_Position = webgl.getAttribLocation(webgl.program, 'a_Position');

// 获取uniform变量的地址
var u_Translation = webgl.getUniformLocation(webgl.program, 'u_Translation');

// 平移矢量值
// 为什么平移矢量最后一个值是0:因为点(x,y,z,w)的w值只能为1.0,而原来点的w坐标已经是1.0了
var tVal = [0.5, 0.5, 0, 0];
// 设置uniform变量的值
webgl.uniform4f(u_Translation, tVal[0], tVal[1], tVal[2], tVal[3]);

// 顶点数据
var points = new Float32Array([0, 0.5, -0.5, -0.5, 0.5, -0.5]);
var n = points.length/2;

// buffer对象
var vertexBuffer = webgl.createBuffer();
if ( !vertexBuffer ) {
console.error('Failed to create buffer!');
return -1;
}

// 绑定对象到缓冲区指针(顶点数据)上
webgl.bindBuffer(webgl.ARRAY_BUFFER, vertexBuffer);
// 写入数据到缓冲区
webgl.bufferData(webgl.ARRAY_BUFFER, points, webgl.STATIC_DRAW);
// 指定attribute变量解析规则
webgl.vertexAttribPointer(a_Position, 2, webgl.FLOAT, false, 0, 0);
// 启用attribute变量 => 即链接缓冲区到attribute变量上
webgl.enableVertexAttribArray(a_Position);

// 清空颜色缓冲区
webgl.clearColor(0, 0, 0, 0.6);
webgl.clear(webgl.COLOR_BUFFER_BIT);

// 绘制
webgl.drawArrays(webgl.TRIANGLES, 0, n);

旋转

右手螺旋法则(right-hand-rule rotation)

右手握拳,大拇指伸直并使其指向旋转轴的正方向,那么右手的其余几个手指就指明了正旋转(positive rotation)的方向。

1557316884620
1557316884620

假如点p经过正旋转\(\beta\)角度,变成p',其计算公式: \[ x = r \cos \alpha \\\ y = r \sin \alpha \\\ x' = r \cos (\alpha + \beta) = r(\cos \alpha \cos \beta - \sin \alpha \sin \beta ) = x\cos\beta - y \sin \beta \\\ y' = r \sin (\alpha + \beta) = r(\sin \alpha \cos \beta + \cos \alpha \sin \beta ) = y \cos \beta + x \sin \beta \\\ z' = z \] 利用顶点着色器来进行旋转:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
// 顶点着色器程序
var VSHADER_SOURCE =
`attribute vec4 a_Position;
uniform float u_CosB, u_SinB;//变换量
void main() {
//旋转变换方式
gl_Position.x = a_Position.x * u_CosB - a_Position.y * u_SinB;
gl_Position.y = a_Position.x * u_SinB + a_Position.y * u_CosB;
gl_Position.z = a_Position.z;
gl_Position.w = 1.0;
}`;

//计算变换值
var tAngle = 90;//角度制
var tRadian = tAngle * Math.PI / 180;//弧度制

// 获取uniform变量的地址
var u_CosB = webgl.getUniformLocation(webgl.program, 'u_CosB');
var u_SinB = webgl.getUniformLocation(webgl.program, 'u_SinB');

// 设置uniform变量的值
webgl.uniform1f(u_SinB, Math.sin(tRadian));
webgl.uniform1f(u_CosB, Math.cos(tRadian));

变换矩阵 Transformation Matrix

上面的旋转过程可以表示成: \[ \left[ \begin{array}{c}{x^{\prime}} \\ {y^{\prime}} \\ {z^{\prime}}\end{array}\right]=\left[ \begin{array}{ccc}{\cos \beta} & {-\sin \beta} & {0} \\ {\sin \beta} & {\cos \beta} & {0} \\ {0} & {0} & {1}\end{array}\right] \times \left[ \begin{array}{l}{x} \\ {y} \\ {z}\end{array}\right] \] 在webGL里面点的坐标是四维的,所以最终表示为\(4 \times 4\)的旋转矩阵。

平移

\[ \left[ \begin{array}{c}{x^{\prime}} \\ {y^{\prime}} \\ {z^{\prime}} \\ {1}\end{array}\right]=\left[ \begin{array}{cccc}{1} & {0} & {0} & {T x} \\ {0} & {1} & {0} & {T y} \\ {0} & {0} & {1} & {T z} \\ {0} & {0} & {0} & {1}\end{array}\right] \times \left[ \begin{array}{l}{x} \\ {y} \\ {z} \\ {1}\end{array}\right] \]

旋转

\[ \left[ \begin{array}{c}{x^{\prime}} \\ {y^{\prime}} \\ {z^{\prime}} \\ {1}\end{array}\right]=\left[ \begin{array}{cccc}{\cos \beta} & {-\sin \beta} & {0} & {0} \\ {\sin \beta} & {\cos \beta} & {0} & {0} \\ {0} & {0} & {1} & {0} \\ {0} & {0} & {0} & {1}\end{array}\right] \times \left[ \begin{array}{l}{x} \\ {y} \\ {z} \\ {1}\end{array}\right] \]

缩放

\[ \left[ \begin{array}{c}{x^{\prime}} \\ {y^{\prime}} \\ {z^{\prime}} \\ {1}\end{array}\right]=\left[ \begin{array}{cccc}{S x} & {0} & {0} & {0} \\ {0} & {S y} & {0} & {0} \\ {0} & {0} & {S z} & {0} \\ {0} & {0} & {0} & {1}\end{array}\right] \times \left[ \begin{array}{l}{x} \\ {y} \\ {z} \\ {1}\end{array}\right] \]

相关代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
// 顶点着色器程序
var VSHADER_SOURCE =
`attribute vec4 a_Position;
uniform mat4 u_xformMatrix; //变化矩阵
void main() {
// 注意矩阵乘法的先后顺序
gl_Position = u_xformMatrix * a_Position;
}`;


// 旋转变换矩阵
var tAngle = 90;//角度制
var tRadian = (tAngle * Math.PI) / 180;//弧度制
var cosB = Math.cos(tRadian);
var sinB = Math.sin(tRadian);
var rotateMatrix = new Float32Array([// webgl中矩阵是列主序
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 tx=0.5, ty=0.5, tz=0.0;
var translateMatrix = new Float32Array([// webgl中矩阵是列主序
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.5, sy=1.5, sz=1.0;
var scaleMatrix = new Float32Array([// webgl中矩阵是列主序
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
]);

var xformMatrix = scaleMatrix;
// var xformMatrix = translateMatrix
// var xformMatrix = rotateMatrix

// 获取uniform变量的地址
var u_xformMatrix = webgl.getUniformLocation( webgl.program, 'u_xformMatrix');
webgl.uniformMatrix4fv(u_xformMatrix, false, xformMatrix);

Matrix4对象

自定义对象,用来处理\(4 \times 4\)矩阵。

详见:https://github.com/MrZJD/webgl/blob/master/lib/base.js

方法与属性名称 描述
Matrix4.elements 类型化数组(Float32Array)了Matrix4实例的矩阵元素
Matrix4.setIdentity() 将Matrix4实例初始化为单位阵
Matrix4.setTranslate(x,y,z) 将Matrix4实例设置为平移变换矩阵,在x轴上平移的距离为x,在y轴上平移的距离为y,在z轴上平移的距离为 z
Matrix4.setRotate(angle,x,y,z) 将Matrix4实例设置为旋转变换矩阵,旋转的角度为angle,旋转轴为(x,y,z)。旋转轴(x,y,z)无须归一化。
Matrix4.setScale(x,y,z) 将Matrix4实例设置为缩放变换矩阵,在三个轴上的缩放因子分别为x、y和z
Matrix4.translate(x,y,z) 将Matrix4实例乘以一个平移变换矩阵(该平移矩阵在x 轴上平移的距离为x,在y轴上平移的距离是y,在z轴上平移的距离是z),所得的结果还存储在Matrix4中
Matrix4.rotate(angle,x,y,z) 将Matrix4实例乘以一个旋转变换矩阵(该旋转矩阵旋转的角度为angle,旋转轴为(x,y,z)。旋转轴(x,y,z)无须归一化),所得的结果还存储AMatrix4中
Matrix4.scale(x,y,z) 将Matrix4实例乘以一个缩放变换矩阵(该缩放矩阵在三个轴上的缩放因子分别为x, y和z),所得的结果还存储在Matrix4中
Matrix4.set(m) 将Matrix4实例设置为m,m必须也是一个Matrix4实例

每秒钟自动旋转

需要引入:https://github.com/MrZJD/webgl/blob/master/lib/base.js

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
var VSHADER = 
`attribute vec4 a_Position;
uniform mat4 u_xformMatrix;
void main() {
gl_Position = u_xformMatrix * a_Position;
}`

var FSHADER =
`void main() {
gl_FragColor = vec4(1.0, 0, 0, 1.0);
}
`

var canvas = document.getElementById('webgl')
var gl = getWebGLContext(canvas)

initShaders(gl, VSHADER, FSHADER)

var a_Position = gl.getAttribLocation(gl.program, 'a_Position')
var u_xformMatrix = gl.getUniformLocation(gl.program, 'u_xformMatrix') // 变换矩阵

// 顶点数据
var points = new Float32Array([0, 0.3, -0.3, -0.3, 0.3, -0.3])

// 创建buffer对象
var vBuffer = gl.createBuffer()

// 绑定buffer对象到指针上
gl.bindBuffer(gl.ARRAY_BUFFER, vBuffer)

// 将数据写入buffer
gl.bufferData(gl.ARRAY_BUFFER, points, gl.STATIC_DRAW)

// 指定attribute变量解析规则
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, 0, 0)

// 将buffer传入到attribute变量上
gl.enableVertexAttribArray(a_Position)

// 绘制
gl.clearColor(0, 0, 0, 0.6)


var beginTime = new Date()
var currentAngle = 0
const anglePerSec = 30 // 每秒钟旋转30度


var tick = function () {
var time = new Date()
currentAngle = anglePerSec * (time - beginTime) / 1000

var rotateMatrix = new Matrix4()
rotateMatrix.setRotate(currentAngle, 0, 0, 1)
gl.uniformMatrix4fv(u_xformMatrix, false, rotateMatrix.elements)

gl.clear(gl.COLOR_BUFFER_BIT)
gl.drawArrays(gl.TRIANGLES, 0, points.length / 2)

requestAnimationFrame(tick)
}

tick()

颜色与纹理

非坐标数据传入顶点着色器

加入节点size

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
// 顶点着色器程序
var VSHADER_SOURCE =
`attribute vec4 a_Position;
attribute float a_PointSize;
void main() {
gl_Position = a_Position;
gl_PointSize = a_PointSize;
}`;

// 获取attribute变量的地址
var a_Position = webgl.getAttribLocation(webgl.program, 'a_Position');
var a_PointSize = webgl.getAttribLocation(webgl.program, 'a_PointSize');

// 顶点数据(x,y,size)
var points = new Float32Array([0, 0.5, 2.0, -0.5, -0.5, 5.0, 0.5, -0.5, 10.0]);
var n = points.length / 3;
var FSIZE = points.BYTES_PER_ELEMENT;

// buffer对象
var vertexBuffer = webgl.createBuffer();
if ( !vertexBuffer ) {
console.error('Failed to create buffer!');
return -1;
}

// 绑定对象到缓冲区指针(顶点数据)上
webgl.bindBuffer(webgl.ARRAY_BUFFER, vertexBuffer);
// 写入数据到缓冲区
webgl.bufferData(webgl.ARRAY_BUFFER, points, webgl.STATIC_DRAW);

// 指定attribute变量解析规则
// 这里的stride参数设置为了 FSIZE*3,因为一个节点现在有三个参数:x,y,size
webgl.vertexAttribPointer(a_Position, 2, webgl.FLOAT, false, FSIZE*3, 0);
webgl.vertexAttribPointer(a_PointSize, 1, webgl.FLOAT, false, FSIZE*3, FSIZE*2);

// 启用attribute变量 => 即链接缓冲区到attribute变量上
webgl.enableVertexAttribArray(a_Position);
webgl.enableVertexAttribArray(a_PointSize);

varying变量

之前利用uniform变量来将颜色信息传入片元着色器;利用varying变量可以从顶点着色器中向片元着色器中传输颜色;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
// 顶点着色器程序
var VSHADER_SOURCE =
`attribute vec4 a_Position;
attribute vec4 a_Color;
varying vec4 v_Color;
void main() {
gl_Position = a_Position;
v_Color = a_Color;
}`;

// 片元着色器程序
var FSHADER_SOURCE =
`precision mediump float;
varying vec4 v_Color;
void main() {
gl_FragColor = v_Color;
}`;

// 顶点数据: (x,y,r,g,b)
var points = new Float32Array([0, 0.5, 1.0, 0.0, 0.0,
-0.5, -0.5, 0.0, 1.0, 0.0,
0.5, -0.5, 0.0, 0.0, 1.0]);
var n = points.length / 5;
var FSIZE = points.BYTES_PER_ELEMENT;

// buffer对象
var vertexBuffer = webgl.createBuffer();
if ( !vertexBuffer ) {
console.error('Failed to create buffer!');
return -1;
}

// 绑定对象到缓冲区指针(顶点数据)上
webgl.bindBuffer(webgl.ARRAY_BUFFER, vertexBuffer);
// 写入数据到缓冲区
webgl.bufferData(webgl.ARRAY_BUFFER, points, webgl.STATIC_DRAW);

// 指定attribute变量解析规则
// 坐标信息占两位
webgl.vertexAttribPointer(a_Position, 2, webgl.FLOAT, false, FSIZE*5, 0);
// 颜色信息占三位
webgl.vertexAttribPointer(a_Color, 3, webgl.FLOAT, false, FSIZE*5, FSIZE*2);

// 启用attribute变量 => 即链接缓冲区到attribute变量上
webgl.enableVertexAttribArray(a_Position);
webgl.enableVertexAttribArray(a_Color);
1557388439171
1557388439171

为什么是彩色三角形?

顶点坐标,图形装配,光栅化,执行片元着色器
顶点坐标,图形装配,光栅化,执行片元着色器
  • 图形装配指的是:将孤立的顶点坐标装配成几何图形(几何图形的类型由drawArrays的第一个参数决定)
  • 光栅化过程:将创配好的几何图形转化成片元

纹理

纹理坐标系统和webGL坐标系统
纹理坐标系统和webGL坐标系统
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// 顶点着色器程序
var VSHADER_SOURCE = `
attribute vec4 a_Position;
attribute vec2 a_TextCoord; // 纹理坐标
varying vec2 v_TextCoord;
void main() {
gl_Position = a_Position;
v_TextCoord = a_TextCoord;
}
`;

// 片元着色器程序
var FSHADER_SOURCE = `
precision mediump float;
uniform sampler2D u_Sampler; //
varying vec2 v_TextCoord; //
void main() {
// 利用shader对图片进行反转
// gl_FragColor = texture2D(u_Sampler, vec2(v_TextCoord.x, -v_TextCoord.y));

gl_FragColor = texture2D(u_Sampler, v_TextCoord);
}
`;

var canvas = document.getElementById('webgl');
var webgl = canvas.getContext('webgl');

initShaders( webgl, VSHADER_SOURCE, FSHADER_SOURCE);

webgl.clearColor(0, 0, 0, 0.6);

// 初始化buffer
var attrData = new Float32Array([
//顶点坐标 纹理坐标
-0.5, 0.5, 0.0, 1.0,
-0.5, -0.5, 0.0, 0.0,
0.5, 0.5, 1.0, 1.0,
0.5, -0.5, 1.0, 0.0
]);
var n = attrData.length / 4;
var FSIZE = attrData.BYTES_PER_ELEMENT;

var buffer = webgl.createBuffer();
webgl.bindBuffer(webgl.ARRAY_BUFFER, buffer);
webgl.bufferData(webgl.ARRAY_BUFFER, attrData, webgl.STATIC_DRAW);

var a_Position = webgl.getAttribLocation(webgl.program, 'a_Position');
var a_TextCoord = webgl.getAttribLocation(webgl.program, 'a_TextCoord');

webgl.vertexAttribPointer(a_Position, 2, webgl.FLOAT, false, FSIZE*4, 0);
webgl.vertexAttribPointer(a_TextCoord, 2, webgl.FLOAT, false, FSIZE*4, FSIZE*2);
webgl.enableVertexAttribArray(a_Position);
webgl.enableVertexAttribArray(a_TextCoord);


//图片加载成功后 => 进行纹理加载
loadImage('http://localhost:8080/static/sky.jpg')
.then(function(img){
loadTexture(webgl, n, img);
});

// 加载图片
function loadImage (url) {
return new Promise(function(resolve, reject){
var img = new Image();
img.onload = () => { resolve(img) };
img.onerror = (e) => { console.error('Failed to load image!'); reject(e)};
img.src = url;
});
}

// 绘制纹理
function loadTexture (webgl, n, image) {
// 创建纹理对象
var texture = webgl.createTexture();

// 获取纹理的存储位置
var u_Sampler = webgl.getUniformLocation(webgl.program, 'u_Sampler');

// 对纹理进行Y轴反转,第一个参数表示对Y轴进行反转,第二个参数1表示true,0表示false
// 因为纹理坐标系统中的t轴方向与PNG,BMP,JPG等格式图片的坐标系统Y轴方向相反
// 也可以在着色器中手动反转
webgl.pixelStorei(webgl.UNPACK_FLIP_Y_WEBGL, 1);

//开启0号纹理单元,一共有0-7八个纹理单元
webgl.activeTexture(webgl.TEXTURE0);

//向target绑定纹理对象
webgl.bindTexture(webgl.TEXTURE_2D, texture);

// 配置纹理参数
// 第一个参数为纹理地址
// 第二个参数为 映射方法:
// * gl.TEXTURE_MAG_FILTER(纹理绘制范围超过纹理本身时,进行放大)
// * gl.TEXTURE_MIN_FILTER(纹理绘制范围小于纹理本身时,进行缩小)
// * gl.TEXTURE_WRAP_S(如何对纹理左侧或者右侧的区域进行填充)
// * gl.TEXTURE_WRAP_T(如何对纹理上方或者下方的区域进行填充)
// 第三个参数是对第二个参数的补充:
// * 如果选择(TEXTURE_MAG_FILTER/TEXTURE_MIN_FILTER),第三个参数可以选择:
// * gl.NEAREST:使用原纹理上距离映射后像素(新像素)中心最近的那个像素的颜色值,作为新像素的值(使用曼哈顿距离)
// * gl.LINEAR:使用距离新像素中心最近的四个像素的颜色值的加权平均,作为新像素的值(与gl.NEAREST相比,该方法图像质量更好,但是会有较大的开销。)
// * 如果选择(TEXTURE_WRAP_S/TEXTURE_WRAP_T),第三个参数可以选择:
// * gl.REPEAT:平铺式的重复纹理
// * gl.MIRRORED_REPEAT:镜像对称式的重复纹理
// * gl.CLAMP_TO_EDGE:使用纹理图像边缘值
webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MIN_FILTER, webgl.LINEAR);

/** gl.texImage2D(target, level, internalformat, format, type, image);
* 配置纹理图像
* @param target 纹理对象的目标地址
* @param level 为金字塔纹理准备的,平时传入0
* @param internalformat 图像的内部格式(RGB,RBGA,ALPHA,LUMINANCE,LUMUNANCE_ALPHA)
* @param format 纹理数据的格式,必须使用与internalformat相同的值
* @param type 纹理数据的类型(UNISGNED_BYTE,每个颜色分量占一个字节;UNSIGNED_SHORT_5_6_5,RGB每个分量分别占5/6/5比特;UNSIGNED_SHORT_4_4_4_4,RGBA每个分量分别占4/4/4/4比特;UNSIGNED_SHORT_5_5_5_1:RGBA每个分量占5/5/5/1比特)
* @param image 纹理图像的image对象
*/
webgl.texImage2D(webgl.TEXTURE_2D, 0, webgl.RGB, webgl.RGB, webgl.UNSIGNED_BYTE, image);

// 将0号纹理传递给着色器
webgl.uniform1i(u_Sampler, 0);

// 绘图
webgl.clear(webgl.COLOR_BUFFER_BIT);
webgl.drawArrays(webgl.TRIANGLE_STRIP, 0, n);
}

当修改纹理区域:

1
2
3
4
5
6
7
8
// 初始化buffer
var attrData = new Float32Array([
//顶点坐标 纹理坐标
-0.5, 0.5, -0.3, 1.7,
-0.5, -0.5, -0.3, -0.2,
0.5, 0.5, 1.7, 1.7,
0.5, -0.5, 1.7, -0.2
]);

结果就会变成:

1557454940388
1557454940388

因为TEXTURE_WRAP_STEXTURE_WRAP_T的默认值都是REPEAT

当修改:

1
2
3
webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MIN_FILTER, webgl.LINEAR);
webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_S, webgl.CLAMP_TO_EDGE);
webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_T, webgl.MIRRORED_REPEAT);
1557455085332
1557455085332

左右变成了边缘值的重复;上下则是镜像;

多幅纹理

详见:https://github.com/MrZJD/webgl/blob/master/05/lesson05.js