WebGL漫游之旅(一)

原文鏈接:WebGL漫游之旅(一)

一荆针、WebGL基本概念

WebGL (Web Graphics Library) is a JavaScript API for rendering interactive 3D and 2D graphics within any compatible web browser without the use of plug-ins. WebGL does so by introducing an API that closely conforms to OpenGL ES 2.0 that can be used in HTML5 canvas elements. --MDN

以上是MDN對于WebGL的描述寒屯,簡單來說干发,WebGL 就是一組基于 JavaScript 語言的圖形規(guī)范唯卖,瀏覽器廠商按照這組規(guī)范進(jìn)行實(shí)現(xiàn),為 Web 開發(fā)者提供一套3D圖形相關(guān)的 API。

我們可以通過這些API直接使用JavaScript直接和GPU進(jìn)行通信,從而實(shí)現(xiàn)一些非常炫酷的圖形虑润。而webGL是在GPU上運(yùn)行的,因此我們需要使用能夠在GPU上運(yùn)行的代碼加酵,首先我們需要一種叫做GLSL的語言拳喻,它是一種和C or CPP類似的強(qiáng)類型的語言,所以寫起來很麻煩(這也是很多人吐槽WebGL的一個(gè)方面)猪腕,其次這樣的代碼需要提供成對的方法冗澈,每對方法中,一個(gè)叫做頂點(diǎn)著色器陋葡,一個(gè)叫做片段著色器亚亲,這樣的每一對組合起來就稱作一個(gè)program(著色程序)。其中頂點(diǎn)著色器的作用是計(jì)算頂點(diǎn)的位置腐缤,根據(jù)計(jì)算出來的一系列的頂點(diǎn)的位置捌归,WebGL就可以對點(diǎn)、線以及三角形在內(nèi)的一些圖元進(jìn)行光柵化處理岭粤。當(dāng)對這些圖元進(jìn)行光柵化處理的時(shí)候就需要使用片段著色器方法了惜索,它的作用是計(jì)算出當(dāng)前繪制圖元中的每個(gè)像素的顏色值。

1.1 什么是GLSL

上面我們提到了GLSL剃浇,其中文的意思是OpenGL著色語言巾兆,它是用來在 OpenGL 編寫著色器程序的語言,全稱為 OpenGL Shading Language虎囚。而著色器程序則是在GPU上運(yùn)行的簡短的程序角塑,代替了GPU固定渲染管線的一部分,使GPU渲染過程中的某些部分允許開發(fā)者通過編程進(jìn)行控制淘讥。

而GPU渲染過程中具體允許我們對其進(jìn)行控制的部分有以下幾個(gè)方面:

  • JavaScript程序吉拳,處理著色器所需要的頂點(diǎn)坐標(biāo)、法向量适揉、顏色留攒、紋理等。
  • 頂點(diǎn)著色器嫉嘀,接受JavaScript傳遞過來的頂點(diǎn)信息炼邀,將頂點(diǎn)繪制到對應(yīng)的坐標(biāo)。
  • 圖元裝配階段剪侮,將三個(gè)頂點(diǎn)裝配成指定的圖元類型拭宁。
  • 光柵化階段洛退,將三角形內(nèi)部區(qū)域用空像素進(jìn)行填充。
  • 片元著色器杰标,為三角形內(nèi)部的像素填充顏色信息兵怯。

1.2 WebGL工作流程

上面對WebGL的基本情況進(jìn)行了一個(gè)簡單的概述,但好像也沒有解答webGL將3D模型顯示到屏幕上的工作原理及流程腔剂。其實(shí)這個(gè)過程就好比富士康工作流水一樣媒区,按照既定的工作流程來對原材料進(jìn)行加工,從而生產(chǎn)出完整的產(chǎn)品掸犬。WebGL大致也是如此袜漩,按照工作流水線的方式,將3D的模型數(shù)據(jù)渲染到2D屏幕上的湾碎,這個(gè)渲染方式的過程一般被稱之為圖形管線或者渲染管線宙攻。

上面我們又說到過點(diǎn)、線介褥、三角形這些基本圖元座掘,但我們經(jīng)常看見很多通過WebGL所繪制出來的諸如球體柔滔、圓柱溢陪、各式的立方體等模型,也看見了很多炫酷廊遍、復(fù)雜的模型,很顯然這些并不屬于這些基本圖元里面贩挣,但其實(shí)這些模型本質(zhì)上都是有一個(gè)個(gè)頂點(diǎn)組成的喉前,GPU將這些點(diǎn)用三角線圖元繪制成一個(gè)個(gè)微小的小平面,然后通過這些小平面的互相連接王财,來組成各種各樣的的立體模型卵迂。因此通常來說,我們首先要做的就是創(chuàng)建組成模型的頂點(diǎn)數(shù)據(jù)绒净。

