webgl值得重視的基礎構建

此篇文章的主要目的是鞏固本身對於建立webgl的時候一些知識點,主要參考了《webgl編程指南》以及_Hahn_的webgl環境搭建這篇文章,在此附上連接,方便你們查看( juejin.im/post/5baaf3… )。javascript

我會分爲三篇文章進行總結,爲何不在一篇文章中寫完呢?
由於我我的不太喜歡技術的文章寫的很長,那樣在讀的時候感受很煩。html

《webgl編程指南》這本書中知識點介紹的很詳細,所以我就不在此贅述那些基礎的知識點了,直接上代碼。java

實現一個紅色的長方形

最終咱們要實現的效果:git

相應的代碼web

<!DOCTYPE html>
<html lang="en">
<head>
	<meta charset="UTF-8">
	<title>Document</title>
	<script src="./lib/webgl-debug.js"></script>
	<script src="./lib/webgl-utils.js"></script>
	<script src="./lib/cuon-matrix.js"></script>
	<script src="./lib/cuon-utils.js"></script>
	<style>*{padding:0;margin:0;}</style>
</head>
<body onload="main()">
	<canvas id="canvas" width="750" height="1334"></canvas>
	<script type="text/javascript">
		var VSHADER_SOURCE = `
			attribute vec4 a_Position;
			varying vec2 uv;

			void main(){
				gl_Position = vec4(vec2(a_Position), 0.0, 1.0);
				uv = vec2(0.5, 0.5) * (vec2(a_Position) + vec2(1.0, 1.0));
			}
		`
		var FSHADER_SOURCE = `
			precision mediump float;
			varying vec2 uv;

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

		function main(){
			var canvas = document.getElementById('canvas');
			canvas.width = 750;
			canvas.height = 1334;

			var gl = getWebGLContext(canvas);
			if(!gl){
				console.log("Failed to get the tendering context for WevGl");
				return;
			}

			if(!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)){
				console.log("Failed to set the vertex information");
				return;
			}
			//清空畫布
			gl.clearColor(0.0, 0.0, 0.0, 1.0);
			gl.clear(gl.COLOR_BUFFER_BIT);
			
			var n = initVertexBuffers(gl);
			//繪製頂點
			gl.drawArrays(gl.TRIANGLE_FAN, 0, n);
			
		}

		function initVertexBuffers(gl){
			var verticesTexCoords = new Float32Array([
				1.0, -1.0,
				1.0, 1.0,
				-1.0, 1.0,
				-1.0, -1.0
			]);
			
			var n = 4;
			
			var vertexTexCoordBuffer = gl.createBuffer();
			if(!vertexTexCoordBuffer){
				console.log("Failed to create the buffer object");
				return -1;
			}
			
			gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexCoordBuffer);
			gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW);

			var FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;
			
			var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
			if(a_Position < 0){
				console.log("Failed to get the stroage location og a_Position");
			}
			gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 2, 0);
			
			gl.enableVertexAttribArray(a_Position);

			return n;

		}
	</script>
</body>
</html>

複製代碼

接下來咱們作具體的理解分析。

首先咱們能夠看到在開始咱們引用了幾個js的文件,這幾個文件是《webgl編程指南》中的一些輔助函數的文件,暫時咱們只是應用,不深刻的探究。編程

<script src="./lib/webgl-debug.js"></script>
<script src="./lib/webgl-utils.js"></script>
<script src="./lib/cuon-matrix.js"></script>
<script src="./lib/cuon-utils.js"></script>
複製代碼

咱們在DOM中能夠看到一個canvas的結構,這個DOM結構就是告訴瀏覽器咱們要一個畫布。之後繪製的webgl都會出如今這個canvas中。
接下來就是js這個重點了,咱們在js中首先定義了一個頂點着色器。canvas

var VSHADER_SOURCE = `
    attribute vec4 a_Position;
    varying vec2 uv;

    void main(){
        gl_Position = vec4(vec2(a_Position), 0.0, 1.0);
        uv = vec2(0.5, 0.5) * (vec2(a_Position) + vec2(1.0, 1.0));
    }
    `
複製代碼

什麼是頂點着色器呢?

頂點着色器就是用來描述頂點的特性(例如位置、顏色等)的程序。頂點是指二維或三維空間中的一個點,好比二維或者三維圖形的端點或焦點。數組

在上面咱們首先定義了一個attribute vec4 a_Position;這個a_Position變量的做用就是js向頂點着色器傳入頂點位置的入口,通俗來講就是告訴瀏覽器咱們要在哪幾個點的範圍內進行「做畫」,由於咱們如今做畫都是在二維平面上面進行的所以呢
gl_Position = vec4(vec2(a_Position), 0.0, 1.0);
這個賦值操做的第三個參數就是0.0,第四個參數咱們就設置爲默認的1.0。瀏覽器

而咱們定義的uv變量就是紋理的座標,這個uv變量通常會傳入片元着色器中進行操做,你可將其理解爲咱們「做畫」的座標。
uv = vec2(0.5, 0.5) * (vec2(a_Position) + vec2(1.0, 1.0));
可是這個計算是什麼鬼呢?
首先咱們在initVertexBuffers函數中能夠看到bash

var verticesTexCoords = new Float32Array([
	1.0, -1.0,
	1.0, 1.0,
	-1.0, 1.0,
	-1.0, -1.0
]);
複製代碼

這四個座標就是咱們想要畫的圖像的座標,也就是紋理座標了。

上面紅色框內就是咱們的紋理座標。

上面的藍色框內就是咱們着色器中的座標了。
咱們將紅色座標系的點帶入那個計算公式(注意是逐份量計算)

A => vec2(0.5, 0.5) * (vec2(1.0, -1.0) + vec2(1.0, 1.0)) => (1.0, 0.0)

B => vec2(0.5, 0.5) * (vec2(1.0, 1.0) + vec2(1.0, 1.0)) => (1.0, 1.0)

C => vec2(0.5, 0.5) * (vec2(-1.0, -1.0) + vec2(1.0, 1.0)) => (0.0, 1.0)

D => vec2(0.5, 0.5) * (vec2(-1.0, -1.0) + vec2(1.0, 1.0)) => (0.0, 0.0)

最後的結果是紅色座標系中的點變成了藍色座標系中對應的點,這就是那個計算公式的做用。

OK到這裏頂點着色器就研究完成了,接下來就是片元着色器了

什麼是片元着色器呢?

片元着色器就是進行逐片元處理過程的程序,片元是一個webgl術語,你能夠將其理解爲像素。

var FSHADER_SOURCE = `
	precision mediump float;
	varying vec2 uv;

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

