1. 程式人生 > >【OpenGL4.0】GLSL-使用Uniform Block實現著色器的資料共享

【OpenGL4.0】GLSL-使用Uniform Block實現著色器的資料共享

一、在GLSL中使用Uniform Block

在GLSL渲染語言中,我們經常採用緩衝區來儲存Uniform型的Block。

比如我們需要繪製下面這樣的效果:


在這裡我們繪製了一個圓,圓內部顏色和外部顏色不同,而且邊緣部分,顏色是平滑過渡的。

我們這樣來實現這一效果:

首先定義一個內徑和外徑。然後給這個正方形分配紋理座標,然後通過紋理座標離(0.5,0.5)的距離與內徑和外徑的關係來控制當前畫素點的顏色。

我們要在Shader中提供這樣的一些資訊:圓的內徑及外徑、內部顏色和外部顏色。採用Block來將這些資訊封裝在一起是合適的選擇。

採用Block的原因是:

如果你的程式中包含了多個著色器,而且這些著色器使用了相同的Uniform變數,你就不得不為每個著色器分別管理這些變數。Uniform變數的location是在程式連結的時候產生的,因此Uniform變數的location會隨著著色器的不同而發生變化。因此,這些Uniform變數的資料必須重新產生,然後應用到新的location上。

而Uniform Block正是為了使在著色器間共享Uniform資料變得更加容易而設計的。有了Uniform Block,我們可以建立一個緩衝區物件來儲存這些Uniform變數的值,然後將緩衝區物件繫結到Uniform Block。當著色器程式改變的時候,只需要將同樣的緩衝區對重新繫結到在新的著色器中與之相關的Block即可。

Uniform Block的定義和C/C++中的struct很類似。在本文的例子中我們需要定義的Uniform Block為:

方法一:

[cpp] view plaincopyprint?
  1. uniform BlobSettings {  
  2.   vec4 InnerColor;  
  3.   vec4 OuterColor;  
  4. float RadiusInner;  
  5. float RadiusOuter;  
  6. };  
uniform BlobSettings {
  vec4 InnerColor;
  vec4 OuterColor;
  float RadiusInner;
  float RadiusOuter;
};

或者是

方法二:

[cpp] view plaincopyprint?
  1. uniform BlobSettings{  
  2.     vec4 InnerColor;  
  3.     vec4 OuterColor;  
  4. float RadiusInner;  
  5. float RadiusOuter;  
  6. }Blob;  
uniform BlobSettings{
	vec4 InnerColor;
	vec4 OuterColor;
	float RadiusInner;
	float RadiusOuter;
}Blob;

這樣我們就定義了一個名為BlobSettings的Block,包含了4個uniform 變數。
在方法一中,Block中的變數依然是全域性域的一部分,取用的時候不用Block名。

而在方法二中,Block中的變數則是在Blob名字域中,取用的時候需要加上Block名。

這兩種方法在使用上有少許差別。

用來儲存uniform資料的緩衝區物件稱作UBO(uniform buffer object)。它只是一個繫結到特定location的緩衝區物件。

注意下面的程式碼是基於上述定義Block的方法二的基礎之上的。

下面貼出完整的GLSL渲染器程式碼:

basic_block.vert:

[cpp] view plaincopyprint?
  1. #version 400
  2. layout (location = 0) in vec3 VertexPosition;  
  3. layout (location = 1) in vec3 VertexTexCoord;  
  4. out vec3 TexCoord;  
  5. void main()  
  6. {  
  7.     TexCoord = VertexTexCoord;  
  8.     gl_Position = vec4(VertexPosition,1.0);  
  9. }  
#version 400

layout (location = 0) in vec3 VertexPosition;
layout (location = 1) in vec3 VertexTexCoord;

out vec3 TexCoord;

void main()
{
	TexCoord = VertexTexCoord;
	gl_Position = vec4(VertexPosition,1.0);
}

basic_block.frag:

[cpp] view plaincopyprint?
  1. #version 400
  2. in vec3 TexCoord;  
  3. layout (location = 0) out vec4 FragColor;  
  4. uniform BlobSettings{  
  5.     vec4 InnerColor;  
  6.     vec4 OuterColor;  
  7. float RadiusInner;  
  8. float RadiusOuter;  
  9. }Blob;//Use an instance name with a uniform block
  10. void main()  
  11. {  
  12. float dx = TexCoord.x - 0.5;  
  13. float dy = TexCoord.y - 0.5;  
  14. float dist = sqrt(dx * dx + dy * dy);  
  15. //smoothstep : perform Hermite interpolation between two values
  16.     FragColor = mix(Blob.InnerColor,Blob.OuterColor,  
  17.             smoothstep(Blob.RadiusInner,Blob.RadiusOuter,dist));  
  18. }  
