OpenGL學習筆記19 Mesh

2021-10-08 15:05:06 字數 3687 閱讀 1985

有了assimp,我們可以將許多不同的模型載入到應用程式中,但是載入之後它們都儲存在assimp的資料結構中。我們最終想要的是將資料轉換成opengl能理解的格式,這樣我們就能呈現物件。我們從上一章了解到網格表示乙個可繪製實體,所以讓我們從定義乙個我們自己的網格類開始。

讓我們回顧一下到目前為止我們學過的關於網格的最小資料應該是什麼。乙個網格至少需要一組頂點,其中每個頂點包含乙個位置向量、乙個法向量和乙個紋理座標向量。網格還應該包含索引繪圖的索引,以及材質資料(漫反射/高光貼圖)。

現在我們設定了網格類的最小要求,我們可以在opengl中定義乙個頂點:

struct vertex ;
我們將每個必需的頂點屬性儲存在乙個名為vertex的結構中。在頂點結構的旁邊,我們也想組織紋理資料在紋理結構:

struct texture ;
我們儲存紋理的id和它的型別,例如,漫反射紋理或鏡面紋理。

知道了乙個頂點和乙個紋理的實際表示,我們可以開始定義網格類的結構:

class mesh ;
如您所見,這個類並不太複雜。在建構函式中,我們為網格提供所有必要的資料,在setupmesh函式中初始化緩衝區,最後通過draw函式繪製網格。注意,我們給了繪製函式乙個著色器;通過將著色器傳遞給網格,我們可以在繪製之前設定一些uniform(比如將取樣器鏈結到紋理單位)。

建構函式的函式內容非常簡單。我們只需用建構函式對應的引數變數設定類的公共變數。我們也在建構函式中呼叫setupmesh函式:

mesh(vectorvertices, vectorindices, vectortextures)

沒什麼特別的。現在讓我們深入研究setupmesh函式。

多虧了建構函式,我們現在有了可以用於渲染的大網格資料列表。我們需要設定適當的緩衝區,並通過頂點屬性指標指定頂點著色器布局。到目前為止,你應該對這些概念沒有問題,但我們已經新增了一點,這一次在struct中引入頂點資料:

void setupmesh()

**與您預期的沒有太大的不同,但是在頂點結構的幫助下使用了一些小技巧。

結構體在c++中有乙個很好的特性,即它們的記憶體布局是順序的。也就是說,如果我們要將乙個結構表示為乙個資料陣列,它將只包含按順序排列的結構變數,這將直接轉換為乙個float(實際上是位元組)陣列,這是我們想要用於陣列緩衝區的陣列。例如,如果我們有乙個填充的頂點結構,它的記憶體布局將等於:

vertex vertex;

vertex.position = glm::vec3(0.2f, 0.4f, 0.6f);

vertex.normal = glm::vec3(0.0f, 1.0f, 0.0f);

vertex.texcoords = glm::vec2(1.0f, 0.0f);

// = [0.2f, 0.4f, 0.6f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f];

由於這個有用的屬性,我們可以直接傳遞乙個指標到乙個大列表的頂點結構作為緩衝區的資料,它們完美地轉化為glbufferdata期望的引數:

glbufferdata(gl_array_buffer, vertices.size() * sizeof(vertex), vertices[0], gl_static_draw);
當然,也可以在結構上使用sizeof操作符來獲得適當的位元組大小。這應該是32位元組(8個浮點數*每個4位元組)。

struct的另乙個重要用途是乙個名為offsetof(s,m)的預處理器指令,它的第乙個引數是struct,第二個引數是struct的變數名。該巨集返回該變數從結構開始時的位元組偏移量。這非常適合定義glvertexattribpointer函式的偏移量引數:

glvertexattribpointer(1, 3, gl_float, gl_false, sizeof(vertex), (void*)offsetof(vertex, normal));
現在使用巨集的偏移量來定義偏移量,在這種情況下,它將法向量的位元組偏移量設定為等於結構中normal屬性的位元組偏移量,該結構為3浮點數,因此為12位元組。

