坐穩(wěn)咯趾娃,webgl入門小貼士缭嫡!

經(jīng)過這一個月來欲仙欲死的摸索,總算在摸索出了一些入門webGl的門道抬闷。關(guān)于webGl的學(xué)習(xí)妇蛀,我建議大家去入手一本《webGl編程指南》和《線性代數(shù)》,里面的內(nèi)容非常詳細(xì)笤成,這里也不需要在多說了评架。還有,編程指南那本書的代碼結(jié)構(gòu)寫得還是值得人吐槽的炕泳,所以纵诞,遇到問題也請多多善用搜索引擎,或者:https://stackoverflow.com/search?q=你的問題 培遵。
本文不做關(guān)于webgl的任何教程內(nèi)容浙芙,本文旨在分享一下我在摸索webgl中的一些姿勢和一些坑,幫助一些初學(xué)者學(xué)習(xí)得更舒服一點籽腕。

第一嗡呼,webGL!==web 3D

我不知道多少人最開始學(xué)習(xí)webGL是把它當(dāng)web 3d方面去學(xué)習(xí)的,至少最開始我以為webGL就是用來繪制3D模型的皇耗。
Naive南窗!
webGL是比canvas.getContext('2d')更加底層的圖形繪制接口。而它的工作原理,實際上就是遍歷每一個像素點万伤,然后給各個像素點填充顏色窒悔,然后才構(gòu)成一幅2d或者3d的圖像。至于你想直接先搞3d方面的東西壕翩, 使用three.js比直接擼webGL舒服多了蛉迹。
而且,如果你愿意放妈,webGL更適合去做圖像處理北救。

第二,shaders

webGL工作的基本單位是shaders芜抒,中文喚作著色器珍策。
而我們親愛的js在這個環(huán)境里能做的就只有跑跑腿傳傳值,并不能像ctx.stroke()那樣親自上陣宅倒。而著色器攘宙,完蛋了,根本就是一門新語言拐迁,叫glsl蹭劈。
我們的js,是跑在瀏覽器里的語言线召。而glsl铺韧,它是跑在顯卡里面的,它需要手動使用js去調(diào)用WebGL編譯它的方法缓淹,然后變成二進(jìn)制包哈打,然后讓瀏覽器把它塞入顯卡里,最后才能夠使用讯壶。
所以webGL繪制比js去繪制的好處在于料仗,webGL占用的是顯卡里的資源,并不過多占用內(nèi)存伏蚊,性能比起canvas2D來立轧,那是不知道高到哪里去了。
開始學(xué)習(xí)shaders語言的時候躏吊,建議跟著編程指南的例子去敲肺孵,不過這里有一個坑,是我學(xué)習(xí)的時候遇到的颜阐。我跟著書里的例子去敲,卻發(fā)現(xiàn)書里的例子無論如何也無法通過編譯吓肋,它會報一行這樣的錯:

error

經(jīng)過谷歌凳怨、百度、stackoverflow等多方詢問,最終的解決辦法也非常簡單肤舞,在你每一個著色器程序頭部加上這樣一行:
precision mediump float;
這句話的意思是紫新,設(shè)定中等精度為float型。顯卡程序里面有三種精度:

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

那這些精度是干嘛用的呢李剖?當(dāng)然是用來精確計算的芒率。(隔壁連0.1+0.2都算不準(zhǔn)的js醬躲在墻角默默哭泣)。比如說一個3d模型篙顺,它每個點的位置最好使用highp精度去計算偶芍,這樣定位準(zhǔn)確。而這個3d模型的貼圖紋理德玫,其實都是圖片匪蟀,對于圖片像素位置的計算,使用中等精度的mediump就行了宰僧。最后的lowp材彪,適合去計算像素的顏色值。
然后說了這么多琴儿,還只是科普一下而已段化,因為不同設(shè)備對這三種精度的支持不一致(前端人深有體會,萬惡的兼容)造成,對三種精度的默認(rèn)設(shè)置也不一致显熏,比如某些垃圾的設(shè)備就把mediump這個級別設(shè)定為int型整數(shù),這個計算精度一下子就下降了谜疤。
所以在webGL里面要加上這句佃延,統(tǒng)一設(shè)置mediump的默認(rèn)值。這樣夷磕,程序就可以通過編譯了履肃。

第三,shaders坐桩,著色器程序glsl的加載

就目前看到的大部分教程來看尺棋,加載shaders程序的方法無非以下幾種:

  1. 寫在html里面,在html里面插入一個<script type="text/plain">绵跷,然后把glsl寫在里面膘螟。而js這邊就需要寫一個獲取這個script的innerHTML的方法,讀取到glsl的源碼碾局,再去編譯荆残。
    不過這樣有個缺點,當(dāng)你的代碼編輯器净当,比如vscode内斯,存在html代碼格式優(yōu)化這種功能的時候蕴潦,會傻逼傻逼地將glsl源碼壓縮成一行。俘闯。潭苞。
  2. 直接使用字符串拼接,就是
