【Modern OpenGL】纹理 Textures
說明:跟著learnopengl的內容學習,不是純翻譯,只是自己整理記錄。
強烈推薦原文,無論是內容還是排版。 原文鏈接
本文地址: http://blog.csdn.net/aganlengzi/article/details/50421006
紋理 Textures
為了使我們創建的對象(比如說三角形)更加生動,我們已經學習了為對象的每個像素點設置不同的顏色來使它變得更加有趣。但是,在實際應用中,這種方式需要我們為模型創建太多的顏色,還要完成顏色與點的映射,工作量巨大,這不現實。
一種普遍為人們所接受的方式是使用紋理。紋理是二維圖像(雖然也有一維或者三維紋理的存在),用于描述對象的細節。比如用一張印有磚頭的圖像貼到一個三維的房子對象的表面上,這個三維的房子變得逼真。因為圖像上能夠呈現的細節是非常多的,所以我們可以讓我們的創建的對象呈現出非常逼真的細節,而無需為它添加更多的點或者顏色,比如門、窗等等。
除了圖像,紋理也可以用于存儲要發送給shader的大量數據,我們將會在一次單獨的教程給予講解。
本教程中,你將學會怎樣將一張磚頭的圖像映射到之前我們已經創建的三角形上。如下圖所示:
為了將一個紋理映射到三角形上,我們需要為三角形的每個頂點綁定相關紋理的哪個位置。每個頂點都應該關聯一個紋理坐標,這個坐標指定了這個點要綁定的紋理部分。片段(一個要顯示在屏幕上的像素點的所有信息)插值完成其他片段的綁定。
紋理坐標在x和y坐標(我們使用的是二維圖像)上的取值范圍都是[0,1]。利用紋理坐標來獲得紋理顏色的過程叫做采樣。紋理坐標中的原點(0,0)在一個紋理的左下角,(1,1)在其右上角。下圖展示了我們怎樣將紋理坐標映射到三角形:
我們為三角形指定了三個坐標點。我們想要將這個矩形二維圖像的左下角映射到我們創建三角形的左下角;將這個矩形圖形的右下角映射到三角形的右下角;將這個矩形圖片的上邊緣中點映射到三角形的上頂點,這正好是矩形圖片中三角形磚墻的范圍。在圖中顯示的結果就是,圖片的(0,0)坐標點映射到左下角,(1,0)坐標點映射到右下角,(0.5,1)坐標點映射到上頂點。我們只需給頂點處理器發送三個紋理坐標,而頂點處理器將它們傳遞給片段處理器,片段處理器利用插值的方法完成所有點的映射。
所以上述我們需要指定的紋理坐標如下所示:
GLfloat texCoords[] = {0.0f, 0.0f, // Lower-left corner 1.0f, 0.0f, // Lower-right corner0.5f, 1.0f // Top-center corner };?
紋理采樣是比較籠統的說法,它可以通過多種方式完成,完成的效果當然各有不同。所以我們的一項重要工作就是告訴OpenGL如何進行紋理采樣。
紋理包裹 Texture Wrapping
紋理坐標的范圍通常是從(0,0)到(1,1),但是當我們指定的坐標值超出這個范圍怎么辦呢?OpenGL默認的處理方式是一值重復這張圖片,但是實際上我們有多種方式可供選擇。
- GL_REPEAT: 默認的方式,重復紋理
- GL_MIRRORED_REPEAT: 和重復紋理相似,但是重復的是鏡像處理過的紋理
- GL_CLAMP_TO_EDGE: 在紋理和邊緣之間拉伸紋理
- GL_CLAMP_TO_BORDER: 坐標之外顯示為用戶定義的邊緣顏色
當我們以以上三種不同方式制定坐標值在規定的(0,1)之外時,每一種方式都會產生不同的效果。如下圖所示:
前面提到的不同的方式都可以通過glTexParameter*函數設置沒個坐標軸(s,t或者r(僅在使用三維紋理時),和x,y,z是相同的):
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);函數的第一個參數指定了紋理目標;我們使用的是二維圖像,所以紋理目標是GL_TEXTURE_2D。
第二個參數指定了我們想要設置的坐標軸。最后一個參數指定了我們想要設置的包裹模式,以方便OpenGL在設置當前激活的紋理的包裹方式時選擇我們指定的方式。
需要注意的是,如果我們指定的是GL_CLAMP_TO_BORDER方式,我們還應該指定一種邊緣顏色。這通過glTexParameter加后綴fv函數并通過GL_TEXTURE_BORDER_COLOR和一個顏色向量參數來指定,即如下所示的方式:
float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f }; glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);?
紋理過濾 Texture Filtering
紋理坐標不依賴分辨率,可以使任何的浮點值,因此OpenGL必須要解析哪一個紋理坐標應該映射到哪一個紋理像素(texture pixel,簡寫texel)。如果你想要將一個低分辨率的紋理映射到一個很大的對象上的時候,這就顯得尤為重要。你可能會猜想OpenGL肯定提供了紋理像素向屏幕像素之間的映射選項。確實,有很多可選的方式,但是目前我們討論最重要的兩個選項GL_NEAREST和GL_LINEAR。
GL_NEAREST(也叫作最鄰近過濾)是OpenGL采取的默認紋理過濾方式。當過濾方式設置成GL_NEAREST,OpenGL選擇最靠近紋理坐標的像素點。在下圖中,你可以看到4個像素點,中間那個十字號表示紋理坐標的精確位置。左上角的紋理像素的中心是和精確的紋理坐標值最接近的,所以被選為采樣的顏色,如下圖所示:
GL_LINEAR(也叫作(雙)線性過濾),它選擇紋理坐標臨近紋理像素的顏色值的插值作為采樣的顏色。離紋理坐標越近的點的顏色值就越多地被采樣。下圖可見,返回的是臨近像素的混合顏色值:
但是這兩種過濾方式下的真實視覺效果是怎么樣的呢?讓我們試著將一個低分辨率的紋理綁定到一個比這個紋理尺寸要大的對象上的效果(這種情況下,紋理會被拉伸,我們應該能夠看到獨立的像素點)。
使用GL_NEAREST方式的結果是我們能夠清楚地看到一個個的像素點,但是使用GL_LINEAR方式產生的效果完全不同,它產生了一種更為平滑的效果,單獨的像素點在這種方式下不容易被看到。GL_LINEAR方式產生了更實際的效果,但是一些開發者更喜歡那種像素點的效果所以選擇GL_NEAREST方式。
紋理過濾可以用于設置放大U或者縮小操作,所以你可以在縮小的時候使用最鄰近方式,但是在放大的時候使用線性過濾方式。因此我們需要通過glTexParameter*函數指定這兩種選項。代碼如下所示:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);?
變頻譯碼 Mipmaps
假設我們有一個分廠大的空間,里面有成千上萬的對象,每一個對象都要貼圖。在人眼看到的場景中,必然有很多對象在很遠處,但是它們有著和近處對象一樣高分辨率的貼圖。因為這些對象對于觀察者來說在很遠處,那么它們可能只會產生很少的片段。OpenGL很難在高分辨率的紋理中采樣正確的顏色值,因為它不得不從一大塊紋理中為一個片段取出一個顏色值(這是不值得的)。這將會在小的對象上面產生視覺上的錯誤,更不用提在小對象上使用高分辨率紋理的內存浪費。
OpenGL使用一種叫做變頻譯碼的方式來解決這個問題,簡單來說,就是一組紋理圖像,它們中每一個后來的紋理都是原來的1/2*1/2大小,如下圖所示。其中的原理是顯而易見的:在舉例觀察者一定距離的時候,OpenGL將會使用一個與這個距離最佳匹配的不同的變頻譯碼紋理。因為這個對象在很遠處,較小的分辨率觀察者也不會覺察出來。并且mipmaps對性能也有好處。
手工創建一系列類似于上面的變頻映射圖像是繁雜的,但是幸運的是,OpenGL為我們提供了這項功能。只需要在我們創建一個紋理之后調用glGenerateMipmaps,后面你將會看到使用的實例。
在不同的變頻映射紋理之間選擇的時候,OpenGL可能會造成一些顯而易見的瑕疵,比如說兩種規格的紋理之間邊緣錯位,無法對接。像正常的紋理過濾,在mipmap的不同等級之間進行過濾也是可以的,實際上和正常的紋理過濾的原理是類似的。在mipmap等級改變的時候,我們也可以使用NEAREST或者LINEAR過濾的方式來消除上面所述的問題。一下是可以用來原始過濾方式的四種選項:
- GL_NEAREST_MIPMAP_NEAREST: 使用最鄰近的mipmap來匹配像素尺寸并且使用最鄰近值方式進行紋理采樣
- GL_LINEAR_MIPMAP_NEAREST: 使用最鄰近的mipmap級別并且使用線性插值方式
- GL_NEAREST_MIPMAP_LINEAR: 在最靠近紋理坐標值的不同mipmap間使用線性插值方式并且使用最鄰近插值方式
- GL_LINEAR_MIPMAP_LINEAR: 在最靠近的mipmap間使用線性插值方式并且使用線性插值方式進行采樣
正如紋理過濾設置方法一樣,我們需要使用glTexParameteri函數來設置選擇以上的四種方式之一,如下所示:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);?
一個常見的錯誤是設置這四種方式中的一種為放大映射,這種方式不會產生任何效果,因為mipmap最主要的應用是在縮小的時候使用(對象在遠處,觀察者看到的對象是變小的)。所以紋理放大的時候是不用mipmap的,如果為GL_TEXTURE_MAG_FILTER設置了上述mipmap選項中的一種,那么OpenGL就會報GL_INVALID_ENUM錯誤。
加載和創建紋理
在使用紋理之前,我們首先要做的是將它們加載到我們的應用中。紋理圖像可以通過多種格式存儲,每一中都有不同的數據結構和組織方式,那么我們應該怎樣在應用中得到這些紋理的數據呢?一種解決方法是我們選定一種喜歡的圖像格式,例如.PNG并且自己寫一個圖像加載和轉換模塊,將這種格式的紋理轉換成二維數組格式的數據進行存儲。雖然寫這個模塊并不是太繁瑣,但是如果你想要使用更多的圖像格式應該怎么辦?再寫一個?最終是不是將要為你使用的每一種紋理格式都寫一個對應的紋理圖像加載模塊?
另一種可能更好的解決方法是,使用一個支持多種常用圖像個數的圖像加載函數庫,由它來幫助我們完成紋理的加載,即由不同格式的圖像加載到我們的程序中變成一個存儲著紋理數據的二維數組。就像我們使用的SOIL一樣。
SOIL
SOIL(Simple OpenGL Image Library)支持最常用的多種圖像格式,并且非常易用,你可以從這兒下載到。像其它你已經使用的函數庫(GLFW,GLEW)一樣,你可能需要自己生成.lib文件。
下面就是在講怎么配置和使用SOIL了。
我配置的方法是:
1)下載得到壓縮包soil.zip
2)解壓后用Visual Studio 2012打開soil.zip\Simple OpenGL Image Library\projects\VC9的工程,并同意做單向版本提升
3)編譯得到Debug/SOIL.lib
4)將yourpathto\soil.zip\Simple OpenGL Image Library\src添加到工程的包含路徑下
5)將your\path\to\SOIL.lib添加到工程的庫目錄下
6)在編寫的工程源文件中添加頭文件#include
?
這個函數的第一個參數是圖像文件container.jpg的位置,第二個和第三個參數是圖像的寬度和高度。我們需要在后面生成紋理的時候使用到這兩個參數。第四個參數指定了這個圖像含有的通道數,但是目前我們將這個值設置成0.最后一個參數指定了SOIL應該以何種方式加載這個圖像:我們只對圖像的RGB分量感興趣,加載的結果是將圖像存儲成一個大的字符/字節數組。
生成一個紋理
就像之前生成OpenGL中的對象一樣,每個紋理生成的時候也綁定一個唯一的ID,如下所示:
GLuint texture; glGenTextures(1, &texture);?
glGenTextures函數的第一個參數指定我們要生成紋理個數,它們的ID將會保存到第二參數中,第二個參數是一個地址,可以使一個變量(在第一個參數為1的情況下)或者是數組的起始地址。我們的例子中因為只生成一個紋理,所以我們給的參數是1和變量texture的地址。像之前的VBO,VAO 和EBO等對象,我們將紋理綁定到其目標上以方便我們對當前綁定的紋理進行操作:
glBindTexture(GL_TEXTURE_2D, texture);?
現在這個紋理對象已經綁定到了二維紋理目標上(實際是將其ID進行了綁定),接下來我們就可以利用之前加載的圖像數據來生成紋理了。紋理的生成需要使用glTexImage2D函數完成:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); glGenerateMipmap(GL_TEXTURE_2D);?
這個函數是一個有很多參數的“大”函數,以下對每個參數進行逐一講解:
第一個參數指定紋理的目標,我們將GL_TEXTURE_2D作為實參傳進去表示接下來的操作將對綁定到GL_TEXTURE_2D的紋理對象進行操作,而不會操作綁定到GL_TEXTURE_1D或者綁定到G_TEXTURE_3D目標的紋理對象。
第二個參數指定了mipmap的級別,實際上我們可以手工設置每一個創建的mipmap的級別,但是我們目前只是將這個值設置為基礎級別,也就是0.
第三個參數指定了OpenGL應該以什么樣的數據類型來存儲創建的紋理。我們的圖像應為只加載了RGB信息,所以這里設置的是GL_RGB。
第四個和第五個參數指定了生成紋理的寬度和高度。我們之前已經保存了我們設置的值,這里傳進去的就是這兩個變量。
下一個參數總是0,先不用管。
第七個和第八個參數指定了源圖像的格式和數據類型。我們在加載的時候因為加載的是RGB信息,所以這里指定的是GL_RGB,同時我們之前說過,加載的圖像會被保存成字符或者字節類型,所以這里傳進去的實參是GL_UNSIGNED_BYTE。
最后一個參數是實際的圖像數據地址,也就是我們之前利用那個庫加載和轉換的數據。
在調用glTexImage2D之后,當前綁定的紋理對象就已經附加上了紋理圖像。但是,當前它只有基礎級別的mipmap(因為我們目前指定的就只是基礎級別的),如果我們想要使用其它級別的mipmap,一種方法是我們需要手工改變第二個參數來生成不同級別的紋理;另一種方法是在生成紋理之后調用glGenerateMipmap函數,它將會自動為我們生成當前綁定紋理對象所有級別的mipmap。
在我們生成了紋理和對應的mipmap之后,釋放圖像內存和(為特定目標)解綁紋理對象是一種好的習慣。
SOIL_free_image_data(image); glBindTexture(GL_TEXTURE_2D, 0);?
以上紋理生成的整個過程大致是下面代碼描述的樣子:
GLuint texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); // Set the texture wrapping/filtering options (on the currently bound texture object) ... // Load and generate the texture int width, height; unsigned char* image = SOIL_load_image("container.jpg", &width, &height, 0, SOIL_LOAD_RGB); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); glGenerateMipmap(GL_TEXTURE_2D); SOIL_free_image_data(image); glBindTexture(GL_TEXTURE_2D, 0);使用紋理
下面我們將會使用在前面的幾個教程中創建的由兩個三角形拼成的矩形來演示怎樣使用紋理。我們首先要設置OpenGL采樣紋理的方式,所以我們先來修改之前的頂點數據,向其中添加紋理坐標:
GLfloat vertices[] = {// Positions // Colors // Texture Coords0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // Top Right0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // Bottom Right-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // Bottom Left-0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // Top Left };因為設置了紋理坐標,那么之前的關于數據放入內存的方式和VBO的設置以及OpenGL解釋數據的方式都要進行更新。目前在內存中數據的組織方式如下圖所示:
所以要首先修改vertex shader,讓OpenGL能夠正確讀入數據,并且將紋理坐標信息輸出到片段處理程序:
#version 330 core layout (location = 0) in vec3 position; layout (location = 1) in vec3 color; layout (location = 2) in vec2 texCoord;out vec3 ourColor; out vec2 TexCoord;void main() {gl_Position = vec4(position, 1.0f);ourColor = color;TexCoord = texCoord; }然后指定VBO的設置和OpenGL解釋數據的方式:
glVertexAttribPointer(2, 2, GL_FLOAT,GL_FALSE, 8 * sizeof(GLfloat), (GLvoid*)(6 * sizeof(GLfloat))); glEnableVertexAttribArray(2);?
需要注意的是,我們在設置location=2即剛加載的紋理數據的時候的步長指定為8 * sizeof(GLfloat),在設置position和color的步長的時候也需要設置這個值。
片段處理程序中,我們首先設置其接收頂點處理程序中傳遞進來的紋理坐標,其次,只知道坐標是不能完成顏色值的采樣的,還必須能夠訪問到紋理數據。但是怎樣才能夠讓片段處理程序訪問到紋理對象的數據呢?GLSL中專為紋理對象內嵌了一個數據類型sampler*,后綴是我們要訪問的紋理類型,比如說sampler1D,sampler2D等。所以我們可以通過uniform類型的變量很方便地為片段處理程序添加一個紋理,如下所示的sampler2D類型的變量ourTexture,注意這個ourTexture是在OpenGL程序中當前綁定的紋理對象,這也正是使用uniform的原因。
#version 330 core in vec3 ourColor; in vec2 TexCoord;out vec4 color;uniform sampler2D ourTexture;void main() {color = texture(ourTexture, TexCoord); }我們利用GLSL內嵌的紋理顏色采樣函數texture來完成采樣,這個函數的第一個參數是一個sampler數據類型的變量,第二個參數是相應的紋理坐標。這個texture函數對ourTexture指定的紋理數據通過texCoord指定的紋理坐標進行采樣,采樣的方式是我們之前已經講過的默認或者已經設置過的方式(最鄰近或者插值)。
目前整個紋理對象的使用剩下的就是在調用渲染函數glDrawElements之前對將這個紋理對象進行綁定了。沒錯利用我們之前講過的能夠存儲狀態設置信息的VAO來實現:
glBindTexture(GL_TEXTURE_2D, texture); glBindVertexArray(VAO); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); glBindVertexArray(0);如果一切都正確的話,我們將會得到如下所示的運行結果:
如果你得到的結果與我得到的結果不同,那么可能你的程序中的某個地方存在錯誤,完成的代碼在這兒。
后文是為了得到更為酷炫一點的效果,將紋理和通過指定頂點顏色的方式相結合。得到不同的效果,也就是在片段處理程序中進行如下修改:
color = texture(ourTexture, TexCoord) * vec4(ourColor, 1.0f);?
得到的效果應該是紋理顏色值和指定的頂點顏色值插值結果的混合顏色;
還不錯~
紋理單位 Texture Units
前面說過,我們在fragment shader中設置的uniform sampler2D類型的變量并沒有像之前我們使用uniform類型的變量一樣需要在OpenGL函數中對其值進行設定。其值默認已經設置成了我們之前綁定的紋理對象。實際上我們也可以在代碼中指定我們要操作的紋理對象的位置。
紋理單位的主要目的是讓我們在shader中使用超過一個紋理對象。通過為sampler指定紋理單位,我們可以一次綁定多個紋理,只要我們能夠在此之前激活相關的紋理單位。就像調用glBindTexture函數一樣,我們可以通過glActiveTexture函數激活我們想要使用的紋理,如下所示:
glActiveTexture(GL_TEXTURE0); // Activate the texture unit first before binding texture glBindTexture(GL_TEXTURE_2D, texture);?
激活了紋理單位之后,隨后的glBindTexture調用就會綁定紋理到當前激活的紋理單位上。上面代碼中指定的紋理單位GL_TEXTURE0實際上是一致默認激活的,所以在之前的代碼中我們調用glBindTexture中不需要對紋理單位進行任何的激活操作。
以下的講解就是如何使用多個紋理。
OpenGL中至少應該有16個紋理單位供我們使用,使用上面的函數可以激活GL_TEXTURE0到GL_TEXTURE15的紋理單位。它們是線性組織的,所以我們也可以通過GL_TEXTURE0 + 8的方式指定GL_TEXTURE8,這在我們使用循環對多個紋理單位進行操作的時候就會顯得十分好用。但是我們還應該通過編輯fragment shader的方式來接受另一個sampler對象,這個是相對簡單的:
#version 330 core ...uniform sampler2D ourTexture1; uniform sampler2D ourTexture2;void main() {color = mix(texture(ourTexture1, TexCoord), texture(ourTexture2, TexCoord), 0.2); }現在通過這個片段處理器產生的最終顏色就是兩個紋理顏色的混合值了。GLSL的內置mix函數要求兩個參數值,需要對這兩個顏色值進行線性插值:上述的texture函數產生顏色值,而mix函數產生這兩個顏色值的混合值,混合的方式是由第三個參數指定的:如果第三個參數是0.0,那么整個mix函數返回的就是第一個參數值,如果第三個參數值是1.0,那么返回的就是第二個參數值。所以0.2代表值返回的顏色值是第一個參數的80%和第二個參數的20%的顏色混合值。
我們現在加載和創建另一個紋理。我們應該已經對這個過程比較熟悉了。首先確保創建另一個紋理對象,加載一個圖像(利用上面講到的SOIL)并且通過glTexImage2D函數生成紋理,第二個紋理圖像我們將使用你學習OpenGL時候的表情來生成,就是這個。
為了使用兩個紋理對象,我們需要對繪制過程進行一點修改,為的是像上面所說的,將兩個紋理都綁定到激活的(不同)紋理單位上,并且需要指定哪一個紋理對象對應著哪一個紋理單位:
glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture1); glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture1"), 0); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, texture2); glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture2"), 1);glBindVertexArray(VAO); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); glBindVertexArray(0);需要注意的是,我們通過glUniform1i函數來為我們設置的sampler對象設置我們的紋理單位的位置。這樣我們將對應的uniform變量(也就是sampler變量)設置成正確的紋理單位(GL_TEXTURE0和GL_TEXTURE1),我們應該得到的效果是這樣的:
我們注意到我們的笑臉圖案是倒置的,產生這種現象的原因是OpenGL默認原點坐標在圖像的左下角但是圖像通常認為原點坐標在左上角,所以就會造成了倒置的現象。有一些類似于SOIL的加載圖像的庫是提供加載的時候對y軸進行設置的選項的,但是SOIL沒有。
目前我們解決這個問題的方法有兩個:
第一種方法是在指定紋理坐標的時候將y坐標軸的坐標值反向(原來的坐標應該綁定在下面的設置成綁定到上面,倒個)。
我們可以通過設置vertex shader來幫助我們完成上述y軸坐標的交換,實際上很簡單,只需要將原來的坐標值做如下的操作:TexCoord = vec2(texCoord.x, 1.0f - texCoord.y)。
上述的兩種方法實際上只是針對我們這個例子的具體方法,在真正的實現的時候兩種方式只能算是奇技淫巧。。。。。。可能在大工程中使用會出亂子的。最好的方式還是在圖像數據加載的時候進行,或者是將圖像本身進行反轉操作。這樣的話,得到的數據是適合于OpenGL使用的。
不管使用了哪種方法,我們做過處理之后得到最終的顯示效果應該是如下圖所示的這樣的(我使用的方法是將原圖像進行倒置):
最終的源碼在這兒包括vertex shader和fragment shader。
總結
以上是生活随笔為你收集整理的【Modern OpenGL】纹理 Textures的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 基于Android 13定制的OPPO
- 下一篇: cygwin和mingw的区别