内容简介:此篇文章的主要目的是巩固自己对于创建webgl的时候一些知识点,主要参考了《webgl编程指南》以及_Hahn_的webgl环境搭建这篇文章,在此附上链接,方便大家查看(我会分为三篇文章进行总结,为什么不在一篇文章中写完呢?因为我个人不太喜欢技术的文章写的很长,那样在读的时候感觉很烦。
此篇文章的主要目的是巩固自己对于创建webgl的时候一些知识点,主要参考了《webgl编程指南》以及_Hahn_的webgl环境搭建这篇文章,在此附上链接,方便大家查看( juejin.im/post/5baaf3… )。
我会分为三篇文章进行总结,为什么不在一篇文章中写完呢?
因为我个人不太喜欢技术的文章写的很长,那样在读的时候感觉很烦。
《webgl编程指南》这本书中知识点介绍的很详细,因此我就不在此赘述那些基础的知识点了,直接上代码。
实现一个红色的长方形
最终我们要实现的效果:
相应的代码
<!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中首先定义了一个顶点着色器。
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函数中可以看到
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进行初始化操作
我们写好了顶点着色器和片元着色器以后浏览器并不能理解那是什么,因为对于浏览器来说那就是一些字符串,因此我们需要使用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函数开始绘制。
最后就是详解我们自己写的initVertexBuffers函数了,我们需要知道缓冲区是什么?
缓冲区对象是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表示只会向缓冲区对象中写入一次数据,但需要绘制很多次。
将缓冲区对象分配给一个attribute变量
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)。
最后一个参数表示缓冲区对象中的偏移量。
开启attribute变量
gl.enableVertexAttribArray(a_Position); 复制代码
在最后我们需要开启我们要使用的attribute变量,这样一个完整的buffer就创建完成了。
经过上面的操作,我们就得到了一个长方形了。
以上的知识都是比较基础,但确实是很重要的东西。
代码地址: gitee.com/wangtao_it_…
我本次的事例是此地址下面的basics.html文件。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 小代码导致整个项目失败!合约安全问题该如何重视?
- 重视失败是让公司成长的几条规则
- 思科更路由及交换CCNA认证:更加重视软件定义网络
- 思科更路由及交换CCNA认证:更加重视软件定义网络
- 人民日报谈“刷脸”技术:须充分重视并保障信息安全
- 调查报告:容器和Kubernetes的安全没有得到足够重视
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。