1. 程式人生 > >opelgl(四)紋理1

opelgl(四)紋理1

如果想讓圖形看起來真實一點,那我們就必須有足夠多的頂點來指定足夠多的顏色,這會產生許多額外的開銷,於是產生了一種叫做紋理的圖形,可以用他來新增物體的細節,你可以想象紋理是一張會有磚塊的紙並且可以無縫的摺疊貼合到你的3D的房子上,這樣你的房子看起來就像有磚牆外表了。 這裡寫圖片描述 為了能夠把紋理對映到三角形上,我們需要指定三角形的每個頂點對應的紋理的哪個部分,這樣每個頂點就會關聯一個紋理座標,我們為三角形指定了3個紋理座標點。如上圖所示,我們希望三角形的左下角對應紋理的左下角,因此我們把三角形左下角頂點的紋理座標設定為(0, 0);三角形的上頂點對應於圖片的上中位置所以我們把它的紋理座標設定為(0.5, 1.0);同理右下方的頂點設定為(1, 0)。我們只要給頂點著色器傳遞這三個紋理座標就行了,接下來它們會被傳片段著色器中,它會為每個片段進行紋理座標的插值。 1紋理環繞方式

  • GL_REPEAT 對紋理的預設行為,重複紋理圖形,
  • GL_MIRRORED_REPEAT 和GL_REPEAT一樣,但每次重複圖片都是映象放置的
  • GL_CLAMP_TO_EDGE 紋理座標會被約束在0和1之間,超出的部分會重複紋理座標的邊緣,產生一種被拉伸的效果。
  • GL_CLAMP_TO_BORDER 超出的座標為使用者指定的顏色 以上的這些選項都可以用glTexParameter*函式對每個座標軸單獨設定引數,(s,t,r分別對應下,x,y,z)
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WARP_S,GL_MIRRORED_REPEAT)
; glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WARP_T,GL_MIRRORED_REPEAT);

如果選擇GL_CLAMP_TO_BORDED選項,還需要指定一個邊緣的顏色,此時需要使用fv字尾形式

float borderColor[] ={1.0f,1.0f,0.0f,1.0f};
glTexPatameterfv(GL_TEXTURE_2D,GL_TEXTURE_BORDER_COLOR,borderColor);

2紋理過濾 兩種最重要的 GL_NEAREST 和GL_LINEAR 第一種是openGL的預設處理方式,會選擇影象最中心最接近紋理座標的那個畫素, 第二種就是綜合座標附近的紋理畫素並且計算出一個插值。 當進行放大(Magnify)和縮小(Minify)操作的時候可以設定紋理過濾選項,這段程式碼設置於紋理選項很像

glTexparameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);//縮小用線性法
glTexpatameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);放大就用鄰近法

3多級漸遠紋理 想象一下,假設我們有一個包含著上千物體的大房間,每個物體上都有紋理。有些物體會很遠,但其紋理會擁有與近處物體同樣高的解析度。由於遠處的物體可能只產生很少的片段,OpenGL從高解析度紋理中為這些片段獲取正確的顏色值就很困難,因為它需要對一個跨過紋理很大部分的片段只拾取一個紋理顏色。在小物體上這會產生不真實的感覺,更不用說對它們使用高解析度紋理浪費記憶體的問題了。 openGL使用一種叫做多級漸遠紋理(Mipmap)的概念解決這個問題, 這裡寫圖片描述

openGL會根據影象距離觀察者的距離來選擇合適的多級漸遠紋理,而且他的效能十分優秀,在建立完一個紋理物件之後,呼叫glGenerateMipmap函式,這樣openGL會承擔接下來的所有工作。 OpenGL在兩個多級漸遠紋理級別之間會產生不真實的生硬的邊界,此時,我們可以使用Nearest和Linear兩種方式對其進行過濾,可以使用以下四個選項代替原有的過濾方式。

過濾方式 描述 - GL_NEAREST_MIPMAP_NEAREST 使用最鄰近的多級漸遠紋理來匹配畫素大小,並使用鄰近插值進行紋理取樣 - GL_LINEAR_MIPMAP_NEAREST 使用最鄰近的多級漸遠紋理級別,並使用線性插值進行取樣 - GL_NEAREST_MIPMAP_LINEAR 在兩個最匹配畫素大小的多級漸遠紋理之間進行線性插值,使用鄰近插值進行取樣 - GL_LINEAR_MIPMAP_LINEAR 在兩個鄰近的多級漸遠紋理之間使用線性插值,並使用線性插值進行取樣 。 如同紋理過濾一樣,可以用glTexParameteri將過濾方式設定為前面提到的四種方式之一

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR)
//常見的錯誤就是為放大選項設定多級紋理過濾,這樣沒有任何效果,紋理放大不會使用多級漸遠紋理

4stb_image.h 使用stb_image.h來載入大部分流行的檔案格式,並且很簡單的整合到你的工程中去。 可以從這裡下載 5生成紋理, 與著色器等OpenGL物件一樣,紋理也是通過ID來建立的

unsigned int texture;
glGenTexture(GL_TEXTURE_2D,texture);
glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,width,height,0,GL_RGB,GL_UNSIGNED_BYTE,data);
glGenerateMipmap(GL_TEXTURE_2D);
stbi_image_free(data);

生成一個影象應該是如下所示

unsigned int texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
// 為當前繫結的紋理物件設定環繞、過濾方式
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);

接下來就是應用紋理了 首先修改頂點陣列,使其增加紋理座標;

float vertices[] = {
//     ---- 位置 ----       ---- 顏色 ----     - 紋理座標 -
     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);

接著修改頂點著色器

#version 330 core
layout(location=0) in vec3 aPos;
layout(location=1) in vec3 aColor;
layout(location=2) in vec3 aTexCoord;

out vec3 ourColor;
out vec2 TexCoord;
void main()
{
    gl_position =vec4(aPos,1.0);
    ourColor =aColor;
    TexCoord=aTexCoord;
}

GLSL有一個供紋理物件使用的內建資料型別,叫做取樣器(Sampler),它以紋理型別作為字尾,比如sampler1D、sampler3D,或在我們的例子中的sampler2D。

#version 330 core
out vec4 FragColor;

in vec3 ourColor;
in vec2 TexCoord;

uniform sampler2D ourtexture;
void main()
{
    FragColor =texture(ourtexture,TexCoord);
}

在繪畫前繫結紋理, glBindTexture(GL_TEXTURE_2D,texture); glBindVertexArray(VAO); glDrawElements(GL_TRIANGLES,6,GL_UNSIGNED_INT,0);