
- WebGL 示例
- WebGL - 繪製點
- WebGL - 繪製三角形
- WebGL - 繪製模式
- WebGL - 繪製四邊形
- WebGL - 顏色
- WebGL - 平移
- WebGL - 縮放
- WebGL - 旋轉
- WebGL - 立方體旋轉
- WebGL - 互動式立方體
- WebGL 有用資源
- WebGL - 快速指南
- WebGL - 有用資源
- WebGL - 討論
WebGL - 快速指南
WebGL - 簡介
幾年前,Java 應用程式(作為 applet 和 JOGL 的組合)透過訪問 GPU(圖形處理單元)用於在 Web 上處理 3D 圖形。由於 applet 需要 JVM 才能執行,因此難以依賴 Java applet。幾年後,人們停止使用 Java applet。
Adobe(Flash、AIR)提供的 Stage3D API 提供了 GPU 硬體加速架構。使用這些技術,程式設計師可以在 Web 瀏覽器以及 IOS 和 Android 平臺上開發具有 2D 和 3D 功能的應用程式。由於 Flash 是專有軟體,因此它沒有被用作 Web 標準。
2011 年 3 月,WebGL 釋出。它是一個開源軟體,可以在沒有 JVM 的情況下執行。它完全由 Web 瀏覽器控制。
HTML 5 的新版本具有多個支援 3D 圖形的特性,例如 2D Canvas、WebGL、SVG、3D CSS 變換和 SMIL。在本教程中,我們將介紹 WebGL 的基礎知識。
什麼是 OpenGL?
OpenGL(開放圖形庫)是用於 2D 和 3D 圖形的跨語言、跨平臺 API。它是一組命令。OpenGL4.5 是 OpenGL 的最新版本。下表列出了一組與 OpenGL 相關的技術。
API | 使用的技術 |
---|---|
OpenGL ES | 它是嵌入式系統(包括控制檯、手機、裝置和車輛)上 2D 和 3D 圖形的庫。OpenGL ES 3.1 是其最新版本。它由 Khronos Group 維護 www.khronos.org |
JOGL | 它是 OpenGL 的 Java 繫結。JOGL 4.5 是其最新版本,由 jogamp.org 維護。 |
WebGL | 它是 OpenGL 的 JavaScript 繫結。WebGL 1.0 是其最新版本,由 khronos group 維護。 |
OpenGLSL | OpenGL 著色語言。它是一種程式語言,是 OpenGL 2.0 及更高版本的配套語言。它是核心 OpenGL 4.4 規範的一部分。它是一個專門為嵌入式系統(例如手機和平板電腦上的系統)定製的 API。 |
注意 - 在 WebGL 中,我們使用 GLSL 來編寫著色器。
什麼是 WebGL?
WebGL(Web 圖形庫)是 Web 上 3D 圖形的新標準,它旨在用於渲染 2D 圖形和互動式 3D 圖形。它源自 OpenGL 的 ES 2.0 庫,該庫是用於手機和其他移動裝置的低階 3D API。WebGL 提供了與 ES 2.0(嵌入式系統)類似的功能,並且在現代 3D 圖形硬體上表現良好。
它是一個 JavaScript API,可以與 HTML5 一起使用。WebGL 程式碼寫在 HTML5 的 <canvas> 標籤內。它是一個規範,允許 Internet 瀏覽器訪問那些計算機上的圖形處理單元 (GPU),這些計算機上使用了 GPU。
誰開發了 WebGL
一位名叫Vladimir Vukicevic的美籍塞爾維亞軟體工程師完成了基礎工作並領導了 WebGL 的建立
2007 年,Vladimir 開始在 HTML 文件的 Canvas 元素上開發OpenGL原型。
2011 年 3 月,Kronos Group 建立了 WebGL。
渲染
渲染是使用計算機程式從模型生成影像的過程。在圖形中,虛擬場景使用幾何、視點、紋理、燈光和陰影等資訊進行描述,這些資訊透過渲染程式傳遞。此渲染程式的輸出將是數字影像。
渲染有兩種型別:
軟體渲染 - 所有渲染計算都在 CPU 的幫助下完成。
硬體渲染 - 所有圖形計算均由 GPU(圖形處理單元)完成。
渲染可以在本地或遠端執行。如果要渲染的影像過於複雜,則渲染會在具有渲染複雜場景所需足夠硬體資源的專用伺服器上遠端執行。這也被稱為伺服器端渲染。渲染也可以由 CPU 在本地執行。這被稱為客戶端渲染。
WebGL 採用基於客戶端的渲染方法來渲染 3D 場景。獲取影像所需的所有處理都在本地使用客戶端的圖形硬體執行。
GPU
根據 NVIDIA 的說法,GPU 是“一個單晶片處理器,集成了變換、照明、三角形設定/裁剪和渲染引擎,能夠每秒處理至少 1000 萬個多邊形。”與具有少量針對順序處理最佳化的核心的多核處理器不同,GPU 由數千個較小的核心組成,這些核心可以有效地處理並行工作負載。因此,GPU 加速了為輸出到顯示器而打算的幀緩衝區(RAM 的一部分,其中包含完整的幀資料)中影像的建立。

GPU 加速計算
在 GPU 加速計算中,應用程式載入到 CPU 中。每當它遇到程式碼的計算密集型部分時,該部分程式碼將被載入並在 GPU 上執行。它使系統能夠以有效的方式處理圖形。

GPU 將擁有單獨的記憶體,並且它會同時執行一小部分程式碼的多個副本。GPU 處理其本地記憶體中的所有資料,而不是中央記憶體。因此,需要由 GPU 處理的資料應載入/複製到 GPU 記憶體,然後進行處理。
在具有上述架構的系統中,應減少 CPU 和 GPU 之間的通訊開銷以實現 3D 程式的更快處理。為此,我們必須複製所有資料並將其保留在 GPU 上,而不是反覆與 GPU 通訊。
支援的瀏覽器
下表顯示了支援 WebGL 的瀏覽器列表:
Web 瀏覽器
瀏覽器名稱 | 版本 | 支援 |
---|---|---|
Internet Explorer | 11 及以上 | 完全支援 |
Google Chrome | 39 及以上 | 完全支援 |
Safari | 8 | 完全支援 |
Firefox | 36 及以上 | 部分支援 |
Opera | 27 及以上 | 部分支援 |
移動瀏覽器
瀏覽器名稱 | 版本 | 支援 |
---|---|---|
Chrome for Android | 42 | 部分支援 |
Android 瀏覽器 | 40 | 部分支援 |
IOS Safari | 8.3 | 完全支援 |
Opera Mini | 8 | 不支援 |
Blackberry 瀏覽器 | 10 | 完全支援 |
IE 移動版 | 10 | 部分支援 |
WebGL 的優勢
以下是使用 WebGL 的優勢:
JavaScript 程式設計 - WebGL 應用程式是用 JavaScript 編寫的。使用這些應用程式,您可以直接與 HTML 文件的其他元素互動。您還可以使用其他 JavaScript 庫(例如 JQuery)和 HTML 技術來豐富 WebGL 應用程式。
移動瀏覽器支援度不斷提高 - WebGL 還支援 iOS Safari、Android 瀏覽器和 Chrome for Android 等移動瀏覽器。
開源 - WebGL 是開源的。您可以訪問庫的原始碼並瞭解其工作原理以及開發方式。
無需編譯 - JavaScript 是一種半程式設計半 HTML 元件。要執行此指令碼,無需編譯檔案。相反,您可以使用任何瀏覽器直接開啟檔案並檢查結果。由於 WebGL 應用程式使用 JavaScript 開發,因此也無需編譯 WebGL 應用程式。
自動記憶體管理 - JavaScript 支援自動記憶體管理。無需手動分配記憶體。WebGL 繼承了 JavaScript 的此功能。
易於設定 - 由於 WebGL 整合在 HTML 5 中,因此無需額外的設定。要編寫 WebGL 應用程式,您只需要一個文字編輯器和一個 Web 瀏覽器。
環境設定
無需為 WebGL 設定不同的環境。支援 WebGL 的瀏覽器具有自己的內建 WebGL 設定。
WebGL - Html5 Canvas 概述
為了在 Web 上建立圖形應用程式,HTML-5 提供了一套豐富的功能,例如 2D Canvas、WebGL、SVG、3D CSS 變換和 SMIL。要編寫 WebGL 應用程式,我們使用 HTML-5 現有的 canvas 元素。本章概述了 HTML-5 2D canvas 元素。
HTML5 Canvas
HTML-5 <canvas> 提供了一個簡單而強大的選項,可以使用 JavaScript 繪製圖形。它可用於繪製圖表、製作照片合成或進行簡單的(以及不太簡單的)動畫。
這是一個簡單的<canvas>元素,只有兩個特定屬性width和height,以及所有核心 HTML-5 屬性,如 id、name 和 class。
語法
HTML canvas 標籤的語法如下所示。您必須在雙引號 (“ ”) 內提及畫布的名稱。
<canvas id = "mycanvas" width = "100" height = "100"></canvas>
Canvas 屬性
canvas 標籤具有三個屬性,即 id、width 和 height。
Id - Id 表示畫布元素在文件物件模型 (DOM)中的識別符號。
Width - Width 表示畫布的寬度。
Height - Height 表示畫布的高度。
這些屬性決定了畫布的大小。如果程式設計師沒有在 canvas 標籤下指定它們,則 Firefox、Chrome 和 Web Kit 等瀏覽器預設提供大小為 300 × 150 的 canvas 元素。
示例 - 建立畫布
以下程式碼顯示瞭如何建立畫布。我們使用 CSS 為畫布提供了彩色邊框。
<html> <head> <style> #mycanvas{border:1px solid red;} </style> </head> <body> <canvas id = "mycanvas" width = "100" height = "100"></canvas> </body> </html>
執行上述程式碼將產生以下輸出:
渲染上下文
<canvas> 最初是空白的。要在 canvas 元素上顯示內容,我們必須使用指令碼語言。此指令碼語言應訪問渲染上下文並在其上繪製。
canvas 元素有一個名為getContext()的 DOM 方法,用於獲取渲染上下文及其繪圖函式。此方法接受一個引數,即上下文型別2d。
以下程式碼需要編寫以獲取所需的上下文。您可以在 body 標籤內編寫此指令碼,如下所示。
<!DOCTYPE HTML> <html> <body> <canvas id = "mycanvas" width = "600" height = "200"></canvas> <script> var canvas = document.getElementById('mycanvas'); var context = canvas.getContext('2d'); context.font = '20pt Calibri'; context.fillStyle = 'green'; context.fillText('Welcome to Tutorialspoint', 70, 70); </script> </body> </html>
執行上述程式碼將產生以下輸出:
有關 HTML-5 2D Canvas 的更多示例,請檢視以下連結 HTML-5 Canvas。
WebGL 上下文
HTML5 Canvas 也用於編寫 WebGL 應用程式。要在 canvas 元素上建立 WebGL 渲染上下文,您應該將字串experimental-webgl而不是2d傳遞給canvas.getContext()方法。某些瀏覽器僅支援“webgl”。
<!DOCTYPE html> <html> <canvas id = 'my_canvas'></canvas> <script> var canvas = document.getElementById('my_canvas'); var gl = canvas.getContext('experimental-webgl'); gl.clearColor(0.9,0.9,0.8,1); gl.clear(gl.COLOR_BUFFER_BIT); </script> </html>
執行上述程式碼將產生以下輸出:
WebGL - 基礎
WebGL 主要是一個低階光柵化 API,而不是 3D API。要使用 WebGL 繪製圖像,您必須傳遞表示該影像的向量。然後,它使用 OpenGL SL 將給定的向量轉換為畫素格式,並在螢幕上顯示影像。編寫 WebGL 應用程式涉及一系列步驟,我們將在本章中解釋這些步驟。
WebGL - 座標系
就像任何其他 3D 系統一樣,在 WebGL 中你將擁有 x、y 和 z 軸,其中 **z** 軸表示 **深度**。WebGL 中的座標限制在 (1, 1, 1) 和 (-1, -1, -1) 之間。這意味著 - 如果你將螢幕投影 WebGL 圖形視為一個立方體,那麼該立方體的一個角將是 (1, 1, 1),而相對的角將是 (-1, -1, -1)。WebGL 不會顯示繪製在這些邊界之外的任何內容。
下圖描繪了 WebGL 座標系。z 軸表示深度。z 的正值表示物體靠近螢幕/觀察者,而 z 的負值表示物體遠離螢幕。同樣,x 的正值表示物體位於螢幕的右側,負值表示物體位於左側。類似地,y 的正值和負值表示物體位於螢幕的上部還是下部。

WebGL 圖形
在獲取畫布物件的 WebGL 上下文後,您可以使用 JavaScript 中的 WebGL API 開始繪製圖形元素。
以下是一些在開始使用 WebGL 之前需要了解的基本術語。
頂點
通常,要繪製諸如多邊形之類的物體,我們在平面上標記點並將它們連線起來以形成所需的多邊形。**頂點**是定義 3D 物體邊緣連線的點。它由三個浮點數表示,每個浮點數分別代表 x、y、z 軸。
示例
在以下示例中,我們正在繪製一個具有以下頂點的三角形: (0.5, 0.5)、(-0.5, 0.5)、(-0.5, -0.5)。

**注意** - 我們必須使用 JavaScript 陣列手動儲存這些頂點,並使用頂點緩衝區將它們傳遞給 WebGL 渲染管道。
索引
在 WebGL 中,數值用於識別頂點。這些數值稱為索引。這些索引用於在 WebGL 中繪製網格。

