坐稳咯,webgl入门小贴士!

经过这一个月来欲仙欲死的摸索,总算在摸索出了一些入门webGl的门道。关于webGl的学习,我建议大家去入手一本《webGl编程指南》和《线性代数》,里面的内容非常详细,这里也不需要在多说了。还有,编程指南那本书的代码结构写得还是值得人吐槽的,所以,遇到问题也请多多善用搜索引擎,或者:https://stackoverflow.com/search?q=你的问题
本文不做关于webgl的任何教程内容,本文旨在分享一下我在摸索webgl中的一些姿势和一些坑,帮助一些初学者学习得更舒服一点。

第一,webGL!==web 3D

我不知道多少人最开始学习webGL是把它当web 3d方面去学习的,至少最开始我以为webGL就是用来绘制3D模型的。
Naive!
webGL是比canvas.getContext('2d')更加底层的图形绘制接口。而它的工作原理,实际上就是遍历每一个像素点,然后给各个像素点填充颜色,然后才构成一幅2d或者3d的图像。至于你想直接先搞3d方面的东西, 使用three.js比直接撸webGL舒服多了。
而且,如果你愿意,webGL更适合去做图像处理。

第二,shaders

webGL工作的基本单位是shaders,中文唤作着色器。
而我们亲爱的js在这个环境里能做的就只有跑跑腿传传值,并不能像ctx.stroke()那样亲自上阵。而着色器,完蛋了,根本就是一门新语言,叫glsl
我们的js,是跑在浏览器里的语言。而glsl,它是跑在显卡里面的,它需要手动使用js去调用WebGL编译它的方法,然后变成二进制包,然后让浏览器把它塞入显卡里,最后才能够使用。
所以webGL绘制比js去绘制的好处在于,webGL占用的是显卡里的资源,并不过多占用内存,性能比起canvas2D来,那是不知道高到哪里去了。
开始学习shaders语言的时候,建议跟着编程指南的例子去敲,不过这里有一个坑,是我学习的时候遇到的。我跟着书里的例子去敲,却发现书里的例子无论如何也无法通过编译,它会报一行这样的错:

error

经过谷歌、百度、stackoverflow等多方询问,最终的解决办法也非常简单,在你每一个着色器程序头部加上这样一行:
precision mediump float;
这句话的意思是,设定中等精度为float型。显卡程序里面有三种精度:

  • 高精度highp
  • 中等精度mediump
  • 低精度lowp

那这些精度是干嘛用的呢?当然是用来精确计算的。(隔壁连0.1+0.2都算不准的js酱躲在墙角默默哭泣)。比如说一个3d模型,它每个点的位置最好使用highp精度去计算,这样定位准确。而这个3d模型的贴图纹理,其实都是图片,对于图片像素位置的计算,使用中等精度的mediump就行了。最后的lowp,适合去计算像素的颜色值。
然后说了这么多,还只是科普一下而已,因为不同设备对这三种精度的支持不一致(前端人深有体会,万恶的兼容),对三种精度的默认设置也不一致,比如某些垃圾的设备就把mediump这个级别设定为int型整数,这个计算精度一下子就下降了。
所以在webGL里面要加上这句,统一设置mediump的默认值。这样,程序就可以通过编译了。

第三,shaders,着色器程序glsl的加载

就目前看到的大部分教程来看,加载shaders程序的方法无非以下几种:

  1. 写在html里面,在html里面插入一个<script type="text/plain">,然后把glsl写在里面。而js这边就需要写一个获取这个script的innerHTML的方法,读取到glsl的源码,再去编译。
    不过这样有个缺点,当你的代码编辑器,比如vscode,存在html代码格式优化这种功能的时候,会傻逼傻逼地将glsl源码压缩成一行。。。
  2. 直接使用字符串拼接,就是
var vShaderSource='precision mediump float;'+
'attribute vec4 a_Position;'+
...

就跟我们使用 jquery拼接html一样去拼接glsl的源码。不得不说,很烦。

  1. ajax加载,这个就可以舒舒服服把glsl的源码写在.glsl文件里,然后通过ajax加载进来。如果你的代码编辑器可以的话,甚至有.glsl文件的语法高亮,就像vscode安装了高亮插件之后:

语法高亮
比起写在javascript和html里面好看多了,舒服多了。喜欢语法高亮的个个都是人才,说话都好听,我超喜欢这样写。。。当然前提是你开启了本地服务器才能使用ajax读取。
不过呢,作为新时代的前端人,掌握了webpack工程化开发习惯的我们怎么能忍受上面几种类似jq时代的写法呢?
什么?配置babel然后使用es6的字符串模板写源码?
Naive!
webpack连css都能读进来,区区glsl!?这里我直接是使用了row-loader这个加载器去加载.glsl文件,然后既不用考虑ajax的异步同步问题,还能够保持.glsl文件语法高亮,通过一句var vGlsl=require('./xxx/xxx/xx.glsl')就能够将源码引入到js中,十分方便。

