WebGL 入门必看(二)

说明

本文转载自:淘系前端团队:一起来玩玩WebGL–第二弹

NO.1 书接上文

上一弹中主要介绍了一下什么是WebGL,和大家一起理解了这货到底是个啥东西,不知道大家还记得多少,毕竟这一更也太久了,忘记了的话可以回去快速回顾一下哦,其实嘛,内容不多,就是图形编程的简单过程,最重要的还是,WebGL可以为HTML5的Canvas提供硬件加速,也就是说在浏览器用JS调用GL的API进行渲染咯,哇塞(kao),JS真的是啥都可以干啊!然后让大家感受了OpenGLES(WebGL是基于它的嘛)的渲染管线以及着色语言是怎么编写的,只不过还没有去实践写写例子罢了;今天这一弹我就来分享一下我的入门例子咯!

话再多说一句,我也是初学者,是前端初学者,更是WebGL的初学者,不敢说这几篇文章是在教大家什么,这只是我的学习记录,因为刚转前端,为了培养兴趣,找点东西玩玩,恰好就碰上了这玩意,那就借助下班业余时间从零开始学学,然后总结分享出来与大家交流学习罢了,当中不免会有不少理解错误的地方,大家可以评论指出一起交流学习心得啊。有同学问过我是怎么学习的,其实很简单,无非就是网上去搜索各种资料查阅、买书阅读,使劲去啃,然后理解,总结,找找例子代码练习,时间一长,就学会了。这过程经历的内容呢远比这几篇文章表达的内容量大得多,文章只是我的一个学习过程思路,未必适合每一个人,就好比我也看了好多别人的文章以及书籍,他们的思路也不一定适合我,只不过是这过程中我一直在消化他们的内容,最后总结转化为自己的理解。

NO.2 什么是Canvas

还记得不,第一次百科了解WebGL的时候,Get到的三个点就是:JS、Canvas、OpenGLES,那好,JS我们都会啦,现在就来了解一下Canvas;各位看客都是前端大佬,Canvas肯定都会,就我这种刚入坑前端的可能还是需要学习一下的哦。这里我也不想浪费各位大佬的时间,我自个去w3school学习就好了,其实我是Android出身的嘛,也用过Canvas,就是一个画布嘛,拿到画笔Paint,然后调用API在上面画东西嘛,点、线、圆、圆弧、矩形、Bitmap(图)等等啦。在HTML5也差不多的,通过 组件获取到context以后就调用各种各样的API来绘制元素。回想一下HTML的历史,以前想要在浏览器上显示图像,也就只能使用 标签了,当然还有视频、flash这些方案嘛,不过这还都局限了伟大的艺术创造家(程序员- -!)的发挥啊,直到了Canvas的出现,就如马良有了神笔啊!

WebGL 入门必看(二)

想象一下,如果我们程序性能是瓶颈了,你觉得高级语言的编译器可能实现不好,没有最大化发挥你的CPU能力,于是你去了解了你的CPU所有特性,然后自己写了汇编调用你的CPU的指令集,利用各种高速缓存和寄存器来实现你的功能,果真性能就提升了!这是不是针对CPU的硬件加速了啊/p>

硬件加速无非就是往底层去了解了硬件的特性来编程,实际上还是程序员在干活,只不过是大家的领域不同了,之前也说过OpenGL和DirectX就是在中间层针对图形这块帮我们做了很多事情,而GPU和CPU不同的特性就是它有非常多的核,几百上千都有,因此可以并发去运行,这就是他加速的关键核心啊。

WebGL 入门必看(二)

大家用canvas的api三两下就实现了,那么如果用WebGL是如何做到的呢下面步骤一步一步来试试看。

编写HTML代码

既然是浏览器运行的代码,当然就是先来写html文件来,新建一个hellworld.html文件,编写以下代码:

可以看到代码其实好简单啊,就是一个简单的HTML文件使用了 标签(如果浏览器不支持canvas的话就会显示提示文案了),引入了两个js文件,并且在body的onload时候触发调用一下main()函数就可以了。简单说一下这两个js文件,Helloworld.js是js的代码逻辑,而Helloworld-Shaders.js则是包装了一下着色器的代码,里面其实就是字符串;下面都会详细讲到的。

编写着色器代码