**注意** - 就像頂點一樣,我們使用 JavaScript 陣列儲存索引,並使用索引緩衝區將它們傳遞給 WebGL 渲染管道。
陣列
與 OpenGL 和 JoGL 不同,WebGL 中沒有預定義的方法可以直接渲染頂點。我們必須使用 JavaScript 陣列手動儲存它們。
示例
var vertices = [ 0.5, 0.5, 0.1,-0.5, 0.5,-0.5]
緩衝區
緩衝區是 WebGL 中儲存資料的記憶體區域。有各種緩衝區,例如繪製緩衝區、幀緩衝區、頂點緩衝區和索引緩衝區。**頂點緩衝區**和**索引緩衝區**用於描述和處理模型的幾何體。
頂點緩衝區物件儲存有關頂點的資料,而索引緩衝區物件儲存有關索引的資料。將頂點儲存到陣列中後,我們使用這些緩衝區物件將它們傳遞給 WebGL 圖形管道。
**幀緩衝區**是圖形記憶體的一部分,用於儲存場景資料。該緩衝區包含諸如表面寬度和高度(以畫素為單位)、每個畫素的顏色、深度和模板緩衝區等詳細資訊。
網格
為了繪製 2D 或 3D 物件,WebGL API 提供了兩種方法,即 **drawArrays()** 和 **drawElements()**。這兩種方法都接受一個名為 **mode** 的引數,您可以使用該引數選擇要繪製的物件。此欄位提供的選項僅限於點、線和三角形。
要使用這兩種方法繪製 3D 物件,我們必須使用點、線或三角形構建一個或多個基本多邊形。然後,使用這些基本多邊形,我們可以形成一個網格。
使用基本多邊形繪製的 3D 物件稱為**網格**。WebGL 提供了幾種繪製 3D 圖形物件的方法,但是使用者通常更喜歡繪製網格。
示例
在以下示例中,您可以觀察到我們使用兩個三角形繪製了一個正方形:{1, 2, 3} 和 {4, 1, 3}。

著色器程式
我們通常使用三角形來構建網格。由於 WebGL 使用 GPU 加速計算,因此有關這些三角形的資訊應從 CPU 傳輸到 GPU,這會佔用大量的通訊開銷。
WebGL 提供了一種減少通訊開銷的解決方案。由於它使用在 GPU 上執行的 ES SL(嵌入式系統著色語言),因此我們使用**著色器程式**(我們使用 OpenGL ES 著色語言/ **GLSL** 編寫的程式)編寫在客戶端系統上繪製圖形元素所需的所有程式。
這些著色器是 GPU 的程式,用於編寫著色器程式的語言是 GLSL。在這些著色器中,我們準確地定義了頂點、變換、材質、燈光和攝像機如何相互互動以建立特定影像。
簡而言之,它是一個實現演算法以獲取網格畫素的程式碼片段。我們將在後面的章節中詳細討論著色器。著色器有兩種型別:頂點著色器和片段著色器。
頂點著色器
頂點著色器是在每個頂點上呼叫的程式程式碼。它用於將幾何體(例如三角形)從一個位置變換(移動)到另一個位置。它處理每個頂點的資料(每個頂點資料),例如頂點座標、法線、顏色和紋理座標。
在頂點著色器的 **ES GL** 程式碼中,程式設計師必須定義屬性來處理資料。這些屬性指向用 JavaScript 編寫的**頂點緩衝區物件**。
可以使用頂點著色器執行以下任務:
- 頂點變換
- 法線變換和歸一化
- 紋理座標生成
- 紋理座標變換
- 光照
- 顏色材質應用
片段著色器(畫素著色器)
網格由多個三角形組成,每個三角形的表面稱為**片段**。片段著色器是在每個片段的所有畫素上執行的程式碼。它被編寫用於計算和填充單個畫素上的顏色。
可以使用片段著色器執行以下任務:
- 插值值的運算
- 紋理訪問
- 紋理應用
- 霧
- 顏色相加

OpenGL ES SL 變數
**OpenGL ES SL** 的全稱是 OpenGL 嵌入式系統著色語言。為了處理著色器程式中的資料,ES SL 提供了三種類型的變數。它們如下:
**屬性** - 這些變數儲存頂點著色器程式的輸入值。屬性指向包含每個頂點資料的頂點緩衝區物件。每次呼叫頂點著色器時,屬性都會指向不同頂點的 VBO。
**統一變數** - 這些變數儲存頂點和片段著色器通用的輸入資料,例如燈光位置、紋理座標和顏色。
**變化變數** - 這些變數用於將資料從頂點著色器傳遞到片段著色器。
有了這些基礎知識,我們現在將繼續討論圖形管道。
WebGL - 圖形流水線
為了渲染 3D 圖形,我們必須遵循一系列步驟。這些步驟稱為**圖形管道**或**渲染管道**。下圖描繪了 WebGL 圖形管道。

在以下部分中,我們將逐一討論管道中每個步驟的作用。
JavaScript
在開發 WebGL 應用程式時,我們編寫著色器語言程式碼來與 GPU 通訊。JavaScript 用於編寫程式的控制程式碼,其中包括以下操作:
**初始化 WebGL** - JavaScript 用於初始化 WebGL 上下文。
**建立陣列** - 我們建立 JavaScript 陣列來儲存幾何體的資料。
**緩衝區物件** - 我們透過將陣列作為引數來建立緩衝區物件(頂點和索引)。
**著色器** - 我們使用 JavaScript 建立、編譯和連結著色器。
**屬性** - 我們可以使用 JavaScript 建立屬性、啟用它們並將它們與緩衝區物件關聯。
**統一變數** - 我們也可以使用 JavaScript 關聯統一變數。
**變換矩陣** - 使用 JavaScript,我們可以建立變換矩陣。
最初,我們為所需的幾何體建立資料,並以緩衝區形式將它們傳遞給著色器。著色器語言的屬性變數指向緩衝區物件,這些物件作為輸入傳遞給頂點著色器。
頂點著色器
當我們透過呼叫方法 **drawElements()** 和 **drawArray()** 開始渲染過程時,將為頂點緩衝區物件中提供的每個頂點執行頂點著色器。它計算基本多邊形每個頂點的位置並將其儲存在變化變數 **gl_position** 中。它還會計算其他屬性,例如通常與頂點關聯的 **顏色、紋理座標**和**頂點**。
圖元裝配
計算每個頂點的位置和其他詳細資訊後,下一階段是**圖元裝配階段**。在此,三角形被組裝並傳遞給光柵化器。
光柵化
在光柵化步驟中,確定基本圖形中最終影像中的畫素。它有兩個步驟:
**剔除** - 最初確定多邊形的朝向(它是正面還是背面?)。所有那些在視角區域中不可見的、方向不正確的三角形都會被丟棄。此過程稱為剔除。
**裁剪** - 如果三角形部分位於視角區域之外,則將視角區域之外的部分移除。此過程稱為裁剪。
片段著色器
片段著色器獲取
- 來自頂點著色器在變化變數中的資料,
- 來自光柵化階段的圖元,然後
- 計算頂點之間每個畫素的顏色值。
片段著色器將每個片段中每個畫素的顏色值儲存起來。這些顏色值可以在片段操作期間訪問,我們將在後面討論這些操作。
片段操作
在確定基本圖形中每個畫素的顏色後,執行片段操作。這些片段操作可能包括以下內容:
- 深度
- 顏色緩衝區混合
- 抖動
一旦所有片段都處理完畢,就會形成一個 2D 影像並在螢幕上顯示。**幀緩衝區**是渲染管道的最終目標。

幀緩衝區
幀緩衝區是圖形記憶體的一部分,用於儲存場景資料。該緩衝區包含諸如表面寬度和高度(以畫素為單位)、每個畫素的顏色以及深度和模板緩衝區等詳細資訊。
WebGL - 示例應用程式
我們已經討論了 WebGL 和 WebGL 管道(渲染圖形應用程式遵循的過程)的基礎知識。在本章中,我們將以一個示例應用程式為例,使用 WebGL 建立一個三角形,並觀察應用程式中遵循的步驟。
WebGL 應用程式結構
WebGL 應用程式程式碼是 JavaScript 和 OpenGL 著色器語言的組合。
- JavaScript 用於與 CPU 通訊
- OpenGL 著色器語言用於與 GPU 通訊。