#version 400

in vec3 TexCoord;
layout (location = 0) out vec4 FragColor;

uniform BlobSettings{
	vec4 InnerColor;
	vec4 OuterColor;
	float RadiusInner;
	float RadiusOuter;
}Blob;//Use an instance name with a uniform block

void main()
{
	float dx = TexCoord.x - 0.5;
	float dy = TexCoord.y - 0.5;
	float dist = sqrt(dx * dx + dy * dy);
	//smoothstep : perform Hermite interpolation between two values
	FragColor = mix(Blob.InnerColor,Blob.OuterColor,
			smoothstep(Blob.RadiusInner,Blob.RadiusOuter,dist));
}

UBO的設定:

[cpp] view plaincopyprint?
  1. void Scenebasic_block::setUniformBlock()  
  2. {  
  3.     GLuint blockIndex = glGetUniformBlockIndex(prog.getHandle(),  
  4. "BlobSettings");  
  5.     GLint blockSize;  
  6.     glGetActiveUniformBlockiv(prog.getHandle(),  
  7.                             blockIndex,  
  8.                             GL_UNIFORM_BLOCK_DATA_SIZE,  
  9.                             &blockSize);  
  10.     GLubyte *blockBuffer = (GLubyte*)malloc(blockSize);  
  11. //Query for the offsets of the each block variable
  12. //layout of the data within a uniform block is implementation dependent
  13. const GLchar *names[] = {"BlobSettings.InnerColor","BlobSettings.OuterColor",  
  14. "BlobSettings.RadiusInner","BlobSettings.RadiusOuter"};  
  15.     GLuint indices[4];  
  16.     glGetUniformIndices(prog.getHandle(), 4, names, indices);  
  17.     GLint offset[4];  
  18.     glGetActiveUniformsiv(prog.getHandle(), 4, indices, GL_UNIFORM_OFFSET, offset);  
  19.     GLfloat outerColor[] = {0.0f, 0.0f, 0.0f, 0.0f};  
  20.     GLfloat innerColor[] = {1.0f, 1.0f, 0.75f, 1.0f};  
  21.     GLfloat innerRadius = 0.25f, outerRadius = 0.45f;  
  22.     memcpy(blockBuffer + offset[0],innerColor,4 * sizeof(float));  
  23.     memcpy(blockBuffer + offset[1],outerColor,4 * sizeof(float));  
  24.     memcpy(blockBuffer + offset[2],&innerRadius,sizeof(float));  
  25.     memcpy(blockBuffer + offset[3],&outerRadius,sizeof(float));  
  26. //create OpenGL buffer UBO to store the data
  27.     GLuint uboHandle;  
  28.     glGenBuffers(1,&uboHandle);  
  29.     glBindBuffer(GL_UNIFORM_BUFFER,uboHandle);  
  30.     glBufferData(GL_UNIFORM_BUFFER,blockSize,blockBuffer,  
  31.                     GL_DYNAMIC_DRAW);  
  32. //bind the UBO th the uniform block
  33.     glBindBufferBase(GL_UNIFORM_BUFFER,blockIndex,uboHandle);  
  34.     free(blockBuffer);  
  35. }  
void Scenebasic_block::setUniformBlock()
{
	GLuint blockIndex = glGetUniformBlockIndex(prog.getHandle(),
												"BlobSettings");
	GLint blockSize;
	glGetActiveUniformBlockiv(prog.getHandle(),
							blockIndex,
							GL_UNIFORM_BLOCK_DATA_SIZE,
							&blockSize);
	GLubyte *blockBuffer = (GLubyte*)malloc(blockSize);
	//Query for the offsets of the each block variable
	//layout of the data within a uniform block is implementation dependent
	const GLchar *names[] = {"BlobSettings.InnerColor","BlobSettings.OuterColor",
							"BlobSettings.RadiusInner","BlobSettings.RadiusOuter"};
	GLuint indices[4];
	glGetUniformIndices(prog.getHandle(), 4, names, indices);

	GLint offset[4];
	glGetActiveUniformsiv(prog.getHandle(), 4, indices, GL_UNIFORM_OFFSET, offset);

	GLfloat outerColor[] = {0.0f, 0.0f, 0.0f, 0.0f};
	GLfloat innerColor[] = {1.0f, 1.0f, 0.75f, 1.0f};
	GLfloat innerRadius = 0.25f, outerRadius = 0.45f;

	memcpy(blockBuffer + offset[0],innerColor,4 * sizeof(float));
	memcpy(blockBuffer + offset[1],outerColor,4 * sizeof(float));
	memcpy(blockBuffer + offset[2],&innerRadius,sizeof(float));
	memcpy(blockBuffer + offset[3],&outerRadius,sizeof(float));

	//create OpenGL buffer UBO to store the data
	GLuint uboHandle;
	glGenBuffers(1,&uboHandle);
	glBindBuffer(GL_UNIFORM_BUFFER,uboHandle);
	glBufferData(GL_UNIFORM_BUFFER,blockSize,blockBuffer,
					GL_DYNAMIC_DRAW);

	//bind the UBO th the uniform block
	glBindBufferBase(GL_UNIFORM_BUFFER,blockIndex,uboHandle);

	free(blockBuffer);
}


