Texture Filtering and Mipmapping 紋理過濾與多級紋理
前面我們已經講了單個2D圖像的2D紋理的介紹,這篇文章主要講解多級紋理。紋理坐標是用於生成一個2D索引,當放大和縮小設置為GL_NEAREST時,將發生一個單一紋理將匹配到紋理坐標位置中,這是一個最近點的采樣。
當使用一個多級紋理時,我們可以設置過濾模式,,為了達到屏幕像素和紋理圖片像素更合適的比例,減少鋸齒。因為多級紋理貼圖的成功過濾,當在更遠處觀察時,我們向貼圖鏈后移動,鋸齒減少,實現高質量的圖像。
// Load mipmap level 0 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels); level = 1; prevImage = &pixels[0]; while(width > 1 && height > 1) { int newWidth, newHeight; // Generate the next mipmap level GenMipMap2D( prevImage, &newImage, width, height, &newWidth, &newHeight); // Load the mipmap level glTexImage2D(GL_TEXTURE_2D, level, GL_RGB, newWidth, newHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, newImage); // Free the previous image free(prevImage); // Set the previous image for the next iteration prevImage = newImage; level++; // Half the width and height width = newWidth; height = newHeight; } free(newlmage);
GenMipMap2D用於用於實現多級紋理。紋理過濾有2種:放大和縮小。當屏幕上設計的多邊形的大小小於紋理圖像時,我們使用縮小紋理。反之使用放大。使用過濾類型由具體硬件自動選擇,但是API也提供了過濾控制,放大處理是不相關的,因為我們總是使用最大
可用的級別。對於縮小,有各種的采樣模式可以使用。哪種模式的使用選擇基於你需要實現的可視質量和你想要實現多大性能的紋理過濾來決定的。過濾模式使用glTexParameter[i|f][v]來指定。
放大過濾可以是GL_NEAREST 或 GL_LINEAR。在GL_NEAREST 放大過濾模式,紋理最近的單點將做紋理坐標。在GL_LINEAR 模式,雙線性(四個點平均)作為紋理坐標。
縮小過濾可以是下列值:
•• GL_NEAREST—Takes a single point sample from the texture nearest to the texture coordinate.
•• GL_LINEAR—Takes a bilinear sample from the texture nearest to the texture coordinate.
•• GL_NEAREST_MIPMAP_NEAREST—Takes a single point sample from the closest mip level chosen.
•• GL_NEAREST_MIPMAP_LINEAR—Takes a sample from the two closest mip levels and interpolates between those samples.
•• GL_LINEAR_MIPMAP_NEAREST—Takes a bilinear fetch from the closest mip level chosen.
•• GL_LINEAR_MIPMAP_LINEAR—Takes a bilinear fetch from each of the two closest mip levels and then interpolates between them. This last mode, which is typically referred to as trilinear filtering, produces the best quality of all modes.
GL_NEAREST 和 GL_LINEAR 是唯一不需要完整多級紋理的縮小過濾模式,其他都需要完整的多級處理。
GL_NEAREST 和GL_LINEAR_MIPMAP_LINEAR的過濾設置。
值得一提的是一些性能將影響你選擇的紋理過濾模式。對於大多數硬件來說,使用多級紋理是最好的選擇。
Seamless Cubemap Filtering
它是3.0新特性。當一個線性過濾核心在一個立方體紋理的邊框時,這個過濾只發生在線所在立方體的一面中。你不需要設置Seamless Cubemap Filtering,線性過濾會自動使用它。
自動多級紋理生成
前面我們已經創建了一個level為0的多級紋理,這是一種方法。另外也提供了自動多級紋理生成函數 glGenerateMipmap。
我們隊綁定的紋理對象調用glGenerateMipmap,它會為我們生成從原始圖像到level為0的多級紋理鏈。當你使用framebuffer對象時,自動多級紋理生成變得尤為重要。當渲染一個紋理時,我們不想將紋理讀回CPU中生成多級紋理。glGenerateMipmap可以解決這個問題。
Texture Coordinate Wrapping
當紋理坐標超過了范圍[0.0, 1.0] 時,使用紋理包裝來實現。紋理包裝模式使用glTexParameter[i|f][v]來指定。
紋理包裝模式能被獨立的設定為s坐標和t坐標。GL_TEXTURE_WRAP_S模式定義s 坐標超出范圍[0.0, 1.0]的情況,GL_TEXTURE_WRAP_T 設定t 坐標超出范圍[0.0, 1.0]的情況。有三種包裝模式供選擇
注意,紋理包裝模式對過濾行為有影響。例如紋理坐標是邊緣時,雙線性過濾將掃描紋理的邊緣。這時包裝模式將決定哪個紋理是紋理邊緣的外面而應用於過濾算法。如果你不想要任何形式的重復,應該使用GL_CLAMP_TO_EDGE。
下圖是使用紋理繪制正方形在三種包裝模式中的效果圖
// void Draw ( ESContext *esContext ) { UserData *userData = esContext->userData; GLfloat vVertices[] = { -0.3f, 0.3f, 0.0f, 1.0f, // Position 0 -1.0f, -1.0f, // TexCoord 0 -0.3f, -0.3f, 0.0f, 1.0f, // Position 1 -1.0f, 2.0f, // TexCoord 1 0.3f, -0.3f, 0.0f, 1.0f, // Position 2 2.0f, 2.0f, // TexCoord 2 0.3f, 0.3f, 0.0f, 1.0f, // Position 3 2.0f, -1.0f // TexCoord 3 }; GLushort indices[] = { 0, 1, 2, 0, 2, 3 }; // Set the viewport glViewport ( 0, 0, esContext->width, esContext->height ); // Clear the color buffer glClear ( GL_COLOR_BUFFER_BIT ); // Use the program object glUseProgram ( userData->programObject ); // Load the vertex position glVertexAttribPointer ( 0, 4, GL_FLOAT, GL_FALSE, 6 * sizeof ( GLfloat ), vVertices ); // Load the texture coordinate glVertexAttribPointer ( 1, 2, GL_FLOAT, GL_FALSE, 6 * sizeof ( GLfloat ), &vVertices[4] ); glEnableVertexAttribArray ( 0 ); glEnableVertexAttribArray ( 1 ); // Bind the texture glActiveTexture ( GL_TEXTURE0 ); glBindTexture ( GL_TEXTURE_2D, userData->textureId ); // Set the sampler texture unit to 0 glUniform1i ( userData->samplerLoc, 0 ); // Draw quad with repeat wrap mode glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT ); glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT ); glUniform1f ( userData->offsetLoc, -0.7f ); glDrawElements ( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices ); // Draw quad with clamp to edge wrap mode glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); glUniform1f ( userData->offsetLoc, 0.0f ); glDrawElements ( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices ); // Draw quad with mirrored repeat glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT ); glTexParameteri ( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT ); glUniform1f ( userData->offsetLoc, 0.7f ); glDrawElements ( GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, indices ); }