var vShaderSource='precision mediump float;'+
'attribute vec4 a_Position;'+
...

就跟我們使用 jquery拼接html一樣去拼接glsl的源碼真朗。不得不說此疹,很煩。

  1. ajax加載遮婶,這個就可以舒舒服服把glsl的源碼寫在.glsl文件里蝗碎,然后通過ajax加載進(jìn)來。如果你的代碼編輯器可以的話蹭睡,甚至有.glsl文件的語法高亮衍菱,就像vscode安裝了高亮插件之后:

語法高亮
比起寫在javascript和html里面好看多了,舒服多了肩豁。喜歡語法高亮的個個都是人才脊串,說話都好聽,我超喜歡這樣寫清钥。琼锋。。當(dāng)然前提是你開啟了本地服務(wù)器才能使用ajax讀取祟昭。
不過呢缕坎,作為新時代的前端人,掌握了webpack工程化開發(fā)習(xí)慣的我們怎么能忍受上面幾種類似jq時代的寫法呢篡悟?
什么谜叹?配置babel然后使用es6的字符串模板寫源碼?
Naive搬葬!
webpack連css都能讀進(jìn)來荷腊,區(qū)區(qū)glsl!急凰?這里我直接是使用了row-loader這個加載器去加載.glsl文件女仰,然后既不用考慮ajax的異步同步問題,還能夠保持.glsl文件語法高亮抡锈,通過一句var vGlsl=require('./xxx/xxx/xx.glsl')就能夠?qū)⒃创a引入到j(luò)s中疾忍,十分方便。

懶得配置webpack的同學(xué)床三,這里我給你寫好了一個簡單的webpack模板了:

webGL-Webpack-Template

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

第四 紋理加載的一些小問題

如果你參照《webGL編程指南》的demo去寫添加紋理,如果你是在網(wǎng)上隨便找一張自己的圖片的話撇簿,你可能會發(fā)現(xiàn)紋理渲染不出來擒抛,即便你的代碼和例子一摸一樣推汽。它會報這樣一行錯:

image.png

這里的解決辦法非常簡單,最簡單的解決辦法歧沪,是先檢查你使用的貼圖尺寸。如果長和寬的大小都不是2的n次冪(即錯誤信息里面所說的non-power-of-2)莲组,那么請用PS等圖像處理軟件把它的長和寬分別處理為2的n次冪诊胞,如:1x1 2x2 4x4 8x8 16x16 32x32 64x64 128x128 256x256 512x512.....
一般來說這樣就能夠解決了,然后參考一下stackoverflow一位dalao給的代碼锹杈,你可以這樣寫一個創(chuàng)建紋理的函數(shù):

    /**創(chuàng)建紋理貼圖
      * @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
    }
    
    /**檢查數(shù)字是否為2的指數(shù)
      * @param {Number} value - 要檢查的值
      * @return {Boolean}
      */
    function isPowerOf2(value) {
        return !(value & (value - 1));
    }

當(dāng)圖片的尺寸不滿足2的指數(shù)的時候撵孤,你要寫滿四個texParameteri方法。
這個方法是用來設(shè)定紋理貼圖參數(shù)的竭望,有四個值可以設(shè)定邪码,分別是

  1. TEXTURE_MAG_FILTER 設(shè)定圖片放大后像素點的取值方式
  2. TEXTURE_MIN_FILTER 設(shè)定圖片縮小后像素點的取值方式
  3. TEXTURE_WRAP_S 設(shè)定圖片橫向平鋪樣式
  4. TEXTURE_WRAP_T 設(shè)定圖片垂直平鋪樣式

默認(rèn)貼圖在webgl中是平鋪的,只有設(shè)定為不平鋪時(webgl.CLAMP_TO_EDGE)咬清,才能夠渲染出來闭专。

至于這個原因呢,很簡單旧烧,對于尺寸不是2的指數(shù)的圖片影钉,GPU對其遍歷是十分消耗性能的。所以掘剪,你想做一個平鋪重復(fù)的紋理平委,就必須使用符合規(guī)則的圖片。

第五夺谁,關(guān)于glsl語言的debug