示例應用程式
現在讓我們來看一個簡單的示例,學習如何使用 WebGL 繪製一個帶有 2D 座標的簡單三角形。
<!doctype html> <html> <body> <canvas width = "300" height = "300" id = "my_Canvas"></canvas> <script> /* Step1: Prepare the canvas and get WebGL context */ var canvas = document.getElementById('my_Canvas'); var gl = canvas.getContext('experimental-webgl'); /* Step2: Define the geometry and store it in buffer objects */ var vertices = [-0.5, 0.5, -0.5, -0.5, 0.0, -0.5,]; // Create a new buffer object var vertex_buffer = gl.createBuffer(); // Bind an empty array buffer to it gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); // Pass the vertices data to the buffer gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); // Unbind the buffer gl.bindBuffer(gl.ARRAY_BUFFER, null); /* Step3: Create and compile Shader programs */ // Vertex shader source code var vertCode = 'attribute vec2 coordinates;' + 'void main(void) {' + ' gl_Position = vec4(coordinates,0.0, 1.0);' + '}'; //Create a vertex shader object var vertShader = gl.createShader(gl.VERTEX_SHADER); //Attach vertex shader source code gl.shaderSource(vertShader, vertCode); //Compile the vertex shader gl.compileShader(vertShader); //Fragment shader source code var fragCode = 'void main(void) {' + 'gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' + '}'; // Create fragment shader object var fragShader = gl.createShader(gl.FRAGMENT_SHADER); // Attach fragment shader source code gl.shaderSource(fragShader, fragCode); // Compile the fragment shader gl.compileShader(fragShader); // Create a shader program object to store combined shader program var shaderProgram = gl.createProgram(); // Attach a vertex shader gl.attachShader(shaderProgram, vertShader); // Attach a fragment shader gl.attachShader(shaderProgram, fragShader); // Link both programs gl.linkProgram(shaderProgram); // Use the combined shader program object gl.useProgram(shaderProgram); /* Step 4: Associate the shader programs to buffer objects */ //Bind vertex buffer object gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); //Get the attribute location var coord = gl.getAttribLocation(shaderProgram, "coordinates"); //point an attribute to the currently bound VBO gl.vertexAttribPointer(coord, 2, gl.FLOAT, false, 0, 0); //Enable the attribute gl.enableVertexAttribArray(coord); /* Step5: Drawing the required object (triangle) */ // Clear the canvas gl.clearColor(0.5, 0.5, 0.5, 0.9); // Enable the depth test gl.enable(gl.DEPTH_TEST); // Clear the color buffer bit gl.clear(gl.COLOR_BUFFER_BIT); // Set the view port gl.viewport(0,0,canvas.width,canvas.height); // Draw the triangle gl.drawArrays(gl.TRIANGLES, 0, 3); </script> </body> </html>
它將產生以下結果:
如果你仔細觀察上面的程式,我們會遵循五個連續的步驟來使用 WebGL 繪製一個簡單的三角形。步驟如下:
步驟 1 - 準備畫布並獲取 WebGL 渲染上下文
我們獲取當前的 HTML 畫布物件並獲取其 WebGL 渲染上下文。
步驟 2 - 定義幾何體並將其儲存在緩衝區物件中
我們定義幾何體的屬性,例如頂點、索引、顏色等,並將它們儲存在 JavaScript 陣列中。然後,我們建立一個或多個緩衝區物件,並將包含資料的陣列傳遞到相應的緩衝區物件。在示例中,我們將三角形的頂點儲存在 JavaScript 陣列中,並將此陣列傳遞給頂點緩衝區物件。
步驟 3 - 建立和編譯著色器程式
我們編寫頂點著色器和片段著色器程式,編譯它們,並透過連結這兩個程式建立一個組合程式。
步驟 4 - 將著色器程式與緩衝區物件關聯
我們將緩衝區物件和組合的著色器程式關聯起來。
步驟 5 - 繪製所需物件(三角形)
此步驟包括清除顏色、清除緩衝區位、啟用深度測試、設定視口等操作。最後,您需要使用以下方法之一繪製所需的圖元 - drawArrays() 或 drawElements()。
本教程將進一步解釋所有這些步驟。
WebGL - 上下文
要編寫 WebGL 應用程式,第一步是獲取 WebGL 渲染上下文物件。此物件與 WebGL 繪圖緩衝區互動,並且可以呼叫所有 WebGL 方法。要獲取 WebGL 上下文,需要執行以下操作:
- 建立 HTML-5 畫布
- 獲取畫布 ID
- 獲取 WebGL
建立 HTML-5 畫布元素
在第 5 章中,我們討論瞭如何建立 HTML-5 畫布元素。在 HTML-5 文件的主體中,編寫一個畫布,為其命名,並將其作為引數傳遞給屬性 id。您可以使用 width 和 height 屬性(可選)定義畫布的尺寸。
示例
以下示例顯示瞭如何建立一個尺寸為 500 × 500 的畫布元素。為了可見性,我們使用 CSS 為畫布建立了一個邊框。將以下程式碼複製並貼上到名為 my_canvas.html 的檔案中。
<!DOCTYPE HTML> <html> <head> <style> #mycanvas{border:1px solid blue;} </style> </head> <body> <canvas id = "mycanvas" width = "300" height = "300"></canvas> </body> </html>
它將產生以下結果:
獲取畫布 ID
建立畫布後,您必須獲取 WebGL 上下文。獲取 WebGL 繪圖上下文的第一步是獲取當前畫布元素的 id。
透過呼叫 DOM(文件物件模型)方法 getElementById() 獲取畫布 ID。此方法接受字串值作為引數,因此我們將當前畫布的名稱傳遞給它。
例如,如果畫布名稱為 my_canvas,則獲取畫布 ID 如下所示:
var canvas = document.getElementById('my_Canvas');
獲取 WebGL 繪圖上下文
要獲取 WebGLRenderingContext 物件(或 WebGL 繪圖上下文物件或簡稱 WebGL 上下文),請呼叫當前 HTMLCanvasElement 的 getContext() 方法。getContext() 的語法如下:
canvas.getContext(contextType, contextAttributes);
將字串 webgl 或 experimental-webgl 作為 contentType 傳遞。contextAttributes 引數是可選的。(執行此步驟時,請確保您的瀏覽器實現了WebGL 版本 1(OpenGL ES 2.0)。)
以下程式碼片段顯示瞭如何獲取 WebGL 渲染上下文。這裡 gl 是獲取的上下文物件的引用變數。
var canvas = document.getElementById('my_Canvas'); var gl = canvas.getContext('experimental-webgl');
WebGLContextAttributes
引數 WebGLContextAttributes 不是必需的。此引數提供各種接受布林值作為選項,如下所示:
序號 | 屬性和描述 |
---|---|
1 |
Alpha 如果其值為 true,則為畫布提供 alpha 緩衝區。 預設情況下,其值為 true。 |
2 |
depth 如果其值為 true,您將獲得一個包含至少 16 位深度緩衝區的繪圖緩衝區。 預設情況下,其值為 true。 |
3 |
stencil 如果其值為 true,您將獲得一個包含至少 8 位模板緩衝區的繪圖緩衝區。 預設情況下,其值為 false。 |
4 |
antialias 如果其值為 true,您將獲得一個執行抗鋸齒的繪圖緩衝區。 預設情況下,其值為 true。 |
5 |
premultipliedAlpha 如果其值為 true,您將獲得一個包含顏色預乘 alpha 的繪圖緩衝區。 預設情況下,其值為 true。 |
6 |
preserveDrawingBuffer 如果其值為 true,則緩衝區將不會被清除,並且會保留其值,直到被作者清除或覆蓋。 預設情況下,其值為 false。 |
以下程式碼片段顯示瞭如何建立一個帶有模板緩衝區的 WebGL 上下文,它不會執行抗鋸齒。
var canvas = document.getElementById('canvas1'); var context = canvas.getContext('webgl', { antialias: false, stencil: true });
在建立 WebGLRenderingContext 時,會建立一個繪圖緩衝區。上下文物件管理 OpenGL 狀態並渲染到繪圖緩衝區。
WebGLRenderingContext
它是 WebGL 中的主要介面。它表示 WebGL 繪圖上下文。此介面包含用於在繪圖緩衝區上執行各種任務的所有方法。此介面的屬性在以下表格中給出。
序號 | 屬性和描述 |
---|---|
1 | Canvas 這是對建立此上下文的畫布元素的引用。 |
2 | drawingBufferWidth 此屬性表示繪圖緩衝區的實際寬度。它可能與 HTMLCanvasElement 的 width 屬性不同。 |
3 | drawingBufferHeight 此屬性表示繪圖緩衝區的實際高度。它可能與 HTMLCanvasElement 的 height 屬性不同。 |
WebGL - 幾何體
獲取 WebGL 上下文後,您必須為圖元(要繪製的物件)定義幾何體並將其儲存。在 WebGL 中,我們使用 JavaScript 陣列定義幾何體的詳細資訊,例如圖元的頂點、索引、顏色。要將這些詳細資訊傳遞到著色器程式,我們必須建立緩衝區物件並將包含資料的 JavaScript 陣列儲存(附加)到相應的緩衝區中。
注意:稍後,這些緩衝區物件將與著色器程式(頂點著色器)的屬性相關聯。
定義所需的幾何體
使用頂點繪製的 2D 或 3D 模型稱為網格。網格中的每個面稱為多邊形,多邊形由 3 個或更多個頂點組成。
要在 WebGL 渲染上下文中繪製模型,您必須使用 JavaScript 陣列定義頂點和索引。例如,如果我們想建立一個位於座標 {(5,5), (-5,5), (-5,-5)} 上的三角形,如圖所示,則可以為頂點建立一個數組,如下所示:
var vertices = [ 0.5,0.5, //Vertex 1 0.5,-0.5, //Vertex 2 -0.5,-0.5, //Vertex 3 ];

類似地,您可以為索引建立一個數組。上述三角形索引的索引將為 [0, 1, 2],可以定義為:
var indices = [ 0,1,2 ]
為了更好地理解索引,請考慮更復雜的模型,例如正方形。我們可以將正方形表示為兩個三角形的集合。如果 (0,3,1) 和 (3,1,2) 是我們打算用來繪製正方形的兩個三角形,則索引將定義為:
var indices = [0,3,1,3,1,2];

注意 -
為了繪製圖元,WebGL 提供了以下兩種方法:
drawArrays() - 使用此方法時,我們使用 JavaScript 陣列傳遞圖元的頂點。
drawElements() - 使用此方法時,我們使用 JavaScript 陣列傳遞圖元的頂點和索引。
緩衝區物件
緩衝區物件是 WebGL 提供的一種機制,它指示在系統中分配的記憶體區域。在這些緩衝區物件中,您可以儲存要繪製的模型的資料,對應於頂點、索引、顏色等。
使用這些緩衝區物件,您可以透過其屬性變數之一將多個數據傳遞到著色器程式(頂點著色器)。由於這些緩衝區物件駐留在 GPU 記憶體中,因此可以對其進行直接渲染,從而提高效能。
要處理幾何體,有兩種型別的緩衝區物件。它們是:
頂點緩衝區物件 (VBO) - 它儲存要渲染的圖形模型的每個頂點資料。我們在 WebGL 中使用頂點緩衝區物件來儲存和處理有關頂點的資料,例如頂點座標、法線、顏色和紋理座標。
索引緩衝區物件 (IBO) - 它儲存要渲染的圖形模型的索引(索引資料)。
定義所需的幾何體並將其儲存在 JavaScript 陣列中後,您需要將這些陣列傳遞到緩衝區物件,然後資料將傳遞到著色器程式。要將資料儲存在緩衝區中,需要遵循以下步驟。
建立一個空緩衝區。
將適當的陣列物件繫結到空緩衝區。
使用型別化陣列之一將資料(頂點/索引)傳遞到緩衝區。
取消繫結緩衝區(可選)。
建立緩衝區
要建立一個空緩衝區物件,WebGL 提供了一個名為 createBuffer() 的方法。如果建立成功,此方法將返回一個新建立的緩衝區物件;否則,如果失敗,則返回 null 值。
WebGL 作為狀態機執行。建立緩衝區後,任何後續的緩衝區操作都將在當前緩衝區上執行,直到我們取消繫結它。使用以下程式碼建立緩衝區:
var vertex_buffer = gl.createBuffer();
注意 - gl 是當前 WebGL 上下文的引用變數。
繫結緩衝區
建立空緩衝區物件後,您需要將適當的陣列緩衝區(目標)繫結到它。WebGL 提供了一個名為 bindBuffer() 的方法來實現此目的。
語法
bindBuffer() 方法的語法如下:
void bindBuffer (enum target, Object buffer)
此方法接受兩個引數,如下所述。
target - 第一個變數是一個列舉值,表示我們要繫結到空緩衝區的緩衝區型別。您有兩個預定義的列舉值可作為此引數的選項。它們是:
ARRAY_BUFFER 表示頂點資料。
ELEMENT_ARRAY_BUFFER 表示索引資料。
Object buffer - 第二個是上一步中建立的緩衝區物件的引用變數。引用變數可以是頂點緩衝區物件或索引緩衝區物件的引用變數。
示例
以下程式碼片段顯示瞭如何使用 bindBuffer() 方法。
//vertex buffer var vertex_buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); //Index buffer var Index_Buffer = gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer);
將資料傳遞到緩衝區
下一步是將資料(頂點/索引)傳遞到緩衝區。到目前為止,資料以陣列的形式存在,在將其傳遞到緩衝區之前,我們需要將其包裝在 WebGL 型別化陣列之一中。WebGL 提供了一個名為 bufferData() 的方法來實現此目的。
語法
bufferData() 方法的語法如下:
void bufferData (enum target, Object data, enum usage)
此方法接受三個引數,如下所述:
target - 第一個引數是一個列舉值,表示我們使用的陣列緩衝區型別。此引數的選項為:
ARRAY_BUFFER 表示頂點資料。
ELEMENT_ARRAY_BUFFER 表示索引資料。
Object data - 第二個引數是包含要寫入緩衝區物件的資料的物件值。這裡我們必須使用型別化陣列傳遞資料。
Usage - 此方法的第三個引數是一個列舉變數,它指定如何使用緩衝區物件資料(儲存的資料)繪製形狀。此引數有三個選項,如下所示。
gl.STATIC_DRAW − 資料將被指定一次並使用多次。
gl.STREAM_DRAW − 資料將被指定一次並使用幾次。
gl.DYNAMIC_DRAW − 資料將被重複指定並使用多次。
示例
以下程式碼片段展示瞭如何使用bufferData()方法。假設vertices和indices分別是儲存頂點和索引資料的陣列。
//vertex buffer gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); //Index buffer gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW);
型別化陣列
WebGL提供了一種稱為型別化陣列的特殊型別的陣列來傳輸資料元素,例如索引頂點和紋理。這些型別化陣列儲存大量資料並以原生二進位制格式處理它們,從而提高效能。WebGL使用的型別化陣列有Int8Array、Uint8Array、Int16Array、Uint16Array、Int32Array、UInt32Array、Float32Array和Float64Array。
注意
通常,用於儲存頂點資料,我們使用Float32Array;而用於儲存索引資料,我們使用Uint16Array。
您可以像使用JavaScript陣列一樣使用new關鍵字建立型別化陣列。
解除繫結緩衝區
建議您在使用完緩衝區後解除繫結。這可以透過傳遞一個空值來代替緩衝區物件來完成,如下所示。
gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null);
WebGL提供了以下方法來執行緩衝區操作:
序號 | 方法和描述 |
---|---|
1 | void bindBuffer (enum target, Object buffer) target − ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER |
2 | void bufferData(enum target, long size, enum usage) target − ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER usage − STATIC_DRAW, STREAM_DRAW, DYNAMIC_DRAW |
3 | void bufferData (enum target, Object data, enum usage) target 和 usage − 與上面的bufferData相同 |
4 | void bufferSubData(enum target, long offset, Object data) target − ARRAY_BUFFER, ELEMENT_ARRAY_BUFFER |
5 | Object createBuffer() |
6 | void deleteBuffer(Object buffer) |
7 | any getBufferParameter(enum target, enum pname) target − ARRAY_BUFFER, ELEMENT_ ARRAY_BUFFER pname − BUFFER_SIZE, BUFFER_USAGE |
8 | bool isBuffer(Object buffer) |
WebGL - 著色器
著色器是在GPU上執行的程式。著色器是用OpenGL ES著色器語言(稱為ES SL)編寫的。ES SL有自己的變數、資料型別、限定符、內建輸入和輸出。
資料型別
下表列出了OpenGL ES SL提供的基本資料型別。
序號 | 型別 & 描述 |
---|---|
1 | void 表示空值。 |
2 | bool 接受true或false。 |
3 | int 這是一個有符號整數資料型別。 |
4 | float 這是一個浮點標量資料型別。 |
5 | vec2, vec3, vec4 n分量浮點向量 |
6 | bvec2, bvec3, bvec4 布林向量 |
7 | ivec2, ivec3, ivec4 有符號整數向量 |
8 | mat2, mat3, mat4 2x2、3x3、4x4浮點矩陣 |
9 | sampler2D 訪問二維紋理 |
10 | samplerCube 訪問立方體貼圖紋理 |
限定符
OpenGL ES SL中有三個主要的限定符:
序號 | 限定符 & 描述 |
---|---|
1 | attribute 此限定符充當頂點著色器和OpenGL ES之間每個頂點資料的連結。此屬性的值在每次執行頂點著色器時都會更改。 |
2 | uniform 此限定符連結著色器程式和WebGL應用程式。與attribute限定符不同,uniform的值不會改變。Uniforms是隻讀的;您可以將它們與任何基本資料型別一起使用,來宣告一個變數。 示例 − uniform vec4 lightPosition; |
3 | varying 此限定符在頂點著色器和片段著色器之間形成插值資料的連結。它可以與以下資料型別一起使用:float、vec2、vec3、vec4、mat2、mat3、mat4或陣列。 示例 − varying vec3 normal; |
頂點著色器
頂點著色器是一個程式程式碼,它在每個頂點上呼叫。它將幾何圖形(例如三角形)從一個位置轉換(移動)到另一個位置。它處理每個頂點的資料(每個頂點資料),例如頂點座標、法線、顏色和紋理座標。
在頂點著色器的ES GL程式碼中,程式設計師必須定義屬性來處理資料。這些屬性指向用JavaScript編寫的頂點緩衝區物件。可以使用頂點著色器以及頂點變換執行以下任務:
- 頂點變換
- 法線變換和歸一化
- 紋理座標生成
- 紋理座標變換
- 光照
- 顏色材質應用
預定義變數
OpenGL ES SL為頂點著色器提供以下預定義變數:
序號 | 變數 & 描述 |
---|---|
1 | highp vec4 gl_Position; 儲存頂點的位置。 |
2 | mediump float gl_PointSize; 儲存變換後的點大小。此變數的單位為畫素。 |
示例程式碼
請檢視以下頂點著色器的示例程式碼。它處理三角形的頂點。
attribute vec2 coordinates; void main(void) { gl_Position = vec4(coordinates, 0.0, 1.0); };
如果您仔細觀察上面的程式碼,我們聲明瞭一個名為coordinates的屬性變數。(此變數將使用getAttribLocation()方法與頂點緩衝區物件關聯。屬性coordinates作為引數傳遞給此方法以及著色器程式物件。)
在給定頂點著色器程式的第二步中,定義了gl_position變數。
gl_Position
gl_Position是僅在頂點著色器程式中可用的預定義變數。它包含頂點位置。在上面的程式碼中,coordinates屬性以向量的形式傳遞。由於頂點著色器是每個頂點的操作,因此為每個頂點計算gl_position值。
稍後,gl_position值將被圖元裝配、裁剪、剔除和其他固定功能操作使用,這些操作在頂點處理完成後對圖元進行操作。
我們可以為頂點著色器的所有可能操作編寫頂點著色器程式,我們將在本教程中分別討論這些操作。
片段著色器
網格由多個三角形組成,每個三角形的表面稱為片段。片段著色器是在每個片段的每個畫素上執行的程式碼。它是為了計算和填充各個畫素的顏色而編寫的。可以使用片段著色器執行以下任務:
- 插值值的運算
- 紋理訪問
- 紋理應用
- 霧
- 顏色相加
預定義變數
OpenGL ES SL為片段著色器提供以下預定義變數:
序號 | 變數 & 描述 |
---|---|
1 | mediump vec4 gl_FragCoord; 儲存片段在幀緩衝區中的位置。 |
2 | bool gl_FrontFacing; 儲存屬於正面圖元的片段。 |
3 | mediump vec2 gl_PointCoord; 儲存片段在點中的位置(僅限點光柵化)。 |
4 | mediump vec4 gl_FragColor; 儲存著色器的輸出片段顏色值 |
5 | mediump vec4 gl_FragData[n] 儲存顏色附件n的片段顏色。 |
示例程式碼
以下片段著色器的示例程式碼展示瞭如何為三角形中的每個畫素應用顏色。
void main(void) { gl_FragColor = vec4(0, 0.8, 0, 1); }
在上面的程式碼中,color值儲存在變數gl.FragColor
中。片段著色器程式使用固定函式變數將輸出傳遞給管道;FragColor就是其中之一。此變數儲存模型畫素的顏色值。
儲存和編譯著色器程式
由於著色器是獨立的程式,因此我們可以將它們作為單獨的指令碼編寫並在應用程式中使用。或者,您可以將它們直接儲存在字串格式中,如下所示。
var vertCode = 'attribute vec2 coordinates;' + 'void main(void) {' + ' gl_Position = vec4(coordinates, 0.0, 1.0);' + '}';
編譯著色器
編譯涉及以下三個步驟:
- 建立著色器物件
- 將原始碼附加到建立的著色器物件
- 編譯程式
建立頂點著色器
要建立空著色器,WebGL提供了一個名為createShader()的方法。它建立並返回著色器物件。其語法如下:
Object createShader (enum type)
如語法所示,此方法接受預定義的列舉值作為引數。我們有兩個選擇:
gl.VERTEX_SHADER 用於建立頂點著色器
gl.FRAGMENT_SHADER 用於建立片段著色器。
將原始碼附加到著色器
您可以使用shaderSource()方法將原始碼附加到建立的著色器物件。其語法如下:
void shaderSource(Object shader, string source)
此方法接受兩個引數:
shader − 您必須將建立的著色器物件作為其中一個引數傳遞。
Source − 您必須以字串格式傳遞著色器程式程式碼。
編譯程式
要編譯程式,您必須使用compileShader()方法。其語法如下:
compileShader(Object shader)
此方法接受著色器程式物件作為引數。建立著色器程式物件後,將原始碼附加到它並將其傳遞給此方法。
以下程式碼片段展示瞭如何建立和編譯頂點著色器以及片段著色器以建立三角形。
// Vertex Shader var vertCode = 'attribute vec3 coordinates;' + 'void main(void) {' + ' gl_Position = vec4(coordinates, 1.0);' + '}'; var vertShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertShader, vertCode); gl.compileShader(vertShader); // Fragment Shader var fragCode = 'void main(void) {' + ' gl_FragColor = vec4(0, 0.8, 0, 1);' + '}'; var fragShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragShader, fragCode); gl.compileShader(fragShader);
組合程式
建立和編譯這兩個著色器程式後,您需要建立一個包含這兩個著色器(頂點和片段)的組合程式。需要遵循以下步驟:
- 建立程式物件
- 附加兩個著色器
- 連結兩個著色器
- 使用程式
建立程式物件
使用createProgram()方法建立程式物件。它將返回一個空程式物件。以下是其語法:
createProgram();
附加著色器
使用attachShader()方法將著色器附加到建立的程式物件。其語法如下:
attachShader(Object program, Object shader);
此方法接受兩個引數:
Program − 將建立的空程式物件作為其中一個引數傳遞。
Shader − 傳遞已編譯的著色器程式之一(頂點著色器、片段著色器)
注意 − 您需要使用此方法附加兩個著色器。
連結著色器
透過將程式物件傳遞給已附加著色器的程式物件,使用linkProgram()方法連結著色器。其語法如下:
linkProgram(shaderProgram);
使用程式
WebGL提供了一個名為useProgram()的方法。您需要將連結的程式傳遞給它。其語法如下:
useProgram(shaderProgram);
以下程式碼片段展示瞭如何建立、連結和使用組合著色器程式。
var shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, vertShader); gl.attachShader(shaderProgram, fragShader); gl.linkProgram(shaderProgram); gl.useProgram(shaderProgram);
關聯屬性和緩衝區物件
頂點著色器程式中的每個屬性都指向一個頂點緩衝區物件。建立頂點緩衝區物件後,程式設計師必須將它們與頂點著色器程式的屬性關聯。每個屬性僅指向一個頂點緩衝區物件,從中提取資料值,然後將這些屬性傳遞給著色器程式。
要將頂點緩衝區物件與頂點著色器程式的屬性關聯,您必須按照以下步驟操作:
- 獲取屬性位置
- 將屬性指向頂點緩衝區物件
- 啟用屬性
獲取屬性位置
WebGL 提供了一種名為 getAttribLocation() 的方法,該方法返回屬性位置。其語法如下:
ulong getAttribLocation(Object program, string name)
此方法接受頂點著色器程式物件和頂點著色器程式的屬性值。
以下程式碼片段演示瞭如何使用此方法。
var coordinatesVar = gl.getAttribLocation(shader_program, "coordinates");
這裡,shader_program 是著色器程式的物件,coordinates 是頂點著色器程式的屬性。
將屬性指向 VBO
為了將緩衝區物件分配給屬性變數,WebGL 提供了一種名為 vertexAttribPointer() 的方法。以下是此方法的語法:
void vertexAttribPointer(location, int size, enum type, bool normalized, long stride, long offset)
此方法接受六個引數,下面將對它們進行討論。
Location - 指定屬性變數的儲存位置。在此選項下,您必須傳遞 getAttribLocation() 方法返回的值。
Size - 指定緩衝區物件中每個頂點的元件數。
Type - 指定資料型別。
Normalized - 這是一個布林值。如果為真,則非浮點資料將歸一化到 [0, 1];否則,它將歸一化到 [-1, 1]。
Stride - 指定不同頂點資料元素之間的位元組數,或對於預設步長為零。
Offset - 指定緩衝區物件中的偏移量(以位元組為單位),以指示從哪個位元組儲存頂點資料。如果資料從開頭儲存,則offset 為 0。
以下程式碼片段演示瞭如何在程式中使用 vertexAttribPointer():
gl.vertexAttribPointer(coordinatesVar, 3, gl.FLOAT, false, 0, 0);
啟用屬性
啟用頂點著色器屬性以訪問頂點著色器中的緩衝區物件。對於此操作,WebGL 提供了 enableVertexAttribArray() 方法。此方法接受屬性的位置作為引數。以下是在程式中使用此方法的方法:
gl.enableVertexAttribArray(coordinatesVar);
WebGL - 繪製模型
將緩衝區與著色器關聯後,最後一步是繪製所需的圖元。WebGL 提供了兩種方法,即 drawArrays() 和 drawElements() 來繪製模型。
drawArrays()
drawArrays() 是用於使用頂點繪製模型的方法。以下是其語法:
void drawArrays(enum mode, int first, long count)
此方法採用以下三個引數:
mode - 在 WebGL 中,模型使用圖元型別繪製。使用 mode,程式設計師必須選擇 WebGL 提供的圖元型別之一。此選項的可能值為:gl.POINTS、gl.LINE_STRIP、gl.LINE_LOOP、gl.LINES、gl.TRIANGLE_STRIP、gl.TRIANGLE_FAN 和 gl.TRIANGLES。
first - 此選項指定已啟用陣列中的起始元素。它不能是負值。
count - 此選項指定要渲染的元素數。
如果使用 drawArrays() 方法繪製模型,則 WebGL 在渲染形狀時會按照定義頂點座標的順序建立幾何體。
示例
如果要使用 drawArray() 方法繪製單個三角形,則必須傳遞三個頂點並呼叫 drawArrays() 方法,如下所示。
var vertices = [-0.5,-0.5, -0.25,0.5, 0.0,-0.5,]; gl.drawArrays(gl.TRIANGLES, 0, 3);
它將生成如下所示的三角形。