二、在Uniform Block中使用佈局識別符號std140

1、std140的使用
由於UBO中資料的佈局和具體實現有關(記憶體對齊等等),因此我們需要在設定其值的時候查詢偏移量。然後,我們可以使用標準佈局std40來避免這一操作。這可以通過在定義Uniform Block的時候使用佈局識別符號來實現:

layout( std140 ) uniform BlobSettings {
   …
};

2、std140詳解

std40佈局識別符號要求GLSL著色器依據一組規則來組織Uniform Block中的變數,這樣使得我們能夠預先計算出其中的變數的偏移量。

變數的偏移量是根據起始偏移量和在其之前的變數的大小累積計算得到的。第一個成員變數的起始偏移量總是0.

std40的佈局規則

變數型別

變數大小/偏移量

標量資料型別(bool,int,uint,float)

基於基本機器型別的標量值大小

(例如,sizeof(GLfloat))

二元向量(bvec2,ivec2,uvec2,vec2)

標量型別大小的兩倍

三元向量(bvec3,ivec3,uvec3,vec3)

標量型別大小的四倍

三元向量(bvec4,ivec4,uvec4,vec4)

標量型別大小的四倍

標量的陣列或向量

陣列中每個元素大小是基本型別的大小,偏移量是其索引值(從0開始)與元素大小的乘積。整個陣列必須是vec4型別的大小的整數倍(不足將在尾部填充)。

一個或多個C列R行列主序矩陣組成的陣列

以C個向量(每個有R個元素)組成的陣列形式儲存。會像其他陣列一樣填充。

如果變數是M個列主序矩陣的陣列,那麼它的儲存形式是:M*C個向量(每個有R個元素)組成的陣列。

一個或多個R行C列的行主序矩陣組成的陣列

以R個向量(每個有C個元素)組成的陣列。預設像其他陣列一樣填充。

如果變數是M個行主序矩陣組成的陣列,則儲存形式是M*R個向量(每個有C個元素)組成的陣列。

單個結構體或多個結構體組成的陣列

單個結構體成員的偏移量和大小可以由前面的規則計算出。結構大小總是vec4大小的整數倍(不足在後面補齊)。

由結構組成的陣列,偏移量的計算需要考慮單個結構的對齊和補齊。結構的成員偏移量由前面的規則計算出。

相關推薦

OpenGL4.0GLSL-使用Uniform Block實現著色資料共享

一、在GLSL中使用Uniform Block 在GLSL渲染語言中,我們經常採用緩衝區來儲存Uniform型的Block。 比如我們需要繪製下面這樣的效果: 在這裡我們繪製了一個圓,圓內部顏色和外部顏色不同,而且邊緣部分,顏色是平滑過渡的。 我們這樣來實現這一效果: 首

51NOD-01011 最大公約數GCD

style lose gif lap blog %d 51nod ret display 【算法】歐幾裏德算法 #include<cstdio> int gcd(int a,int b) {return b==0?a:gcd(b,a%b);} int mai

51NOD-01018 排序

i++ logs closed img mes close for play class 【算法】排序 #include<cstdio> #include<algorithm> using namespace std; int n,a[50010

51NOD-01019 逆序數

+= open clas tdi for string d+ display algorithm 【算法】離散化+樹狀數組(求逆序對) 【題解】經典,原理是統計在i之前插入的且值≤i的個數,然後答案就是i-getsum(i) #include<cstdio>

51NOD-01106 質數檢測

scanf nbsp return span scan printf 技術分享 for == 【算法】數學 #include<cstdio> #include<cmath> bool ok(int x) { int m=(int)sqrt

51NOD-01118 機器人走方格