glsl這門語言不像js那樣有console打印或者瀏覽器斷點調(diào)試那樣方便去調(diào)試一個程序廉赔。換句話說,當(dāng)js以buffer的形式丟一個值給glsl匾鸥,你沒辦法在glsl里面打印這個值是否正確蜡塌。
更何況glsl是靜態(tài)類型語言,有時候忘記寫類型聲明扫腺,或者不同類型的值賦值的時候就會報錯岗照,甚至你后面少寫了個分號都會報錯,都會導(dǎo)致編譯不通過笆环。
對于上面兩種情況攒至,首先是打印這個問題,沒辦法躁劣,glsl不能打印的時候你只能去猜這個變量到底是個什么值迫吐,然后給每個像素的顏色RGB設(shè)定為這個值,然后觀察繪制的結(jié)果账忘,通過顏色去驗證數(shù)值正不正確志膀,只是我目前能夠用到的debug方法熙宇。。溉浙。期待有更好的方法出現(xiàn)烫止。
第二種情況,這個一方面依賴于自己對于glsl語言的學(xué)習(xí)戳稽,同時你也可以通過你的代碼編輯器去檢查是否有語法錯誤馆蠕,或者,如果你在chrome調(diào)試的話惊奇,你可以去下載這些個chrome插件:

image.png

它們可以更好的幫助你檢查程序錯誤已經(jīng)其他問題互躬。


這就是我目前學(xué)習(xí)的過程中踩到的一些坑或者解決的一些小問題吧,而且學(xué)了一個月都還只在2d繪制上搞來搞去颂郎,想往3d方向走吼渡,需要的數(shù)學(xué)知識要更多,這些都只是基礎(chǔ)而已乓序。
最后以一張作品圖作為這篇文章的結(jié)尾吧(當(dāng)然glsl的內(nèi)容都是從網(wǎng)絡(luò)上“移植”下來的寺酪。。竭缝。法線貼圖生成算法移植自某位lua的dalao手筆)房维,今后學(xué)習(xí)如果遇到新坑會繼續(xù)寫文章回填的。

GIF.gif
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末抬纸,一起剝皮案震驚了整個濱河市咙俩,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌湿故,老刑警劉巖阿趁,帶你破解...
    沈念sama閱讀 216,324評論 6 498
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異坛猪,居然都是意外死亡脖阵,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,356評論 3 392
  • 文/潘曉璐 我一進(jìn)店門墅茉,熙熙樓的掌柜王于貴愁眉苦臉地迎上來命黔,“玉大人,你說我怎么就攤上這事就斤『纺迹” “怎么了?”我有些...
    開封第一講書人閱讀 162,328評論 0 353
  • 文/不壞的土叔 我叫張陵洋机,是天一觀的道長坠宴。 經(jīng)常有香客問我,道長绷旗,這世上最難降的妖魔是什么喜鼓? 我笑而不...
    開封第一講書人閱讀 58,147評論 1 292
  • 正文 為了忘掉前任副砍,我火速辦了婚禮,結(jié)果婚禮上庄岖,老公的妹妹穿的比我還像新娘豁翎。我一直安慰自己,他們只是感情好顿锰,可當(dāng)我...
    茶點故事閱讀 67,160評論 6 388
  • 文/花漫 我一把揭開白布谨垃。 她就那樣靜靜地躺著,像睡著了一般硼控。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上胳赌,一...
    開封第一講書人閱讀 51,115評論 1 296
  • 那天牢撼,我揣著相機與錄音,去河邊找鬼疑苫。 笑死熏版,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的捍掺。 我是一名探鬼主播撼短,決...
    沈念sama閱讀 40,025評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼挺勿!你這毒婦竟也來了曲横?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,867評論 0 274
  • 序言:老撾萬榮一對情侶失蹤不瓶,失蹤者是張志新(化名)和其女友劉穎禾嫉,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體蚊丐,經(jīng)...
    沈念sama閱讀 45,307評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡熙参,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,528評論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了麦备。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片孽椰。...
    茶點故事閱讀 39,688評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖凛篙,靈堂內(nèi)的尸體忽然破棺而出黍匾,到底是詐尸還是另有隱情,我是刑警寧澤鞋诗,帶...
    沈念sama閱讀 35,409評論 5 343
  • 正文 年R本政府宣布膀捷,位于F島的核電站,受9級特大地震影響削彬,放射性物質(zhì)發(fā)生泄漏全庸。R本人自食惡果不足惜秀仲,卻給世界環(huán)境...
    茶點故事閱讀 41,001評論 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望壶笼。 院中可真熱鬧神僵,春花似錦、人聲如沸覆劈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,657評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽责语。三九已至炮障,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間坤候,已是汗流浹背胁赢。 一陣腳步聲響...
    開封第一講書人閱讀 32,811評論 1 268
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留白筹,地道東北人智末。 一個月前我還...
    沈念sama閱讀 47,685評論 2 368
  • 正文 我出身青樓,卻偏偏與公主長得像徒河,于是被迫代替她去往敵國和親系馆。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,573評論 2 353

推薦閱讀更多精彩內(nèi)容