一般情況下见咒,最初的頂點(diǎn)坐標(biāo)是相對于模型中心的,我們需要對頂點(diǎn)坐標(biāo)按照一系列步驟執(zhí)行模型轉(zhuǎn)換挂疆、視圖轉(zhuǎn)換改览、投影轉(zhuǎn)換,在通過這一系列的轉(zhuǎn)換后的坐標(biāo)叫做裁剪空間坐標(biāo)缤言,這個(gè)坐標(biāo)才是WebGL可以接受的坐標(biāo)宝当。我們把最后的變換矩陣和原始頂點(diǎn)坐標(biāo)傳遞給GPU,GPU的渲染管線然后對他們執(zhí)行流水工作胆萧,主要過程如下:

  1. 進(jìn)入頂點(diǎn)著色器庆揩,利用GPU的并行計(jì)算優(yōu)勢對頂點(diǎn)逐個(gè)進(jìn)行坐標(biāo)變換。
  2. 進(jìn)入圖元裝配階段,將會(huì)頂點(diǎn)按照圖元類型組裝成圖形
  3. 進(jìn)入光柵化階段订晌,光柵化階段對圖像用不包含顏色信息的像素進(jìn)行填充
  4. 進(jìn)入著色器階段虏辫,為像素著色,并最終顯示在屏幕上

二锈拨、WebGL初體驗(yàn)

上面將WebGL的大致情況進(jìn)行了描述砌庄,下面就是真刀實(shí)槍的來搞事情了,和Three.js一樣(這是廢話推励。)鹤耍,我們在使用WebGL進(jìn)行開發(fā)的時(shí)候首先需要使用canvas,我們可以再HTML文件里的這樣聲明一個(gè)canvas验辞。順便對瀏覽器對canvas的支持情況進(jìn)行一個(gè)檢查:

<body onload="main()">
  <canvas id="glcanvas" width="640" height="480">
    Your browser doesn't appear to support the HTML5 <code>&lt;canvas&gt;</code> element.
  </canvas>
</body>

webGL應(yīng)用主要包含兩個(gè)要素:JavaScript程序和著色器程序稿黄。首先讓我們來準(zhǔn)備著色器程序,使用GLSL編寫頂點(diǎn)著色器和片元著色器跌造。

頂點(diǎn)著色器的任務(wù)我們在上面已經(jīng)說了杆怕,它主要是告訴GPU我們所要形成的圖形在裁剪坐標(biāo)系的位置,下面這個(gè)代碼就是告訴GPU我們需要在裁剪坐標(biāo)系的原點(diǎn)壳贪,即屏幕中心畫一個(gè)大小為20的點(diǎn):

void main(){
    //聲明頂點(diǎn)位置
    gl_Position = vec4(0.0, 0.0, 0.0, 1.0)
    //聲明所需繪制的點(diǎn)的大小
    gl_PointSize = 20.0
}

當(dāng)頂點(diǎn)著色器中的數(shù)據(jù)經(jīng)過圖元裝配和光柵化之后陵珍,來到了片元著色器,從而通過片元著色器將像素渲染成我們所需要的顏色:

void main(){
    //設(shè)置像素的填充顏色為紅色违施。
    gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0) 
}

在這里互纯,gl_Position、gl_PointSize磕蒲、gl_FragColor 是 GLSL 的內(nèi)置屬性:

  • gl_Position:頂點(diǎn)的裁剪坐標(biāo)系坐標(biāo)留潦,包含X、Y辣往、Z兔院、W四個(gè)坐標(biāo)分量。頂點(diǎn)著色器接收坐標(biāo)后站削,會(huì)對它進(jìn)行透視除法坊萝,即將各個(gè)分量同時(shí)除以 W,從而轉(zhuǎn)換成 NDC 坐標(biāo)许起,NDC 坐標(biāo)每個(gè)分量的取值范圍都在(-1, 1)之間十偶,GPU 獲取這個(gè)屬性值作為頂點(diǎn)的最終位置進(jìn)行繪制。
  • gl_FragColor:片元(像素)顏色园细,包含 R, G, B, A 四個(gè)顏色分量扯键,且每個(gè)分量的取值范圍在(0,1)之間,GPU 會(huì)獲取這個(gè)值珊肃,作為像素的最終顏色進(jìn)行著色荣刑。
  • gl_PointSize:繪制到屏幕的點(diǎn)的大小馅笙,gl_PointSize只有在繪制圖元是點(diǎn)的時(shí)候才會(huì)生效。