假設您想繪製連續的三角形,則必須按順序將接下來的三個頂點傳遞到頂點緩衝區,並將要渲染的元素數指定為 6。
var vertices = [-0.5,-0.5, -0.25,0.5, 0.0,-0.5, 0.0,-0.5, 0.25,0.5, 0.5,-0.5,]; gl.drawArrays(gl.TRIANGLES, 0, 6);
它將生成如下所示的連續三角形。

drawElements()
drawElements() 是用於使用頂點和索引繪製模型的方法。其語法如下:
void drawElements(enum mode, long count, enum type, long offset)
此方法採用以下四個引數:
mode - WebGL 模型使用圖元型別繪製。使用 mode,程式設計師必須選擇 WebGL 提供的圖元型別之一。此選項的可能值列表為:gl.POINTS、gl.LINE_STRIP、gl.LINE_LOOP、gl.LINES、gl.TRIANGLE_STRIP、gl.TRIANGLE_FAN 和 gl.TRIANGLES。
count - 此選項指定要渲染的元素數。
type - 此選項指定索引的資料型別,必須是 UNSIGNED_BYTE 或 UNSIGNED_SHORT。
offset - 此選項指定渲染的起始點。它通常是第一個元素 (0)。
如果使用 drawElements() 方法繪製模型,則除了頂點緩衝區物件外,還應建立索引緩衝區物件。如果使用此方法,則頂點資料將被處理一次,並根據索引中提到的次數使用多次。
示例
如果要使用索引繪製單個三角形,則需要將索引與頂點一起傳遞並呼叫 drawElements() 方法,如下所示。
var vertices = [ -0.5,-0.5,0.0, -0.25,0.5,0.0, 0.0,-0.5,0.0 ]; var indices = [0,1,2]; gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);
它將產生以下輸出:

如果要使用 drawElements() 方法繪製連續的三角形,只需新增其他頂點併為其餘頂點指定索引即可。
var vertices = [ -0.5,-0.5,0.0, -0.25,0.5,0.0, 0.0,-0.5,0.0, 0.25,0.5,0.0, 0.5,-0.5,0.0 ]; var indices = [0,1,2,2,3,4]; gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);
它將產生以下輸出:

