理论定义

WebGL,是一项用来在网页上绘制和渲染复杂3D图形的技术,是web版的OpenGL,WebGL在电脑的GPU中运行。它是在内嵌在浏览器中的,不需要特殊的编译器就能直接在js文件中编写webgl程序。
WebGL 可以为 HTML5 Canvas 提供硬件 3D 加速渲染,这样就可以借助系统显卡来在浏览器里更流畅地展示 绘制的图形,渲染能力和性能比canvas和svg更好。
GPU渲染图形 : 计算机将存储在内存中的形状转换成实际绘制在屏幕上的对应的过程称为渲染。GPU 的并行计算能力使其能够快速将图形结果计算出来并在屏幕的所有像素中进行显示。渲染过程中最常用的技术就是光栅化。

程序结构

image.png

学习资料

推荐书籍

《webgl编程指南》

API地址

https://www.khronos.org/registry/webgl/specs/1.0/#1

w3cschool学习地址

https://www.w3cschool.cn/webgl/vjxu1jt0.html

视频学习地址(27-53与图书《webgl编程指南》顺序一致)

https://www.bilibili.com/video/BV1WQ4y1m7NF?p=27

基础入门

  1. 创建webgl画布与创建canvas画布一样需要在HTML中定义一个canvas标签,然后获取上下文方法。<br />示例:
  1. //html代码
  2. <canvas id="canvas" width="600" height="600" style="border:1px #666 solid;"></canvas>
  3. //js代码
  4. //获取canvas元素
  5. var canvas = document.getElementById('canvas');
  6. //获取绘制webgl上下文
  7. var gl = canvas.getContext('webgl');

坐标

webgl坐标

webgl可以绘制三维图形,有x,y,z三个坐标,坐标范围是 -1 到 1。
无论要实现的图形尺寸有多大,其投影矩阵的坐标的范围始终是从 -1 到 1,

  • x轴最左边为-1,最右边为1;
  • y轴最下边为-1,最上边为1;
  • z轴朝向你的方向最大值为1,远离你的方向最大值为-1;
  • 这些值与Canvas的尺寸无关,无论Canvas的长宽比是多少,WebGL的区间值都是一致的

image.png

canvas坐标

熟悉canvas的开发者都知道,canvas坐标是以画布的左上角为坐标原点,向右为x,向下为y,并且单位与dom的单位一致可用px表示,下图红色线为canvas坐标。
而webgl的坐标原点在画布的中心处,分别为x[-1, 1],y[-1, 1],z[-1,1],下图绿色为webgl的xy坐标,假设z坐标是指向屏幕外的方向。
image.png
图1

坐标转换

若要绘制webgl图形或者同时在一块区域绘制webgl和canvas图形,那就必须要对坐标进行转换。
参考图1
(1)屏幕上的点p转换为canvas上的坐标
a.获取canvas在浏览器客户区中的坐标(x,y)

  1. //点距离浏览器可视区边界的位置
  2. var x = ev.clientX;
  3. var y = ev.client.Y;
  4. //获取canvas相对于视窗的位置,有rightlefttop等属性
  5. var rect = ev.target.getBoundingClientRect();

(b)获取图中p点在屏幕坐标系下的canvas坐标值为(x’,y’)

  1. // (a,b)为canvas原点相对可视区的位置
  2. x' = x-a = x - rect.left;
  3. y' = y-b = y - rect.top;

(2)canva上的坐标转换为webgl的坐标
a.首先我们通过图片可知,canvas坐标系的Y轴和webgl的坐标系的Y轴方向是相反的,即在后面的转换过程中,y坐标值要进行取反操作。
b.通过代码我们可以获取canvas画布的宽和高:
width = canvas.width;
height = canvas.height;
则webgl原点在canvas坐标系中的位置(即canvas的中心点位置)坐标我们可以直接获得为(width/2,height/2);
现在我们所得到的数据有:

  1. P点在canvas坐标系下的坐标值:(x’, y’)->(x - rect.left,y - rect.top)
  2. canvas的中心点在canvas坐标系下的坐标值:(width/2,height/2)

