程序关键部分:1.WebGL如何获取<canvas>元素,如何在其上绘图;2.HTML文件如何引入WebGL JavaScript文件;3.简单的WebGL绘图函数;4.WebGL中的着色程序
-
Canvas:允许JavaScript动态地绘制图形
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <title>Draw a blue rectangle</title> </head> <body onload="main()"> <canvas id ='example' width="400" height="400"> Please use a browser that supports "canvas" </canvas> <script type="text/javascript" src="DrawRectangle.js"></script> </body> </html>
function main() { // 获取<canvas>元素 var canvas = document.getElementById('example') if(!canvas){ console.log('Failed to retrieve this <canvas> element!') return } // 获取绘制二位图形的绘图上下文 var ctx = canvas.getContext('2d') // 绘制蓝色矩形 ctx.fillStyle = 'rgba(0,0,255,1.0)' ctx.fillRect(120,10,150,150) }
最短的WebGL程序:清空绘图区
<!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 a browser that supports "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() {
// 获取<canvas>元素
var canvas = document.getElementById('webgl');
// 获取WebGL绘图上下文
var gl = getWebGLContext(canvas);
if (!gl) {
console.log('Failed to get the rendering context for WebGL');
return;
}
// 指定清空<canvas>的颜色
gl.clearColor(0.0, 0.0, 0.0, 1.0);
// 清空<canvas>
gl.clear(gl.COLOR_BUFFER_BIT);
}
- 绘制一个点1.0:此处用矩阵而不是圆
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 intialize shaders.');
return;
}
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
// 绘制一个点
gl.drawArrays(gl.POINTS, 0, 1);
}
-
着色器:是以字符串形式“嵌入”在js文件中的,在程序真正开始运行前就已经设置好了
- 顶点着色器(Vertex shader):用来描述顶点特性(如位置、尺寸等)的程序
- 片元着色器(Fragment shader):进行逐片元处理过程(如光照)的程序。片元是一个WebGL的术语,可以理解为像素。
使用着色器的WebGL程序的结构:顶点着色器程序(GLSL ES语言)、片元着色器程序(GLSL ES语言)、主程序(JavaScript语言)
初始化着色器:调用了辅助函数initShaders()。作用是将字符串形式的着色器代码从Js传给WebGL系统
顶点着色器:gl_Position变量必须被赋值,变量必须指定类型,且赋的值要和类型一致。
片元着色器:作用是处理片元,使其显示在屏幕上。
绘制操作:gl.drawArrays()是一个强大的函数,可以用来绘制各种图形。当程序调用该函数时,顶点着色器将被执行count次,每次处理一个顶点。一旦顶点着色器执行完成后,片元着色器就会开始执行。
WebGL坐标系统:三维坐标系统(笛卡尔坐标系)
默认情况下:X轴是水平的(正方向为右),Y轴是垂直的(正方向为上),Z轴垂直于屏幕(正方向为外),即右手坐标系。
WebGL的坐标系和<canvas>绘图区的坐标系不同,需要将前者映射到后者。
绘制一个点2.0(在JavaScript和着色器之间传输数据,可扩展)
- 使用attribute变量:传输与顶点相关的数据
使用步骤:
- 在顶点着色器中,声明attribute变量
- 将attribute变量赋值给gl_Position变量
- 向attribute变量传输数据
关键代码:
// 声明attribute变量
var VSHADER_SOURCE =
//<存储限定符> <类型> <变量名>
'attribute vec4 a_Position;\n' + // attribute变量必须声明成全局变量
'void main() {\n' +
' gl_Position = a_Position;\n' +
' gl_PointSize = 10.0;\n' +
'}\n';
// 获取attribute变量的存储位置
var a_Position = gl.getAttribLocation(gl.program, 'a_Position'); // 必须在initShader()之后访问gl.program
// 将顶点位置传输给attribute变量
gl.vertexAttrib3f(a_Position, 0.0, 0.0, 0.0);
- 通过鼠标点击绘制点
关键代码:
// 注册鼠标点击事件相应函数
canvas.onmousedown = function(ev){ click(ev, gl, canvas, a_Position); };
var g_points = []; // 鼠标点击位置数组
function click(ev, gl, canvas, a_Position) {
var x = ev.clientX; // 鼠标点击处的x坐标
var y = ev.clientY; // 鼠标点击处的y坐标
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); g_points.push(y);
// 清空<canvas>
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);
}
}
uniform变量:将颜色值传给着色器
- 使用步骤:
- 在片元着色器中准备uniform变量
- 用这个uniform变量向gl_FragColor赋值
- 将颜色数据从JavaScript传给该uniform变量
关键代码:
// 片元着色器
var FSHADER_SOURCE =
// 精度限定词
'precision mediump float;\n' +
// <存储限定符> <类型> <变量名>
'uniform vec4 u_FragColor;\n' + // uniform変量
'void main() {\n' +
' gl_FragColor = u_FragColor;\n' +
'}\n';
// 获取u_FragColor变量的存储位置
var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');
// 注册鼠标点击时的事件响应事件
canvas.onmousedown = function(ev){ click(ev, gl, canvas, a_Position, u_FragColor) };
var g_colors = []; // 存储点颜色的数组
function click(ev, gl, canvas, a_Position, u_FragColor) {
......
// 将点的颜色存储到g_colors数组中
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
}
......
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);
}
}