所需操作
在繪製圖元之前,您需要執行一些操作,這些操作將在下面解釋。
清除畫布
首先,您應該使用 clearColor() 方法清除畫布。您可以將所需顏色的 RGBA 值作為引數傳遞給此方法。然後 WebGL 清除畫布並用指定的顏色填充它。因此,您可以使用此方法設定背景顏色。
檢視以下示例。這裡我們正在傳遞灰色顏色的 RGBA 值。
gl.clearColor(0.5, 0.5, .5, 1);
啟用深度測試
使用 enable() 方法啟用深度測試,如下所示。
gl.enable(gl.DEPTH_TEST);
清除顏色緩衝區位
使用 clear() 方法清除顏色和深度緩衝區,如下所示。
gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
設定視口
視口表示包含繪圖緩衝區渲染結果的矩形可視區域。您可以使用 viewport() 方法設定視口的尺寸。在以下程式碼中,視口尺寸設定為畫布尺寸。
gl.viewport(0,0,canvas.width,canvas.height);
WebGL - 繪製點
我們之前(在第 5 章)討論瞭如何遵循分步過程來繪製圖元。我們已經分五個步驟解釋了這個過程。每次繪製新形狀時,您都需要重複這些步驟。本章解釋瞭如何在 WebGL 中繪製具有 3D 座標的點。在繼續之前,讓我們重新看一下五個步驟。
所需步驟
建立 WebGL 應用程式以繪製點需要執行以下步驟。
步驟 1 - 準備畫布並獲取 WebGL 渲染上下文
在此步驟中,我們使用 getContext() 方法獲取 WebGL 渲染上下文物件。
步驟 2 - 定義幾何體並將其儲存在緩衝區物件中
由於我們正在繪製三個點,因此我們定義了三個具有 3D 座標的頂點並將它們儲存在緩衝區中。
var vertices = [ -0.5,0.5,0.0, 0.0,0.5,0.0, -0.25,0.25,0.0, ];
步驟 3 - 建立和編譯著色器程式
在此步驟中,您需要編寫頂點著色器和片段著色器程式,編譯它們,並透過連結這兩個程式來建立一個組合程式。
頂點著色器 - 在給定示例的頂點著色器中,我們定義了一個向量屬性來儲存 3D 座標,並將其分配給 gl_position 變數。
gl_pointsize 是用於為點分配大小的變數。我們將點大小分配為 10。
var vertCode = 'attribute vec3 coordinates;' + 'void main(void) {' + ' gl_Position = vec4(coordinates, 1.0);' + 'gl_PointSize = 10.0;'+ '}';
片段著色器 - 在片段著色器中,我們只需將片段顏色分配給 gl_FragColor 變數
var fragCode = 'void main(void) {' +' gl_FragColor = vec4(1, 0.5, 0.0, 1);' +'}';
步驟 4 - 將著色器程式關聯到緩衝區物件
在此步驟中,我們將緩衝區物件與著色器程式關聯。
步驟 5 - 繪製所需物件
我們使用 drawArrays() 方法繪製點。由於我們要繪製的點數為三個,因此 count 值為 3。
gl.drawArrays(gl.POINTS, 0, 3)
示例 – 使用 WebGL 繪製三個點
以下是使用 WebGL 繪製三個點的完整 WebGL 程式:
<!doctype html> <html> <body> <canvas width = "570" height = "570" id = "my_Canvas"></canvas> <script> /*================Creating a canvas=================*/ var canvas = document.getElementById('my_Canvas'); gl = canvas.getContext('experimental-webgl'); /*==========Defining and storing the geometry=======*/ var vertices = [ -0.5,0.5,0.0, 0.0,0.5,0.0, -0.25,0.25,0.0, ]; // Create an empty buffer object to store the vertex buffer var vertex_buffer = gl.createBuffer(); //Bind appropriate array buffer to it gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); // Pass the vertex data to the buffer gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); // Unbind the buffer gl.bindBuffer(gl.ARRAY_BUFFER, null); /*=========================Shaders========================*/ // vertex shader source code var vertCode = 'attribute vec3 coordinates;' + 'void main(void) {' + ' gl_Position = vec4(coordinates, 1.0);' + 'gl_PointSize = 10.0;'+ '}'; // Create a vertex shader object var vertShader = gl.createShader(gl.VERTEX_SHADER); // Attach vertex shader source code gl.shaderSource(vertShader, vertCode); // Compile the vertex shader gl.compileShader(vertShader); // fragment shader source code var fragCode = 'void main(void) {' + ' gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' + '}'; // Create fragment shader object var fragShader = gl.createShader(gl.FRAGMENT_SHADER); // Attach fragment shader source code gl.shaderSource(fragShader, fragCode); // Compile the fragmentt shader gl.compileShader(fragShader); // Create a shader program object to store // the combined shader program var shaderProgram = gl.createProgram(); // Attach a vertex shader gl.attachShader(shaderProgram, vertShader); // Attach a fragment shader gl.attachShader(shaderProgram, fragShader); // Link both programs gl.linkProgram(shaderProgram); // Use the combined shader program object gl.useProgram(shaderProgram); /*======== Associating shaders to buffer objects ========*/ // Bind vertex buffer object gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); // Get the attribute location var coord = gl.getAttribLocation(shaderProgram, "coordinates"); // Point an attribute to the currently bound VBO gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0); // Enable the attribute gl.enableVertexAttribArray(coord); /*============= Drawing the primitive ===============*/ // Clear the canvas gl.clearColor(0.5, 0.5, 0.5, 0.9); // Enable the depth test gl.enable(gl.DEPTH_TEST); // Clear the color buffer bit gl.clear(gl.COLOR_BUFFER_BIT); // Set the view port gl.viewport(0,0,canvas.width,canvas.height); // Draw the triangle gl.drawArrays(gl.POINTS, 0, 3); </script> </body> </html>
它將產生以下結果:
WebGL - 繪製三角形
在上一章(第 11 章)中,我們討論瞭如何使用 WebGL 繪製三個點。在第 5 章中,我們以示例應用程式演示瞭如何繪製三角形。在這兩個示例中,我們都只使用頂點繪製了圖元。
為了繪製更復雜的形狀/網格,我們除了頂點之外,還將幾何體的索引傳遞給著色器。在本章中,我們將瞭解如何使用索引繪製三角形。
繪製三角形所需的步驟
建立 WebGL 應用程式以繪製三角形需要執行以下步驟。
步驟 1 - 準備畫布並獲取 WebGL 渲染上下文
在此步驟中,我們使用 getContext() 獲取 WebGL 渲染上下文物件。
步驟 2 - 定義幾何體並將其儲存在緩衝區物件中
由於我們正在使用索引繪製三角形,因此我們必須傳遞三角形的三個頂點(包括索引),並將它們儲存在緩衝區中。
var vertices = [ -0.5,0.5,0.0, -0.5,-0.5,0.0, 0.5,-0.5,0.0, ]; indices = [0,1,2];
步驟 3 - 建立和編譯著色器程式
在此步驟中,您需要編寫頂點著色器和片段著色器程式,編譯它們,並透過連結這兩個程式來建立一個組合程式。
頂點著色器 - 在程式的頂點著色器中,我們定義了向量屬性來儲存 3D 座標並將其分配給 gl_position。
var vertCode = 'attribute vec3 coordinates;' + 'void main(void) {' + ' gl_Position = vec4(coordinates, 1.0);' + '}';
片段著色器 - 在片段著色器中,我們只需將片段顏色分配給 gl_FragColor 變數。
var fragCode = 'void main(void) {' + ' gl_FragColor = vec4(1, 0.5, 0.0, 1);' + '}';
步驟 4 - 將著色器程式關聯到緩衝區物件
在此步驟中,我們將緩衝區物件與著色器程式關聯。
步驟 5 - 繪製所需物件
由於我們正在使用索引繪製三角形,因此我們將使用 drawElements()
。對於此方法,我們必須傳遞索引數。indices.length 的值表示索引數。
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);
示例 – 繪製三角形
以下程式程式碼演示瞭如何使用索引在 WebGL 中繪製三角形:
<!doctype html> <html> <body> <canvas width = "570" height = "570" id = "my_Canvas"></canvas> <script> /*============== Creating a canvas ====================*/ var canvas = document.getElementById('my_Canvas'); gl = canvas.getContext('experimental-webgl'); /*======== Defining and storing the geometry ===========*/ var vertices = [ -0.5,0.5,0.0, -0.5,-0.5,0.0, 0.5,-0.5,0.0, ]; indices = [0,1,2]; // Create an empty buffer object to store vertex buffer var vertex_buffer = gl.createBuffer(); // Bind appropriate array buffer to it gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); // Pass the vertex data to the buffer gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); // Unbind the buffer gl.bindBuffer(gl.ARRAY_BUFFER, null); // Create an empty buffer object to store Index buffer var Index_Buffer = gl.createBuffer(); // Bind appropriate array buffer to it gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer); // Pass the vertex data to the buffer gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); // Unbind the buffer gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); /*================ Shaders ====================*/ // Vertex shader source code var vertCode = 'attribute vec3 coordinates;' + 'void main(void) {' + ' gl_Position = vec4(coordinates, 1.0);' + '}'; // Create a vertex shader object var vertShader = gl.createShader(gl.VERTEX_SHADER); // Attach vertex shader source code gl.shaderSource(vertShader, vertCode); // Compile the vertex shader gl.compileShader(vertShader); //fragment shader source code var fragCode = 'void main(void) {' + ' gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' + '}'; // Create fragment shader object var fragShader = gl.createShader(gl.FRAGMENT_SHADER); // Attach fragment shader source code gl.shaderSource(fragShader, fragCode); // Compile the fragmentt shader gl.compileShader(fragShader); // Create a shader program object to store // the combined shader program var shaderProgram = gl.createProgram(); // Attach a vertex shader gl.attachShader(shaderProgram, vertShader); // Attach a fragment shader gl.attachShader(shaderProgram, fragShader); // Link both the programs gl.linkProgram(shaderProgram); // Use the combined shader program object gl.useProgram(shaderProgram); /*======= Associating shaders to buffer objects =======*/ // Bind vertex buffer object gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); // Bind index buffer object gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer); // Get the attribute location var coord = gl.getAttribLocation(shaderProgram, "coordinates"); // Point an attribute to the currently bound VBO gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0); // Enable the attribute gl.enableVertexAttribArray(coord); /*=========Drawing the triangle===========*/ // Clear the canvas gl.clearColor(0.5, 0.5, 0.5, 0.9); // Enable the depth test gl.enable(gl.DEPTH_TEST); // Clear the color buffer bit gl.clear(gl.COLOR_BUFFER_BIT); // Set the view port gl.viewport(0,0,canvas.width,canvas.height); // Draw the triangle gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0); </script> </body> </html>
如果執行此示例,它將產生以下輸出:
WebGL - 繪製模式
在上一章(第 12 章)中,我們討論瞭如何使用 WebGL 繪製三角形。除了三角形之外,WebGL 還支援各種其他繪製模式。本章解釋了 WebGL 支援的繪製模式。
mode 引數
讓我們看一下方法的語法:drawElements() 和 draw Arrays()。
void drawElements(enum mode, long count, enum type, long offset); void drawArrays(enum mode, int first, long count);
如果您仔細觀察,這兩個方法都接受一個引數 mode。使用此引數,程式設計師可以在 WebGL 中選擇繪製模式。
WebGL 提供的繪製模式列在下表中。
序號 | 模式和說明 |
---|---|
1 | gl.POINTS 繪製一系列點。 |
2 | gl.LINES 繪製一系列不連線的線段(單獨的線)。 |
3 | gl.LINE_STRIP 繪製一系列連線的線段。 |
4 | gl.LINE_LOOP 繪製一系列連線的線段。它還連線第一個和最後一個頂點以形成迴圈。 |
5 | gl.TRIANGLES 繪製一系列單獨的三角形。 |
6 | gl.TRIANGLE_STRIP 以條帶方式繪製一系列連線的三角形。 |
7 | gl.TRIANGLE_FAN 以扇形方式繪製一系列連線的三角形,這些三角形共享第一個頂點。 |
示例 – 繪製三條平行線
以下示例演示瞭如何使用gl.LINES繪製三條平行線。
<!doctype html> <html> <body> <canvas width = "300" height = "300" id = "my_Canvas"></canvas> <script> /*======= Creating a canvas =========*/ var canvas = document.getElementById('my_Canvas'); var gl = canvas.getContext('experimental-webgl'); /*======= Defining and storing the geometry ======*/ var vertices = [ -0.7,-0.1,0, -0.3,0.6,0, -0.3,-0.3,0, 0.2,0.6,0, 0.3,-0.3,0, 0.7,0.6,0 ] // Create an empty buffer object var vertex_buffer = gl.createBuffer(); // Bind appropriate array buffer to it gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); // Pass the vertex data to the buffer gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); // Unbind the buffer gl.bindBuffer(gl.ARRAY_BUFFER, null); /*=================== Shaders ====================*/ // Vertex shader source code var vertCode = 'attribute vec3 coordinates;' + 'void main(void) {' + ' gl_Position = vec4(coordinates, 1.0);' + '}'; // Create a vertex shader object var vertShader = gl.createShader(gl.VERTEX_SHADER); // Attach vertex shader source code gl.shaderSource(vertShader, vertCode); // Compile the vertex shader gl.compileShader(vertShader); // Fragment shader source code var fragCode = 'void main(void) {' + 'gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' + '}'; // Create fragment shader object var fragShader = gl.createShader(gl.FRAGMENT_SHADER); // Attach fragment shader source code gl.shaderSource(fragShader, fragCode); // Compile the fragmentt shader gl.compileShader(fragShader); // Create a shader program object to store // the combined shader program var shaderProgram = gl.createProgram(); // Attach a vertex shader gl.attachShader(shaderProgram, vertShader); // Attach a fragment shader gl.attachShader(shaderProgram, fragShader); // Link both the programs gl.linkProgram(shaderProgram); // Use the combined shader program object gl.useProgram(shaderProgram); /*======= Associating shaders to buffer objects ======*/ // Bind vertex buffer object gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); // Get the attribute location var coord = gl.getAttribLocation(shaderProgram, "coordinates"); // Point an attribute to the currently bound VBO gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0); // Enable the attribute gl.enableVertexAttribArray(coord); /*============ Drawing the triangle =============*/ // Clear the canvas gl.clearColor(0.5, 0.5, 0.5, 0.9); // Enable the depth test gl.enable(gl.DEPTH_TEST); // Clear the color and depth buffer gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); // Set the view port gl.viewport(0,0,canvas.width,canvas.height); // Draw the triangle gl.drawArrays(gl.LINES, 0, 6); // POINTS, LINE_STRIP, LINE_LOOP, LINES, // TRIANGLE_STRIP,TRIANGLE_FAN, TRIANGLES </script> </body> </html>
如果執行此示例,它將產生以下輸出:
繪製模式
在上面的程式中,如果將drawArrays()的模式替換為以下繪製模式之一,則每次都會產生不同的輸出。
繪製模式 | 輸出結果 |
---|---|
LINE_STRIP | ![]() |
LINE_LOOP | ![]() |
TRIANGLE_STRIP | ![]() |
TRIANGLE_FAN | ![]() |
TRIANGLES | ![]() |
WebGL - 繪製四邊形
在上一章中,我們討論了WebGL提供的不同繪製模式。我們還可以使用索引使用這些模式之一來繪製圖元。要在WebGL中繪製模型,我們必須選擇其中一個圖元並繪製所需的網格(即,使用一個或多個圖元形成的模型)。
在本章中,我們將以一個示例來演示如何使用WebGL繪製四邊形。
繪製四邊形的步驟
建立WebGL應用程式以繪製四邊形需要以下步驟。
步驟 1 - 準備畫布並獲取 WebGL 渲染上下文
在此步驟中,我們使用 getContext() 獲取 WebGL 渲染上下文物件。
步驟 2 - 定義幾何體並將其儲存在緩衝區物件中
正方形可以使用兩個三角形繪製。在本例中,我們提供了兩個三角形(具有一個公共邊)和索引的頂點。
var vertices = [ -0.5,0.5,0.0, -0.5,-0.5,0.0, 0.5,-0.5,0.0, 0.5,0.5,0.0 ]; indices = [3,2,1,3,1,0];
步驟 3 - 建立和編譯著色器程式
在此步驟中,您需要編寫頂點著色器和片段著色器程式,編譯它們,並透過連結這兩個程式建立一個組合程式。
頂點著色器 - 在程式的頂點著色器中,我們定義了向量屬性來儲存 3D 座標並將其分配給 gl_position。
var vertCode = 'attribute vec3 coordinates;' + 'void main(void) {' + ' gl_Position = vec4(coordinates, 1.0);' + '}';
片段著色器 - 在片段著色器中,我們只需將片段顏色分配給 gl_FragColor 變數。
var fragCode = 'void main(void) {' +' gl_FragColor = vec4(0.5, 0.3, 0.0, 7.5);' +'}';
步驟 4 - 將著色器程式關聯到緩衝區物件
在此步驟中,我們將緩衝區物件與著色器程式關聯。
步驟 5 - 繪製所需物件
由於我們正在使用索引繪製兩個三角形以形成一個四邊形,因此我們將使用drawElements()方法。對於此方法,我們必須傳遞索引的數量。indices.length的值給出索引的數量。
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);
示例 – 繪製四邊形
以下程式顯示瞭如何建立WebGL應用程式以繪製四邊形。
<!doctype html> <html> <body> <canvas width = "570" height = "570" id = "my_Canvas"></canvas> <script> /*============ Creating a canvas =================*/ var canvas = document.getElementById('my_Canvas'); gl = canvas.getContext('experimental-webgl'); /*========== Defining and storing the geometry =========*/ var vertices = [ -0.5,0.5,0.0, -0.5,-0.5,0.0, 0.5,-0.5,0.0, 0.5,0.5,0.0 ]; indices = [3,2,1,3,1,0]; // Create an empty buffer object to store vertex buffer var vertex_buffer = gl.createBuffer(); // Bind appropriate array buffer to it gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); // Pass the vertex data to the buffer gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); // Unbind the buffer gl.bindBuffer(gl.ARRAY_BUFFER, null); // Create an empty buffer object to store Index buffer var Index_Buffer = gl.createBuffer(); // Bind appropriate array buffer to it gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer); // Pass the vertex data to the buffer gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); // Unbind the buffer gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); /*====================== Shaders =======================*/ // Vertex shader source code var vertCode = 'attribute vec3 coordinates;' + 'void main(void) {' + ' gl_Position = vec4(coordinates, 1.0);' + '}'; // Create a vertex shader object var vertShader = gl.createShader(gl.VERTEX_SHADER); // Attach vertex shader source code gl.shaderSource(vertShader, vertCode); // Compile the vertex shader gl.compileShader(vertShader); // Fragment shader source code var fragCode = 'void main(void) {' + ' gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' + '}'; // Create fragment shader object var fragShader = gl.createShader(gl.FRAGMENT_SHADER); // Attach fragment shader source code gl.shaderSource(fragShader, fragCode); // Compile the fragmentt shader gl.compileShader(fragShader); // Create a shader program object to // store the combined shader program var shaderProgram = gl.createProgram(); // Attach a vertex shader gl.attachShader(shaderProgram, vertShader); // Attach a fragment shader gl.attachShader(shaderProgram, fragShader); // Link both the programs gl.linkProgram(shaderProgram); // Use the combined shader program object gl.useProgram(shaderProgram); /* ======= Associating shaders to buffer objects =======*/ // Bind vertex buffer object gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); // Bind index buffer object gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer); // Get the attribute location var coord = gl.getAttribLocation(shaderProgram, "coordinates"); // Point an attribute to the currently bound VBO gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0); // Enable the attribute gl.enableVertexAttribArray(coord); /*============= Drawing the Quad ================*/ // Clear the canvas gl.clearColor(0.5, 0.5, 0.5, 0.9); // Enable the depth test gl.enable(gl.DEPTH_TEST); // Clear the color buffer bit gl.clear(gl.COLOR_BUFFER_BIT); // Set the view port gl.viewport(0,0,canvas.width,canvas.height); // Draw the triangle gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0); </script> </body> </html>
如果執行此示例,它將產生以下輸出:
WebGL - 顏色
在我們之前的所有示例中,我們透過為gl_FragColor變數分配所需的顏色值來為物件應用顏色。除此之外,我們還可以為每個頂點定義顏色 - 就像頂點座標和索引一樣。本章以一個示例來演示如何使用WebGL為四邊形應用顏色。
應用顏色
要應用顏色,您必須使用RGB值在JavaScript陣列中為每個頂點定義顏色。您可以為所有頂點分配相同的值,以使物件具有唯一的顏色。定義顏色後,您必須建立一個顏色緩衝區並將這些值儲存在其中,並將其與頂點著色器屬性關聯。
在頂點著色器中,除了儲存頂點位置的座標屬性外,我們還定義了一個attribute和一個varying來處理顏色。
color屬性儲存每個頂點的顏色值,而varying是作為片段著色器輸入傳遞的變數。因此,我們必須將color值分配給varying。
在片段著色器中,儲存顏色值的varying被分配給gl_FragColor,gl_FragColor儲存物件的最終顏色。
應用顏色的步驟
建立WebGL應用程式以繪製四邊形併為其應用顏色需要以下步驟。
步驟 1 - 準備畫布並獲取 WebGL 渲染上下文
在此步驟中,我們使用 getContext() 獲取 WebGL 渲染上下文物件。
步驟 2 - 定義幾何體並將其儲存在緩衝區物件中
正方形可以使用兩個三角形繪製。因此,在本例中,我們提供了兩個三角形(具有一個公共邊)和索引的頂點。由於我們希望為其應用顏色,因此還定義了一個儲存顏色值的變數,並將每個(紅色、藍色、綠色和粉紅色)的顏色值分配給它。
var vertices = [ -0.5,0.5,0.0, -0.5,-0.5,0.0, 0.5,-0.5,0.0, 0.5,0.5,0.0 ]; var colors = [ 0,0,1, 1,0,0, 0,1,0, 1,0,1,]; indices = [3,2,1,3,1,0];
步驟 3 - 建立和編譯著色器程式
在此步驟中,您需要編寫頂點著色器和片段著色器程式,編譯它們,並透過連結這兩個程式建立一個組合程式。
頂點著色器 - 在程式的頂點著色器中,我們定義向量屬性來儲存3D座標(位置)和每個頂點的顏色。宣告一個varing變數以將顏色值從頂點著色器傳遞到片段著色器。最後,將儲存在顏色屬性中的值分配給varying。
var vertCode = 'attribute vec3 coordinates;'+ 'attribute vec3 color;'+ 'varying vec3 vColor;'+ 'void main(void) {' + ' gl_Position = vec4(coordinates, 1.0);' + 'vColor = color;'+ '}';
片段著色器 - 在片段著色器中,我們將varying分配給gl_FragColor變數。
var fragCode = 'precision mediump float;'+ 'varying vec3 vColor;'+ 'void main(void) {'+ 'gl_FragColor = vec4(vColor, 1.);'+ '}';
步驟4 - 將著色器程式與緩衝區物件關聯
在此步驟中,我們將緩衝區物件與著色器程式關聯。
步驟 5 - 繪製所需物件
由於我們正在使用索引繪製兩個三角形以形成一個四邊形,因此我們將使用drawElements()方法。對於此方法,我們必須傳遞索引的數量。indices.length的值表示索引的數量。
gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0);
示例 – 應用顏色
以下程式演示瞭如何使用WebGL應用程式繪製四邊形併為其應用顏色。
<!doctype html> <html> <body> <canvas width = "300" height = "300" id = "my_Canvas"></canvas> <script> /*============= Creating a canvas ==================*/ var canvas = document.getElementById('my_Canvas'); gl = canvas.getContext('experimental-webgl'); /*========== Defining and storing the geometry ==========*/ var vertices = [ -0.5,0.5,0.0, -0.5,-0.5,0.0, 0.5,-0.5,0.0, 0.5,0.5,0.0 ]; var colors = [0,0,1, 1,0,0, 0,1,0, 1,0,1,]; indices = [3,2,1,3,1,0]; // Create an empty buffer object and store vertex data var vertex_buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); gl.bindBuffer(gl.ARRAY_BUFFER, null); // Create an empty buffer object and store Index data var Index_Buffer = gl.createBuffer(); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, null); // Create an empty buffer object and store color data var color_buffer = gl.createBuffer (); gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW); /*======================= Shaders =======================*/ // vertex shader source code var vertCode = 'attribute vec3 coordinates;'+ 'attribute vec3 color;'+ 'varying vec3 vColor;'+ 'void main(void) {' + ' gl_Position = vec4(coordinates, 1.0);' + 'vColor = color;'+ '}'; // Create a vertex shader object var vertShader = gl.createShader(gl.VERTEX_SHADER); // Attach vertex shader source code gl.shaderSource(vertShader, vertCode); // Compile the vertex shader gl.compileShader(vertShader); // fragment shader source code var fragCode = 'precision mediump float;'+ 'varying vec3 vColor;'+ 'void main(void) {'+ 'gl_FragColor = vec4(vColor, 1.);'+ '}'; // Create fragment shader object var fragShader = gl.createShader(gl.FRAGMENT_SHADER); // Attach fragment shader source code gl.shaderSource(fragShader, fragCode); // Compile the fragmentt shader gl.compileShader(fragShader); // Create a shader program object to // store the combined shader program var shaderProgram = gl.createProgram(); // Attach a vertex shader gl.attachShader(shaderProgram, vertShader); // Attach a fragment shader gl.attachShader(shaderProgram, fragShader); // Link both the programs gl.linkProgram(shaderProgram); // Use the combined shader program object gl.useProgram(shaderProgram); /* ======== Associating shaders to buffer objects =======*/ // Bind vertex buffer object gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); // Bind index buffer object gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, Index_Buffer); // Get the attribute location var coord = gl.getAttribLocation(shaderProgram, "coordinates"); // point an attribute to the currently bound VBO gl.vertexAttribPointer(coord, 3, gl.FLOAT, false, 0, 0); // Enable the attribute gl.enableVertexAttribArray(coord); // bind the color buffer gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer); // get the attribute location var color = gl.getAttribLocation(shaderProgram, "color"); // point attribute to the volor buffer object gl.vertexAttribPointer(color, 3, gl.FLOAT, false,0,0) ; // enable the color attribute gl.enableVertexAttribArray(color); /*============Drawing the Quad====================*/ // Clear the canvas gl.clearColor(0.5, 0.5, 0.5, 0.9); // Enable the depth test gl.enable(gl.DEPTH_TEST); // Clear the color buffer bit gl.clear(gl.COLOR_BUFFER_BIT); // Set the view port gl.viewport(0,0,canvas.width,canvas.height); //Draw the triangle gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT,0); </script> </body> </html>
如果執行此示例,它將產生以下輸出:
WebGL - 平移
到目前為止,我們討論瞭如何使用WebGL繪製各種形狀並在其中應用顏色。在這裡,在本章中,我們將以一個示例來展示如何平移三角形。
平移
平移是WebGL提供的仿射變換之一。使用平移,我們可以移動三角形(任何物件)在xyz平面上。假設我們有一個三角形[a,b,c],我們希望將三角形移動到一個位置,該位置向正X軸移動5個單位,向正Y軸移動3個單位。那麼新的頂點將是[a+5,b+3,c+0]。這意味著,要平移三角形,我們需要將平移距離(例如,tx、ty、tz)新增到每個頂點。
由於它是一個逐頂點操作,因此我們可以在頂點著色器程式中執行它。
在頂點著色器中,除了儲存頂點位置的屬性coordinates外,我們還定義了一個uniform變數來儲存平移距離(x、y、z)。稍後,我們將此uniform變數新增到coordinates變數中,並將結果分配給gl_Position變數。
注意 - 由於頂點著色器將在每個頂點上執行,因此三角形的每個頂點都將被平移。
平移三角形的步驟
建立WebGL應用程式以繪製三角形並將其平移到新位置需要以下步驟。
步驟 1 - 準備畫布並獲取 WebGL 渲染上下文
在此步驟中,我們使用 getContext() 獲取 WebGL 渲染上下文物件。
步驟 2 - 定義幾何體並將其儲存在緩衝區物件中
由於我們正在繪製三角形,因此我們必須傳遞三角形的三個頂點,並將它們儲存在緩衝區中。
var vertices = [ -0.5,0.5,0.0, -0.5,-0.5,0.0, 0.5,-0.5,0.0, ];
步驟 3 - 建立和編譯著色器程式
在此步驟中,您需要編寫頂點著色器和片段著色器程式,編譯它們,並透過連結這兩個程式建立一個組合程式。
頂點著色器 - 在程式的頂點著色器中,我們定義了一個向量屬性來儲存3D座標。同時,我們定義了一個uniform變數來儲存平移距離,最後,我們將這兩個值相加並將其分配給gl_position,gl_position儲存頂點的最終位置。
var vertCode = 'attribute vec4 coordinates;' + 'uniform vec4 translation;'+ 'void main(void) {' + ' gl_Position = coordinates + translation;' + '}';
片段著色器 - 在片段著色器中,我們只需將片段顏色分配給變數gl_FragColor。
var fragCode = 'void main(void) {' +' gl_FragColor = vec4(1, 0.5, 0.0, 1);' +'}';
步驟 4 - 將著色器程式關聯到緩衝區物件
在此步驟中,我們將緩衝區物件與著色器程式關聯。
步驟 5 - 繪製所需物件
由於我們正在使用索引繪製三角形,因此我們將使用drawArrays()方法。對於此方法,我們必須傳遞要考慮的頂點/元素的數量。由於我們正在繪製三角形,因此我們將傳遞3作為引數。
gl.drawArrays(gl.TRIANGLES, 0, 3);
示例 – 平移三角形
以下示例演示瞭如何在xyz平面上平移三角形。
<!doctype html> <html> <body> <canvas width = "300" height = "300" id = "my_Canvas"></canvas> <script> /*=================Creating a canvas=========================*/ var canvas = document.getElementById('my_Canvas'); gl = canvas.getContext('experimental-webgl'); /*===========Defining and storing the geometry==============*/ var vertices = [ -0.5,0.5,0.0, -0.5,-0.5,0.0, 0.5,-0.5,0.0, ]; //Create an empty buffer object and store vertex data var vertex_buffer = gl.createBuffer(); //Create a new buffer gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); //bind it to the current buffer gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); // Pass the buffer data gl.bindBuffer(gl.ARRAY_BUFFER, null); /*========================Shaders============================*/ //vertex shader source code var vertCode = 'attribute vec4 coordinates;' + 'uniform vec4 translation;'+ 'void main(void) {' + ' gl_Position = coordinates + translation;' + '}'; //Create a vertex shader program object and compile it var vertShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertShader, vertCode); gl.compileShader(vertShader); //fragment shader source code var fragCode = 'void main(void) {' + ' gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' + '}'; //Create a fragment shader program object and compile it var fragShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragShader, fragCode); gl.compileShader(fragShader); //Create and use combiened shader program var shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, vertShader); gl.attachShader(shaderProgram, fragShader); gl.linkProgram(shaderProgram); gl.useProgram(shaderProgram); /* ===========Associating shaders to buffer objects============*/ gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); var coordinatesVar = gl.getAttribLocation(shaderProgram, "coordinates"); gl.vertexAttribPointer(coordinatesVar, 3, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(coordinatesVar); /* ==========translation======================================*/ var Tx = 0.5, Ty = 0.5, Tz = 0.0; var translation = gl.getUniformLocation(shaderProgram, 'translation'); gl.uniform4f(translation, Tx, Ty, Tz, 0.0); /*=================Drawing the riangle and transforming it========================*/ gl.clearColor(0.5, 0.5, 0.5, 0.9); gl.enable(gl.DEPTH_TEST); gl.clear(gl.COLOR_BUFFER_BIT); gl.viewport(0,0,canvas.width,canvas.height); gl.drawArrays(gl.TRIANGLES, 0, 3); </script> </body> </html>
如果執行此示例,它將產生以下輸出:
WebGL - 縮放
在本章中,我們將以一個示例來演示如何使用WebGL修改三角形的比例。
縮放
縮放只不過是增加或減小物件的大小。例如,如果一個三角形的頂點大小為[a,b,c],那麼頂點為[2a,2b,2c]的三角形將是其大小的兩倍。因此,要縮放三角形,您必須將每個頂點乘以縮放因子。您也可以縮放特定頂點。
要縮放三角形,在程式的頂點著色器中,我們建立一個uniform矩陣並將座標值與該矩陣相乘。稍後,我們傳遞一個4×4對角矩陣,該矩陣在對角位置(最後一個對角位置為1)具有x、y、z座標的縮放因子。
所需步驟
建立WebGL應用程式以縮放三角形需要以下步驟。
步驟 1 - 準備畫布並獲取 WebGL 渲染上下文
在此步驟中,我們使用 getContext() 獲取 WebGL 渲染上下文物件。
步驟 2 - 定義幾何體並將其儲存在緩衝區物件中
由於我們正在繪製三角形,因此我們必須傳遞三角形的三個頂點,並將它們儲存在緩衝區中。
var vertices = [ -0.5,0.5,0.0, -0.5,-0.5,0.0, 0.5,-0.5,0.0, ];
步驟 3 - 建立和編譯著色器程式
在此步驟中,您需要編寫頂點著色器和片段著色器程式,編譯它們,並透過連結這兩個程式建立一個組合程式。
頂點著色器 - 在程式的頂點著色器中,我們定義了一個向量屬性來儲存3D座標。同時,我們定義了一個uniform矩陣來儲存縮放因子,最後,我們將這兩個值相乘並將其分配給gl_position,gl_position儲存頂點的最終位置。
var vertCode = 'attribute vec4 coordinates;' + 'uniform mat4 u_xformMatrix;' + 'void main(void) {' + ' gl_Position = u_xformMatrix * coordinates;' + '}';
片段著色器 - 在片段著色器中,我們只需將片段顏色分配給 gl_FragColor 變數。
var fragCode = 'void main(void) {' +' gl_FragColor = vec4(1, 0.5, 0.0, 1);' +'}';
步驟4 - 將著色器程式與緩衝區物件關聯
在此步驟中,我們將緩衝區物件與著色器程式關聯。
步驟 5 - 繪製所需物件
由於我們正在使用索引繪製三角形,因此我們使用drawArrays()方法。對於此方法,我們必須傳遞要考慮的頂點/元素的數量。由於我們正在繪製三角形,因此我們將傳遞3作為引數。
gl.drawArrays(gl.TRIANGLES, 0, 3);
示例 – 縮放三角形
以下示例演示瞭如何縮放三角形 -
<!doctype html> <html> <body> <canvas width = "300" height = "300" id = "my_Canvas"></canvas> <script> /*=================Creating a canvas=========================*/ var canvas = document.getElementById('my_Canvas'); gl = canvas.getContext('experimental-webgl'); /*===========Defining and storing the geometry==============*/ var vertices = [ -0.5,0.5,0.0, -0.5,-0.5,0.0, 0.5,-0.5,0.0, ]; //Create an empty buffer object and store vertex data var vertex_buffer = gl.createBuffer(); gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); gl.bindBuffer(gl.ARRAY_BUFFER, null); /*========================Shaders============================*/ //Vertex shader source code var vertCode = 'attribute vec4 coordinates;' + 'uniform mat4 u_xformMatrix;' + 'void main(void) {' + ' gl_Position = u_xformMatrix * coordinates;' + '}'; //Create a vertex shader program object and compile it var vertShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertShader, vertCode); gl.compileShader(vertShader); //fragment shader source code var fragCode = 'void main(void) {' + ' gl_FragColor = vec4(0.0, 0.0, 0.0, 0.1);' + '}'; //Create a fragment shader program object and compile it var fragShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragShader, fragCode); gl.compileShader(fragShader); //Create and use combiened shader program var shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, vertShader); gl.attachShader(shaderProgram, fragShader); gl.linkProgram(shaderProgram); gl.useProgram(shaderProgram); /*===================scaling==========================*/ var Sx = 1.0, Sy = 1.5, Sz = 1.0; var xformMatrix = new Float32Array([ Sx, 0.0, 0.0, 0.0, 0.0, Sy, 0.0, 0.0, 0.0, 0.0, Sz, 0.0, 0.0, 0.0, 0.0, 1.0 ]); var u_xformMatrix = gl.getUniformLocation(shaderProgram, 'u_xformMatrix'); gl.uniformMatrix4fv(u_xformMatrix, false, xformMatrix); /* ===========Associating shaders to buffer objects============*/ gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); var coordinatesVar = gl.getAttribLocation(shaderProgram, "coordinates"); gl.vertexAttribPointer(coordinatesVar, 3, gl.FLOAT, false, 0, 0); gl.enableVertexAttribArray(coordinatesVar); /*=================Drawing the Quad========================*/ gl.clearColor(0.5, 0.5, 0.5, 0.9); gl.enable(gl.DEPTH_TEST); gl.clear(gl.COLOR_BUFFER_BIT); gl.viewport(0,0,canvas.width,canvas.height); gl.drawArrays(gl.TRIANGLES, 0, 3); </script> </body> </html>
如果執行此示例,它將產生以下輸出:
WebGL - 旋轉
在本章中,我們將以一個示例來演示如何使用WebGL旋轉三角形。
示例 – 旋轉三角形
以下程式顯示瞭如何使用WebGL旋轉三角形。
<!doctype html> <html> <body> <canvas width = "400" height = "400" id = "my_Canvas"></canvas> <script> /*=================Creating a canvas=========================*/ var canvas = document.getElementById('my_Canvas'); gl = canvas.getContext('experimental-webgl'); /*===========Defining and storing the geometry==============*/ var vertices = [ -1,-1,-1, 1,-1,-1, 1, 1,-1 ]; var colors = [ 1,1,1, 1,1,1, 1,1,1 ]; var indices = [ 0,1,2 ]; //Create and store data into vertex buffer var vertex_buffer = gl.createBuffer (); gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); //Create and store data into color buffer var color_buffer = gl.createBuffer (); gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW); //Create and store data into index buffer var index_buffer = gl.createBuffer (); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); /*==========================Shaders=========================*/ var vertCode = 'attribute vec3 position;'+ 'uniform mat4 Pmatrix;'+ 'uniform mat4 Vmatrix;'+ 'uniform mat4 Mmatrix;'+ 'attribute vec3 color;'+//the color of the point 'varying vec3 vColor;'+ 'void main(void) { '+//pre-built function 'gl_Position = Pmatrix*Vmatrix*Mmatrix*vec4(position, 1.);'+ 'vColor = color;'+ '}'; var fragCode = 'precision mediump float;'+ 'varying vec3 vColor;'+ 'void main(void) {'+ 'gl_FragColor = vec4(vColor, 1.);'+ '}'; var vertShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertShader, vertCode); gl.compileShader(vertShader); var fragShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragShader, fragCode); gl.compileShader(fragShader); var shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, vertShader); gl.attachShader(shaderProgram, fragShader); gl.linkProgram(shaderProgram); /*===========associating attributes to vertex shader ============*/ var Pmatrix = gl.getUniformLocation(shaderProgram, "Pmatrix"); var Vmatrix = gl.getUniformLocation(shaderProgram, "Vmatrix"); var Mmatrix = gl.getUniformLocation(shaderProgram, "Mmatrix"); gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); var position = gl.getAttribLocation(shaderProgram, "position"); gl.vertexAttribPointer(position, 3, gl.FLOAT, false,0,0) ; //position gl.enableVertexAttribArray(position); gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer); var color = gl.getAttribLocation(shaderProgram, "color"); gl.vertexAttribPointer(color, 3, gl.FLOAT, false,0,0) ; //color gl.enableVertexAttribArray(color); gl.useProgram(shaderProgram); /*========================= MATRIX ========================= */ function get_projection(angle, a, zMin, zMax) { var ang = Math.tan((angle*.5)*Math.PI/180);//angle*.5 return [ 0.5/ang, 0 , 0, 0, 0, 0.5*a/ang, 0, 0, 0, 0, -(zMax+zMin)/(zMax-zMin), -1, 0, 0, (-2*zMax*zMin)/(zMax-zMin), 0 ]; } var proj_matrix = get_projection(40, canvas.width/canvas.height, 1, 100); var mov_matrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]; var view_matrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]; //translating z view_matrix[14] = view_matrix[14]-6; //zoom /*=======================rotation========================*/ function rotateZ(m, angle) { var c = Math.cos(angle); var s = Math.sin(angle); var mv0 = m[0], mv4 = m[4], mv8 = m[8]; m[0] = c*m[0]-s*m[1]; m[4] = c*m[4]-s*m[5]; m[8] = c*m[8]-s*m[9]; m[1] = c*m[1]+s*mv0; m[5] = c*m[5]+s*mv4; m[9] = c*m[9]+s*mv8; } /*=================Drawing===========================*/ var time_old = 0; var animate = function(time) { var dt = time-time_old; rotateZ(mov_matrix, dt*0.002); time_old = time; gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL); gl.clearColor(0.5, 0.5, 0.5, 0.9); gl.clearDepth(1.0); gl.viewport(0.0, 0.0, canvas.width, canvas.height); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.uniformMatrix4fv(Pmatrix, false, proj_matrix); gl.uniformMatrix4fv(Vmatrix, false, view_matrix); gl.uniformMatrix4fv(Mmatrix, false, mov_matrix); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer); gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0); window.requestAnimationFrame(animate); } animate(0); </script> </body> </html>
如果執行此示例,它將產生以下輸出:
WebGL - 立方體旋轉
在本章中,我們將以一個示例來演示如何使用WebGL繪製旋轉的3D立方體。
示例 – 繪製旋轉的3D立方體
以下程式顯示瞭如何繪製旋轉的3D立方體 -
<!doctype html> <html> <body> <canvas width = "570" height = "570" id = "my_Canvas"></canvas> <script> /*============= Creating a canvas =================*/ var canvas = document.getElementById('my_Canvas'); gl = canvas.getContext('experimental-webgl'); /*============ Defining and storing the geometry =========*/ var vertices = [ -1,-1,-1, 1,-1,-1, 1, 1,-1, -1, 1,-1, -1,-1, 1, 1,-1, 1, 1, 1, 1, -1, 1, 1, -1,-1,-1, -1, 1,-1, -1, 1, 1, -1,-1, 1, 1,-1,-1, 1, 1,-1, 1, 1, 1, 1,-1, 1, -1,-1,-1, -1,-1, 1, 1,-1, 1, 1,-1,-1, -1, 1,-1, -1, 1, 1, 1, 1, 1, 1, 1,-1, ]; var colors = [ 5,3,7, 5,3,7, 5,3,7, 5,3,7, 1,1,3, 1,1,3, 1,1,3, 1,1,3, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 1,0,0, 1,0,0, 1,0,0, 1,0,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0 ]; var indices = [ 0,1,2, 0,2,3, 4,5,6, 4,6,7, 8,9,10, 8,10,11, 12,13,14, 12,14,15, 16,17,18, 16,18,19, 20,21,22, 20,22,23 ]; // Create and store data into vertex buffer var vertex_buffer = gl.createBuffer (); gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); // Create and store data into color buffer var color_buffer = gl.createBuffer (); gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW); // Create and store data into index buffer var index_buffer = gl.createBuffer (); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); /*=================== Shaders =========================*/ var vertCode = 'attribute vec3 position;'+ 'uniform mat4 Pmatrix;'+ 'uniform mat4 Vmatrix;'+ 'uniform mat4 Mmatrix;'+ 'attribute vec3 color;'+//the color of the point 'varying vec3 vColor;'+ 'void main(void) { '+//pre-built function 'gl_Position = Pmatrix*Vmatrix*Mmatrix*vec4(position, 1.);'+ 'vColor = color;'+ '}'; var fragCode = 'precision mediump float;'+ 'varying vec3 vColor;'+ 'void main(void) {'+ 'gl_FragColor = vec4(vColor, 1.);'+ '}'; var vertShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertShader, vertCode); gl.compileShader(vertShader); var fragShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragShader, fragCode); gl.compileShader(fragShader); var shaderProgram = gl.createProgram(); gl.attachShader(shaderProgram, vertShader); gl.attachShader(shaderProgram, fragShader); gl.linkProgram(shaderProgram); /* ====== Associating attributes to vertex shader =====*/ var Pmatrix = gl.getUniformLocation(shaderProgram, "Pmatrix"); var Vmatrix = gl.getUniformLocation(shaderProgram, "Vmatrix"); var Mmatrix = gl.getUniformLocation(shaderProgram, "Mmatrix"); gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); var position = gl.getAttribLocation(shaderProgram, "position"); gl.vertexAttribPointer(position, 3, gl.FLOAT, false,0,0) ; // Position gl.enableVertexAttribArray(position); gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer); var color = gl.getAttribLocation(shaderProgram, "color"); gl.vertexAttribPointer(color, 3, gl.FLOAT, false,0,0) ; // Color gl.enableVertexAttribArray(color); gl.useProgram(shaderProgram); /*==================== MATRIX =====================*/ function get_projection(angle, a, zMin, zMax) { var ang = Math.tan((angle*.5)*Math.PI/180);//angle*.5 return [ 0.5/ang, 0 , 0, 0, 0, 0.5*a/ang, 0, 0, 0, 0, -(zMax+zMin)/(zMax-zMin), -1, 0, 0, (-2*zMax*zMin)/(zMax-zMin), 0 ]; } var proj_matrix = get_projection(40, canvas.width/canvas.height, 1, 100); var mov_matrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]; var view_matrix = [1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1]; // translating z view_matrix[14] = view_matrix[14]-6;//zoom /*==================== Rotation ====================*/ function rotateZ(m, angle) { var c = Math.cos(angle); var s = Math.sin(angle); var mv0 = m[0], mv4 = m[4], mv8 = m[8]; m[0] = c*m[0]-s*m[1]; m[4] = c*m[4]-s*m[5]; m[8] = c*m[8]-s*m[9]; m[1]=c*m[1]+s*mv0; m[5]=c*m[5]+s*mv4; m[9]=c*m[9]+s*mv8; } function rotateX(m, angle) { var c = Math.cos(angle); var s = Math.sin(angle); var mv1 = m[1], mv5 = m[5], mv9 = m[9]; m[1] = m[1]*c-m[2]*s; m[5] = m[5]*c-m[6]*s; m[9] = m[9]*c-m[10]*s; m[2] = m[2]*c+mv1*s; m[6] = m[6]*c+mv5*s; m[10] = m[10]*c+mv9*s; } function rotateY(m, angle) { var c = Math.cos(angle); var s = Math.sin(angle); var mv0 = m[0], mv4 = m[4], mv8 = m[8]; m[0] = c*m[0]+s*m[2]; m[4] = c*m[4]+s*m[6]; m[8] = c*m[8]+s*m[10]; m[2] = c*m[2]-s*mv0; m[6] = c*m[6]-s*mv4; m[10] = c*m[10]-s*mv8; } /*================= Drawing ===========================*/ var time_old = 0; var animate = function(time) { var dt = time-time_old; rotateZ(mov_matrix, dt*0.005);//time rotateY(mov_matrix, dt*0.002); rotateX(mov_matrix, dt*0.003); time_old = time; gl.enable(gl.DEPTH_TEST); gl.depthFunc(gl.LEQUAL); gl.clearColor(0.5, 0.5, 0.5, 0.9); gl.clearDepth(1.0); gl.viewport(0.0, 0.0, canvas.width, canvas.height); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.uniformMatrix4fv(Pmatrix, false, proj_matrix); gl.uniformMatrix4fv(Vmatrix, false, view_matrix); gl.uniformMatrix4fv(Mmatrix, false, mov_matrix); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer); gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0); window.requestAnimationFrame(animate); } animate(0); </script> </body> </html>
如果執行此示例,它將產生以下輸出:
WebGL - 互動式立方體
在本章中,我們將以一個示例來演示如何繪製一個可以使用滑鼠控制旋轉的3D立方體。
示例 – 繪製互動式立方體
以下程式顯示瞭如何使用滑鼠控制旋轉立方體 -
<!doctype html> <html> <body> <canvas width = "570" height = "570" id = "my_Canvas"></canvas> <script> /*============= Creating a canvas ======================*/ var canvas = document.getElementById('my_Canvas'); gl = canvas.getContext('experimental-webgl'); /*========== Defining and storing the geometry ==========*/ var vertices = [ -1,-1,-1, 1,-1,-1, 1, 1,-1, -1, 1,-1, -1,-1, 1, 1,-1, 1, 1, 1, 1, -1, 1, 1, -1,-1,-1, -1, 1,-1, -1, 1, 1, -1,-1, 1, 1,-1,-1, 1, 1,-1, 1, 1, 1, 1,-1, 1, -1,-1,-1, -1,-1, 1, 1,-1, 1, 1,-1,-1, -1, 1,-1, -1, 1, 1, 1, 1, 1, 1, 1,-1, ]; var colors = [ 5,3,7, 5,3,7, 5,3,7, 5,3,7, 1,1,3, 1,1,3, 1,1,3, 1,1,3, 0,0,1, 0,0,1, 0,0,1, 0,0,1, 1,0,0, 1,0,0, 1,0,0, 1,0,0, 1,1,0, 1,1,0, 1,1,0, 1,1,0, 0,1,0, 0,1,0, 0,1,0, 0,1,0 ]; var indices = [ 0,1,2, 0,2,3, 4,5,6, 4,6,7, 8,9,10, 8,10,11, 12,13,14, 12,14,15, 16,17,18, 16,18,19, 20,21,22, 20,22,23 ]; // Create and store data into vertex buffer var vertex_buffer = gl.createBuffer (); gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(vertices), gl.STATIC_DRAW); // Create and store data into color buffer var color_buffer = gl.createBuffer (); gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer); gl.bufferData(gl.ARRAY_BUFFER, new Float32Array(colors), gl.STATIC_DRAW); // Create and store data into index buffer var index_buffer = gl.createBuffer (); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer); gl.bufferData(gl.ELEMENT_ARRAY_BUFFER, new Uint16Array(indices), gl.STATIC_DRAW); /*=================== SHADERS =================== */ var vertCode = 'attribute vec3 position;'+ 'uniform mat4 Pmatrix;'+ 'uniform mat4 Vmatrix;'+ 'uniform mat4 Mmatrix;'+ 'attribute vec3 color;'+//the color of the point 'varying vec3 vColor;'+ 'void main(void) { '+//pre-built function 'gl_Position = Pmatrix*Vmatrix*Mmatrix*vec4(position, 1.);'+ 'vColor = color;'+ '}'; var fragCode = 'precision mediump float;'+ 'varying vec3 vColor;'+ 'void main(void) {'+ 'gl_FragColor = vec4(vColor, 1.);'+ '}'; var vertShader = gl.createShader(gl.VERTEX_SHADER); gl.shaderSource(vertShader, vertCode); gl.compileShader(vertShader); var fragShader = gl.createShader(gl.FRAGMENT_SHADER); gl.shaderSource(fragShader, fragCode); gl.compileShader(fragShader); var shaderprogram = gl.createProgram(); gl.attachShader(shaderprogram, vertShader); gl.attachShader(shaderprogram, fragShader); gl.linkProgram(shaderprogram); /*======== Associating attributes to vertex shader =====*/ var _Pmatrix = gl.getUniformLocation(shaderprogram, "Pmatrix"); var _Vmatrix = gl.getUniformLocation(shaderprogram, "Vmatrix"); var _Mmatrix = gl.getUniformLocation(shaderprogram, "Mmatrix"); gl.bindBuffer(gl.ARRAY_BUFFER, vertex_buffer); var _position = gl.getAttribLocation(shaderprogram, "position"); gl.vertexAttribPointer(_position, 3, gl.FLOAT, false,0,0); gl.enableVertexAttribArray(_position); gl.bindBuffer(gl.ARRAY_BUFFER, color_buffer); var _color = gl.getAttribLocation(shaderprogram, "color"); gl.vertexAttribPointer(_color, 3, gl.FLOAT, false,0,0) ; gl.enableVertexAttribArray(_color); gl.useProgram(shaderprogram); /*==================== MATRIX ====================== */ function get_projection(angle, a, zMin, zMax) { var ang = Math.tan((angle*.5)*Math.PI/180);//angle*.5 return [ 0.5/ang, 0 , 0, 0, 0, 0.5*a/ang, 0, 0, 0, 0, -(zMax+zMin)/(zMax-zMin), -1, 0, 0, (-2*zMax*zMin)/(zMax-zMin), 0 ]; } var proj_matrix = get_projection(40, canvas.width/canvas.height, 1, 100); var mo_matrix = [ 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 ]; var view_matrix = [ 1,0,0,0, 0,1,0,0, 0,0,1,0, 0,0,0,1 ]; view_matrix[14] = view_matrix[14]-6; /*================= Mouse events ======================*/ var AMORTIZATION = 0.95; var drag = false; var old_x, old_y; var dX = 0, dY = 0; var mouseDown = function(e) { drag = true; old_x = e.pageX, old_y = e.pageY; e.preventDefault(); return false; }; var mouseUp = function(e){ drag = false; }; var mouseMove = function(e) { if (!drag) return false; dX = (e.pageX-old_x)*2*Math.PI/canvas.width, dY = (e.pageY-old_y)*2*Math.PI/canvas.height; THETA+= dX; PHI+=dY; old_x = e.pageX, old_y = e.pageY; e.preventDefault(); }; canvas.addEventListener("mousedown", mouseDown, false); canvas.addEventListener("mouseup", mouseUp, false); canvas.addEventListener("mouseout", mouseUp, false); canvas.addEventListener("mousemove", mouseMove, false); /*=========================rotation================*/ function rotateX(m, angle) { var c = Math.cos(angle); var s = Math.sin(angle); var mv1 = m[1], mv5 = m[5], mv9 = m[9]; m[1] = m[1]*c-m[2]*s; m[5] = m[5]*c-m[6]*s; m[9] = m[9]*c-m[10]*s; m[2] = m[2]*c+mv1*s; m[6] = m[6]*c+mv5*s; m[10] = m[10]*c+mv9*s; } function rotateY(m, angle) { var c = Math.cos(angle); var s = Math.sin(angle); var mv0 = m[0], mv4 = m[4], mv8 = m[8]; m[0] = c*m[0]+s*m[2]; m[4] = c*m[4]+s*m[6]; m[8] = c*m[8]+s*m[10]; m[2] = c*m[2]-s*mv0; m[6] = c*m[6]-s*mv4; m[10] = c*m[10]-s*mv8; } /*=================== Drawing =================== */ var THETA = 0, PHI = 0; var time_old = 0; var animate = function(time) { var dt = time-time_old; if (!drag) { dX *= AMORTIZATION, dY*=AMORTIZATION; THETA+=dX, PHI+=dY; } //set model matrix to I4 mo_matrix[0] = 1, mo_matrix[1] = 0, mo_matrix[2] = 0, mo_matrix[3] = 0, mo_matrix[4] = 0, mo_matrix[5] = 1, mo_matrix[6] = 0, mo_matrix[7] = 0, mo_matrix[8] = 0, mo_matrix[9] = 0, mo_matrix[10] = 1, mo_matrix[11] = 0, mo_matrix[12] = 0, mo_matrix[13] = 0, mo_matrix[14] = 0, mo_matrix[15] = 1; rotateY(mo_matrix, THETA); rotateX(mo_matrix, PHI); time_old = time; gl.enable(gl.DEPTH_TEST); // gl.depthFunc(gl.LEQUAL); gl.clearColor(0.5, 0.5, 0.5, 0.9); gl.clearDepth(1.0); gl.viewport(0.0, 0.0, canvas.width, canvas.height); gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT); gl.uniformMatrix4fv(_Pmatrix, false, proj_matrix); gl.uniformMatrix4fv(_Vmatrix, false, view_matrix); gl.uniformMatrix4fv(_Mmatrix, false, mo_matrix); gl.bindBuffer(gl.ELEMENT_ARRAY_BUFFER, index_buffer); gl.drawElements(gl.TRIANGLES, indices.length, gl.UNSIGNED_SHORT, 0); window.requestAnimationFrame(animate); } animate(0); </script> </body> </html>
如果執行此示例,它將產生以下輸出: