OpenGL Texture¶
-
While process:
- loading image
- images -> textures
- bind texture unit & prepare shader
- rendering :)
-
TL;DR for loading
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
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); // unsigned char *data = load_image("example.jpg"); // 自訂載入圖片方式 uint32_t width = /* 圖片的寬 */; uint32_t height = /* 圖片的高 */; 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; } free_image(data); // 釋放圖片
-
There is no TL;DR for using :(
Loading Textures¶
- Create texture
1 2 |
|
- Bind texture
1 2 3 4 5 |
|
- Loading images to textures
- 各個平台、不同的圖片格式甚至是底層的 library 都有不同的圖片載入的方法,因此這裡不贅述
- stb_image
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);
glGenerateMipmap(GL_TEXTURE_2D);
glTexImage2D(target, level, internalFormat, width, height, border, format, type, * data)
- Target
- Mipmap Level
- 0 = base
- internalFormat 儲存格式
- width 寬度, height 高度
- border 永遠 = 0 (歷史遺留)
- format 原圖格式
- type 資料類型
- data 真正的圖像資料
Texture Wrapping 材質環繞方式¶
材質座標的範圍是 \([0, 1]\)(第一象限) ,如果超出這個座標之外預設的行為是重複,但其實 OpenGL 提供了更多的選擇:
環繞方式 | 描述 |
---|---|
GL_REPEAT |
預設,重複材質圖像 |
GL_MIRRORED_REPEAT |
一樣是重複材質,但圖片是鏡像的 |
GL_CLAMP_TO_EDGE |
材質座標會被約束在 \([0,1]\),超出的部分會重複邊緣 並產生拉伸的效果 |
GL_CLAMP_TO_BORDER |
超出的座標為指定的顏色 |
這些選項都可以對單獨的材質座標軸設定,s, t 和 r 軸(如果是 3D 的話)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_MIRRORED_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_MIRRORED_REPEAT);
// glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_R, GL_MIRRORED_REPEAT);
- 選擇
GL_CLAMP_TO_BORDER
則還需要指定一個顏色
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)映射到紋理座標(Texture Coordinate)。 如果一個很大的物體,可是紋理解析度很低,就會有明顯的瑕疵出現。
-
紋理濾波 Texture Filter
GL_NEAREST
GL_LINEAR
- 其他過濾方式,參見
-
鄰近濾波 Nearest Neighbor Filtering
- OpenGL 預設使用
GL_NEAREST
- 選 Texture Coordinate 最接近的 紋素(Texel)
- 看起來產生了顆粒狀
- OpenGL 預設使用
- 雙線性過濾 Bilinear Filtering
GL_LINEAR
- 基於紋理座標附近的紋理像素,計算插值,近似出這些紋理像素們之間的顏色
- 紋理座標離該紋理像素越接近則對最終顏色貢獻越大
- 看起來平滑
- OpenGL 可以設定紋理在放大或縮小時要使用的過濾方式
GL_TEXTURE_MIN_FILTER
縮小GL_TEXTURE_MAG_FILTER
放大
// 縮小使用 GL_NEAREST
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
// 放大使用 GL_LINEAR
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
Mipmapping¶
假如說場景在開闊的地方,有上千個物體,每個物體上都有紋理,近處的物體的紋理解析度很高,但遠處的物體可能只產生很小的片段(Fragment),在高解析度的紋理中這種物體的顏色通常都不正確,並且也浪費記憶體。
多級漸遠紋理(Mipmap)是一系列的紋理圖像,後一個的大小是前個的二分之一。當觀察者的距離超過一定的閾值(threshold),自動切換至適合該距離的紋理。距離遠用解析度比較小的,也不容易被察覺。
Mipmap 可以自己手動產生,不過 OpenGL 可以幫我們自動產生。
- 紋理過濾等級 + Mipmap
_MIPMAP_NEAREST
- 使用最接近的 Mip Level
_MIPMAP_LINEAR
- 在兩個最接近的 Mip Level 插值
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glGenerateMipmap(GL_TEXTURE_2D);
Info
放大過濾不可以使用 Mipmap ,因為 Mipmap 是給縮小紋理使用的
Using Textures¶
Need to prepare texture coordinates in vertices like:
float vertices[] = {
// ---- 位置 ---- 紋理座標 -
0.5f, 0.5f, 0.0f, 1.0f, 1.0f, // 右上
0.5f, -0.5f, 0.0f, 1.0f, 0.0f, // 右下
-0.5f, -0.5f, 0.0f, 0.0f, 0.0f, // 左下
-0.5f, 0.5f, 0.0f, 0.0f, 1.0f // 左上
};
- Setting up vertex attributes
1 2 3 4 5 6 |
|
- In Shaders
- vs
1 2 3 4 5 6 7 8 9 10 11
#version 450 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec2 aTexCoord; out vec2 TexCoord; void main() { gl_Position = vec4(aPos.x, aPos.y, aPos.z, 1.0); TexCoord = aTexCoord; }
- fs
1 2 3 4 5 6 7 8 9 10 11 12 13
#version 450 core out vec4 FragColor; in vec2 TexCoord; uniform sampler2D tex; uniform vec4 fColor; void main() { FragColor = texture(tex, TexCoord) * fColor; }
- vs
Info
GLSL 內建 texture() 函數來採樣,第一個參數是採樣的紋理,第二個參數是紋理座標,輸出去紋理座標(插值後)並且經過 Filter 後的顏色。
- Bind texture units
1 2 |
|
Bind multiple textures¶
- fs
1 2 3 4 5 6 7 8 9 |
|
Specify different texture unit for two textures
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
|