使用這樣的結構不僅可以讓我們獲得更可讀的**,還可以方便地擴充套件結構。如果我們想要另乙個頂點屬性,我們可以簡單地將它新增到結構中,由於它的靈活性,渲染**不會中斷。

我們需要為mesh類定義的最後乙個函式是它的繪製函式。在渲染網格之前,我們首先想在呼叫gldrawelements之前繫結適當的紋理。然而,這有點困難,因為我們不知道從一開始網格有多少(如果有的話)紋理和它們可能有什麼型別。那麼我們如何在著色器中設定紋理單元和取樣器呢?

為了解決這個問題,我們將假定乙個特定的命名約定:每個漫反射紋理命名為texture_diffusen,每個高光紋理命名為texture_specularn,其中n是1到允許的最大紋理取樣數量之間的任意數字。假設我們有3個漫反射紋理和2個鏡面紋理,它們的紋理取樣器應該被呼叫:

uniform sampler2d texture_diffuse1;

uniform sampler2d texture_diffuse2;

uniform sampler2d texture_diffuse3;

uniform sampler2d texture_specular1;

uniform sampler2d texture_specular2;

通過這個約定,我們可以在著色器中定義我們想要的任意多的紋理取樣器(直到opengl的最大值),如果乙個網格確實包含了(這麼多)紋理,我們知道它們的名字會是什麼。通過這個約定,我們可以在乙個單一的網格上處理任意數量的紋理,著色器開發者可以通過定義適當的取樣器來自由地使用任意數量的紋理。

像這樣的問題有很多解決方案,如果你不喜歡這個特殊的解決方案,那就取決於你的創造性,想出你自己的方法。

由此產生的繪圖**為:

void draw(shader &shader) 

glactivetexture(gl_texture0);

// draw mesh

glbindvertexarray(vao);

gldrawelements(gl_********s, indices.size(), gl_unsigned_int, 0);

glbindvertexarray(0);

}

我們首先計算每個紋理型別的n個元件,並將其連線到紋理的型別字串以獲得適當的uniform名稱。然後我們找到合適的取樣器,給它與當前活動紋理單元對應的位置值,並繫結紋理。這也是我們在繪製函式中需要著色器的原因。

我們還在最終的統一名稱中新增了「material.」,因為我們通常將紋理儲存在乙個材質結構中(這可能在每個實現中有所不同)。

注意,我們在將漫反射計數器和鏡面計數器轉換為字串時增加了它們。在c++中,increment呼叫:variable++按原樣返回變數,然後增加變數,而++變數首先增加變數,然後返回它。在我們的例子中,傳遞給std::string的值是原始的計數器值。之後,該值將在下一輪中遞增。

你可以在這裡here. 找到mesh類的完整源**。

我們剛剛定義的mesh類是我們在前面章節中討論過的許多主題的乙個抽象。在下一章中,我們將建立乙個模型,作為幾個網格物件的容器,並實現assimp的載入介面。

OpenGL學習筆記

我是一名ios開發者,opengl是乙個比較重要的技術。所以就此開啟我的菜鳥之路。opengl open graphics library 是乙個跨程式設計語 言 跨平台的程式設計圖形程式接 它將計算機的資源抽象稱為乙個個opengl的物件,對這些資源的操作抽象為乙個的opengl指令.direct...

OpenGL學習筆記

數學基礎 叉積mv矩陣 文章內容主要來自 計算機圖形學程式設計 使用opengl和c 光柵化過程確定了用以顯示3個頂點所確定的三角形的所有畫素需要繪製的位置 光柵化過程開始時先對三角形的每對頂點進行插值 如果光柵化過程到此為止,那麼呈現出的影象將會是線框模型 使用 glpolygonmode gl ...

OpenGL學習筆記

因工作需要,這些天要研究opengl。我給自己定的目標是 1.節前能熟悉opengl基礎知識 2.確定一款以opengl為基礎的gui 3.學習如何使用該gui的控制項繪製3d選單 今天找到幾個 嵌入式系統使用的是opengl es,是桌面opengl的乙個子集,定義了一系列靈活高效的圖形加速介面。...