在片元着色器中咱們首先定義了precision mediump float;
這句話的做用就是一個精度的規定,不加的話,片元着色器這裏就會報錯。
接着咱們使用了和在頂點着色器中相同的varying vec2 uv;接收了來自頂點着色器中的紋理座標信息(雖然在上面的代碼中咱們沒有使用紋理座標,可是你須要知道如何傳入)。

重點是在main函數中咱們給gl_FragColor賦值一個vec4類型的變量,這個賦值的做用就是告訴gl_FragColor使用什麼樣的顏色來進行渲染,vec4的四個值分別對應rgba。

js進行初始化操做

咱們寫好了頂點着色器和片元着色器之後瀏覽器並不能理解那是什麼,由於對於瀏覽器來講那就是一些字符串,所以咱們須要使用js進行一些列的操做。
首先第一步,先獲取DOM中的canvas,而後設置它的寬和高。

var gl = getWebGLContext(canvas);
if(!gl){
	console.log("Failed to get the tendering context for WevGl");
	return;
}
複製代碼

第二步,咱們使用上面的代碼將咱們獲取的canvas變爲了gl的畫布(通俗意義上你能夠這麼理解),而且咱們作了一個差錯處理

if(!initShaders(gl, VSHADER_SOURCE, FSHADER_SOURCE)){
	console.log("Failed to set the vertex information");
	return;
}
複製代碼

第三步,初始化shaders,這函數就是咱們引用lib文件夾下面的js文件中存在的,在此你暫時使用就能夠,此函數須要將咱們的「畫布」也就是gl傳入,同時須要傳入頂點着色器變量和片元着色器變量。

//清空畫布
gl.clearColor(0.0, 0.0, 0.0, 1.0);
gl.clear(gl.COLOR_BUFFER_BIT);

var n = initVertexBuffers(gl);
//繪製頂點
gl.drawArrays(gl.TRIANGLE_FAN, 0, n);
複製代碼

