HTML5时代的到来,让WebGL这个重磅核武器渐渐被大多人所熟悉并且使用。笔者最近两周左右的时间因为项目的原因,一直在看WebGL相关的文章。
说说我的基础吧:计算机本科毕业生水平,大学本科用MFC编写图形学的基础,除此之外在使用WebGL之前自己偷摸着用了一阵子的opengl以及jogl,也就是写几个小例子的水平。写网页的话有点HTML的小基础,写javascript基本每次都是现学的。本人眼高手低,心想着WebGL肯定api也跟这两者差不多,应该很快就入门。没想到入门就吃了闭门羹。终于静下心来买了一本《WebGL入门指南》慢慢研读,研读下来感悟不少,一本不到两百多页的书硬是被我越读越厚。利用这个博客将我这一段时间的所得写下来吧。第一次在CSDN写博客,有什么不足的地方见谅以及指出。本人的联系邮箱是:terryleeoo@foxmail.com
大学时候有位老师对我说,什么事情都得从小事开始做起。那么我的心路历程也用原生的一个WebGL例子作为引子开始介绍WebGL
例子引自WebGL高级编程
<!DOCTYPE HTML>
<html lang="en">
<head>
<title>Listing 2-1, A First WebGL Example</title>
<meta charset="utf-8">
<script type="text/javascript">
var gl;
var canvas;
var shaderProgram;
var vertexBuffer;
function createGLContext(canvas) {
var names = ["webgl", "experimental-webgl"];
var context = null;
for (var i=0; i < names.length; i++) {
try {
context = canvas.getContext(names[i]);
} catch(e) {}
if (context) {
break;
}
}
if (context) {
context.viewportWidth = canvas.width;
context.viewportHeight = canvas.height;
} else {
alert("Failed to create WebGL context!");
}
return context;
}
function loadShader(type, shaderSource) {
var shader = gl.createShader(type);
gl.shaderSource(shader, shaderSource);
gl.compileShader(shader);
if (!gl.getShaderParameter(shader, gl.COMPILE_STATUS)) {
alert("Error compiling shader" + gl.getShaderInfoLog(shader));
gl.deleteShader(shader);
return null;
}
return shader;
}
function setupShaders() {
var vertexShaderSource =
"attribute vec3 aVertexPosition; \n" +
"void main() { \n" +
" gl_Position = vec4(aVertexPosition, 1.0); \n" +
"} \n";
var fragmentShaderSource =
"precision mediump float; \n"+
"void main() { \n"+
" gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); \n"+
"} \n";
var vertexShader = loadShader(gl.VERTEX_SHADER, vertexShaderSource);
var fragmentShader = loadShader(gl.FRAGMENT_SHADER, fragmentShaderSource);
shaderProgram = gl.createProgram();
gl.attachShader(shaderProgram, vertexShader);
gl.attachShader(shaderProgram, fragmentShader);
gl.linkProgram(shaderProgram);
if (!gl.getProgramParameter(shaderProgram, gl.LINK_STATUS)) {
alert("Failed to setup shaders");
}
gl.useProgram(shaderProgram);
shaderProgram.vertexPositionAttribute = gl.getAttribLocation(shaderProgram, "aVertexPosition");
}
function setupBuffers() {
vertexBuffer = gl.createBuffer();
gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);
var triangleVertices = [
0.0, 0.5, 0.0,
-0.5, -0.5, 0.0,
0.5, -0.5, 0.0
];
gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(triangleVertices), gl.STATIC_DRAW);
vertexBuffer.itemSize = 3;
vertexBuffer.numberOfItems = 3;
}
function draw() {
gl.viewport(0, 0, gl.viewportWidth, gl.viewportHeight);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.vertexAttribPointer(shaderProgram.vertexPositionAttribute,
vertexBuffer.itemSize, gl.FLOAT, false, 0, 0);
gl.enableVertexAttribArray(shaderProgram.vertexPositionAttribute);
gl.drawArrays(gl.TRIANGLES, 0, vertexBuffer.numberOfItems);
}
function startup() {
canvas = document.getElementById("myGLCanvas");
gl = createGLContext(canvas);
setupShaders();
setupBuffers();
gl.clearColor(0.0, 0.0, 0.0, 1.0);
draw();
}
</script>
</head>
<body οnlοad="startup();">
<canvas id="myGLCanvas" width="500" height="500"></canvas>
</body>
</html>
很不幸地说,这个例子看似代码很长,出来的效果,如下图:
有点失望是吧,这么一大段的代码,出来的结果却是这样的。
我在这里说说自己对这个例子的几点感悟,希望对于想要学WebGL的同学有所帮助(有关HTML以及JS一些基础的内容,自行百度脑补XD)
1. 这个例子是最基础的webGL程序,你甚至可以把它当成一个模板应用在你后面的一些例子当中。当然了,如果对于只是使用webGL来快速开发而不是研究webGL的朋友来说,你会有更好的方法来达到你的目的。比如使用一些成熟的WebGL框架。
2. createGLContext函数判断浏览器是否支持支持WebGL,如果支持的话返回一个context对象,我对这个context对象的理解就相当于opengl里面的gl,事实证明例子中把该函数的返回值也赋给了一个名为gl的变量。对于这个函数,您大可不必大费周章,因为很多时候您对它的操作也就是copy+paste。
3. 来到关键的setupShaders函数,对于webGL来说,个人认为最最关键的技术就在于顶点着色器和片段着色器了。为什么要使用这两个东西呢?这么来说吧,如果我只用CPU来完成绘图工作的话,效率已经很慢了,如果我再用效率更低的js来绘图的话,可想而知效率会怎么样?那么webGL是将点的渲染交给GPU,采用GLSL的类C语言的语法编写这一部分的代码,将代码传入CPU编译连接,这样就提高了效率。这一部分的代码主要有两种:顶点着色器和片段着色器。关于这两个知识点,我会在后面的文章中说明。
4. setupBuffers函数定义了图形的顶点,你还可以在这里面定义图形的颜色等等信息。
5. 最后的draw函数,利用之前做好的准备和数据画出图形
整个过程就是这样了,我的建议是,对于这一大段代码,您大可不必大费周章每行都明白。现在,你只需要知道每一步是干什么的,以及能大概在脑子里面有个大体的流程,知道WebGL的程序的流程就好。
后面的内容我将以Three.js这个框架为基础,谈谈自己最近的感受。等最后,再回来看看这个程序,就会很明白每一步的细节了。需要说明的是,《webGL入门指南》这本书虽然讲的很好,而且基于Three.js这个框架,但是在中间又引入本书作者自己的Sim.js框架让我很不习惯。因为本来Three.js已经封装了一层代码的情况下,我不太喜欢有人再给我封装一层不符合我的学习习惯,这就是导致我这本书越读越厚的原因了。另外,我在很纠结的情况下也去拜读了一下Sim.js框架的代码,从中学到了很多,也写了自己的一个小框架,会在后面的学习中一点点引入。