優雅的學習webgl(1)—從0開始構造你的第一個webgl程序


    學習webgl也有小半年的時間了,有了一些心得和體會,在這裏作一個記錄,整個系列的代碼都會給出,這篇文章是這個系列的第一篇文章,帶你走進webgl的世界。javascript

  • 什麼是webgl
  • 用webgl畫點
  • 用webgl實現一個彩色正方形

這是一個系列的文章,首發自個人博客:github.com/fortheallli…html

這個系列的源碼地址爲:源碼的地址爲: github.com/fortheallli…java

1、什麼是webgl

  在介紹什麼是webgl以前,咱們來看一個最簡單的webgl程序。git

<html lang="en">
  <head> <title>WebGL Demo</title> <meta charset="utf-8"> </head> <body> <canvas id="glcanvas" width="640" height="480"></canvas> </body> <script> main(); // start here function main() { const canvas = document.querySelector("#glcanvas"); // Initialize the GL context const gl = canvas.getContext("webgl"); // Only continue if WebGL is available and working if (!gl) { alert("你的瀏覽器不支持webgl"); return; } // Set clear color to black, fully opaque gl.clearColor(0.0, 0.0, 0.0, 1.0); // Clear the color buffer with specified clear color gl.clear(gl.COLOR_BUFFER_BIT); } </script> </html>

複製代碼

上述就是一個webgl的例子,作的事情很簡單就是清空了webgl畫布的顏色。在上述的例子中,咱們沒有引入什麼其餘的插件,就構建了一個webgl程序,由於webgl自己就是瀏覽器層面的。Webgl是內嵌在瀏覽器中的,你沒必要安裝任何插件或者庫,若是瀏覽器支持webgl,就能夠經過getContext的方法建立一個webgl實例。github

const gl = canvas.getContext("webgl");
複製代碼

    一提到webgl就容易跟3D渲染繪圖聯繫在一塊兒,實際上這二者並無絕對關聯,webgl也能夠用來繪製2D平面圖形,2D動畫等等。那麼webgl究竟是什麼呢?一句話歸納就是:web

經過繪圖渲染技術OpenGL在瀏覽器裏面進行圖形渲染的技術編程

    瞭解過圖形渲染技術的同窗都聽過OpenGL等,咱們能夠經過C語言,在window等平臺上編寫複雜和渲染出複雜的圖形,經過OpenGL繪製和渲染圖形有很高的平臺要求以及編程語言的限制。而Webgl源自於OpenGL,從OpenGL2.0的着色器行爲中誕生了適用於移動式穿戴設備的OpenGL ES標準,在這個標準下演化出了webgl,使得咱們能夠經過結合javascript語言和GLSL ES着色器語言,在瀏覽器中繪製出複雜的圖形,不須要編譯也不須要引入任何插件.canvas

2、用webgl畫點

    接着咱們來看如何用webgl來繪製一個完整的圖案,繪製圖案跟上一小節最簡單的清空畫布的webgl程序不一樣。咱們須要webgl中的兩個重要的概念——着色器。     webgl的本質就是經過頂點着色器和片元着色器,將圖形渲染到瀏覽器中。以後咱們會詳細的介紹頂點着色器和片元着色器,這裏能夠簡單的理解爲頂點着色器決定了每一個頂點的位置,片元着色器決定了圖形的顏色。瀏覽器

一、初始化着色器程序

    繪製圖案以前必須進行***着色器程序初始化***,根據着色球類型建立着色器對象,將着色器對象編譯後綁定到程序對象,最後編譯和鏈接程序對象從而完整了初始化的過程,接下來就可使用程序對象來繪製圖形。編程語言

    上面的兩段話特別繞,總結就是:

GLSL着色器語言是以字符串的形式存在瀏覽器中的,爲了可以將字符串編譯成能夠在顯卡中運行的着色器程序,必須進行着色器程序初始化

在初始化着色器程序中咱們必須用到兩個對象着色器對象和程序對象,咱們來簡單介紹一下這二者。

  • 着色器對象: 咱們前面提到了頂點着色器和片元着色器,着色器對象就是管理頂點着色器或者片元着色器的對象
  • 程序對象: 程序對象則是管理着色器對象的容器

咱們用圖來區別這二者的關係:

Lark20191204-145309

從這個圖能夠看出層級關係,咱們要初始化着色器程序,就是按照以下的層級關係依次來初始化。

  1. 建立着色器對象(gl.createShader())
  2. 向着色器對象中填充着色器(gl.shaderSource())
  3. 編譯着色球(gl.compileShader())
  4. 建立程序對象(gl.createProgram())
  5. 爲程序對象分配着色器(gl.attashShader())
  6. 鏈接程序對象(gl.linkProgram())
  7. 使用程序對象(gl.useProgram())

結合上面圖形的層次結構,以及上述的7步初始化着色器的步驟,咱們能夠來介紹着色器初始化函數initShaderProgram

