1. 程式人生 > >WebGl學習入門心得

WebGl學習入門心得

寫在前面

想寫這篇文章想好久了,這半年在學校的課程中,自己最認真學習的就是圖形學這門課程的內容,當然主要是因為自己想要研究一下WebGl,最終課程部分也取得了5.0的滿績, 也算是對自己一個小小的認可。

WebGl和OpenGL

關於WebGl的學習,雖然有Web也有Gl,但是說實話,首先要懂OpenGl(3.0+),會OpenGl再學WebGl,就會感覺比較順利,而會Web打算學習WebGl,感覺還是需要學很多東西。

最後我們做的是一個基於WebSocket的多人協作線上密室逃脫,關於遊戲的玩家視訊,可以看這個連結。還是有很多不完善的地方,比較適合新手入門。

學習WebGl的前期過程是比較苦惱的,這裡大家可以直接學習WebGl,也可以先學習OpenGL,後者比較適合c++用的熟練的同學,否則直接上手WebGL也沒什麼問題。

關於c++的OpenGL,現在學校大多數還是教1.0+,用glut,實際上1.0+的很多概念已經是非常落後了,glut也已經是十多年沒有更新的一個庫(雖然倒是沒有bug),並且1.0如果想畫點東西的話相對十分簡單,我建議如果不是必須要用1.0,大家都應該擁抱3.0+,關於學習資料,我建議大家可以看看這個頁面,是我收藏的一些OpenGL的比較好的教程,慢慢按照教程走一遍,你就大概能摸清點門路了。另外本部落格也寫過一些配置方面的文章,大家也可以翻翻看。

WebGL入門

學習WebGl的話,我認為如果想要做出像樣的小東西,應該有一定的html\css\js功底,至少bootstrap是要會用的, css3應該也是知道一些的。

WebGl在js中的寫法幾乎和c++中的思想無差,主要就是1.建立/解析模型檔案並且匯入,2.編寫可程式設計管線。前期的主要難題在於WebGl的函式實在是太多,根本不能理解,後期的難點主要在於演算法,光照演算法繪製演算法等。

這裡我推薦大家《WebGl程式設計指南》這本書,這本書講的是原生WebGl,而其他有的書以three.js為主,我認為,如果是本著學習目的,不建議直接上手three.js等第三方庫,還是先了解清楚WebGl本身再說。

這裡還有一點值得說的是,我認為WebGL有3個大的作用域(可以這麼理解…)一個是JS的作用域,一個是頂點著色器的作用域,一個是片元著色器的作用域,JS作用域就是我們寫JS程式碼的地方,可以定義全域性變數全域性可見,然而,這個全域性變數另外兩個著色器的作用域是不可見的。一個頂點著色器的程式碼大概像這樣(這裡我建議大家寫在特定標籤中然後再寫個函式讀取裡面的內容,程式設計指南那本書把所有的頂點著色器程式碼和片元著色器程式碼都寫在一個字串裡了,這實在是一個難以維護的方式):

<script id="shader-fs" type="x-shader/x-fragment">
        #ifdef GL_FRAGMENT_PRECISION_HIGH
            precision highp float;
            precision highp int;
        #else
            precision mediump float;
            precision mediump int;
        #endif
        uniform samplerCube s_texture;
        varying vec3 v_texCoord;
        uniform int uiShadowMode;
        uniform float u_If_Fog;
        varying float If_Fog;

        void main(void)
        {
              If_Fog = u_If_Fog;
            if(uiShadowMode == 0) gl_FragColor = textureCube(s_texture, v_texCoord);
            else if(uiShadowMode == 1) gl_FragColor = vec4(0.7, 0.7, 0.7, 1.0);
            else if(uiShadowMode == 2) gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0);
            else if(uiShadowMode == 3) gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
        }
</script>

當然,上面給出的是一個比較簡單的,片元著色器也是寫在帶有特殊type的script標籤中類似這樣:

<script id="shader-fs" type="x-shader/x-fragment">
    #ifdef GL_ES
    precision mediump float;
    #endif
    varying vec3 v_TextCord;
    varying vec4 v_Color;
    varying float v_Clicked;
    varying vec4 v_tempColor;
    uniform sampler2D u_Sampler;
    uniform vec3 u_FogColor;
    uniform vec2 u_FogDist;
    varying float If_Fog;
    varying float v_Dist;
    void main() {
        vec4 temp_gl_FragColor;
        if(v_TextCord.z==1.0){
             temp_gl_FragColor = texture2D(u_Sampler, v_TextCord.xy) * 0.7 + v_Color * 0.3;
        }
        else{
             temp_gl_FragColor = v_Color;
        }
        if(If_Fog==1.0){
             float fogFactor = clamp((u_FogDist.y - v_Dist) / (u_FogDist.y - u_FogDist.x), 0.0, 1.0);
             vec3 color = mix(u_FogColor, vec3(temp_gl_FragColor), fogFactor);
             //temp_gl_FragColor = vec4(u_FogColor, v_Color.a);
             temp_gl_FragColor=vec4(vec3(temp_gl_FragColor*fogFactor),1);
        }
        if(v_Clicked == 1.0){
             temp_gl_FragColor = v_tempColor;
        }
        gl_FragColor=temp_gl_FragColor;
    }
</script>

我們如果想共享變數,只能通過繫結的方式比如uniform:

//獲取頂點著色器中的uniform:
program.u_If_Fog = gl.getUniformLocation(program, 'u_If_Fog');
//js中的變數:
var If_Fog = 1.0;
//繫結uniform:
gl.uniform1f(gl.program.u_If_Fog,If_Fog);

這一點在臨近期末的時候我們小組有的同學還不知道,所以我在這裡單獨先講講。

至於其他的,前期一定要花時間瞭解WebGl中提供的常用的函式,並且搞懂整個流程,想要畫個三角形估計加起來都要100多行程式碼,這裡面的流程大概是這樣的:

  • 初始化一個WebGl上下文->編譯頂點著色器,片元著色器->賦值三角形要用的頂點陣列->繫結頂點陣列到著色器變數->繪製函式。

以上的各個步驟,都有相應的函式可供呼叫,如果把這個過程瞭解了,也是不難的。

另外,我們在後期一般是通過匯入obj的方式來繪製場景,程式設計指南這本書裡面只是簡單帶過這一部分並且給了一個並不是很好用的demo,但我認為這一部分是重點,所以在原書的啟發下,我寫了一個obj載入器:這個obj載入器因為經過很多次測試,自認為健壯性是很高的,但是因為目前沒有人關注,我的文件也沒有太多動力可以寫,如果你認為這個對你可能會有幫助,請直接在GitHub上提issue或者給我郵件,從而讓我有動力把相關內容補充全面。

這篇文章大概總結了自己學WebGl的心得和資料,當然,後期我們又在演算法上有所嘗試,相關內容在我們的報告中也有提到,歡迎和我交流相關內容。