本篇文章介绍如何搭建WebGL开发环境
WebGL的技术规范继承自免费和开源的OpenGL ES标准,从某种意义上说,WebGL就是Web版的OpenGL ES,而OpenGL ES是从OpenGL中派生出来的。他们的应用环境有区别,一般来说:
既然OpenGL、OpenGL ES和WebGL存在继承关系,那必然涉及到版本信息
先看一下OpenGL的版本信息:
看一下OpenGL ES的版本信息:
看一下WebGL的版本信息:
一个最简单的WebGL程序之需要一个html文件和几个js文件就可以了,下面详细介绍
html主要是为了放置canvas元素,因为WebGL的渲染都是在canvas进行的。
下面是一个html部分:
wgl render ins
这个简单的不能再简单的html就已经够用了。
上面的html我们命名为main.html
。从上面的html中,可以发现我们需要一个main函数,这个函数一般来说在js中实现,这也是我们WebGL程序正式开始的入口函数,我们添加一个js文件sk_init.js
,并实现该函数,当然我们也需要在html中加入引用js的代码,为了保持代码的简洁,已经在上面的html中加入了该代码。
下面是sk_init.js的代码:
var gl;
function main()
{
var canvas = document.getElementById("wglCanvas");
gl = create3DContext(canvas);
if(!gl)
{
console.log("获取webgl渲染上下文失败");
return;
}
gl.clearColor(0.0,0.0,0.0,1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
}
var create3DContext = function(canvas) {
var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"];
var context = null;
for (var ii = 0; ii < names.length; ++ii) {
try {
context = canvas.getContext(names[ii]);
} catch(e) {}
if (context) {
break;
}
}
return context;
}
上面这个代码分为几部分:
虽然代码很简单,但是这几步是所有webgl程序都需要做的事情,当前main.html已经可以运行了。不过只会显示一个黑色背景。下面我们开始加入些东西
想编写一个真正可以使用的webgl程序,首先需要写shader,由于本篇文章我们着重介绍WebGL框架,所以我们只是简单说明一下,关于shader语法,会有专门文章介绍。
顶点着色器
和片元着色器
下面是两个简单的着色器代码,我们创建一个sk_shader.js
存放这部分代码:
顶点着色器:
var VERTEX_SHADER=`
attribute vec4 a_Position;
uniform float u_PointSize;
void main() {
gl_Position = a_Position;
gl_PointSize = u_PointSize;
}`;
片元着色器:
var FRAG_SHADER=`
void main() {
gl_FragColor = vec4(1.0,0.0,0.0,1.0);
}`;
创建好了着色器以后,下一步就要初始化着色器了
我们新建一个sk_util.js
文件,里面封装一些公共的方法,首先我们要添加的方法就是加载着色器代码的方法:
// gl: webgl上下文
// type: 着色器类型
// source: 着色器代码
function loadShader(gl, type, source) {
var shader = gl.createShader(type);
if (shader == null) {
console.log('unable to create shader');
return null;
}
gl.shaderSource(shader, source);
gl.compileShader(shader);
var compiled = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
if (!compiled) {
var error = gl.getShaderInfoLog(shader);
console.log('Failed to compile shader: ' + error);
gl.deleteShader(shader);
return null;
}
return shader;
}
加载一个着色器需要以下步骤:
gl.createShader
接口,参数为着色器类型,着色器类型一般有两类:
gl.shaderSource
接口,第一个参数为第一步创建的着色器对象,第二个参数为着色器的字符串表示gl.compileShader
接口,参数为第一步创建的着色器对象下面是创建着色器程序的代码:
function createProgram(gl, vshader, fshader) {
var vertexShader = loadShader(gl, gl.VERTEX_SHADER, vshader);
var fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fshader);
if (!vertexShader || !fragmentShader) {
return null;
}
var program = gl.createProgram();
if (!program) {
return null;
}
gl.attachShader(program, vertexShader);
gl.attachShader(program, fragmentShader);
gl.linkProgram(program);
var linked = gl.getProgramParameter(program, gl.LINK_STATUS);
if (!linked) {
var error = gl.getProgramInfoLog(program);
console.log('Failed to link program: ' + error);
gl.deleteProgram(program);
gl.deleteShader(fragmentShader);
gl.deleteShader(vertexShader);
return null;
}
return program;
}
创建着色器程序的步骤如下:
gl.createProgram
接口,返回着色器程序对象gl.attachShader
接口,第一个参数是第一步创建的着色器程序对象,第二个参数是关联的着色器对象gl.linkProgram
接口链接程序,参数是第一步创建的着色器程序对象gl.useProgram
接口使用我们上一步创建的着色器程序对象。代码如下:
function initShaders(gl, vshader, fshader) {
var program = createProgram(gl, vshader, fshader);
if (!program) {
console.log('Failed to create program');
return false;
}
gl.useProgram(program);
gl.program = program;
return true;
}
经过这三步以后,着色器程序已经初始化完成了,接下来就是向着色器传递数据
所谓的传递数据就是将内存的数据传递给显存
。
一般情况下shader中使用的数据有三种:
attribute
标记的数据,这个也叫做顶点数据,就是每个顶点都会有自己的数值uniform
标记的数据,这个也叫做统一数据,就是所有顶点共用的数据varying
标记的数据,这个是在着色器之间传递的数据gl.getAttribLocation
接口获取attribute数据的标记,第一个参数是着色器程序对象,第二个参数是数据在shader中的名称的字符串表示gl.getUniformLocation
接口获取uniform数据的标记,第一个参数是着色器程序对象,第二个参数是数据在shader中的名称的字符串表示下面我们获取前面着色器程序的a_Position和u_PointSize的标记:
var u_PointSize = gl.getUniformLocation(gl.program, "u_PointSize");
if (u_PointSize < 0) {
console.log('Failed to get the storage location of u_PointSize');
return -1;
}
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 -1;
}
gl.createBuffer()
创建缓冲区gl.bindBuffer
接口绑定缓冲区,第一个参数是缓冲区类型,这里我们使用gl.ARRAY_BUFFER,第二个参数是上一步创建的缓冲区gl.bufferData
接口传递数据,第一参数是缓冲区类型,这里我们使用gl.ARRAY_BUFFER,第二个参数是内存数据指针,第三个参数是数据的使用方式,这里我们使用gl.STATIC_DRAW。var verticesTexCoords = new Float32Array([
-0.5, 0.5, 0.0, 1.0,
-0.5, -0.5, 0.0, 1.0,
0.5, 0.5, 0.0, 1.0,
0.5, -0.5, 0.0, 1.0,
]);
var FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;
var vertexTexCoordBuffer = gl.createBuffer();
if (!vertexTexCoordBuffer) {
console.log('Failed to create the buffer object');
return -1;
}
gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexCoordBuffer);
gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW);
使用gl.uniform1f
接口传递单个float数值到显存,在当前程序中用来传递u_PointSize的值
代码如下:
gl.uniform1f(u_PointSize, 10);
gl.vertexAttribPointer
接口绑定缓冲区与attribute顶点属性gl.enableVertexAttribArray
接口开启顶点属性数组,如果不开启的话,默认会使用静态属性,就是所有顶点数据都使用一个默认的值
。gl.vertexAttribPointer(a_Position, 4, gl.FLOAT, false, FSIZE * 4, 0);
gl.enableVertexAttribArray(a_Position);
当前程序使用gl.drawArrays
来开始绘制操作
gl.drawArrays的第一个参数是绘制的类型,我们当前程序就使用gl.POINTS方式
gl.drawArrays的第二个参数是绘制顶点的偏移,当前为0
gl.drawArrays的第三个参数是绘制顶点的数量,当前为4
代码如下:
gl.clearColor(0.0,0.0,0.0,1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
gl.drawArrays(gl.POINTS, 0, 4);
完整的代码已经上传gitlab:
https://gitlab.com/lingyanTools/sk_webgl.git
该例子绘制了四个顶点