function initShaderProgram(gl, vsSource, fsSource) {
  const vertexShader = loadShader(gl, gl.VERTEX_SHADER, vsSource); //建立頂點着色器對象
  const fragmentShader = loadShader(gl, gl.FRAGMENT_SHADER, fsSource);//建立片元着色器對象

  // Create the shader program

  const shaderProgram = gl.createProgram();
  gl.attachShader(shaderProgram, vertexShader);
  gl.attachShader(shaderProgram, fragmentShader);
  gl.linkProgram(shaderProgram);

  return shaderProgram;
}

複製代碼

這就是最上層的程序對象的建立過程,至於着色器對象的建立能夠經過以下函數 loadShader:

function loadShader(gl, type, source) {
  const shader = gl.createShader(type);
  gl.shaderSource(shader, source);
  gl.compileShader(shader);

  return shader;
}

複製代碼

上述咱們就完成了着色器程序的初始化,就能夠在瀏覽器中編譯GLSL ES語言渲染出圖形。下面咱們來看最簡單的畫點

二、畫一個點

    咱們上一小節將瞭如何初始化着色器,初始化着色器能夠經過initShaderProgram方法,咱們能夠用webgl來畫一個最簡單的size爲10的點

首先經過字符串的方式定義頂點着色器和片元着色器:

const vsSource = ` ashouttribute vec4 aVertexPosition; uniform mat4 uModelViewMatrix; uniform mat4 uProjectionMatrix; void main() { gl_Position = vec4(0.0,0.0,0.0,1.0); gl_PointSize = 10.0; } `;

  // Fragment shader program

  const fsSource = ` void main() { gl_FragColor = vec4(1.0, 1.0, 1.0, 1.0); } `;
複製代碼

而後將這兩個字符串傳入initShaderProgram程序,就完成了着色器初始化。

const shaderProgram = initShaderProgram(gl, vsSource, fsSource)

複製代碼

最後清空畫布,並使用這個着色器程序開始畫圖:

gl.clearColor(0.0, 0.0, 0.0, 1.0);  
  gl.clearDepth(1.0);               
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT)
  gl.useProgram(shaderProgram);
  gl.drawArrays(gl.POINTS,0,1);
複製代碼

這樣就在瀏覽器中畫出了一個點。

Lark20191204-154158

源碼的地址爲: github.com/fortheallli…

3、用webgl實現一個彩色正方形(選擇性閱讀)

    最後咱們來實現一個較爲複雜的例子,用webgl來畫一個正方形。咱們能夠經過4次每次畫一個點,畫4個點,而後將這4個點連起來就成爲了一個正方形,此外若是咱們要一次性的畫出4個點並連成正方形,就須要使用緩衝區。

咱們能夠簡單理解,緩衝區保存了不少信息,咱們能夠讀取緩衝區的信息,在一次繪製中繪出咱們想要的圖形。

咱們來看建立一個緩衝區的步驟:

  1. 建立緩衝區對象(gl.createBuffer())
  2. 綁定緩衝區對象(gl.bindBuffer())
  3. 將數據寫入緩衝區對象(gl.bufferData())
  4. 將緩衝區對象分配給一個attribute變量(gl.vertexAttribPointer())
  5. 開啓attribute變量(gl.enableVertexAttribArray())

這個五步能夠簡記爲:建立-綁定-寫入-分配-開啓這麼幾步,而且咱們要使用緩衝區對象這五步是必不可免的.

咱們能夠定義initBuffers方法來建立緩衝區,咱們能夠分別建立一個頂點緩衝區(用於讀取圖形的頂點座標)以及片元緩衝區(用於讀取每一個片元的顏色信息)

function initBuffers(gl) {
  //頂點緩衝區
  const positionBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);

  const positions = [
     1.0,  1.0,
    -1.0,  1.0,
     1.0, -1.0,
    -1.0, -1.0,
  ];

  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(positions), gl.STATIC_DRAW);
  var colors = [
    1.0,  1.0,  1.0,  1.0,    // white
    1.0,  0.0,  0.0,  1.0,    // red
    0.0,  1.0,  0.0,  1.0,    // green
    0.0,  0.0,  1.0,  1.0,    // blue
  ];
 //顏色緩衝區
  const colorBuffer = gl.createBuffer();
  gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
  gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW);

  return {
    position: positionBuffer,
    color: colorBuffer,
  };
}
複製代碼

而後,在繪圖的時候,經過:

gl.bindBuffer();
gl.vertexAttribPointer();
gl.enableVertexAttribArray()

複製代碼

來分別使用緩衝區,除了緩衝區外,咱們還須要在頂點着色器和片元着色器中定義全局變量,來接受從緩衝區的傳遞過來的值。這裏先省略,後期在詳細介紹緩衝區和着色器的時候會提到.

最後給出繪製出來的圖像:

Lark20191204-162205

源碼的地址爲: github.com/fortheallli…

    最後補充一下,在第二張的畫一個點的例子中,咱們機會沒有引入任何插件,實際上雖然webgl是瀏覽器內置的不須要引入,可是爲了方便矩陣操做等,也有一些比較實用的工具

相關文章
相關標籤/搜索