然后我們就可以著手寫我們的JavaScript部分的代碼了厉亏,首先我們需要獲取webGL的繪圖環(huán)境:

const canvas = document.querySelector('#canvas')
const gl = canvas.getContext('webgl')

然后創(chuàng)建頂點(diǎn)著色器:

// 獲取頂點(diǎn)著色器源碼
const vertexShaderSource = document.querySelector('#vertexShader').innerHTML
// 創(chuàng)建頂點(diǎn)著色器對象
const vertexShader = gl.createShader(gl.VERTEX_SHADER)
// 將源碼分配給頂點(diǎn)著色器對象
gl.shaderSource(vertexShader, vertexShaderSource)
// 編譯頂點(diǎn)著色器程序
gl.compileShader(vertexShader)

再就是創(chuàng)建片元著色器:

// 獲取片元著色器源碼
const fragmentShaderSource = document.querySelector('#fragmentShader').innerHTML
// 創(chuàng)建片元著色器程序
const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
// 將源碼分配給片元著色器對象
gl.shaderSource(fragmentShader, fragmentShaderSource)
// 編譯片元著色器
gl.compileShader(fragmentShader)

以上就將我們的著色器對象創(chuàng)建完成了董习,接下來我們就可以創(chuàng)建著色器程序了:

//創(chuàng)建著色器程序
const program = gl.createProgram()
//將頂點(diǎn)著色器掛載在著色器程序上。
gl.attachShader(program, vertexShader)
//將片元著色器掛載在著色器程序上爱只。
gl.attachShader(program, fragmentShader)
//鏈接著色器程序
gl.linkProgram(program)

我們在進(jìn)行webgl開發(fā)的時(shí)候皿淋,可能會(huì)在一個(gè)WebGL應(yīng)用里包含多個(gè)program,因此我們在使用某狗program繪制前恬试,要先啟用它窝趣,才能進(jìn)行繪制:

gl.useProgram(program)
// 繪制
gl.clearColor(0.0, 0.0, 0.0, 1.0)
gl.clear(gl.COLOR_BUFFER_BIT)
gl.drawArrays(gl.POINTS, 0, 1)

如此我們完成了我們的第一個(gè)webGL代碼了,效果如下:


輸入圖片說明

完整代碼如下:

<body onload="main()">
    <!-- 頂點(diǎn)著色器源碼 -->
    <script type="shader-source" id="vertexShader">
     void main(){
        //聲明頂點(diǎn)位置
        gl_Position = vec4(0.0, 0.0, 0.0, 1.0);
        //聲明要繪制的點(diǎn)的大小训柴。
        gl_PointSize = 10.0;
    }
    </script>
    
    <!-- 片元著色器源碼 -->
    <script type="shader-source" id="fragmentShader">
     void main(){
        //設(shè)置像素顏色為紅色
        gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0); 
    }
    </script>
    <canvas id="canvas" width="640" height="480">
    Your browser doesn't appear to support the HTML5 <code>&lt;canvas&gt;</code> element.
    </canvas>
<script type="text/javascript">
function main() {
    // 獲取webGL的繪圖環(huán)境
    const canvas = document.querySelector("#canvas")
    const gl = canvas.getContext("webgl")
    // 創(chuàng)建頂點(diǎn)著色器
    const vertexShaderSource = document.querySelector('#vertexShader').innerHTML
    const vertexShader = gl.createShader(gl.VERTEX_SHADER)
    gl.shaderSource(vertexShader, vertexShaderSource)
    gl.compileShader(vertexShader)
    // 創(chuàng)建片元著色器
    const fragmentShaderSource = document.querySelector('#fragmentShader').innerHTML
    const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
    gl.shaderSource(fragmentShader, fragmentShaderSource)
    gl.compileShader(fragmentShader)
    // 創(chuàng)建著色器程序
    const program = gl.createProgram()
    gl.attachShader(program, vertexShader)
    gl.attachShader(program, fragmentShader)
    gl.linkProgram(program)

    gl.useProgram(program)
    // 繪制
    gl.clearColor(0.0, 0.0, 0.0, 1.0)
    gl.clear(gl.COLOR_BUFFER_BIT)
    gl.drawArrays(gl.POINTS, 0, 1)
}
</script>
</body>

