日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

opengl 纹理贴到对应的位置_一步步学OpenGL(27) -《公告牌技术与几何着色器》

發布時間:2023/12/15 编程问答 34 豆豆
生活随笔 收集整理的這篇文章主要介紹了 opengl 纹理贴到对应的位置_一步步学OpenGL(27) -《公告牌技术与几何着色器》 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

教程 27

公告牌技術與幾何著色器

原文: http://ogldev.atspace.co.uk/www/tutorial27/tutorial27.html

CSDN完整版專欄: https://blog.csdn.net/cordova/article/category/9266966


背景

從最初的一系列教程我們已經應用過了頂點著色器和片段著色器,但事實上我們還忽略了一個非常重要的著色階段,叫做幾何著色器(GS)。幾何著色器在微軟的DirectX 10之后被引入,之后也加入到了核心的OpenGL 3.2中。頂點著色器中是按照頂點一個一個執行的,片段著色器則是一個像素一個像素執行,而幾何著色器是以圖元為單位執行,這意味著在我們繪制三角形時,每次調用幾何著色器接收到的就是一個三角形,而繪制直線每次調用收到的就是一條直線,等等。這就給幾何著色器提供了一個看待模型的獨特的角度,開發者可以知道頂點和頂點之間拓撲關系,從而可以基于此開發一些新的技術。

頂點著色器總是以一個頂點作為輸入,并對應一個頂點作輸出(不可以自行增加或減少頂點),而幾何著色器卻有著特殊的功能,它可以改變經過它的圖元,這種改變包括:

  • 改變新傳遞進來的圖元的拓撲結構。幾何著色器可以接收任何拓撲類型的圖元,但只能輸出頂點列表(point lists)、折線(line strip)和三角帶(triangle strips);
  • 幾何著色器接受一個圖元作為輸入,在處理過程中它可以將這個圖元全部丟棄或者輸出一個或更多的圖元(也就是說它可以產生比它得到的更多或更少的頂點)。這個能力被叫做幾何增長(growing geometry)。這一章我們將會利用幾何著色器的這種能力。

幾何著色器是可選擇的。如果我們編譯程序的時候不使用幾何著色器,圖元會直接的從頂點著色器進入片元著色器。這就是為什么我們之前并沒有使用頂點著色器,卻可以直接跳過該階段正常繪制出圖形。

三角形列表中的三角形圖元是以每三個頂點一組構建的,例如,0-2前三個頂點構建起第一個三角形,3-5三個頂點構建起第二個三角形,以此類推。為了計算已有頂點所組成的三角形個數,只要用頂點數除以3即可(多余的頂點直接拋棄)。但事實上,使用三角形帶構建更有效,不需要每個三角形都用專門的三個頂點構建,而是在使用三個頂點將第一個三角形構建完成后,重復利用其中的兩個頂點,然后再添加一個頂點即可構建第二個三角形。例如0-2三個頂點構建起一個三角形后,再添加一個頂點3,1-3三個頂點構成第二個三角形,這樣0-3四個頂點即可緊密拼接構建起兩個三角形,以此類推,再添加頂點4,2-4三個頂點又構成一個三角形等等。也就是說,在第一個三角形使用三個頂點構建完成后,每天添加一個頂點即可再構成一個三角形。這里有一個例子如下圖:

可以看到,三角帶中7個三角形只用了9個頂點,要是在三角形列表中,9個頂點就只能構建3個三角形。

三角帶有一個有關三角形內部環繞順序的重要性質:奇數三角形的環繞順序是反向的。這就意味著如下的順序:[0,1,2],[1,3,2], [2,3,4], [3,5,4],以此類推。下面的圖片顯示了這個順序:

了解了幾何著色器之后,現在看如何利用幾何著色器來實現一種非常有用的熱門技術:公告板技術(billboarding)。公告板是一個始終朝向相機的四邊形,當相機在場景中轉動的時候,公告板也會隨著相機轉動保證相機方向向量始終垂直于公告板的正面。這個和現實中公路邊的公告板類似,公告板的放置方向會讓盡可能多路過的開車司機看到。有了這種面向相機的四邊形之后,我們就很容易將怪物角色、樹木等任何場景中重復性高、數量多的物體以紋理貼圖的形式直接貼到四邊形公告板上(而不需要復雜的計算和渲染實際的3d模型),始終朝向相機。公告板常常用來創建需要大量樹木的森林效果,由于公告板始終朝向相機,玩家會誤以為看到的物體是有實際深度的,而事實上單純就是個平面而已。每一個公告板只需要4個頂點,因此比起使用大量實際的3d模型代價就小得多了。

在這個教程中,我們創建一個頂點緩沖器,并為公告板存放頂點的世界空間坐標,每一個坐標就是一個單獨的3維坐標點。我們會將這些頂點坐標傳送到幾何著色器,然后構建相應的四邊形作為公告板。這意味著幾何著色器會輸入頂點列表,然后輸出三角形帶。利用三角帶的優點我們可以使用四個頂點創建出一個四邊形。