for space blog () algorithm cnblogs amp return closed 【算法】DP #include<cstdio> #include<algorithm> using namespace std; cons

51NOD-01089 最長回文子串 V2(Manacher算法)

lose 最長回文子串 gif () none print struct hide pac 【算法】回文樹 #include<cstdio> #include<algorithm> #include<cstring> using na

51NOD-01134 最長遞增子序列

子序列 can algorithm view hide 但是 open sin cst 【算法】動態規劃 【題解】經典模型:最長上升子序列(n log n) #include<cstdio> #include<algorithm> #includ

9.0對於java集合的叠代的底層分析

trac print post turn pan 很難 分享 對象 nal 前言:如果對java的集合的遍歷(主要是HashMap中的keySet() 和 entrySet()是如何取值並且可以實現遍歷的)不是很明白的話,有興趣深入了解的小夥伴,本文可以作為一個參考,由於時

TP3.2TP3.2下實現ajax分頁(原創+親測可用)

條件 func edit syn model 多條 ade ges var 一,寫在最開始:ajax分頁的原理,是利用了js的ajax執行請求,獲取分頁list和分頁page 【代碼塊】,去替換頁面顯示數據的【代碼塊】 技術:js的ajax + TP3.2的fet

TP5.0model的操作方法

新增 src ted lse 圖片 希望 ace 復制代碼 推薦 //默認主鍵為自動識別,如果需要指定,可以設置屬性: namespace app\index\model; use think\Model; class User extends Model {

原始碼剖析threadpool —— 基於 pthread 實現的簡單執行緒池

部落格新地址:https://github.com/AngryHacker/articles/issues/1#issue-369867252 執行緒池介紹 執行緒池可以說是專案中經常會用到的元件,在這裡假設讀者都有一定的多執行緒基礎,如果沒有的話不妨在這裡進行了解:POSIX

Java筆記多執行緒實現簡單的非同步運算

實現Callable介面,重寫call()方法,使操作執行緒池時能帶有返回值的效果: import java.util.concurrent.Callable; public class GetSumCallable implements Callable<Integer> {

Developer Logjavax ws rs實現Restful

分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!        

程式設計筆記執行緒池實現原始碼(從POCO中剝離出來)

原始碼下載:https://download.csdn.net/download/fzuim/10625204 CThreadPool類 /***************************************************************

SIGAI 4P計劃2.0免費來襲,給你一個深度學習python的機會

SIGAI 4P計劃一期啟動以來,收到了小夥伴們的熱烈反響。共1017人加入計劃學習,其中70%學生使用者,28%企業使用者。不少小夥伴希望加入,但由於人員和管理的限制,一期不再開放。 但是,SIGAI怎麼能辜負同學們一顆想天天向上的好學之心呢?在大家的殷殷期待下,SIGAI總結一期4P計劃經驗

BLE4.0Packet sniffer 過濾廣播MAC地址

一、實驗目的 1.在使用Packet sniffer 進行抓包時,由於有很多藍芽裝置在廣播,使自己很難看到自己裝置的廣播包,影響工作效率,所以需要過濾顯示廣播地址,只顯示自己的裝置廣播資料包; 二、實驗工具 1.USB-Dongle; 2.Packet sniffer2.18.1

樹莓派配置Nginx代理實現樹莓派遠端視訊監控

背景介紹: 在淘寶上入手一個樹莓派攝像頭,它是通過CSI介面連線到樹莓派板上,之前我使用的是CentOS系統,在網上找了很久,沒有發現能在CentOS系統下的攝像頭驅動(如果各位看客有相關資料或資訊,歡迎聯絡本人),沒辦法重新刷了樹莓派官方Debian系統,它自帶樹莓派攝像頭的管理工具—ra

原始碼分享POSCMS功能如何實現簡訊驗證碼

對接簡訊的時候發現一家簡訊公司,有些不錯的簡訊驗證碼的外掛,對接起來挺方便的,有需求的可以看一下。http://www.ihuyi.com/ 外掛說明 本外掛系互億無線針對POSCMS V3.2.0 簡訊外掛開發,外掛內的所有檔案均為對原檔案的修改,如果你的系統經過二次開發,安裝本外掛之前,請仔細核對修改

LeetCode題解232_用棧實現隊列(Implement-Queue-using-Stacks)

復雜 彈出 兩個棧 art 分析 完成後 棧操作 all n) 目錄 描述 解法一:在一個棧中維持所有元素的出隊順序 思路 入隊(push) 出隊(pop) 查看隊首(peek) 是否為空(empty) Java 實現 Python 實現 解法二:一個棧入,一個棧出