此篇文章的主要目的是巩固本身对于建立webgl的时候一些知识点,主要参考了《webgl编程指南》以及_Hahn_的webgl环境搭建这篇文章,在此附上连接,方便你们查看( juejin.im/post/5baaf3… )。javascript
我会分为三篇文章进行总结,为何不在一篇文章中写完呢?
由于我我的不太喜欢技术的文章写的很长,那样在读的时候感受很烦。html
《webgl编程指南》这本书中知识点介绍的很详细,所以我就不在此赘述那些基础的知识点了,直接上代码。java
最终咱们要实现的效果:git
相应的代码web
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<script src="./lib/webgl-debug.js"></script>
<script src="./lib/webgl-utils.js"></script>
<script src="./lib/cuon-matrix.js"></script>
<script src="./lib/cuon-utils.js"></script>
<style>*{padding:0;margin:0;}</style>
</head>
<body onload="main()">
<canvas id="canvas" width="750" height="1334"></canvas>
<script type="text/javascript">
var VSHADER_SOURCE = `
attribute vec4 a_Position;
varying vec2 uv;
void main(){
gl_Position = vec4(vec2(a_Position), 0.0, 1.0);
uv = vec2(0.5, 0.5) * (vec2(a_Position) + vec2(1.0, 1.0));
}
`
var FSHADER_SOURCE = `
precision mediump float;
varying vec2 uv;
void main(){
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`
function main(){
var canvas = document.getElementById('canvas');
canvas.width = 750;
canvas.height = 1334;
var gl = getWebGLContext(canvas);
if(!gl){
console.log("Failed to get the tendering context for WevGl");
return;
}
if(!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)){
console.log("Failed to set the vertex information");
return;
}
//清空画布
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
var n = initVertexBuffers(gl);
//绘制顶点
gl.drawArrays(gl.TRIANGLE_FAN, 0, n);
}
function initVertexBuffers(gl){
var verticesTexCoords = new Float32Array([
1.0, -1.0,
1.0, 1.0,
-1.0, 1.0,
-1.0, -1.0
]);
var n = 4;
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);
var FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if(a_Position < 0){
console.log("Failed to get the stroage location og a_Position");
}
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 2, 0);
gl.enableVertexAttribArray(a_Position);
return n;
}
</script>
</body>
</html>
复制代码
首先咱们能够看到在开始咱们引用了几个js的文件,这几个文件是《webgl编程指南》中的一些辅助函数的文件,暂时咱们只是应用,不深刻的探究。编程
<script src="./lib/webgl-debug.js"></script>
<script src="./lib/webgl-utils.js"></script>
<script src="./lib/cuon-matrix.js"></script>
<script src="./lib/cuon-utils.js"></script>
复制代码
咱们在DOM中能够看到一个canvas的结构,这个DOM结构就是告诉浏览器咱们要一个画布。之后绘制的webgl都会出如今这个canvas中。
接下来就是js这个重点了,咱们在js中首先定义了一个顶点着色器。canvas
var VSHADER_SOURCE = `
attribute vec4 a_Position;
varying vec2 uv;
void main(){
gl_Position = vec4(vec2(a_Position), 0.0, 1.0);
uv = vec2(0.5, 0.5) * (vec2(a_Position) + vec2(1.0, 1.0));
}
`
复制代码
顶点着色器就是用来描述顶点的特性(例如位置、颜色等)的程序。顶点是指二维或三维空间中的一个点,好比二维或者三维图形的端点或焦点。数组
在上面咱们首先定义了一个attribute vec4 a_Position;这个a_Position变量的做用就是js向顶点着色器传入顶点位置的入口,通俗来讲就是告诉浏览器咱们要在哪几个点的范围内进行“做画”,由于咱们如今做画都是在二维平面上面进行的所以呢
gl_Position = vec4(vec2(a_Position), 0.0, 1.0);
这个赋值操做的第三个参数就是0.0,第四个参数咱们就设置为默认的1.0。浏览器
而咱们定义的uv变量就是纹理的坐标,这个uv变量通常会传入片元着色器中进行操做,你可将其理解为咱们“做画”的坐标。
uv = vec2(0.5, 0.5) * (vec2(a_Position) + vec2(1.0, 1.0));
可是这个计算是什么鬼呢?
首先咱们在initVertexBuffers函数中能够看到bash
var verticesTexCoords = new Float32Array([
1.0, -1.0,
1.0, 1.0,
-1.0, 1.0,
-1.0, -1.0
]);
复制代码
这四个坐标就是咱们想要画的图像的坐标,也就是纹理坐标了。
上面红色框内就是咱们的纹理坐标。
上面的蓝色框内就是咱们着色器中的坐标了。
咱们将红色坐标系的点带入那个计算公式(注意是逐份量计算)
A => vec2(0.5, 0.5) * (vec2(1.0, -1.0) + vec2(1.0, 1.0)) => (1.0, 0.0)
B => vec2(0.5, 0.5) * (vec2(1.0, 1.0) + vec2(1.0, 1.0)) => (1.0, 1.0)
C => vec2(0.5, 0.5) * (vec2(-1.0, -1.0) + vec2(1.0, 1.0)) => (0.0, 1.0)
D => vec2(0.5, 0.5) * (vec2(-1.0, -1.0) + vec2(1.0, 1.0)) => (0.0, 0.0)
最后的结果是红色坐标系中的点变成了蓝色坐标系中对应的点,这就是那个计算公式的做用。
OK到这里顶点着色器就研究完成了,接下来就是片元着色器了
片元着色器就是进行逐片元处理过程的程序,片元是一个webgl术语,你能够将其理解为像素。
var FSHADER_SOURCE = `
precision mediump float;
varying vec2 uv;
void main(){
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
}
`
复制代码
在片元着色器中咱们首先定义了precision mediump float;
这句话的做用就是一个精度的规定,不加的话,片元着色器这里就会报错。
接着咱们使用了和在顶点着色器中相同的varying vec2 uv;接收了来自顶点着色器中的纹理坐标信息(虽然在上面的代码中咱们没有使用纹理坐标,可是你须要知道如何传入)。
重点是在main函数中咱们给gl_FragColor赋值一个vec4类型的变量,这个赋值的做用就是告诉gl_FragColor使用什么样的颜色来进行渲染,vec4的四个值分别对应rgba。
咱们写好了顶点着色器和片元着色器之后浏览器并不能理解那是什么,由于对于浏览器来讲那就是一些字符串,所以咱们须要使用js进行一些列的操做。
首先第一步,先获取DOM中的canvas,而后设置它的宽和高。
var gl = getWebGLContext(canvas);
if(!gl){
console.log("Failed to get the tendering context for WevGl");
return;
}
复制代码
第二步,咱们使用上面的代码将咱们获取的canvas变为了gl的画布(通俗意义上你能够这么理解),而且咱们作了一个差错处理
if(!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)){
console.log("Failed to set the vertex information");
return;
}
复制代码
第三步,初始化shaders,这函数就是咱们引用lib文件夹下面的js文件中存在的,在此你暂时使用就能够,此函数须要将咱们的“画布”也就是gl传入,同时须要传入顶点着色器变量和片元着色器变量。
//清空画布
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);
var n = initVertexBuffers(gl);
//绘制顶点
gl.drawArrays(gl.TRIANGLE_FAN, 0, n);
复制代码
第四步,清空画布开始做画,咱们使用gl的函数先将画布清空,而后使用咱们本身写的initVertexBuffers函数初始化缓冲区(这个函数稍后会介绍),而且此函数会返回一个顶点的个数,最后咱们使用gl的drawArrays函数开始绘制。
缓冲区对象是webgl系统中的一块存储区,你能够在缓冲区对象中保存想要绘制的全部顶点的数据。
在上面咱们就说过传入纹理坐标的事情。
var verticesTexCoords = new Float32Array([
1.0, -1.0,
1.0, 1.0,
-1.0, 1.0,
-1.0, -1.0
]);
var n = 4;
复制代码
在函数开始咱们首先定义了一个特殊的数组,这个数组的做用就是定义纹理的顶点坐标,而且咱们定义了n变量,这个变量表示咱们想要建立的顶点个数。
接下来咱们须要建立出缓冲区,而且使用。
var vertexTexCoordBuffer = gl.createBuffer();
if(!vertexTexCoordBuffer){
console.log("Failed to create the buffer object");
return -1;
}
复制代码
在上面的代码中咱们使用传入函数的gl的createBuffer方法将建立的缓冲区赋值给了vertexTexCoordBuffer变量,而且作了一个差错判断。
gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexCoordBuffer);
复制代码
在此咱们使用gl的bindBuffer函数将咱们建立的缓冲区进行了绑定,bindBuffer的第一个参数表示缓冲区对象中包含了顶点的数据,第二个参数就是咱们以前建立的缓冲区对象。
gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW);
复制代码
bufferData函数的做用就是将咱们在上面定义的数组verticesTexCoords写入缓冲区对象,此函数的第一个参数须要是gl.ARRAY_BUFFER或gl.ELEMENT_ARRAY_BUFFER,第一种参数和bindBufferbuffer表示的同样都是顶点的数据,第二种参数表示顶点数据的索引值,第二个参数就是咱们纹理的顶点数组了,第三个参数表示程序将如何使用存储在缓冲区对象中的数据,STATIC_DRAW表示只会向缓冲区对象中写入一次数据,但须要绘制不少次。
var FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;
var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if(a_Position < 0){
console.log("Failed to get the stroage location og a_Position");
}
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 2, 0);
复制代码
在上面的代码中咱们首先定义了一下FSIZE变量,这个变量只是为了咱们在赋值的时候方便操做,接着咱们定义了a_Position变量,而且使用了gl的方法获取到了咱们在顶点着色器中定义的attribute类型的a_Position变量,最后咱们使用了vertexAttribPointer方法为这个attribute变量进行赋值。
vertexAttribPointer函数的第一个参数就是咱们待分配的attribute变量的存储位置;
第二个参数是指定缓冲区每一个顶点的份量个数(此处咱们使用的是2);
第三个参数是指定数据的格式。
第四个参数传入true或false,代表是否将非浮点型的数据归一化。
第五个参数指定相邻两个顶点间的字节数(由于咱们在数组中定义的是每两个数值表示一个顶点,因此此处传入2)。
最后一个参数表示缓冲区对象中的偏移量。
gl.enableVertexAttribArray(a_Position);
复制代码
在最后咱们须要开启咱们要使用的attribute变量,这样一个完整的buffer就建立完成了。
通过上面的操做,咱们就获得了一个长方形了。
以上的知识都是比较基础,但确实是很重要的东西。
代码地址:gitee.com/wangtao_it_… 我本次的事例是此地址下面的basics.html文件。