幾何著色器會負責調整四邊形始終朝向相機,并為每一個輸出的頂點附加紋理坐標,這樣片段著色器只需要直接從紋理上采樣就可以得到最終的顏色信息。

現在看怎樣讓公告板總是朝向相機。在下圖中黑點表示相機,紅點表示公告板的位置。雖然圖中位置看上去好像在一個平面上,但實際兩個點都在世界空間下,世界空間中任意兩個點都是可能的。

這里創建一個從公告板到相機的向量:

然后添加一個向量(0,1,0):

對這兩個向量做叉乘,結果得到一個垂直于這兩個向量所在平面的向量,然后就要沿著這個結果向量的方向來擴展創建我們的四邊形,保證四邊形平面和相機朝向垂直,這樣才符合我們想要的結果。同樣的場景下我們可以得到下面這樣(黃色向量是叉乘的結果向量):

一個容易讓開發者疑惑的事情是關于向量做叉積的順序問題(A叉積B,還是B叉積A?),兩種情況會得到兩個反向的結果向量。事先知道具體的結果向量是很關鍵的,因為我們要這樣輸出頂點,使從相機的視角看時三角形組成的四邊形呈順時針方向。這里要用到左手法則了:

如果站在公告板的位置(紅點),食指指向相機,中指指向上方的天空,然后你的大拇指將會沿著“食指”和“中指”叉乘的結果的方向(這里剩下的兩個手指保持握緊)。此教程中,我們將叉乘的結果稱為“右”向量,因為從相機位置看我們的手,‘右’向量是指向右側的。反過來,“中指”叉乘“食指”又可以產生一個反向的“左”向量。(這里我們使用左手定則的原因是因為我們使用的是左手坐標系(Z軸指向屏幕內))。在右手坐標系中情況就相反了,那樣就該選用右手坐標系了。

源代碼詳解

(billboard_list.h:27)

class BillboardList { public:BillboardList();~BillboardList();bool Init(const std::string& TexFilename);void Render(const Matrix4f& VP, const Vector3f& CameraPos);private:void CreatePositionBuffer();GLuint m_VB;Texture* m_pTexture;BillboardTechnique m_technique; };

BillboardList類封裝了創建公告板需要的所有東西,初始化函數Init()的參數為一個文件名,文件就是那個作為紋理貼圖貼到公告板上的圖像。Render()渲染函數在主渲染循環中被調用,負責設置狀態和渲染公告板。這個函數需要兩個參數:一個是視圖和投影組合矩陣,一個是相機的世界坐標位置。由于公告板的位置也是定義在世界空間中的,所以我們才直接到了視圖和投影階段,跳過世界空間變換的部分。這個類有三個私有屬性:一個存儲公告板位置的頂點緩沖器,一個指向公告板紋理貼圖的指針,和一個包含相關著色器的BillboardTechnique公告板類。

(billboard_list.cpp:80)

void BillboardList::Render(const Matrix4f& VP, const Vector3f& CameraPos) {m_technique.Enable();m_technique.SetVP(VP);m_technique.SetCameraPosition(CameraPos);m_pTexture->Bind(COLOR_TEXTURE_UNIT);glEnableVertexAttribArray(0);glBindBuffer(GL_ARRAY_BUFFER, m_VB);glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vector3f), 0); // position glDrawArrays(GL_POINTS, 0, NUM_ROWS * NUM_COLUMNS);glDisableVertexAttribArray(0); }

這個函數啟用了BillboardTechnique公告板類,并設置了OpenGL中一些必要的狀態,繪制頂點且這些頂點之后會在幾何著色器階段轉化成四邊形面。在這個Demo中,公告板位置是按照嚴格的行列順序排列的,因此我們可以行列數相乘得到頂點的數量。需要注意,我們在繪制的時候使用的繪制模式為點模式(GL_POINTS),在幾何著色器中要與其對應。

(billboard_technique.h:24)

class BillboardTechnique : public Technique { public:BillboardTechnique();virtual bool Init();void SetVP(const Matrix4f& VP);void SetCameraPosition(const Vector3f& Pos);void SetColorTextureUnit(unsigned int TextureUnit);private:GLuint m_VPLocation;GLuint m_cameraPosLocation;GLuint m_colorMapLocation; };

這里是BillboardTechnique的類接口,它只需要三個參數來完成下面的任務:視圖和投影組合矩陣、相機世界空間位置和與公告板綁定的紋理單元的數量。

(billboard.vs)

#version 330

layout (location = 0) in vec3 Position;void main() {gl_Position = vec4(Position, 1.0); }

這是公告板類的頂點著色器,由于主要工作都將在幾何著色器中完成,因此在這里頂點著色器就不要太簡單了哈哈。頂點緩沖器只包含頂點坐標向量,而且這些坐標已經在世界空間中定義了,所以可以直接將它們傳給幾何著色器,即可。

(billboard.gs:1)

#version 330layout (points) in; layout (triangle_strip) out; layout (max_vertices = 4) out;

