<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          幾個(gè)簡(jiǎn)單的小例子手把手帶你入門webgl

          共 14401字,需瀏覽 29分鐘

           ·

          2021-09-14 01:00

           大廠技術(shù)  高級(jí)前端  Node進(jìn)階

          點(diǎn)擊上方 程序員成長(zhǎng)指北,關(guān)注公眾號(hào)

          回復(fù)1,加入高級(jí)Node交流群

          各位同學(xué)們大家好,又到了周末寫文章的時(shí)間,之前群里有粉絲提問, 就是shader不是很理解。然后今天他就來了, 廢話不多說,讀完今天的這篇文章你可以學(xué)到以下幾點(diǎn):
          1. 為什么需要有shader ? shader的作用是什么????
          2. shader 中的每個(gè)參數(shù)到底是什么意思??怎么去用???

          你如果會(huì)了,這篇文章你可以不用看??,不用浪費(fèi)時(shí)間,去看別的文章。如果哪里寫的有問題歡迎大家指正,我也在不斷地學(xué)習(xí)當(dāng)中。

          why need shader

          這里我結(jié)合自己的思考??,講講webgl的整個(gè)的一個(gè)渲染過程。

          渲染管線

          「Webgl」的渲染依賴底層「GPU」的渲染能力。所以「WEBGL」 渲染流程和 「GPU」 內(nèi)部的渲染管線是相符的。

          「渲染管線的作用是將3D模型轉(zhuǎn)換為2維圖像。」

          在早期,渲染管線是不可編程的,叫做「固定渲染管線」,工作的細(xì)節(jié)流程已經(jīng)固定,修改的話需要調(diào)整一些參數(shù)。

          現(xiàn)代的 「GPU」 所包含的渲染管線為「可編程渲染管線」,可以通過編程 「GLSL 著色器語言」 來控制一些渲染階段的細(xì)節(jié)。

          簡(jiǎn)單來說:就是使用「shader」,我們可以對(duì)畫布中「每個(gè)像素點(diǎn)做處理」,然后就可以生成各種酷炫的效果了。

          渲染過程

          渲染過程大概經(jīng)歷了下面這么多過程, 因?yàn)楸酒恼碌闹攸c(diǎn)其實(shí)是在著色器,所以我重點(diǎn)分析從「頂點(diǎn)著色器」—— 「片元著色器」的一個(gè)過程

          • 「頂點(diǎn)著色器」
          • 「圖片裝配」
          • 「光柵化」
          • 「片元著色器」
          • 「逐片段操作(本文不會(huì)分享此內(nèi)容)」
          • 「裁剪測(cè)試」
          • 「多重采樣操作」
          • 「背面剔除」
          • 「模板測(cè)試」
          • 「深度測(cè)試」
          • 「融合」
          • 「緩存」

          頂點(diǎn)著色器

          WebGL就是和GPU打交道,在GPU上運(yùn)行的代碼是一對(duì)著色器,一個(gè)是頂點(diǎn)著色器,另一個(gè)是片元著色器。每次調(diào)用著色程序都會(huì)先執(zhí)行頂點(diǎn)著色器,再執(zhí)行片元著色器。

          一個(gè)頂點(diǎn)著色器的工作是生成裁剪空間坐標(biāo)值,通常是以下的形式:

          const vertexShaderSource = `
              attribute vec3 position; 
              void main() {
                  gl_Position = vec4(position,1); 
              }
          `

          每個(gè)頂點(diǎn)調(diào)用一次(頂點(diǎn))著色器,每次調(diào)用都需要設(shè)置一個(gè)特殊的全局變量 「gl_Position」。該變量的值就是裁減空間坐標(biāo)值。這里有同學(xué)就問了, 什么是「裁剪空間的坐標(biāo)值」???

          其實(shí)我之前有講過,我在講一遍。

          何為裁剪空間坐標(biāo)?就是無論你的畫布有多大,裁剪坐標(biāo)的坐標(biāo)范圍永遠(yuǎn)是 -1 到 1 。

          看下面這張圖:

          裁剪坐標(biāo)系

          如果運(yùn)行一次頂點(diǎn)著色器, 那么gl_Position  就是**(-0.5,-0.5,0,1)** 記住他永遠(yuǎn)是個(gè) 「Vec4」,  簡(jiǎn)單理解就是對(duì)應(yīng)「x、y、z、w」。即使你沒用其他的,也要設(shè)置默認(rèn)值, 這就是所謂的 3維模型轉(zhuǎn)換到我們屏幕中。

          頂點(diǎn)著色器需要的數(shù)據(jù),可以通過以下四種方式獲得。

          1. attributes 屬性(從緩沖讀取數(shù)據(jù))
          2. uniforms 全局變量 (一般用來對(duì)物體做整體變化、 旋轉(zhuǎn)、縮放)
          3. textures 紋理(從像素或者紋理獲得數(shù)據(jù))
          4. varyings 變量  (將頂點(diǎn)著色器的變量 傳給 片元著色器)

          Attributes 屬性

          屬性可以用 float, vec2, vec3, vec4, mat2, mat3mat4 數(shù)據(jù)類型

          所以它內(nèi)建的數(shù)據(jù)類型例如vec2, vec3vec4分別代表兩個(gè)值,三個(gè)值和四個(gè)值, 類似的還有mat2, mat3mat4 分別代表 2x2, 3x3 和 4x4 矩陣。你可以做一些運(yùn)算例如常量和矢量的乘法??磶讉€(gè)例子吧:

          vec4 a = vec4(1234);
          vec4 b = a * 2.0;
          // b 現(xiàn)在是 vec4(2, 4, 6, 8);

          向量乘法 和矩陣乘法 :

          mat4 a = ???
          mat4 b = ???
          mat4 c = a * b;
           
          vec4 v = ???
          vec4 y = c * v;

          它還支持矢量「調(diào)制」,意味者你可以交換或重復(fù)分量。

          v.yyyy  ===  vec4(y, y, y,y )
          v.bgra  ===  vec4(v.b,v.g,v.r,v.a)
          vec4(v.rgb, 1) ===  vec4(v.r, v.g, v.b, 1
          vec4(1) === vec4(1111)

          這樣你在處理圖片的時(shí)候可以輕松進(jìn)「行 顏色通道 對(duì)調(diào)」, 發(fā)現(xiàn)你可以實(shí)現(xiàn)各種各樣的濾鏡了。

          后面的屬性在下面實(shí)戰(zhàn)中會(huì)講解:我們接著往下走:

          圖元裝配和光柵化

          「什么是圖元?」

          ?

          「描述各種圖形元素的函數(shù)叫做圖元,描述幾何元素的稱為幾何圖元(點(diǎn),線段或多邊形)。點(diǎn)和線是最簡(jiǎn)單的幾何圖元」經(jīng)過頂點(diǎn)著色器計(jì)算之后的坐標(biāo)會(huì)被組裝成「組合圖元」。

          ?

          「通俗解釋」「圖元就是一個(gè)點(diǎn)、一條線段、或者是一個(gè)多邊形?!?/strong>

          「什么是圖元裝配呢?」

          「簡(jiǎn)單理解就是說將我們?cè)O(shè)置的頂點(diǎn)、顏色、紋理等內(nèi)容組裝稱為一個(gè)可渲染的多邊形的過程?!?/strong>

          組裝的類型取決于:你最后繪制選擇的圖形類型

          gl.drawArrays(gl.TRIANGLES, 03)

          「如果是三角形的話,頂點(diǎn)著色器就執(zhí)行三次」

          光柵化

          「什么是光柵化:」

          通過圖元裝配生成的多邊形,計(jì)算像素并填充,「剔除」不可見的部分,「剪裁」掉不在可視范圍內(nèi)的部分。最終生成可見的帶有顏色數(shù)據(jù)的圖形并繪制。

          「光柵化流程圖解:」

          光珊化圖解

          剔除和剪裁

          • 「剔除」

            在日常生活中,對(duì)于不透明物體,背面對(duì)于觀察者來說是不可見的。同樣,在「webgl」中,我們也可以設(shè)定物體的背面不可見,那么在渲染過程中,就會(huì)將不可見的部分剔除,不參與繪制。節(jié)省渲染開銷。

          • 「剪裁」

            日常生活中不論是在看電視還是觀察物體,都會(huì)有一個(gè)可視范圍,在可視范圍之外的事物我們是看不到的。類似的,圖形生成后,有的部分可能位于可視范圍之外,這一部分會(huì)被剪裁掉,不參與繪制。以此來提高性能。這個(gè)就是「視椎體」, 在??范圍內(nèi)能看到的東西,才進(jìn)行繪制。

          片元著色器

          「光珊化后,每一個(gè)像素點(diǎn)都包含了 顏色 、深度 、紋理數(shù)據(jù), 這個(gè)我們叫做片元」

          ?

          小tips :每個(gè)像素的顏色由片元著色器的「gl_FragColor」提供

          ?

          接收光柵化階段生成的片元,在光柵化階段中,已經(jīng)計(jì)算出每個(gè)片元的顏色信息,這一階段會(huì)將片元做逐片元挑選的操作,處理過的片元會(huì)繼續(xù)向后面的階段傳遞。「片元著色器運(yùn)行的次數(shù)由圖形有多少個(gè)片元決定的」。

          「逐片元挑選」

          通過模板測(cè)試和深度測(cè)試來確定片元是否要顯示,測(cè)試過程中會(huì)丟棄掉部分無用的片元內(nèi)容,然后生成可繪制的二維圖像繪制并顯示。

          • **深度測(cè)試:**就是對(duì) 「z」 軸的值做測(cè)試,值比較小的片元內(nèi)容會(huì)覆蓋值比較大的。(類似于近處的物體會(huì)遮擋遠(yuǎn)處物體)。
          • **模板測(cè)試:**模擬觀察者的觀察行為,可以接為鏡像觀察。標(biāo)記所有鏡像中出現(xiàn)的片元,最后只繪制有標(biāo)記的內(nèi)容。

          實(shí)戰(zhàn)——繪制個(gè)三角形

          在進(jìn)行實(shí)戰(zhàn)之前,我們先給你看一張圖,讓你能大概了解,用原生webgl生成一個(gè)三角形需要那些步驟:

          draw

          我們就跟著這個(gè)流程圖一步一步去操作:

          初始化canvas

          新建一個(gè)webgl畫布

          <canvas id="webgl" width="500" height="500"></canvas>

          創(chuàng)建webgl 上下文:

          const gl = document.getElementById('webgl').getContext('webgl')

          創(chuàng)建著色器程序

          著色器的程序這些代碼,其實(shí)是重復(fù)的,我們還是先看下圖,看下我們到底需要哪些步驟:

          shader

          那我們就跟著這個(gè)流程圖:一步一步來好吧。

          創(chuàng)建著色器

           const vertexShader = gl.createShader(gl.VERTEX_SHADER)
           const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)

          gl.VERTEX_SHADER  和 gl.FRAGMENT_SHADER  這兩個(gè)是全局變量 分別表示「頂點(diǎn)著色器」「片元著色器」

          綁定數(shù)據(jù)源

          顧名思義:數(shù)據(jù)源,也就是我們的著色器 代碼。

          編寫著色器代碼有很多種方式:

          1. 用 script 標(biāo)簽  type  notjs 這樣去寫
          2. 模板字符串 (比較喜歡推薦這種)

          我們先寫頂點(diǎn)著色器:

          const vertexShaderSource = `
              attribute vec4 a_position;
              void main() {
                  gl_Position = a_position;
              }
           `

          頂點(diǎn)著色器 必須要有 main 函數(shù) ,他是強(qiáng)類型語言, 「記得加分號(hào)哇」 不是js 兄弟們。我這段著色器代碼非常簡(jiǎn)單   定義一個(gè)vec4 的頂點(diǎn)位置, 然后傳給 gl_Position

          這里有小伙伴會(huì)問 ?這里「a_position」一定要這么搞??

          這里其實(shí)是這樣的哇, 就是我們一般進(jìn)行變量命名的時(shí)候  都會(huì)增加帶有關(guān)鍵詞的前綴 用來區(qū)分每個(gè)變量的名字 他是屬性 還是 全局變量 還是紋理   比如這樣:

          uniform mat4 u_mat;

          表示個(gè)矩陣,如果不這樣也可以哈。但是要專業(yè)唄,防止bug 影響。

          我們接著寫片元著色器:

          const fragmentShaderSource = `
              void main() {
                  gl_FragColor = vec4(1.0,0.0,0.0,1.0);
              }
          `

          這個(gè)其實(shí)理解起來非常簡(jiǎn)單哈, 每個(gè)像素點(diǎn)的顏色 是紅色 , gl_FragColor 其實(shí)對(duì)應(yīng)的是 「rgba」  也就是顏色的表示。

          有了數(shù)據(jù)源之后開始綁定:

          // 創(chuàng)建著色器
          const vertexShader = gl.createShader(gl.VERTEX_SHADER)
          const fragmentShader = gl.createShader(gl.FRAGMENT_SHADER)
          //綁定數(shù)據(jù)源
          gl.shaderSource(vertexShader, vertexShaderSource)
          gl.shaderSource(fragmentShader, fragmentShaderSource)

          是不是很簡(jiǎn)單哈哈哈哈,我覺得你應(yīng)該會(huì)了。

          后面著色器的一些操作

          其實(shí)后面「編譯著色器」「綁定著色器」、「連接著色器程序」、「使用著色器程序」  都是一個(gè)api 搞定的事不多說了 直接看代碼:

          // 編譯著色器
          gl.compileShader(vertexShader)
          gl.compileShader(fragmentShader)
          // 創(chuàng)建著色器程序
          const program = gl.createProgram()
          gl.attachShader(program, vertexShader)
          gl.attachShader(program, fragmentShader)
          // 鏈接 并使用著色器
          gl.linkProgram(program)
          gl.useProgram(program)

          這樣我們就創(chuàng)建好了一個(gè)著色器程序了。

          這里又有人問,我怎么知道我創(chuàng)建的著色器是對(duì)的還是錯(cuò)的呢?我就是很粗心的人呢???好的他來了 如何調(diào)試:

          const success = gl.getProgramParameter(program, gl.LINK_STATUS)
          if (success) {
            gl.useProgram(program)
            return program
          }
          console.error(gl.getProgramInfoLog(program), 'test---')
          gl.deleteProgram(program)

          「getProgramParameter」  這個(gè)方法用來判斷 我們著色器 「glsl」 語言寫的是不是對(duì)的, 然后你可以通過 「getProgramInfoLog」這個(gè)方法 類似于打 日志 去發(fā)現(xiàn)?了。

          數(shù)據(jù)存入緩沖區(qū)

          有了著色器,現(xiàn)在我們差的就是數(shù)據(jù)了對(duì)吧。

          上文在寫頂點(diǎn)著色器的時(shí)候用到了Attributes屬性,說明是「這個(gè)變量要從緩沖中讀取數(shù)據(jù)」,下面我們就來把數(shù)據(jù)存入緩沖中。

          首先創(chuàng)建一個(gè)頂點(diǎn)緩沖區(qū)對(duì)象(Vertex Buffer Object, VBO)

          const buffer = gl.createBuffer()

          gl.createBuffer()函數(shù)創(chuàng)建緩沖區(qū)并返回一個(gè)標(biāo)識(shí)符,接下來需要為WebGL綁定這個(gè)buffer

          gl.bindBuffer(gl.ARRAY_BUFFER, buffer)

          gl.bindBuffer()函數(shù)把標(biāo)識(shí)符buffer設(shè)置為「當(dāng)前緩沖區(qū)」,后面的所有的數(shù)據(jù)都會(huì)都會(huì)被放入當(dāng)前緩沖區(qū),「直到bindBuffer綁定另一個(gè)當(dāng)前緩沖區(qū)」

          我們新建一個(gè)數(shù)組 然后并把數(shù)據(jù)存入到緩沖區(qū)中。

          const data = new Float32Array([0.00.0-0.3-0.30.3-0.3])
          gl.bufferData(gl.ARRAY_BUFFER, data, gl.STATIC_DRAW)

          因?yàn)?strong style="color: rgb(57, 144, 3);">「JavaScript與WebGL通信必須是二進(jìn)制的」,不能是傳統(tǒng)的文本格式,所以這里使用了ArrayBuffer對(duì)象將數(shù)據(jù)轉(zhuǎn)化為二進(jìn)制,因?yàn)轫旤c(diǎn)數(shù)據(jù)是浮點(diǎn)數(shù),精度不需要太高,所以使用Float32Array就可以了,這是JavaScript與GPU之間大量實(shí)時(shí)交換數(shù)據(jù)的有效方法。

          「gl.STATIC_DRAW」  指定數(shù)據(jù)存儲(chǔ)區(qū)的使用方法:緩存區(qū)的內(nèi)容可能會(huì)經(jīng)常使用,但是不會(huì)更改

          「gl.DYNAMIC_DRAW」 表示 緩存區(qū)的內(nèi)容經(jīng)常使用,也會(huì)經(jīng)常更改。

          「gl.STREAM_DRAW」 表示緩沖區(qū)的內(nèi)容可能不會(huì)經(jīng)常使用

          從緩沖中讀取數(shù)據(jù)

          「GLSL」著色程序的唯一輸入是一個(gè)屬性值「a_position」。我們要做的第一件事就是從剛才創(chuàng)建的GLSL著色程序中找到這個(gè)屬性值所在的位置。

          const aposlocation = gl.getAttribLocation(program, 'a_position')

          接下來我們需要告訴「WebGL」怎么從我們之前準(zhǔn)備的緩沖中獲取數(shù)據(jù)給著色器中的屬性。首先我們需要啟用對(duì)應(yīng)屬性

          gl.enableVertexAttribArray(aposlocation)

          最后是從緩沖中讀取數(shù)據(jù)綁定給被激活的「aposlocation」的位置

          gl.vertexAttribPointer(aposlocation, 2, gl.FLOAT, false00)

          gl.vertexAttribPointer()函數(shù)有六個(gè)參數(shù):

          1. 讀取的數(shù)據(jù)要綁定到哪
          2. 表示每次從緩存取幾個(gè)數(shù)據(jù),也可以表示每個(gè)頂點(diǎn)有幾個(gè)單位的數(shù)據(jù),取值范圍是1-4。這里每次取2個(gè)數(shù)據(jù),之前vertices聲明的6個(gè)數(shù)據(jù),正好是3個(gè)頂點(diǎn)的二維坐標(biāo)。
          3. 表示數(shù)據(jù)類型,可選參數(shù)有g(shù)l.BYTE有符號(hào)的8位整數(shù),gl.SHORT有符號(hào)的16位整數(shù),gl.UNSIGNED_BYTE無符號(hào)的8位整數(shù),gl.UNSIGNED_SHORT無符號(hào)的16位整數(shù),gl.FLOAT32位IEEE標(biāo)準(zhǔn)的浮點(diǎn)數(shù)。
          4. 表示是否應(yīng)該將整數(shù)數(shù)值歸一化到特定的范圍,對(duì)于類型gl.FLOAT此參數(shù)無效。
          5. 表示每次取數(shù)據(jù)與上次隔了多少位,0表示每次取數(shù)據(jù)連續(xù)緊挨上次數(shù)據(jù)的位置,WebGL會(huì)自己計(jì)算之間的間隔。
          6. 表示首次取數(shù)據(jù)時(shí)的偏移量,必須是字節(jié)大小的倍數(shù)。0表示從頭開始取。

          渲染

          現(xiàn)在著色器程序 和數(shù)據(jù)都已經(jīng)ready 了, 現(xiàn)在就差渲染了。渲染之前和2d canvas 一樣做一個(gè)清除畫布的動(dòng)作:

          // 清除canvas
          gl.clearColor(0000)
          gl.clear(gl.COLOR_BUFFER_BIT)

          我們用「0、0、0、0」清空畫布,分別對(duì)應(yīng) 「r, g, b, alpha (紅,綠,藍(lán),阿爾法」)值, 所以在這個(gè)例子中我們讓畫布變透明了。

          開啟繪制三角形:

          gl.drawArrays(gl.TRIANGLES, 0, 3)
          1. 「第一個(gè)參數(shù)表示繪制的類型」
          2. 「第二個(gè)參數(shù)表示從第幾個(gè)頂點(diǎn)開始繪制」
          3. 「第三個(gè)參數(shù)表示繪制多少個(gè)點(diǎn),緩沖中一共6個(gè)數(shù)據(jù),每次取2個(gè),共3個(gè)點(diǎn)」

          「繪制類型共有下列幾種」 「看圖:」

          drawtype

          這里我們看下畫面是不是一個(gè)紅色的三角形 :

          三角形截圖

          我們創(chuàng)建的數(shù)據(jù)是這樣的:

          「畫布的寬度是 500 * 500 轉(zhuǎn)換出來的實(shí)際數(shù)據(jù)其實(shí)是這樣的」

          0,0  ====>  00 
          -0.3-0.3 ====> 175325
          0.3-0.3 ====>  325325

          矩陣的使用

          有了靜態(tài)的圖形我們開始著色器,對(duì)三角形做一個(gè)縮放。

          改寫頂點(diǎn)著色器:其實(shí)在頂點(diǎn)著色器上加一個(gè)全局變量  這就用到了 著色器的第二個(gè)屬性  uniform

           const vertexShaderSource = `
            attribute vec4 a_position;
            // 添加矩陣代碼
            uniform mat4 u_mat;
            void main() {
                gl_Position = u_mat * a_position;
            }
          `

          然后和屬性一樣,我們需要找到 uniform 對(duì)應(yīng)的位置:

          const matlocation = gl.getUniformLocation(program, 'u_mat')

          然后初始化一個(gè)縮放舉證:

          // 初始化一個(gè)旋轉(zhuǎn)矩陣。
            const mat = new Float32Array([
              Tx,  0.00.00.0,
              0.0,  Ty, 0.00.0,
              0.00.0,  Tz, 0.0,
              0.00.00.01.0,
            ]);

          Tx, Ty, Tz 對(duì)應(yīng)的其實(shí)就是 x y z 軸縮放的比例。

          最后一步, 將矩陣應(yīng)用到著色器上, 在畫之前, 這樣每個(gè)點(diǎn) 就可以?? 這個(gè)縮放矩陣了 ,所以整體圖形 也就進(jìn)行了縮放。

          gl.uniformMatrix4fv(matlocation, false, mat)

          三個(gè)參數(shù)分別代表什么意思:

          1. 全局變量的位置
          2. 是否為轉(zhuǎn)置矩陣
          3. 矩陣數(shù)據(jù)

          OK 我寫了三角形縮放的動(dòng)畫:

            let Tx = 0.1 //x坐標(biāo)的位置
            let Ty = 0.1 //y坐標(biāo)的位置
            let Tz = 1.0 //z坐標(biāo)的位置
            let Tw = 1.0 //差值
            let isOver = true
            let step = 0.08
            function run({
              if (Tx >= 3) {
                isOver = false
              }
              if (Tx <= 0) {
                isOver = true
              }
              if (isOver) {
                Tx += step
                Ty += step
              } else {
                Tx -= step
                Ty -= step
              }
              const mat = new Float32Array([
                Tx,  0.00.00.0,
                0.0,  Ty, 0.00.0,
                0.00.0,  Tz, 0.0,
                0.00.00.01.0,
              ]);
              gl.uniformMatrix4fv(matlocation, false, mat)
              gl.drawArrays(gl.TRIANGLES, 03)

              // 使用此方法實(shí)現(xiàn)一個(gè)動(dòng)畫
              requestAnimationFrame(run)
            }

          效果圖如下:

          縮放動(dòng)畫

          最后 給大家看一下webgl 內(nèi)部是怎么搞的 一張gif 動(dòng)畫 :

          vertex-shader-anim

          原始的數(shù)據(jù)通過 頂點(diǎn)著色器  生成一系列 新的點(diǎn)。

          變量的使用

          說完矩陣了下面??,我們開始說下著色器中的varying 這個(gè)變量 是如何和片元著色器進(jìn)行聯(lián)動(dòng)的。

          我們還是繼續(xù)改造頂點(diǎn)著色器:

          const vertexShaderSource = `
            attribute vec4 a_position;
            uniform mat4 u_mat;
            // 變量
            varying vec4 v_color;
            void main() {
                gl_Position = u_mat * a_position;
                v_color =  gl_Position * 0.5 + 0.5;
            }
          `

          這里有一個(gè)小知識(shí) , gl_Position  他的值范圍是  「-1 -1」 但是片元著色 他是顏色 他的范圍是 「0 - 1」 , 所以呢這時(shí)候呢,我們就要 做一個(gè)范圍轉(zhuǎn)換  所以為什么要 乘 0.5  在加上 0.5 了, 希望你們明白。

          改造下片元著色器:

          const fragmentShaderSource = `
              precision lowp float;
              varying vec4 v_color;
              void main() {
                  gl_FragColor = v_color;
              }
          `

          只要沒一個(gè)像素點(diǎn) 改為由頂點(diǎn)著色器傳過來的就好了。

          我們看下這時(shí)候的三角形 變成啥樣子了。

          彩色三角形

          是不是變成彩色三角形了, 這里很多人就會(huì)問, 這到底是怎么形成呢, 本質(zhì)是在三角形的三個(gè)頂點(diǎn), 做線性插值的過程:


          總結(jié)


          本篇文章大概是對(duì)webgl 做了一個(gè)基本的介紹, 和帶你用幾個(gè)簡(jiǎn)單的小例子 帶你入門了glsl 語言, 你以為webgl 就這樣嘛  那你就錯(cuò)了,其實(shí)有一個(gè)texture 我是沒有講的, 后面我去專門寫一篇文章去將紋理貼圖 , 漫反射貼圖、 法線貼圖。希望你關(guān)注下我,不然找不到我了, 如果你覺得本篇文章對(duì)你有幫助的話,歡迎 點(diǎn)贊 、再看、收藏。我們下期再見??, 我是喜歡「圖形的Fly」。

          Node 社群


          我組建了一個(gè)氛圍特別好的 Node.js 社群,里面有很多 Node.js小伙伴,如果你對(duì)Node.js學(xué)習(xí)感興趣的話(后續(xù)有計(jì)劃也可以),我們可以一起進(jìn)行Node.js相關(guān)的交流、學(xué)習(xí)、共建。下方加 考拉 好友回復(fù)「Node」即可。


             “分享、點(diǎn)贊、在看” 支持一波 

          瀏覽 53
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  成人电影在线观看A | 青娱乐亚洲领先精品 | 中文字幕无码综合 | 人人操人人干人人摸 | 一级黄色免费视屏 |