书本源代码 https://download.csdn.net/download/qfire/10371055
2009年,Khronos建立了WebGL工作小组,开始基于OpenGL ES着手建立WebGL规范,并于2011年发布了WebGL规范的第1个版本。本书主要基于第1版的WebGL规范编写,后续更新目前都是以草案的形式发布,如有需要,也可参考。www.khronos.org/registry/webgl/specs/1.0/
1.1 WebGL程序的结构
1.2 Canvas是什么?
在HTML5出现之前,如果你想在网页上显示图像,只能使用HTML提供的原生方案<img>标签。用这个标签显示图像虽然简单,但只能显示静态的图片,不能进行实时绘制和渲染。因此,后来出现了一些第三方解决方案,如Flash Player等。HTML5的出现改变了一切,它引入了<canvas>标签,允许JavaScript动态绘制图形。
实例
清空
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"/> <title>Clear canvas</title> </head> <body onload="main()"> <canvas id="webgl" width="400" height="400"> Please use the browser supporting "canvas" </canvas> <script src="../lib/webgl-utils.js"></script> <script src="../lib/webgl-debug.js"></script> <script src="../lib/cuon-utils.js"></script> <script src="HelloCanvas.js"></script> </body> </html>
function main() { var canvas = document.getElementById('webgl'); var gl = getWebGLContext(canvas); if (!gl) { console.log("Failed to get the rendering context for WebGL"); return; } //RGBA gl.clearColor(0.0, 0.0, 0.0, 1.0); //清空 gl.clear(gl.COLOR_BUFFER_BIT); //绘制点 //gl.drawColor(1.0, 0.0, 0.0, 1.0); gl.drawPoint(0, 0, 0, 10); //点的位置和大小 }
绘制一个10个像素大的红色的点,WebGL处理的是三维图形,所以我们有必要为这个点指定三维坐标。
WebGL依赖于一种新的称为着色器的绘图机制。着色器提供了灵魂且强大的绘制二维或三维图形的方法,所有WebGL程序必须使用它。着色器不仅强大,而且更复杂,仅仅通过一条简单的绘图命令是不能操作它的。
//顶点着色器程序 var VSHADER_SOURCE = 'void main() {\n' + ' gl_Position = vec4(0.0, 0.0, 0.0, 1.0); \n' + //设置坐标 ' gl_PointSize = 10.0; \n' + //设置尺寸 '}\n'; //片元着色器程序 var FSHADER_SOURCE = 'void main() {\n' + ' gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);\n' + //设置颜色 '}\n'; function main() { var canvas = document.getElementById('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 ; } //RGBA gl.clearColor(0.0, 0.0, 0.0, 1.0); //清空 gl.clear(gl.COLOR_BUFFER_BIT); //绘制点 gl.drawArrays(gl.POINTS, 0, 1); //点的位置和大小 }
WebGL需要两种着色器。
- 顶点着色器(Vertex shader):顶点着色器是用来描述顶点特性(如位置、颜色等)的程序。
- 片元着色器(Fragment shader):进行逐片元处理过程如光照的程序。片元是一个WebGL术语,你可以将其理解为像素。
vec4(x, y, z, w);齐次坐标(x, y, z, w)等价于三维坐标(x/w, y/w, z/w),齐次坐标的存在,使得用矩阵乘法来描述顶点变换成为可能,三维图形系统在计算过程中,通常使用齐次坐标来表示顶点的三维坐标。
gl.drawArrays()可以用来绘制各种图形
1.3 WebGL坐标系统
' gl_Position = vec4(0.5, 0.5, 0.0, 1.0); \n' + //设置坐标
' gl_FragColor = vec4(0.0, 1.0, 0.0, 1.0);\n' + //设置颜色
注:WebGL不需要交换颜色缓冲区
1.4 绘制一点实例
将位置信息从JavaScript程序中传给顶点着色器。有两种方式可以做到:attribute变量和uniform变量。attribute变量传输的是那些与顶点相关的数据,而uniform变量传输的是那些对于所有顶点都相同(或与顶点无关)的数据。
//顶点着色器程序 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() { var canvas = document.getElementById('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; } gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0); //RGBA gl.clearColor(0.0, 0.0, 0.0, 1.0); //清空 gl.clear(gl.COLOR_BUFFER_BIT); //绘制点 gl.drawArrays(gl.POINTS, 0, 1); //点的位置和大小 }
//顶点着色器程序 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() { var canvas = document.getElementById('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; } var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor'); if (u_FragColor < 0) { console.log("failed to get the storage location of u_FragColor"); return; } //注册鼠标 canvas.onmousedown = function(ev) { click(ev, gl, canvas, a_Position, u_FragColor)}; gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0); //RGBA gl.clearColor(0.0, 0.0, 0.0, 1.0); //清空 gl.clear(gl.COLOR_BUFFER_BIT); //绘制点 //gl.drawArrays(gl.POINTS, 0, 1); //点的位置和大小 } 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数组中 g_points.push([x, y]); //将点的颜色存储到g_colors数组中 if (x >= 0.0 && y >= 0.0) { //第一象限 g_colors.push([1.0, 0.0, 0.0, 1.0]); //红色 } else if (x < 0.0 && y < 0.0) { // 第三象限 g_colors.push([0.0, 1.0, 0.0, 1.0]); //绿色 } else { g_colors.push([1.0, 1.0, 1.0, 1.0]); //白色 } //清空 gl.clear(gl.COLOR_BUFFER_BIT); var len = g_points.length; for (var i=0; i<len; i++) { var xy = g_points[i]; var rgba = g_colors[i]; //将点的位置传输到a_Position变量中 gl.vertexAttrib3f(a_Position, xy[0], xy[1], 0.0); //将点的颜色传输到u_FragColor变量中 gl.uniform4f(u_FragColor, rgba[0], rgba[1], rgba[2], rgba[3]); //绘制点 gl.drawArrays(gl.POINTS, 0, 1); } }