公告板技術的核心就在幾何著色器了,我們分解開一步步來看。開始我們先使用‘layout’關鍵字聲明一些全局緩沖器。我們要先告訴渲染管線輸入來的參數結構是點列表,輸出的是三角帶,并且說明輸出的頂點個數最多為4個。這些關鍵詞也會提示圖形驅動器從幾何著色器輸出頂點的最大個數,提前知道頂點個數上限可以給驅動器機會來優化幾何著色器在某些特定情況下的動作。我們知道對于每一個輸入的頂點要輸出的是一個擴展的四邊形,因此我們設置最大頂點數為4。

(billboard.gs:7)

uniform mat4 gVP; uniform vec3 gCameraPos;out vec2 TexCoord;

幾何著色器得到了世界空間坐標,因此他只需要一個視圖和投影組合變換矩陣即可。另外還需要知道相機的位置來計算如何讓公告板始終朝向它。幾何著色器為片段著色器創建出了紋理坐標,因此我們也要聲明紋理坐標變量。

(billboard.gs:12)

void main() {vec3 Pos = gl_in[0].gl_Position.xyz;

上面一行代碼是針對幾何著色器獨有的。由于在幾何著色器中執行的是一個完整的圖元,因此事實上我們可以訪問組成圖元的每一個頂點,這個通過內置的‘gl_in’變量實現。這個變量是一個結構體數組,每個結構體都包含了寫入到頂點著色器gl_Position中的位置信息。為了訪問頂點信息,我們可以使用要訪問的頂點在圖元中的索引找到。在這個特定的例子中,參數的輸入結構為點列表,所以每個圖元只有一個可訪問的單獨的點,可以使用'gl_in[0]'獲取它。如果輸入結構是個三角形,我們可能還會使用'gl_in[1]'和'gl_in[2]'來訪問其他點。我么只需要使用頂點位置向量的前三個xyz分量,通過本地變量'.xyz'提取。

vec3 toCamera = normalize(gCameraPos - Pos);vec3 up = vec3(0.0, 1.0, 0.0);vec3 right = cross(toCamera, up);

這里我們利用文章開始背景部分結尾的原理實現讓公告板朝向相機。我們將當前公告板的位置點到相機位置的向量和垂直向上的方向向量做叉積,得到從相機看公告板視角的‘右’向量,然后我們要使用這個向量圍著公告板的位置擴展一個四邊形面。

Pos -= (right * 0.5);gl_Position = gVP * vec4(Pos, 1.0);TexCoord = vec2(0.0, 0.0);EmitVertex();Pos.y += 1.0;gl_Position = gVP * vec4(Pos, 1.0);TexCoord = vec2(0.0, 1.0);EmitVertex();Pos.y -= 1.0;Pos += right;gl_Position = gVP * vec4(Pos, 1.0);TexCoord = vec2(1.0, 0.0);EmitVertex();Pos.y += 1.0;gl_Position = gVP * vec4(Pos, 1.0);TexCoord = vec2(1.0, 1.0);EmitVertex();EndPrimitive(); }

頂點緩沖器中的點可以被認為是四邊形底邊的中點,我們要從中點創建兩個面朝相機的正面三角形。開始先用中點減去‘右’向量的一半,從而得到四邊形的左下角。然后通過乘以視圖和投影組合變換矩陣計算該點在裁剪空間的位置,并設置該點的紋理坐標為(0,0),便于將整個紋理完整貼到這個平面上。為了將新產生的頂點傳遞到管線的下一個階段,我們需要調用內置的EmitVertex()函數。這個函數調用后,我們之前寫入gl_Position的數據就無效了,因此我們要為其設置新值。和左下角點產生方法類似的,我們繼續創建出四邊形左上角和右下角的點,這樣三個點就構建出了第一個正面三角形。由于幾何著色器的輸出是三角帶,之后我們只需要另外一個頂點即可構建第二個三角形,使用前面三角形的后兩個頂點(四邊形的對角線)和新頂點構建。第四個新頂點也是最后一個頂點,即四邊形的右上角。結束三角帶的構建要調用內置的EndPrimitive()函數。

(billboard.fs)

#version 330uniform sampler2D gColorMap;in vec2 TexCoord; out vec4 FragColor;void main() {FragColor = texture2D(gColorMap, TexCoord);if (FragColor.r == 0 && FragColor.g == 0 && FragColor.b == 0) {discard;} }

片段著色器很簡單,它的主要工作是使用幾何著色器創建的紋理坐標進行紋理采樣。這里有一個新特性:內置的關鍵字'discard'用于在某些情況下將某些像素片元完全丟棄。這個教程中我們用了Doom中地獄騎士的圖片,展示黑色背景下的怪物場景,但是直接貼上整張圖片會有黑色的背景,就是說公告板內容比怪物要大,怪物背景不是透明的,我們不希望這樣,因此我們可以檢測文素的顏色,如果是黑色就直接拋棄該像素,這樣就會只顯示怪物了??梢試L試注釋掉'discard'語句看效果有什么不同。

總結

以上是生活随笔為你收集整理的opengl 纹理贴到对应的位置_一步步学OpenGL(27) -《公告牌技术与几何着色器》的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。