此时canvas中webgl的坐标(x’’,y’’)为:

  1. x'' = (x' - canvas.width/2)/(canvas.width/2)
  2. y'' = -(y' - canvas.height/2)/(canvas.height/2)
  1. x'' = ((x - rect.left)-width/2)/(width/2) = ((x - rect.left)-canvas.width/2)/(canvas.width/2)
  2. y'' = (height/2-(y - rect.top))/(height/2) = (canvas.height/2-(y - rect.top))/(canvas.height/2)

绘制步骤

1、创建webgl上下文
2、定义着色器(着色器语言-GLSL)
顶点着色器-提供坐标、大小
片元着色器-提供颜色
定义方式:硬编码/使用变量
3、初始化着色器(多个着色器合并之后后的着色器程序)
4、drawArrays 函数进行绘制
gl.drawArrays(mode, first, count); mode-类型,first:起始点,count-几个点
mode类型:
gl.POINTS-点 : 画一个点
gl.LINES-线段: 在一对顶点间画一条线,如果点的个数是奇数,最后一个点会被忽略
gl.LINE_STRIP-线条: 绘制到下一个点的直线,将每一个点按顺序连接成线条
gl.LINE_LOOP—回路 : 绘制到下一个顶点的直线,并将最后一个顶点连接回第一个顶点。首位闭合
gl.TRIANGLES-三角形 : 为一组三个顶点绘制一个三角形。(可以用来画圆)
gl.TRIANGLE_STRIP-三角带: 一系列条带状的三角形,前三个点构成三角形,下一个点与前两个点构成三角形,三角形按点的顺序为(V0,V1,V2)(V1,V2,V3) (V2,V3,V4)….
gl.TRIANGLE_FAN- 三角扇形:一系列三角形组成的类似于扇形的图形。前三个点构成三角形,下一个点与与第一个点和前一个点构成三角形,三角形的组成顺序为(V0,V1,V2) (V0,V2,V3) (V0,V3,V4)…. (可以用来画圆)
这些图形(点、线、三角形)就是webgl可以直接绘制的图形,是绘制其他复杂图形的基础。

创建webgl上下文

  1. //获取canvas元素
  2. var canvas = document.getElementById('canvas');
  3. //获取绘制二维上下文
  4. var gl = canvas.getContext('webgl');

定义着色器

着色器语言GLSL是以字符串的形式“嵌入”在js代码中的。语法与C语言较为相似,具体的语法可以参照《webgl编程指南》第六章。
webgl绘图需要两种着色器,顶点着色器用来描述顶点位置例如坐标位置、大小等,片元着色器进行逐片元处理过程如颜色、光照等。
注意:颜色是一个rgba对应的四维矩阵值,例如vec4(1.6, 0.0, 0.0, 1.0)-红色
前三个对应范围是0-1,分别对应0-255,也就是说我们拿到的颜色需要转换成0-1;第四个分量对应透明度

硬编码

  1. // 定义顶点着色器程序
  2. var VSHADER_SOURCE =
  3. `void main() {
  4. //设置坐标 (x,y,z,w)等价于(x/w,y/w,z/w),w通常为1.0,w趋近于0时表示点无穷远
  5. gl_Position = vec4(0.6, 0.2, 0.6, 1.0);
  6. //设置尺寸
  7. gl_PointSize = 40.0;
  8. }`;
  9. //定义片段着色器
  10. var FSHADER_SOURCE =
  11. `void main() {
  12. //设置颜色rgba
  13. gl_FragColor = vec4(0.6, 0.7, 0.9, 1.0);
  14. }`;

初始化着色器

  1. //传入webgl对象,顶点着色器,片元着色器
  2. function initShader(gl,VSHADER_SOURCE,FSHADER_SOURCE) {
  3. //编译着色器
  4. var vertShader = gl.createShader(gl.VERTEX_SHADER);//创建顶点着色器
  5. gl.shaderSource(vertShader, VSHADER_SOURCE);//指定顶点着色器对象
  6. gl.compileShader(vertShader);//编译顶点着色器
  7. var fragShader = gl.createShader(gl.FRAGMENT_SHADER);//创建片元着色器
  8. gl.shaderSource(fragShader, FSHADER_SOURCE);//指定片元着色器对象
  9. gl.compileShader(fragShader);//编译片元着色器
  10. //合并程序
  11. var shaderProgram = gl.createProgram();//创建程序对象
  12. gl.attachShader(shaderProgram, vertShader); //为程序对象分配顶点着色器对象
  13. gl.attachShader(shaderProgram, fragShader); //为程序对象分配片元着色器对象
  14. gl.linkProgram(shaderProgram); //连接程序对象
  15. gl.useProgram(shaderProgram); //使用程序对象
  16. // gl.program = shaderProgram;
  17. return shaderProgram; // 返回程序对象
  18. }

简单完整示例

  1. <body>
  2. <canvas id="canvas" width="600" height="600" style="border:1px #666 solid;"></canvas>
  3. </body>
  4. <script>
  5. window.onload = function () {
  6. //获取canvas元素
  7. var canvas = document.getElementById('canvas');
  8. //获取绘制二维上下文
  9. var gl = canvas.getContext('webgl');
  10. if (!gl) {
  11. console.log("浏览器不支持webgl");
  12. return;
  13. }
  14. // 创建绘图缓冲区 位置、宽高
  15. gl.viewport(0, 0, gl.drawingBufferWidth, gl.drawingBufferHeight);
  16. gl.clearColor(0.9,0,0.5,0.5);//设置画布颜色
  17. gl.clear(gl.COLOR_BUFFER_BIT);//清空画布
  18. //顶点着色器程序
  19. var VSHADER_SOURCE =
  20. "void main() {" +
  21. //设置坐标 xyzw
  22. "gl_Position = vec4(0.0, 0.0, 0.0, 1.0); " +
  23. //设置尺寸
  24. "gl_PointSize = 40.0; " +
  25. "} ";
  26. //片段着色器
  27. var FSHADER_SOURCE =
  28. "void main() {" +
  29. //设置颜色
  30. "gl_FragColor = vec4(0.6, 0.7, 0.9, 1.0);" +
  31. "}";
  32. //初始化着色器创建对象
  33. var program = initShader(gl,VSHADER_SOURCE,FSHADER_SOURCE);
  34. //绘制一个点
  35. gl.drawArrays(gl.POINTS, 0, 1);
  36. }
  37. </script>

变量方式定义

以上为硬编码顶点属性绘制点,下面我们可以通过在js中传入变量给顶点着色器绘图。使用attribute变量(顶点着色器)和uniform(片元着色器)
步骤:
1.在顶点着色器声明变量attribute、uniform
注意声明的变量一般以变量限定符的首字母加下划线开始,如attribute变量以a前缀开始,uniform变量以u开始,这样可以清晰的辨别类型。
2.将变量attribute、uniform赋值给js变量
每个变量都有一个存储地址,当要给变量传输数据时需要向webgl系统请求改变量的存储地址。对应不同类型的变量我们用不同的方法累获取地址。例如:
attribute - gl.getAttribLocation(programe,name)
uniform - getUniformLocation(programe,name)
3.向变量attribute、uniform传输数据
同样的,在向变量传值的时候,不同的数值类型也要对应不同的传值方法,由于类型较多可以参考api或者《webgl编程指南》.例如
传一个vec4(V0,V1,V2,V3)的数值使用 gl.vertexAttrib4f(name, V0,V1,V2,V3);

  1. //顶点着色器中声明两个attribute变量(位置和大小)
  2. var VSHADER_SOURCE =
  3. `attribute vec4 a_Position; //声明attribute变量 变量attribute、类型、变量名
  4. attribute float a_PointSize;
  5. void main(){
  6. gl_Position = a_Position;
  7. gl_PointSize = a_PointSize;
  8. }
  9. `;
  10. //片元着色器中声明一个uniform变量(颜色)
  11. var FSHADER_SOURCE =
  12. `precision mediump float; //精度限定
  13. uniform vec4 u_FragColor;
  14. void main(){
  15. gl_FragColor = u_FragColor;
  16. }
  17. `;
  1. //初始化着色器创建程序对象
  2. var program = initShader(gl,VSHADER_SOURCE,FSHADER_SOURCE);
  3. //必须要在初始化着色器后才能获取变量
  4. var a_Position = gl.getAttribLocation(program, 'a_Position');
  5. var a_PointSize = gl.getAttribLocation(program, 'a_PointSize')
  6. var u_FragColor = gl.getUniformLocation(program, 'u_FragColor');
  1. //向变量attribute、uniform传输数据
  2. var size = 20;
  3. //将顶点位置传输给a_Position变量,顶点大小传给a_PointSize变量
  4. gl.vertexAttrib4f(a_Position, Math.random(), Math.random(), Math.random(), 1);
  5. gl.vertexAttrib1f(a_PointSize, size);
  6. //将颜色值传给u_FragColor
  7. gl.uniform4f(u_FragColor, Math.random(), Math.random(), Math.random(), 1.0);
  8. //画点
  9. gl.drawArrays(gl.POINTS,0,1);

缓冲区

定义

缓冲区对象:可以一次性向着色器传入多个顶点数据。缓冲区对象时webgl系统中的一块内存区域,我们绝一次性地向缓冲区对象中填充大量的顶点数据,然后将这下数据保存在其中,供顶点着色器使用。
避免绘制多个点时用for循环多次调用gl.drawArrays()去绘制。

image.png

float32Array:单精度浮点数

操作步骤:

image.png

关键函数:

  1. //绑定缓冲区
  2. function initBuffer(gl,vertices,bufferName,pointCount) {
  3. var pointCount = pointCount || 3; //顶点分量数 默认为3
  4. // 创建buffer对象
  5. var vertexBuffer = gl.createBuffer();
  6. // 绑定对象到缓冲区指针(顶点数据)上
  7. gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
  8. //写入数据到缓冲区
  9. gl.bufferData(gl.ARRAY_BUFFER,vertices, gl.STATIC_DRAW);
  10. // 指定attribute变量解析规则,缓冲区对象分配给attribute变量
  11. //第二个参数表示顶点分量个数(1到4),设置1时,23为默认04默认1 与vertices数据对应起来
  12. gl.vertexAttribPointer(bufferName, pointCount, gl.FLOAT, false, 0, 0);
  13. // 启用attribute变量 => 即链接缓冲区到attribute变量上
  14. gl.enableVertexAttribArray(bufferName);
  15. }

gl.vertexAttribPointer(location,size,type,normalized,stride,offset)
  • location:指定待分配 attribute 变量的存储位置
  • size:缓冲区给每个顶点分配的分量个数
  • type:数据类型 gl.UNSIGNED_BYTE(Uint8Array),gl.SHORT(Int16Array),….
  • stride:指定相邻两个顶点的字节数量
  • offset:指定缓存的偏移量

    示例

    例如1.将一组含有坐标值和颜色值的数据分别放入两个缓冲区,绘制三个不同颜色的点 ``json // 使用变量定义着色器 var VSHADER_SOURCE =attribute vec4 a_Position; //声明attribute变量 attribute float a_PointSize; void main(){ gl_Position = a_Position; gl_PointSize = a_PointSize; } ; var FSHADER_SOURCE =precision mediump float; //精度限定 uniform vec4 u_FragColor; void main(){ gl_FragColor = u_FragColor; } `;

… //js代码

//获取canvas元素 var canvas = document.getElementById(‘canvas’); //获取绘制二维上下文 var gl = canvas.getContext(‘webgl’);

// 初始化着色器
var program = initShader(gl,VSHADER_SOURCE,FSHADER_SOURCE);

// 获取attribute变量的地址 var a_Position = gl.getAttribLocation(program, ‘a_Position’); var a_PointSize = gl.getAttribLocation(program, ‘a_PointSize’); var u_FragColor = gl.getUniformLocation(program, ‘u_FragColor’);

//定义点数组 单精度浮点数 var vertices = new Float32Array([ -0.7,-0.3,0, -0.35,0.4,0, -0.3,-0.3,0, ]); var size = new Float32Array([5.0,20.0,60.0]);

// 绑定并写入数据到缓冲区 位置缓冲区和size缓冲区 initBuffer(gl,vertices,a_Position); initBuffer(gl,size,a_PointSize,1); gl.uniform4f(u_FragColor, Math.random(), Math.random(), Math.random(), 1.0);

// 画点 gl.drawArrays(gl.POINTS, 0, 3);

  1. 也可以将多个属性放在一个缓冲区里,变量可以分段去取值。<br />会用到varying变量<br />示例2.将坐标和颜色放在同一个缓冲区里,绘制一个颜色渐变的三角形(三个不同颜色的顶点)
  2. ```json
  3. // 在顶点着色器里定义颜色赋值给片元着色器的变量
  4. var VSHADER_SOURCE =
  5. `attribute vec4 a_Position; //声明attribute变量 位置a_Position
  6. attribute vec4 a_color; //声明attribute变量 位置a_color
  7. varying vec4 v_color; //用于片元着色器的varying变量
  8. void main(){
  9. gl_Position = a_Position;
  10. gl_PointSize = 20.0;
  11. v_color = a_color; //数据传给片元着色器
  12. }
  13. `;
  14. var FSHADER_SOURCE =
  15. `precision mediump float; //精度
  16. varying vec4 v_color; //这个变量名一定要和顶点着色器里的一样
  17. void main(){
  18. gl_FragColor = v_color;
  19. }
  20. `;
  21. window.onload = function () {
  22. //获取canvas元素
  23. var canvas = document.getElementById('canvas');
  24. //获取绘制二维上下文
  25. var gl = canvas.getContext('webgl');
  26. var program = initShader(gl,VSHADER_SOURCE,FSHADER_SOURCE);
  27. //存入缓冲区的数据 五个一组,前两个是坐标 后三个是颜色
  28. var dataVertices = new Float32Array([
  29. 0.1,0.2,1.0,0.0,0.0,
  30. 0.0,0.0,0.0,1.0,0.0,
  31. 0.3,0.0,0.0,0.0,1.0,
  32. ]);
  33. // 绑定缓冲区
  34. var vayingBuffer = gl.createBuffer();
  35. gl.bindBuffer(gl.ARRAY_BUFFER, vayingBuffer);
  36. gl.bufferData(gl.ARRAY_BUFFER,dataVertices, gl.STATIC_DRAW);
  37. FSIZE = dataVertices.BYTES_PER_ELEMENT;
  38. var a_Position = gl.getAttribLocation(program,'a_Position');
  39. var a_color = gl.getAttribLocation(program,'a_color');
  40. //取每组的前两个赋值给a_Position
  41. gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE*5, 0);
  42. //从每组的第三个开始取3个数用于赋值给a_color
  43. gl.vertexAttribPointer(a_color, 3, gl.FLOAT, false, FSIZE*5, FSIZE*2);
  44. //启用attribute变量
  45. gl.enableVertexAttribArray(a_Position);
  46. gl.enableVertexAttribArray(a_color);
  47. //绘三角形
  48. gl.drawArrays(gl.TRIANGLES, 0, 3);
  49. }

图形变换

平移

定义位置时加上平移变量

  1. var VSHADER_SOURCE =
  2. `attribute vec4 a_pos; //定义位置变量
  3. uniform vec4 u_change; //定义平移变量
  4. void main(){
  5. gl_Position = a_pos + u_change;
  6. }
  7. `

旋转

2d旋转,围绕z轴旋转
image.png
缩放
image.png

  1. var VSHADER_SOURCE =
  2. `attribute vec4 a_pos;
  3. uniform float u_posW;
  4. void main(){
  5. gl_Position.x = a_pos.x;
  6. gl_Position.y = a_pos.y;
  7. gl_Position.z = a_pos.z;
  8. gl_Position.w = u_posW;
  9. }
  10. `;

高级变换

矩阵基础变换

  1. 着色器语言支持矩阵变换运算<br /> webgl中矩阵以列为主序,矩阵运算时变换矩阵必须写在前面
  1. var VSHADER_SOURCE=
  2. `attribute vec4 a_pos;
  3. uniform mat4 u_change; //定义变换矩阵 类型为mat4
  4. void main(){
  5. gl_Position = u_change*a_pos; //变换矩阵必须写在前面
  6. }
  7. `;

平移矩阵

image.png

缩放矩阵

image.png

旋转矩阵

绕X、Y、Z轴旋转的矩阵对应如下:
image.png
我们可以直接将这几种矩阵定义成方法,需要使用时引入方法直接往里面传参数使用。

  1. // 平移矩阵
  2. function translate(x,y,z){
  3. return new Float32Array ([
  4. 1.0,0.0,0.0,0.0,
  5. 0.0,1.0,0.0,0.1,
  6. 0.0,0.0,1.0,0.1,
  7. x,y,z,1.0
  8. ]);
  9. };
  10. //旋转矩阵
  11. function rotate(angle){
  12. let sinB = Math.sin(angle/360*2*Math.PI);
  13. let cosB = Math.cos(angle/360*2*Math.PI);
  14. // z轴旋转
  15. return new Float32Array ([
  16. cosB,sinB,0.0,0.0,
  17. -sinB,cosB,0.0,0.0,
  18. 0.0,0.0,1.0,0.0,
  19. 0.0,0.0,0.0,1.0
  20. ]);
  21. };
  22. //缩放矩阵
  23. function scale(x,y,z){
  24. return new Float32Array ([
  25. x,0.0,0.0,0.0,
  26. 0.0,y,0.0,0.1,
  27. 0.0,0.0,z,0.1,
  28. 0.0,0.0,0.0,1.0
  29. ]);
  30. };

复合变换

实现一个动画需要由几种变换结合而成
image.png
image.png
示例
1.定义顶点着色器,变换矩阵变量

  1. //先平移后旋转
  2. var VSHADER_SOURCE =
  3. `attribute vec4 a_pos;
  4. uniform mat4 u_translate; //定义平移矩阵变量
  5. uniform mat4 u_rotate; //定义旋转矩阵变量
  6. void main(){
  7. gl_Position = u_translate*u_rotate*a_pos; //先平移后旋转
  8. }
  9. `;

2.定义顶点数据并绑定缓冲区

  1. //顶点数据
  2. var dataVertices = new Float32Array([
  3. 0.1,0.2,0,
  4. 0.0,0.0,0,
  5. 0.5,0.0,0
  6. ]);
  7. // 取出webgl变量赋值给js变量
  8. var a_Position = gl.getAttribLocation(gl.program,'a_pos');
  9. //将顶点数据绑定缓冲区
  10. initBuffer(gl,dataVertices,a_Position);

3.取出变量并赋值

  1. //取出平移变量并赋值
  2. var u_translate = gl.getUniformLocation(gl.program,'u_translate');
  3. //translate为定义好的平移矩阵函数
  4. gl.uniformMatrix4fv(u_translate,false,translate(0.1,0.0,0.0));
  5. ////取出旋转变量并赋值
  6. var u_rotate = gl.getUniformLocation(gl.program,'u_rotate');
  7. gl.uniformMatrix4fv(u_rotate,false,rotate(10.0));//rotate为定义好的旋转矩阵函数
  8. //绘图
  9. gl.drawArrays(gl.TRIANGLES,0,3);

动画

动画就是通过每一个不同状态的静态显示不停的切换实现的
image.png
实现机制:
image.png
requesAnimationFram方法实际作用与setTimeout差不多,只是它经过了优化会依据浏览器的刷新频率来决定回调函数的执行时机。

示例

  1. //顶点着色器
  2. var VSHADER_SOURCE =
  3. `attribute vec4 a_pos;
  4. uniform mat4 u_translate; //平移变量
  5. uniform mat4 u_rotate; //旋转变量
  6. uniform mat4 u_scale; //缩放变量
  7. void main(){
  8. gl_Position = u_rotate * u_scale * u_translate* a_pos;
  9. }
  10. `;
  11. //片元着色器
  12. var FSHADER_SOURCE =
  13. `void main(){
  14. gl_FragColor = vec4(0.1,0.1,0.0,1.0);
  15. }
  16. `;
  17. //js方法
  18. window.onload = function(){
  19. var canvas = document.getElementById('canvas');
  20. var gl = canvas.getContext('webgl');
  21. var program = initShader(gl,VSHADER_SOURCE,FSHADER_SOURCE);//初始化着色器
  22. //定义顶点位置
  23. var dataVertices = new Float32Array([
  24. 0.1,0.2,0,
  25. 0.0,0.0,0,
  26. 0.3,0.0,0
  27. ]);
  28. //取出着色器变量
  29. var a_Position = gl.getAttribLocation(program,'a_pos');
  30. initBuffer(gl,dataVertices,a_Position);//初始化缓冲区
  31. //定义动画参数
  32. var tx=0.0;
  33. var ty=0.0;
  34. var tz=0.0;
  35. var sx=0.5;
  36. var sy=0.5;
  37. var angle=10.0;
  38. var params={};
  39. params = {tx,ty,tz,sx,sy,angle};
  40. //每次绘制方法
  41. function animation(){
  42. params.tx += 0.01;
  43. params.ty += 0.01;
  44. params.sx += 0.01;
  45. params.sy += 0.01;
  46. params.angle += 10.0;
  47. var u_translate = gl.getUniformLocation(gl.program,'u_translate');
  48. var u_scale = gl.getUniformLocation(gl.program,'u_scale');
  49. var u_rotate = gl.getUniformLocation(gl.program,'u_rotate');
  50. // 使用matrix里定义的变换矩阵赋值
  51. gl.uniformMatrix4fv(u_translate,false,translate(params.tx,params.ty,params.tz));
  52. gl.uniformMatrix4fv(u_scale,false,scale(params.sx,params.sy,1.0));
  53. gl.uniformMatrix4fv(u_rotate,false,rotate(params.angle));
  54. //清除画布
  55. gl.clear(gl.COLOR_BUFFER_BIT);
  56. gl.drawArrays(gl.TRIANGLES,0,3);
  57. }
  58. //执行动画
  59. function run (){
  60. animation();
  61. if(params.sx<1) {//即将超出边界时停止
  62. var act = window.requestAnimationFrame(run);
  63. }else{
  64. window.cancelAnimationFrame(run);
  65. }
  66. };
  67. run();
  68. }

纹理贴图

image.png
示例

  1. //顶点着色器
  2. var VSHADER_SOURCE =
  3. `attribute vec4 a_Position; //声明attribute变量
  4. attribute vec2 a_textCoord; //定义纹理坐标信息
  5. varying vec2 v_textCoord;
  6. void main(){
  7. gl_Position = a_Position;
  8. v_textCoord = a_textCoord; //数据传给片元着色器
  9. }
  10. `;
  11. //片元着色器
  12. var FSHADER_SOURCE =
  13. `precision mediump float;
  14. uniform sampler2D u_Sampler; //
  15. varying vec2 v_textCoord; //
  16. void main() {
  17. gl_FragColor = texture2D(u_Sampler, v_textCoord);//定义指定的纹理
  18. }
  19. `;
  20. window.onload = function () {
  21. //获取canvas元素
  22. var canvas = document.getElementById('canvas');
  23. //获取绘制二维上下文
  24. var gl = canvas.getContext('webgl');
  25. var program = initShader(gl,VSHADER_SOURCE,FSHADER_SOURCE);
  26. //顶点坐标 纹理坐标
  27. var dataVertices = new Float32Array([
  28. -0.5, 0.5, -0.3, 1.7,
  29. -0.5, -0.5, -0.3, -0.2,
  30. 0.5, 0.5, 1.7, 1.7,
  31. 0.5, -0.5, 1.7, -0.2
  32. ]);
  33. var a_Position = gl.getAttribLocation(program,'a_Position');
  34. var a_textCoord = gl.getAttribLocation(program,'a_textCoord');
  35. var vayingBuffer = gl.createBuffer();
  36. gl.bindBuffer(gl.ARRAY_BUFFER, vayingBuffer);
  37. gl.bufferData(gl.ARRAY_BUFFER,dataVertices, gl.STATIC_DRAW);
  38. FSIZE = dataVertices.BYTES_PER_ELEMENT;
  39. gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE*4, 0);//取每组前两个值
  40. gl.vertexAttribPointer(a_textCoord, 2, gl.FLOAT, false, FSIZE*4, FSIZE*2);//从第二个开始取2个值
  41. gl.enableVertexAttribArray(a_Position);
  42. gl.enableVertexAttribArray(a_textCoord);
  43. initTexture(gl);
  44. }
  45. //初始化纹理对象
  46. function initTexture(gl){
  47. //创建纹理对象
  48. var texture = gl.createTexture();
  49. var u_Sampler = gl.getUniformLocation(gl.program,'u_Sampler');
  50. var image = new Image();
  51. image.onload = function(){
  52. loadTexture(gl,texture,u_Sampler,image);
  53. };
  54. image.crossOrigin = 'anonymous'; //允许跨域
  55. image.src = 'https://img0.baidu.com/it/u=2753644025,1817671822&fm=253&fmt=auto&app=138&f=JPEG?w=371&h=554';
  56. return true;
  57. }
  58. //为webgl配置纹理
  59. function loadTexture(gl,texture,u_Sampler,image){
  60. gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL,1);//对图片进行Y轴翻转 统一为纹理坐标
  61. gl.activeTexture(gl.TEXTURE0);//开启0号纹理单元
  62. gl.bindTexture(gl.TEXTURE_2D, texture);//绑定纹理对象
  63. //配置纹理参数
  64. gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINERAR);
  65. //将纹理图像分配给纹理对象
  66. gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGB, gl.RGB, gl.UNSIGNED_BYTE, image);
  67. gl.uniform1i(u_Sampler,0);//将0号纹理单元传入着色器中的取样变量
  68. //绘制矩形
  69. gl.drawArrays(gl.TRIANGLE_STRIP, 0 ,4);
  70. }