參考資料:

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末哑舒,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子幻馁,更是在濱河造成了極大的恐慌洗鸵,老刑警劉巖,帶你破解...
    沈念sama閱讀 210,914評論 6 490
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件仗嗦,死亡現(xiàn)場離奇詭異膘滨,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)稀拐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 89,935評論 2 383
  • 文/潘曉璐 我一進(jìn)店門火邓,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人德撬,你說我怎么就攤上這事铲咨。” “怎么了砰逻?”我有些...
    開封第一講書人閱讀 156,531評論 0 345
  • 文/不壞的土叔 我叫張陵鸣驱,是天一觀的道長泛鸟。 經(jīng)常有香客問我蝠咆,道長,這世上最難降的妖魔是什么北滥? 我笑而不...
    開封第一講書人閱讀 56,309評論 1 282
  • 正文 為了忘掉前任刚操,我火速辦了婚禮,結(jié)果婚禮上再芋,老公的妹妹穿的比我還像新娘菊霜。我一直安慰自己,他們只是感情好济赎,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,381評論 5 384
  • 文/花漫 我一把揭開白布鉴逞。 她就那樣靜靜地躺著记某,像睡著了一般。 火紅的嫁衣襯著肌膚如雪构捡。 梳的紋絲不亂的頭發(fā)上液南,一...
    開封第一講書人閱讀 49,730評論 1 289
  • 那天,我揣著相機(jī)與錄音勾徽,去河邊找鬼滑凉。 笑死,一個(gè)胖子當(dāng)著我的面吹牛喘帚,可吹牛的內(nèi)容都是我干的畅姊。 我是一名探鬼主播,決...
    沈念sama閱讀 38,882評論 3 404
  • 文/蒼蘭香墨 我猛地睜開眼吹由,長吁一口氣:“原來是場噩夢啊……” “哼若未!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起溉知,我...
    開封第一講書人閱讀 37,643評論 0 266
  • 序言:老撾萬榮一對情侶失蹤陨瘩,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后级乍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體舌劳,經(jīng)...
    沈念sama閱讀 44,095評論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,448評論 2 325
  • 正文 我和宋清朗相戀三年玫荣,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了甚淡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,566評論 1 339
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡捅厂,死狀恐怖贯卦,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情焙贷,我是刑警寧澤撵割,帶...
    沈念sama閱讀 34,253評論 4 328
  • 正文 年R本政府宣布,位于F島的核電站辙芍,受9級特大地震影響啡彬,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜故硅,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,829評論 3 312
  • 文/蒙蒙 一庶灿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧吃衅,春花似錦往踢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,715評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽利职。三九已至,卻和暖如春瘦癌,著一層夾襖步出監(jiān)牢的瞬間眼耀,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,945評論 1 264
  • 我被黑心中介騙來泰國打工佩憾, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留哮伟,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 46,248評論 2 360
  • 正文 我出身青樓妄帘,卻偏偏與公主長得像楞黄,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子抡驼,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,440評論 2 348

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

  • 1 著色器和程序(Shaders and Programs) 1.1 著色器語言(Language Overvie...
    RichardJieChen閱讀 9,418評論 3 12
  • 圖元處理(Primitive Processing) 如何在場景中使用曲面細(xì)分來添加幾何細(xì)節(jié) 如何使用幾何著色器處...
    RichardJieChen閱讀 6,851評論 2 4
  • 談起WebGL可能有一些人比較陌生雷蹂,實(shí)際上WebGL是一種3D繪圖標(biāo)準(zhǔn),這種繪圖技術(shù)標(biāo)準(zhǔn)允許把JavaScript...
    三石青韋閱讀 18,190評論 2 11
  • 坐在客廳的沙發(fā)上杯道,我放下背包匪煌,從兜里掏出錢包去翻找我的醫(yī)療卡,結(jié)果第一張就抽出了我的身份證党巾,不經(jīng)意看了一眼萎庭,原來我...
    方林歐閱讀 244評論 0 0
  • 兄妹正閑玩,誰知日忽遮齿拂。 雷鳴驚百鳥驳规,雨落綻千花。 云墜濤翻雪署海,風(fēng)號葉卷沙吗购。 人間何處暖?竹篾共還家叹侄。 有一種溫暖...
    汐水之畔閱讀 888評論 3 16