懒得配置webpack的同学,这里我给你写好了一个简单的webpack模板了:

webGL-Webpack-Template

直接去我的github里面clone一下就好了,里面还有一个我写的小demo:
https://github.com/Char-Ten/webGl-Webpack-Template

第四 纹理加载的一些小问题

如果你参照《webGL编程指南》的demo去写添加纹理,如果你是在网上随便找一张自己的图片的话,你可能会发现纹理渲染不出来,即便你的代码和例子一摸一样。它会报这样一行错:

image.png

这里的解决办法非常简单,最简单的解决办法,是先检查你使用的贴图尺寸。如果长和宽的大小都不是2的n次幂(即错误信息里面所说的non-power-of-2),那么请用PS等图像处理软件把它的长和宽分别处理为2的n次幂,如:1x1 2x2 4x4 8x8 16x16 32x32 64x64 128x128 256x256 512x512.....
一般来说这样就能够解决了,然后参考一下stackoverflow一位dalao给的代码,你可以这样写一个创建纹理的函数:

    /**创建纹理贴图
      * @param {WebGLRenderingContext} webgl - 使用webgl的上下文
      * @param {Canvas||Image} image - 要作为纹理的图片对象
      * @return {WebglTexture} texture对象
      */
    function createTexByImage(webgl, image) {
        var texture = webgl.createTexture();
        webgl.bindTexture(webgl.TEXTURE_2D, texture);
        webgl.texImage2D(webgl.TEXTURE_2D, 0, webgl.RGBA, webgl.RGBA, webgl.UNSIGNED_BYTE, image);
        if (isPowerOf2(image.width) && isPowerOf2(image.height)) {
            gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST);
            return texture
        }
        webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MIN_FILTER, webgl.NEAREST);
        webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_MAG_FILTER, webgl.NEAREST);
        webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_S, webgl.CLAMP_TO_EDGE);
        webgl.texParameteri(webgl.TEXTURE_2D, webgl.TEXTURE_WRAP_T, webgl.CLAMP_TO_EDGE);
        return texture
    }
    
    /**检查数字是否为2的指数
      * @param {Number} value - 要检查的值
      * @return {Boolean}
      */
    function isPowerOf2(value) {
        return !(value & (value - 1));
    }

当图片的尺寸不满足2的指数的时候,你要写满四个texParameteri方法。
这个方法是用来设定纹理贴图参数的,有四个值可以设定,分别是

  1. TEXTURE_MAG_FILTER 设定图片放大后像素点的取值方式
  2. TEXTURE_MIN_FILTER 设定图片缩小后像素点的取值方式
  3. TEXTURE_WRAP_S 设定图片横向平铺样式
  4. TEXTURE_WRAP_T 设定图片垂直平铺样式

默认贴图在webgl中是平铺的,只有设定为不平铺时(webgl.CLAMP_TO_EDGE),才能够渲染出来。

至于这个原因呢,很简单,对于尺寸不是2的指数的图片,GPU对其遍历是十分消耗性能的。所以,你想做一个平铺重复的纹理,就必须使用符合规则的图片。

第五,关于glsl语言的debug

glsl这门语言不像js那样有console打印或者浏览器断点调试那样方便去调试一个程序。换句话说,当js以buffer的形式丢一个值给glsl,你没办法在glsl里面打印这个值是否正确。
更何况glsl是静态类型语言,有时候忘记写类型声明,或者不同类型的值赋值的时候就会报错,甚至你后面少写了个分号都会报错,都会导致编译不通过。
对于上面两种情况,首先是打印这个问题,没办法,glsl不能打印的时候你只能去猜这个变量到底是个什么值,然后给每个像素的颜色RGB设定为这个值,然后观察绘制的结果,通过颜色去验证数值正不正确,只是我目前能够用到的debug方法。。。期待有更好的方法出现。
第二种情况,这个一方面依赖于自己对于glsl语言的学习,同时你也可以通过你的代码编辑器去检查是否有语法错误,或者,如果你在chrome调试的话,你可以去下载这些个chrome插件:

image.png

它们可以更好的帮助你检查程序错误已经其他问题。


这就是我目前学习的过程中踩到的一些坑或者解决的一些小问题吧,而且学了一个月都还只在2d绘制上搞来搞去,想往3d方向走,需要的数学知识要更多,这些都只是基础而已。
最后以一张作品图作为这篇文章的结尾吧(当然glsl的内容都是从网络上“移植”下来的。。。法线贴图生成算法移植自某位lua的dalao手笔),今后学习如果遇到新坑会继续写文章回填的。

GIF.gif
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容