接下来就是WebGL编程的关键所在了,以后大部分的编程内容都是写在这里的,新建一个Helloworld-Shaders.js文件,然后编写以下内容:

可以看到,这其实就是js包了两个字符串,当然也是可以纯文本文件,然后XMLHTTPRequest来读取文件,不过这是题外话了,而且意义不是很大。这两个着色器的代码非常简单,和第一弹让大家感受的不太一样,缺少了不少东西,确实少了很多,但是这不会影响啥,反而没有那些东西更好理解主体流程。

顶点着色器

先看顶点着色器的代码,就像c语言一样,有一个main()函数,没有使用到第一弹看到的其他传入的变量,仅使用了两个gl_xxx内置变量,其中gl_Position就是该顶点的坐标,它必须设置,如果不设置的话是不会有任何东西显示出来的,它的类型是vec4,是不是很奇怪为什么不是用三维的坐标vec3实第四维的值是1.0,这是数学上的齐次坐标,(x, y, z, w)就等价于(x/w, y/w, z/w),可以回去温故知新一下高数咯,为啥这里要用齐次坐标呢,主要是未来后续的计算方便,后面的各种变换都会用到矩阵运算,到时候就会感受到了。

gl_PointSize就是该顶点的大小,就是多少个像素,它不是必须的,如果不设置的话,就是默认1.0。注意到的是它们的类型都是float类型的,如果类型写错是不行的,着色语言是强类型语言。

从第一弹我们理解到,我们需要绘制的图形的每一个顶点都会经过顶点着色器进行处理转换,最终产生纹理坐标,而这里我们并没有需要接收图形的顶点进行转换,而仅仅是指定了一个中心的坐标点进行绘制。

片元着色器

再看片元着色器,核心的就是给gl_FragColor赋值,它也是一个内置变量,也是唯一的输出变量,从第一弹了解到,光栅化后的每个片元都会执行一次片元着色器,可以理解为每个像素都执行一次,而这里的例子也就是绘制一个像素。很明显,gl_FragColor就是一个四维向量,之前我们了解到一个像素就是RGBA四个字节,正好需要四个向量来表示,不一样的是,一个字节的取值范围是0-255,为啥这里的值却不是呢是因为GL里面把这些取值范围通通都做了归一化的处理,学过数学的都知道这是啥,小学就知道什么是“单位一”了,大学过了也该知道归一化,这样处理过后,一切的处理都会简单起来了,这也会带来一个精度的问题,一般就是float精度即可。后续学习关于纹理尺寸、坐标系等等,都会涉及到归一化的呢。

各位可以去修改一下上面的值看看啥效果变化。

编写JS代码

主要的流程代码逻辑是在Helloworld.js文件里面,我们新建一个js文件,编写以下代码:

//通过canvas获取gl context,可以传入额外参数//兼容几种浏览器的获取方式function getWebGLContext(canvas, opt_attribs) {  var names = ["webgl", "experimental-webgl", "webkit-3d", "moz-webgl"];  var context = null;  for (var i = 0; i  names.length; ++i) {    try { context = canvas.getContext(names[i], opt_attribs);    } catch(e) {}    if (context) { break;    }  }  return context;}//初始化着色器,传入GL contest、顶点着色器代码、片元着色器代码function initShaders(gl, vshader, fshader) {//创建着色程序,实际上返回的int值,相当于底层的一个句柄引用var program = createProgram(gl, vshader, fshader); if (!program) {console.log('Failed to create program');return false; } //指定这个gl context使用这个着色程序 gl.useProgram(program); gl.program = program; return true;}//创建着色程序function createProgram(gl, vshader, fshader) {//分别编译加载顶点着色器和片元着色器代码,实际上返回的也是int类型 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); //链接程序,是不是和c语言的编译很像/span> gl.linkProgram(program); //获取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;}//加载编译着色器代码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;}//主程序入口function main() {    //获取标签    var canvas = document.getElementById('webgl');    if(!canvas) {console.log('Failed to retrieve the  element');return;    }    //获取web gl context    var gl = getWebGLContext(canvas);    if(!gl) {console.log('Failed to get the rendering context for WebGL');return;    }    //初始化着色器  if (!initShaders(gl来源:凯小默
                                                        

声明:本站部分文章及图片转载于互联网,内容版权归原作者所有,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!

上一篇 2021年3月20日
下一篇 2021年3月20日

相关推荐