OpenGL从入门到精通--纹理
紋理
github源碼倉庫
opengl環(huán)境準(zhǔn)備
opengl編程從入門到精通-hello,window
OpenGL從入門到精通–你好三角形
OpenGL從入門到精通–著色器的使用
我們可以為每個頂點添加顏色來增加圖形的細(xì)節(jié),從而創(chuàng)建更加有趣的圖像。但是、為了讓圖像看起來更加真實,就必須有非常多的頂點,這將產(chǎn)生很多額外的開銷每個模型都需要有更多的頂點,每個頂點又需要有自己的顏色屬性。
使用紋理能在節(jié)省內(nèi)存的情況下展現(xiàn)更多的細(xì)節(jié)
為了能夠把紋理映射到三角形或者其他形狀上,我們需要為每個頂點指定對應(yīng)紋理的哪個部分。這樣每個頂點就關(guān)聯(lián)著一個紋理坐標(biāo)。
為了能夠把紋理映射(Map)到三角形上,我們需要指定三角形的每個頂點各自對應(yīng)紋理的哪個部分。這樣每個頂點就會關(guān)聯(lián)著一個紋理坐標(biāo)(Texture Coordinate),用來標(biāo)明該從紋理圖像的哪個部分采樣(譯注:采集片段顏色)。之后在圖形的其它片段上進(jìn)行片段插值(Fragment Interpolation)。
紋理坐標(biāo)在x和y軸上,范圍為0到1之間(注意我們使用的是2D紋理圖像)。使用紋理坐標(biāo)獲取紋理顏色叫做采樣(Sampling)。紋理坐標(biāo)起始于(0, 0),也就是紋理圖片的左下角,終始于(1, 1),即紋理圖片的右上角。下面的圖片展示了我們是如何把紋理坐標(biāo)映射到三角形上的。
我們?yōu)槿切沃付?個紋理坐標(biāo)點。如上圖所示,我們希望三角形的左下角對應(yīng)紋理的左下角,因此我們把三角形左下角頂點的紋理坐標(biāo)設(shè)置為(0, 0);三角形的上頂點對應(yīng)于圖片的上中位置所以我們把它的紋理坐標(biāo)設(shè)置為(0.5, 1.0);同理右下方的頂點設(shè)置為(1, 0)。我們只要給頂點著色器傳遞這三個紋理坐標(biāo)就行了,接下來它們會被傳片段著色器中,它會為每個片段進(jìn)行紋理坐標(biāo)的插值。
紋理坐標(biāo)看起來就像這樣:
float texCoords[] = {0.0f, 0.0f, // 左下角1.0f, 0.0f, // 右下角0.5f, 1.0f // 上中 }; // 要繪制區(qū)域和對應(yīng)紋理圖的區(qū)域向?qū)?yīng) float vertices[] = {// positions // colors // texture coords0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 2.0f, 2.0f, // top right0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 2.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, 2.0f // top left };使用紋理,取值繪制坐標(biāo)會從對應(yīng)紋理的位置取圖,并進(jìn)行紋理的繪制
紋理環(huán)繞方式
紋理坐標(biāo)的范圍通常是從(0, 0)到(1, 1),那如果我們把紋理坐標(biāo)設(shè)置在范圍之外會發(fā)生什么?OpenGL默認(rèn)的行為是重復(fù)這個紋理圖像(我們基本上忽略浮點紋理坐標(biāo)的整數(shù)部分),但OpenGL提供了更多的選擇:
| GL_REPEAT | 對紋理的默認(rèn)行為。重復(fù)紋理圖像。 |
| GL_MIRRORED_REPEAT | 和GL_REPEAT一樣,但每次重復(fù)圖片是鏡像放置的。 |
| GL_CLAMP_TO_EDGE | 紋理坐標(biāo)會被約束在0到1之間,超出的部分會重復(fù)紋理坐標(biāo)的邊緣,產(chǎn)生一種邊緣被拉伸的效果。 |
| GL_CLAMP_TO_BORDER | 超出的坐標(biāo)為用戶指定的邊緣顏色。 |
GL_REPEAT
GL_MIRRORED_REPEAT注意這個是鏡像的方式進(jìn)行擴(kuò)展
GL_CLAMP_TO_EDGE邊緣拉伸效果
GL_CLAMP_TO_BORDER因為用戶沒有指定,因此這里沒有指定的地方被填充為黑色了。
GL_TEXTURE_2D第一個參數(shù)說明紋理目標(biāo)是2D。第二個參數(shù)是指定設(shè)置參數(shù)的紋理軸。
// GL_REPEAT 垂直軸 // GL_TEXTURE_WRAP_T 水平軸 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);如果我們選擇GL_CLAMP_TO_BORDER選項,我們還需要指定一個邊緣的顏色。這需要使用glTexParameter函數(shù)的fv后綴形式,用GL_TEXTURE_BORDER_COLOR作為它的選項,并且傳遞一個float數(shù)組作為邊緣的顏色值:
float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f }; glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);紋理過濾
紋理不依賴分辨率,當(dāng)一個很大但是紋理分辨率很低時,需要使用紋理過濾選項,先討論兩種重要的選項:GL_NEAREST和GL_LINEA
GL_NEAREST(也叫鄰近過濾,Nearest Neighbor Filtering)是OpenGL默認(rèn)的紋理過濾方式。當(dāng)設(shè)置為GL_NEAREST的時候,OpenGL會選擇中心點最接近紋理坐標(biāo)的那個像素。下圖中你可以看到四個像素,加號代表紋理坐標(biāo)。左上角那個紋理像素的中心距離紋理坐標(biāo)最近,所以它會被選擇為樣本顏色:
GL_LINEAR(也叫線性過濾,(Bi)linear Filtering)它會基于紋理坐標(biāo)附近的紋理像素,計算出一個插值,近似出這些紋理像素之間的顏色。一個紋理像素的中心距離紋理坐標(biāo)越近,那么這個紋理像素的顏色對最終的樣本顏色的貢獻(xiàn)越大。
那么這兩種紋理過濾方式有怎樣的視覺效果呢?讓我們看看在一個很大的物體上應(yīng)用一張低分辨率的紋理會發(fā)生什么吧(紋理被放大了,每個紋理像素都能看到):
GL_NEAREST產(chǎn)生了顆粒狀的圖案,我們能夠清晰看到組成紋理的像素,而GL_LINEAR能夠產(chǎn)生更平滑的圖案,很難看出單個的紋理像素。GL_LINEAR可以產(chǎn)生更真實的輸出.
當(dāng)進(jìn)行放大(Magnify)和縮小(Minify)操作的時候可以設(shè)置紋理過濾的選項,比如你可以在紋理被縮小的時候使用鄰近過濾,被放大時使用線性過濾。我們需要使用glTexParameter*函數(shù)為放大和縮小指定過濾方式。這段代碼看起來會和紋理環(huán)繞方式的設(shè)置很相似:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);多級漸遠(yuǎn)紋理
假設(shè)我們有一個包含成千上萬個物體的大房間,每個物體上都有紋理,有些物體很遠(yuǎn),看起來很小,就不需要分辨率很高的紋理。
OpenGL使用一種叫做多級漸遠(yuǎn)紋理Mipmap的概念解決了這個問題,簡單的來說就i事一系列的紋理圖像,后一個是前一個的二分之一,OpenGL會根據(jù)距離觀察者的遠(yuǎn)近,使用適合的紋理,由于隨著距離的增加,解析度不高也不會被用戶注意到。同事多級漸進(jìn)紋理的另一個優(yōu)點就是它的性能非常好。
OpenGL中使用 glGenerateMipmaps函數(shù)會創(chuàng)建紋理。后面章節(jié)會講到。
在渲染中切換多級漸遠(yuǎn)紋理級別(Level)時,OpenGL在兩個不同級別的多級漸遠(yuǎn)紋理層之間會產(chǎn)生不真實的生硬邊界。就像普通的紋理過濾一樣,切換多級漸遠(yuǎn)紋理級別時你也可以在兩個不同多級漸遠(yuǎn)紋理級別之間使用NEAREST和LINEAR過濾。為了指定不同多級漸遠(yuǎn)紋理級別之間的過濾方式,你可以使用下面四個選項中的一個代替原有的過濾方式:
| GL_NEAREST_MIPMAP_NEAREST | 使用最鄰近的多級漸遠(yuǎn)紋理來匹配像素大小,并使用鄰近插值進(jìn)行紋理采樣 |
| GL_LINEAR_MIPMAP_NEAREST | 使用最鄰近的多級漸遠(yuǎn)紋理級別,并使用線性插值進(jìn)行采樣 |
| GL_NEAREST_MIPMAP_LINEAR | 在兩個最匹配像素大小的多級漸遠(yuǎn)紋理之間進(jìn)行線性插值,使用鄰近插值進(jìn)行采樣 |
| GL_LINEAR_MIPMAP_LINEAR | 在兩個鄰近的多級漸遠(yuǎn)紋理之間使用線性插值,并使用線性插值進(jìn)行采樣 |
就像紋理過濾一樣,我們可以使用glTexParameteri將過濾方式設(shè)置為前面四種提到的方法之一:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);一個常見的錯誤是,將放大過濾的選項設(shè)置為多級漸遠(yuǎn)紋理過濾選項之一。這樣沒有任何效果,因為多級漸遠(yuǎn)紋理主要是使用在紋理被縮小的情況下的:紋理放大不會使用多級漸遠(yuǎn)紋理,為放大過濾設(shè)置多級漸遠(yuǎn)紋理的選項會產(chǎn)生一個GL_INVALID_ENUM錯誤代碼。
加載與創(chuàng)建紋理
紋理的加載,紋理圖像可以被存儲為各種各樣的格式,每種都有自己的數(shù)據(jù)格式,我們可以根據(jù)不同的文件格式實現(xiàn)自己的文件加載,把圖像轉(zhuǎn)換為自己序列,但是使用庫函數(shù)更加快一點 – stb_image.h庫。
stb_image.h
stb_image.h是Sean Barrett的一個非常流行的單頭文件圖像加載庫,它能夠加載大部分流行的文件格式,并且能夠很簡單得整合到你的工程之中。stb_image.h可以在這里下載。下載這一個頭文件,將它以stb_image.h的名字加入你的工程,并另創(chuàng)建一個新的C++文件,輸入以下代碼:
#define STB_IMAGE_IMPLEMENTATION #include "stb_image.h"通過定義STB_IMAGE_IMPLEMENTATION,預(yù)處理器會修改頭文件,讓其只包含相關(guān)的函數(shù)定義源碼,等于是將這個頭文件變?yōu)橐粋€ .cpp 文件了。現(xiàn)在只需要在你的程序中包含stb_image.h并編譯就可以了。
下面的教程中,我們會使用一張木箱的圖片。要使用stb_image.h加載圖片,我們需要使用它的stbi_load函數(shù):
int width, height, nrChannels; unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0);這個函數(shù)首先接受一個圖像文件的位置作為輸入。接下來它需要三個int作為它的第二、第三和第四個參數(shù),stb_image.h將會用圖像的寬度、高度和顏色通道的個數(shù)填充這三個變量。我們之后生成紋理的時候會用到的圖像的寬度和高度的。
生成紋理
和之前生成的OpenGL對象一樣,紋理也是使用ID引用的。讓我們來創(chuàng)建一個:
unsigned int texture; glGenTextures(1, &texture);glGenTextures函數(shù)首先需要輸入生成紋理的數(shù)量,然后把它們儲存在第二個參數(shù)的unsigned int數(shù)組中(我們的例子中只是單獨的一個unsigned int),就像其他對象一樣,我們需要綁定它,讓之后任何的紋理指令都可以配置當(dāng)前綁定的紋理:
glBindTexture(GL_TEXTURE_2D, texture);現(xiàn)在紋理已經(jīng)綁定了,我們可以使用前面載入的圖片數(shù)據(jù)生成一個紋理了。紋理可以通過glTexImage2D來生成:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data); glGenerateMipmap(GL_TEXTURE_2D);函數(shù)很長,參數(shù)也不少,所以我們一個一個地講解:
- 第一個參數(shù)指定了紋理目標(biāo)(Target)。設(shè)置為GL_TEXTURE_2D意味著會生成與當(dāng)前綁定的紋理對象在同一個目標(biāo)上的紋理(任何綁定到GL_TEXTURE_1D和GL_TEXTURE_3D的紋理不會受到影響)。
- 第二個參數(shù)為紋理指定多級漸遠(yuǎn)紋理的級別,如果你希望單獨手動設(shè)置每個多級漸遠(yuǎn)紋理的級別的話。這里我們填0,也就是基本級別。
- 第三個參數(shù)告訴OpenGL我們希望把紋理儲存為何種格式。我們的圖像只有RGB值,因此我們也把紋理儲存為RGB值。
- 第四個和第五個參數(shù)設(shè)置最終的紋理的寬度和高度。我們之前加載圖像的時候儲存了它們,所以我們使用對應(yīng)的變量。
- 下個參數(shù)應(yīng)該總是被設(shè)為0(歷史遺留的問題)。
- 第七第八個參數(shù)定義了源圖的格式和數(shù)據(jù)類型。我們使用RGB值加載這個圖像,并把它們儲存為char(byte)數(shù)組,我們將會傳入對應(yīng)值。
- 最后一個參數(shù)是真正的圖像數(shù)據(jù)。
當(dāng)調(diào)用glTexImage2D時,當(dāng)前綁定的紋理對象就會被附加上紋理圖像。然而,目前只有基本級別(Base-level)的紋理圖像被加載了,如果要使用多級漸遠(yuǎn)紋理,我們必須手動設(shè)置所有不同的圖像(不斷遞增第二個參數(shù))。或者,直接在生成紋理之后調(diào)用glGenerateMipmap。這會為當(dāng)前綁定的紋理自動生成所有需要的多級漸遠(yuǎn)紋理。
生成了紋理和相應(yīng)的多級漸遠(yuǎn)紋理后,釋放圖像的內(nèi)存是一個很好的習(xí)慣。
stbi_image_free(data);生成一個紋理的過程應(yīng)該看起來像這樣:
unsigned int texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); // 為當(dāng)前綁定的紋理對象設(shè)置環(huán)繞、過濾方式 // 環(huán)繞 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); // 紋理 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 加載并生成紋理 int width, height, nrChannels; unsigned char *data = stbi_load("container.jpg", &width, &height, &nrChannels, 0); if (data) {glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);glGenerateMipmap(GL_TEXTURE_2D); } else {std::cout << "Failed to load texture" << std::endl; } stbi_image_free(data);應(yīng)用紋理
后面的這部分我們會使用glDrawElements繪制[「你好,三角形」](https://learnopengl-cn.github.io/01 Getting started/04 Hello Triangle/)教程最后一部分的矩形。我們需要告知OpenGL如何采樣紋理,所以我們必須使用紋理坐標(biāo)更新頂點數(shù)據(jù):
float vertices[] = { // ---- 位置 ---- ---- 顏色 ---- - 紋理坐標(biāo) -0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 1.0f, 1.0f, // 右上0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 1.0f, 0.0f, // 右下-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, 1.0f, 0.0f, 0.0f, // 左下-0.5f, 0.5f, 0.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f // 左上 };由于我們添加了一個額外的頂點屬性,我們必須告訴OpenGL我們新的頂點格式:
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8 * sizeof(float), (void*)(6 * sizeof(float))); glEnableVertexAttribArray(2);注意,我們同樣需要調(diào)整前面兩個頂點屬性的步長參數(shù)為8 * sizeof(float)。
接著我們需要調(diào)整頂點著色器使其能夠接受頂點坐標(biāo)為一個頂點屬性,并把坐標(biāo)傳給片段著色器:
#version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aColor; layout (location = 2) in vec2 aTexCoord;out vec3 ourColor; out vec2 TexCoord;void main() {gl_Position = vec4(aPos, 1.0);ourColor = aColor;TexCoord = aTexCoord; }片段著色器應(yīng)該接下來會把輸出變量TexCoord作為輸入變量。
片段著色器也應(yīng)該能訪問紋理對象,但是我們怎樣能把紋理對象傳給片段著色器呢?GLSL有一個供紋理對象使用的內(nèi)建數(shù)據(jù)類型,叫做采樣器(Sampler),它以紋理類型作為后綴,比如sampler1D、sampler3D,或在我們的例子中的sampler2D。我們可以簡單聲明一個uniform sampler2D把一個紋理添加到片段著色器中,稍后我們會把紋理賦值給這個uniform。
#version 330 core out vec4 FragColor;in vec3 ourColor; in vec2 TexCoord;uniform sampler2D ourTexture;void main() {FragColor = texture(ourTexture, TexCoord); }我們使用GLSL內(nèi)建的texture函數(shù)來采樣紋理的顏色,它第一個參數(shù)是紋理采樣器,第二個參數(shù)是對應(yīng)的紋理坐標(biāo)。texture函數(shù)會使用之前設(shè)置的紋理參數(shù)對相應(yīng)的顏色值進(jìn)行采樣。這個片段著色器的輸出就是紋理的(插值)紋理坐標(biāo)上的(過濾后的)顏色。
現(xiàn)在只剩下在調(diào)用glDrawElements之前綁定紋理了,它會自動把紋理賦值給片段著色器的采樣器:
glBindTexture(GL_TEXTURE_2D, texture); glBindVertexArray(VAO); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);我們還可以把得到的紋理顏色與頂點顏色混合,來獲得更有趣的效果。我們只需把紋理顏色與頂點顏色在片段著色器中相乘來混合二者的顏色:
FragColor = texture(ourTexture, TexCoord) * vec4(ourColor, 1.0);最終的效果應(yīng)該是頂點顏色和紋理顏色的混合色:
我猜你會說我們的箱子喜歡跳70年代的迪斯科。
紋理單元
你可能會奇怪為什么sampler2D變量是個uniform,我們卻不用glUniform給它賦值。使用glUniform1i,我們可以給紋理采樣器分配一個位置值,這樣的話我們能夠在一個片段著色器中設(shè)置多個紋理。一個紋理的位置值通常稱為一個紋理單元(Texture Unit)。一個紋理的默認(rèn)紋理單元是0,它是默認(rèn)的激活紋理單元,所以教程前面部分我們沒有分配一個位置值。
紋理單元的主要目的是讓我們在著色器中可以使用多于一個的紋理。通過把紋理單元賦值給采樣器,我們可以一次綁定多個紋理,只要我們首先激活對應(yīng)的紋理單元。就像glBindTexture一樣,我們可以使用glActiveTexture激活紋理單元,傳入我們需要使用的紋理單元:
glActiveTexture(GL_TEXTURE0); // 在綁定紋理之前先激活紋理單元 glBindTexture(GL_TEXTURE_2D, texture);激活紋理單元之后,接下來的glBindTexture函數(shù)調(diào)用會綁定這個紋理到當(dāng)前激活的紋理單元,紋理單元GL_TEXTURE0默認(rèn)總是被激活,所以我們在前面的例子里當(dāng)我們使用glBindTexture的時候,無需激活任何紋理單元。
OpenGL至少保證有16個紋理單元供你使用,也就是說你可以激活從GL_TEXTURE0到GL_TEXTRUE15。它們都是按順序定義的,所以我們也可以通過GL_TEXTURE0 + 8的方式獲得GL_TEXTURE8,這在當(dāng)我們需要循環(huán)一些紋理單元的時候會很有用。
我們?nèi)匀恍枰庉嬈沃鱽斫邮樟硪粋€采樣器。這應(yīng)該相對來說非常直接了:
#version 330 core ...uniform sampler2D texture1; uniform sampler2D texture2;void main() {FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2); }最終輸出顏色現(xiàn)在是兩個紋理的結(jié)合。GLSL內(nèi)建的mix函數(shù)需要接受兩個值作為參數(shù),并對它們根據(jù)第三個參數(shù)進(jìn)行線性插值。如果第三個值是0.0,它會返回第一個輸入;如果是1.0,會返回第二個輸入值。0.2會返回80%的第一個輸入顏色和20%的第二個輸入顏色,即返回兩個紋理的混合色。
先綁定紋理到對應(yīng)的紋理單元,然后定義哪兒uniform采樣器對應(yīng)哪個紋理單元:
glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture1); glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, texture2);glBindVertexArray(VAO); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);我們還要通過使用glUniform1i設(shè)置每個采樣器的方式告訴OpenGL每個著色器采樣器屬于哪個紋理單元。我們只需要設(shè)置一次即可,所以這個會放在渲染循環(huán)的前面:
ourShader.use(); // 不要忘記在設(shè)置uniform變量之前激活著色器程序! glUniform1i(glGetUniformLocation(ourShader.ID, "texture1"), 0); // 手動設(shè)置 ourShader.setInt("texture2", 1); // 或者使用著色器類設(shè)置while(...) {[...] }通過使用glUniform1i設(shè)置采樣器,我們保證了每個uniform采樣器對應(yīng)著正確的紋理單元。你應(yīng)該能得到下面的結(jié)果:
紋理的鏡像
你可能注意到紋理上下顛倒了!這是因為OpenGL要求y軸0.0坐標(biāo)是在圖片的底部的,但是圖片的y軸0.0坐標(biāo)通常在頂部。很幸運,stb_image.h能夠在圖像加載時幫助我們翻轉(zhuǎn)y軸,只需要在加載任何圖像前加入以下語句即可:
stbi_set_flip_vertically_on_load(true);在讓stb_image.h在加載圖片時翻轉(zhuǎn)y軸之后你就應(yīng)該能夠獲得下面的結(jié)果了:
當(dāng)然也可以通過更改shader的fs函數(shù)實現(xiàn)在x軸上的鏡像。
更改紋理坐標(biāo):
float vertices[] = {// positions // colors // texture coords0.5f, 0.5f, 0.0f, 1.0f, 0.0f, 0.0f, 2.0f, 2.0f, // top right0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f, 2.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, 2.0f // top left }; 與50位技術(shù)專家面對面20年技術(shù)見證,附贈技術(shù)全景圖總結(jié)
以上是生活随笔為你收集整理的OpenGL从入门到精通--纹理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: OpenGL从入门到精通--着色器的使用
- 下一篇: leetCode刷题--两数相加