第四步,清空畫布開始做畫,咱們使用gl的函數先將畫布清空,而後使用咱們本身寫的initVertexBuffers函數初始化緩衝區(這個函數稍後會介紹),而且此函數會返回一個頂點的個數,最後咱們使用gl的drawArrays函數開始繪製。

最後就是詳解咱們本身寫的initVertexBuffers函數了,咱們須要知道緩衝區是什麼?

緩衝區對象是webgl系統中的一塊存儲區,你能夠在緩衝區對象中保存想要繪製的全部頂點的數據。
在上面咱們就說過傳入紋理座標的事情。

var verticesTexCoords = new Float32Array([
	1.0, -1.0,
	1.0, 1.0,
	-1.0, 1.0,
	-1.0, -1.0
]);
var n = 4;
複製代碼

在函數開始咱們首先定義了一個特殊的數組,這個數組的做用就是定義紋理的頂點座標,而且咱們定義了n變量,這個變量表示咱們想要建立的頂點個數。
接下來咱們須要建立出緩衝區,而且使用。

建立緩衝區對象

var vertexTexCoordBuffer = gl.createBuffer();
if(!vertexTexCoordBuffer){
	console.log("Failed to create the buffer object");
	return -1;
}
複製代碼

在上面的代碼中咱們使用傳入函數的gl的createBuffer方法將建立的緩衝區賦值給了vertexTexCoordBuffer變量,而且作了一個差錯判斷。

綁定緩衝區對象

gl.bindBuffer(gl.ARRAY_BUFFER, vertexTexCoordBuffer);
複製代碼

在此咱們使用gl的bindBuffer函數將咱們建立的緩衝區進行了綁定,bindBuffer的第一個參數表示緩衝區對象中包含了頂點的數據,第二個參數就是咱們以前建立的緩衝區對象。

將數據寫入緩衝區對象

gl.bufferData(gl.ARRAY_BUFFER, verticesTexCoords, gl.STATIC_DRAW);
複製代碼

bufferData函數的做用就是將咱們在上面定義的數組verticesTexCoords寫入緩衝區對象,此函數的第一個參數須要是gl.ARRAY_BUFFER或gl.ELEMENT_ARRAY_BUFFER,第一種參數和bindBufferbuffer表示的同樣都是頂點的數據,第二種參數表示頂點數據的索引值,第二個參數就是咱們紋理的頂點數組了,第三個參數表示程序將如何使用存儲在緩衝區對象中的數據,STATIC_DRAW表示只會向緩衝區對象中寫入一次數據,但須要繪製不少次。

將緩衝區對象分配給一個attribute變量

var FSIZE = verticesTexCoords.BYTES_PER_ELEMENT;

var a_Position = gl.getAttribLocation(gl.program, 'a_Position');
if(a_Position < 0){
	console.log("Failed to get the stroage location og a_Position");
}
gl.vertexAttribPointer(a_Position, 2, gl.FLOAT, false, FSIZE * 2, 0);
複製代碼

在上面的代碼中咱們首先定義了一下FSIZE變量,這個變量只是爲了咱們在賦值的時候方便操做,接着咱們定義了a_Position變量,而且使用了gl的方法獲取到了咱們在頂點着色器中定義的attribute類型的a_Position變量,最後咱們使用了vertexAttribPointer方法爲這個attribute變量進行賦值。

vertexAttribPointer函數的第一個參數就是咱們待分配的attribute變量的存儲位置;
第二個參數是指定緩衝區每一個頂點的份量個數(此處咱們使用的是2);
第三個參數是指定數據的格式。
第四個參數傳入true或false,代表是否將非浮點型的數據歸一化。
第五個參數指定相鄰兩個頂點間的字節數(由於咱們在數組中定義的是每兩個數值表示一個頂點,因此此處傳入2)。
最後一個參數表示緩衝區對象中的偏移量。

開啓attribute變量

gl.enableVertexAttribArray(a_Position);
複製代碼

在最後咱們須要開啓咱們要使用的attribute變量,這樣一個完整的buffer就建立完成了。

通過上面的操做,咱們就獲得了一個長方形了。
以上的知識都是比較基礎,但確實是很重要的東西。
代碼地址:gitee.com/wangtao_it_… 我本次的事例是此地址下面的basics.html文件。

相關文章
相關標籤/搜索