天空和背景
對于 3D 場景,通常可以通過在遠處的地平線附近創造一些逼真的效果,來增強其真實感。
我們可以采用天空盒、天空柱(Skydome)或天空穹(Skydome)等技術來模擬天空。
天空盒
天空盒(Skybox)是一種在3D圖形渲染中用于模擬遠處背景的技術。它通過將場景包裹在一個巨大的立方體(或球體)中,并在其內表面貼上紋理來實現。天空盒通常用于表示天空、云、山脈或其他遠景背景。
工作原理:
- 立方體模型:天空盒通常是一個立方體,攝像機位于其中心。
- 紋理貼圖:立方體的六個面分別貼上對應的紋理(前、后、左、右、上、下),這些紋理拼接在一起形成完整的背景。
- 固定位置:天空盒始終跟隨攝像機移動,但不會旋轉或縮放,從而給人一種背景無限遠的錯覺。
- 渲染順序:天空盒通常在渲染場景之前繪制,并禁用深度測試,以確保它始終位于場景的最遠處。
優點:
- 高效:天空盒的實現簡單,性能開銷低。
- 真實感:可以通過高質量紋理提供逼真的背景效果。
- 靈活性:適用于各種場景,如白天、夜晚、宇宙等。
缺點:
- 分辨率限制:紋理分辨率過低可能導致模糊或失真。
- 接縫問題:如果紋理拼接不當,可能會在立方體的邊緣出現接縫。
天空盒廣泛應用于游戲和虛擬現實中,用于增強場景的沉浸感和視覺效果。
對于天空盒,可以有兩下兩種實現方式:
- 采用6張圖片,對應立方體的六個面,分別貼上圖片,然后渲染。
- 采用一張圖片,將圖片貼在立方體的六個面,然后渲染。
我們先采用第二種方式,實現天空盒。
下面是將6張圖片放到一張圖片上形成的紋理
其與立方體六個面的關系如下:
實現思路
- 創建一個立方體模型,設置其紋理坐標,使其與天空盒紋理對應。
- 創建一個紋理對象,將天空盒紋理加載到該對象中。
- 在渲染循環中,將紋理對象綁定到著色器,并繪制立方體模型。
- 立方體的中心位置始終與攝像機的位置相同。在攝像機移動時,更新立方體的位置,使其始終跟隨攝像機。
- 渲染時,不要啟用深度測試,以確保天空盒始終位于場景的最遠處。
- 由于攝像機是在內部,而我們定義立方體時,是從外部定義,外部立方體三角形是逆時針,當我們從內部看時,需要將三角形定義為順時針
立方體的坐標
float cubeVertexPositions[108] ={ -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f,1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f, 1.0f, -1.0f,1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, -1.0f,1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f, -1.0f,1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f,-1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f, 1.0f, 1.0f,-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f,-1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f,-1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, -1.0f,1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, -1.0f, 1.0f,-1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, 1.0f,1.0f, 1.0f, 1.0f, -1.0f, 1.0f, 1.0f, -1.0f, 1.0f, -1.0f};float cubeTextureCoord[72] ={ 1.00f, 0.6666666f, 1.00f, 0.3333333f, 0.75f, 0.3333333f, // back face lower right0.75f, 0.3333333f, 0.75f, 0.6666666f, 1.00f, 0.6666666f, // back face upper left0.75f, 0.3333333f, 0.50f, 0.3333333f, 0.75f, 0.6666666f, // right face lower right0.50f, 0.3333333f, 0.50f, 0.6666666f, 0.75f, 0.6666666f, // right face upper left0.50f, 0.3333333f, 0.25f, 0.3333333f, 0.50f, 0.6666666f, // front face lower right0.25f, 0.3333333f, 0.25f, 0.6666666f, 0.50f, 0.6666666f, // front face upper left0.25f, 0.3333333f, 0.00f, 0.3333333f, 0.25f, 0.6666666f, // left face lower right0.00f, 0.3333333f, 0.00f, 0.6666666f, 0.25f, 0.6666666f, // left face upper left0.25f, 0.3333333f, 0.50f, 0.3333333f, 0.50f, 0.0000000f, // bottom face upper right0.50f, 0.0000000f, 0.25f, 0.0000000f, 0.25f, 0.3333333f, // bottom face lower left0.25f, 1.0000000f, 0.50f, 1.0000000f, 0.50f, 0.6666666f, // top face upper right0.50f, 0.6666666f, 0.25f, 0.6666666f, 0.25f, 1.0000000f // top face lower left};
渲染代碼(部分) 繪制立方體
void display()
{//...//立方體的位置始終同攝像機位置相同mMat = glm::translate(glm::mat4(1.0f), glm::vec3(cameraX, cameraY, cameraZ))glDisable(GL_DEPTH_TEST); // 關閉深度測試glEnable(GL_CULL_FACE); // 開啟面剔除glFrontFace(GL_CCW); // 設置正面為順時針glDrawArrays(GL_TRIANGLES, 0, 36); // 繪制三角形}
頂點著色器代碼
頂點著色器相對簡單,只是將頂點位置和紋理坐標傳遞給片段著色器。
#version 430
// 指定 GLSL 的版本為 4.30layout (location=0) in vec3 position;
layout (location=1) in vec2 texCoord; // 輸入變量,表示頂點的顏色,綁定到 location = 1uniform mat4 mv_matrix;
// uniform 變量,表示模型-視圖矩陣,用于將頂點從模型空間變換到視圖空間uniform mat4 proj_matrix;
// uniform 變量,表示投影矩陣,用于將頂點從視圖空間變換到裁剪空間out vec2 tc;
// 輸出變量,表示頂點的顏色,綁定到 location = 0
void main(void)
// 主函數,計算頂點的最終位置
{gl_Position = proj_matrix * mv_matrix * vec4(position,1.0);// 將頂點位置從模型空間依次變換到視圖空間和裁剪空間// 最終結果存儲在內置變量 gl_Position 中,用于后續的光柵化階段tc = texCoord;}
片段著色器代碼
#version 430
// 指定 GLSL 的版本為 4.30in vec2 tc;out vec4 color;
// 輸出變量,表示片段的最終顏色uniform mat4 mv_matrix;
// uniform 變量,模型-視圖矩陣(未使用)uniform mat4 proj_matrix;
// uniform 變量,投影矩陣(未使用)
layout (binding=0) uniform sampler2D tex0;
//uniform sampler2D tex0;void main(void)
// 主函數,計算片段的最終顏色
{color = texture(tex0, tc);
}
下圖上方能看到立方體的接縫
使用 OpenGL 立方體貼圖
用 OpenGL 立方體貼圖有自己的優點,例如可以減少接縫以及支持環境貼圖
OpenGL 紋理立方體貼圖類似于稍后將要研究的3D 紋理,它們都使用帶有3 個變量的紋理坐標訪問——通常標記為**(s, t, r)**,而不是我們目前為止用到的帶有兩個變量的紋理坐標。OpenGL立方體貼圖的另一個特性是,其中的圖像以紋理圖像的左上角而不是通常的左下角)作為紋理坐標(0, 0, 0)
實現思路
- 創建一個立方體模型,無需額外立方體紋理坐標,立方體頂點坐標就是紋理坐標。
- 創建一個紋理對象(片段著色器中
samplerCube
),將6張天空盒圖片加載到該對象中。 - 在渲染循環中,將紋理對象綁定到著色器,并繪制立方體模型。
- 立方體的中心位置始終與攝像機的位置相同。在攝像機移動時,更新立方體的位置,使其始終跟隨攝像機。
- 渲染時,不要啟用深度測試,以確保天空盒始終位于場景的最遠處。
- 由于攝像機是在內部,而我們定義立方體時,是從外部定義,外部立方體三角形是逆時針,當我們從內部看時,需要將三角形定義為順時針
采樣器類型
采樣器類型 | 維度 | 主要用途 | 特點 |
---|---|---|---|
sampler2D | 2D | 普通2D紋理采樣 | ? 用于常規2D紋理映射 ? 返回(r,g,b,a)四個分量 ? 最常用的紋理采樣器類型 |
samplerCube | 3D | 立方體貼圖采樣 | ? 用于環境映射、天空盒等 ? 使用3D向量作為采樣坐標 ? 六個面的紋理組合成立方體 |
sampler2DShadow | 2D | 陰影貼圖采樣 | ? 專門用于陰影映射 ? 返回單個深度值(0.0到1.0) ? 自動進行深度值比較 ? 通常與深度紋理配合使用 |
代碼實現
以下是運行效果
加載6張天空盒圖片
GLuint Utils::loadCubeMap(const char* mapDir) {GLuint textureRef;string xp = mapDir; xp = xp + "/xp.jpg";string xn = mapDir; xn = xn + "/xn.jpg";string yp = mapDir; yp = yp + "/yp.jpg";string yn = mapDir; yn = yn + "/yn.jpg";string zp = mapDir; zp = zp + "/zp.jpg";string zn = mapDir; zn = zn + "/zn.jpg";textureRef = SOIL_load_OGL_cubemap(xp.c_str(), xn.c_str(), yp.c_str(), yn.c_str(), zp.c_str(), zn.c_str(),SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_MIPMAPS);if (textureRef == 0) cout << "didnt find cube map image file" << endl;// glBindTexture(GL_TEXTURE_CUBE_MAP, textureRef);// reduce seams// glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);// glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);// glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);return textureRef;
}
渲染立方體
此部份與之前代碼基本相同,只是綁定 GL_TEXTURE_CUBE_MAP
glActiveTexture(GL_TEXTURE0); // 激活紋理單元 glBindTexture(GL_TEXTURE_CUBE_MAP, cubeTexture); // 綁定紋理對象glDisable(GL_DEPTH_TEST); // 關閉深度測試glEnable(GL_CULL_FACE); // 開啟面剔除glFrontFace(GL_CCW); // 設置正面為順時針
頂點著色器
此處采用 mat4(mat3(mv_matrix))
來將模型-視圖矩陣轉換為模型-視圖矩陣,去除平移部分,這樣確保天空盒與攝像機始終處于同一位置
#version 430
// 指定 GLSL 的版本為 4.30
layout (location=0) in vec3 position; // 輸入變量,表示頂點的三維位置,綁定到 location = 0out vec3 texCoord; // 輸出變量uniform mat4 mv_matrix;
// uniform 變量,表示模型-視圖矩陣,用于將頂點從模型空間變換到視圖空間
uniform mat4 proj_matrix;
// uniform 變量,表示投影矩陣,用于將頂點從視圖空間變換到裁剪空間
void main(void)
// 主函數,計算頂點的最終位置
{mat4 vrot_matrix=mat4(mat3(mv_matrix)); //remove the translation partgl_Position = proj_matrix * vrot_matrix * vec4(position,1.0);// 將頂點位置從模型空間依次變換到視圖空間和裁剪空間// 最終結果存儲在內置變量 gl_Position 中,用于后續的光柵化階段texCoord = position;
}
片段著色器
片段著色器中 只是進行紋理采樣
#version 430
// 指定 GLSL 的版本為 4.30in vec3 texCoord; // 輸入變量,表示頂點對應的紋理坐標
out vec4 fragColor; // 輸出變量,表示片元最終的顏色
uniform samplerCube texCube; // 紋理采樣器,表示立方體貼圖
void main(void)
{fragColor = texture(texCube, texCoord); // 采樣立方體貼圖,得到片元的顏色
}
環境貼圖
環境貼圖概述
環境貼圖是一種模擬物體表面反射周圍環境的渲染技術,主要用于實現鏡面反射、金屬材質等效果。
工作原理
反射原理
- 通過采集物體周圍環境的圖像信息
- 根據視角和表面法線計算反射向量
- 使用反射向量從立方體貼圖中采樣顏色
主要應用場景
-
鏡面物體
- 鏡子
- 金屬表面
- 光滑水面
-
金屬材質
- 車身漆面
- 金屬器皿
- 珠寶首飾
優缺點
優點
- 渲染效率高
- 可以實現逼真的反射效果
- 適合實時渲染
缺點
- 無法實現真實的反射折射
- 環境貼圖分辨率限制細節表現
- 難以實現動態場景的實時反射
常見變體
-
球形環境貼圖
- 使用單張球形投影的圖像
- 實現簡單但有畸變
-
立方體環境貼圖
- 使用六張圖構成立方體
- 質量更好,無畸變問題
-
動態環境貼圖
- 實時渲染場景到環境貼圖
- 可實現動態反射效果
相應實現原理
頂點著色器
#version 430
// 指定 GLSL 的版本為 4.30layout (location=0) in vec3 position;
layout (location=1) in vec2 texCoord; // 輸入變量,表示頂點的顏色,綁定到 location = 1
layout (location=2) in vec3 normal; // 輸入變量,表示頂點的法線,綁定到 location = 2
// 輸入變量,表示頂點的三維位置,綁定到 location = 0uniform mat4 mv_matrix;
// uniform 變量,表示模型-視圖矩陣,用于將頂點從模型空間變換到視圖空間uniform mat4 proj_matrix;
// uniform 變量,表示投影矩陣,用于將頂點從視圖空間變換到裁剪空間uniform mat4 normal_matrix;
out vec2 tc;out vec3 fragNormal;out vec3 vertPos;
void main(void)
// 主函數,計算頂點的最終位置
{vertPos=(mv_matrix*vec4(position,1.0)).xyz;gl_Position = proj_matrix * mv_matrix * vec4(position,1.0);// 將頂點位置從模型空間依次變換到視圖空間和裁剪空間// 最終結果存儲在內置變量 gl_Position 中,用于后續的光柵化階段tc = texCoord;fragNormal = mat3(normal_matrix) * normal;// 將法線從模型空間變換到視圖空間}
片段著色器
核心代碼為 vec3 R = -reflect(V, N);
其中 reflect
函數的第一個參數為入射向量,第二個參數為法線向量,返回值為反射向量。
#version 430
// 指定 GLSL 的版本為 4.30in vec2 tc;in vec3 fragNormal;
in vec3 vertPos;
out vec4 color;
// 輸出變量,表示片段的最終顏色uniform mat4 mv_matrix;
// uniform 變量,模型-視圖矩陣(未使用)uniform mat4 proj_matrix;
// uniform 變量,投影矩陣(未使用)
layout (binding=0) uniform samplerCube tex0;void main(void)
// 主函數,計算片段的最終顏色
{vec3 N = normalize(fragNormal);vec3 V = normalize(-vertPos); // 視線方向vec3 R = -reflect(V, N); // 反射方向color = texture(tex0, R);// 采樣環境貼圖,獲取反射顏色//color=vec4(R,1.0); // 僅用于調試,顯示反射方向
}
參考
- 學習筆記完整代碼下載
- OpenGL shader開發實戰學習筆記:第十一章 立方體貼圖和天空盒_opengl 天空盒-CSDN博客