作者:位元組流動
來源:
https://blog.csdn.net/Kennethdroid/article/details/102991524OpenGL ES 立方體貼圖

OpenGL ES 立方體貼圖本質上還是紋理映射,是一種 3D 紋理映射。立方體貼圖所使的紋理稱為立方圖紋理,它是由 6 個單獨的 2D 紋理組成,每個 2D 紋理是立方圖的一個面。
立方圖紋理的采樣通過一個 3D 向量(s, t, r)作為紋理坐标,這個 3D 向量隻作為方向向量使用,OpenGL ES 擷取方向向量觸碰到立方圖表面上的紋理像素作為采樣結果。方向向量觸碰到立方圖表面對應的紋理位置作為采樣點,要求立方圖的中心必須位于原點。
立方圖各個面的指定方法與 2D 紋理基本相同,且每個面必須為正方形(寬度和高度必須相同)。
立方圖紋理的使用與 2D 紋理基本一緻,首先生成一個紋理,激活相應紋理單元,然後綁定到
GL_TEXTURE_CUBE_MAP
類型紋理。
GLuint textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
由于立方圖包含 6 個紋理,每個面對應一個紋理,需要調用
glTexImage2D
函數 6 次,OpenGL ES 為立方圖提供了 6 個不同的紋理目标,對應立方圖的 6 個面,且 6 個紋理目标按順序依次增 1。
将立方圖的 6 個面對應的圖像資料加載到紋理,其中
m_pSkyBoxRenderImg
為圖像資料的數組:
glGenTextures(1, &m_TextureId);
glBindTexture(GL_TEXTURE_CUBE_MAP, m_TextureId);
for (int i = 0; i < sizeof(m_pSkyBoxRenderImg) / sizeof(NativeImage); ++i)
{
glTexImage2D(
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0,
GL_RGBA, m_pSkyBoxRenderImg[i].width, m_pSkyBoxRenderImg[i].height, 0, GL_RGBA, GL_UNSIGNED_BYTE, m_pSkyBoxRenderImg[i].ppPlane[0]
);
}
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
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);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
類似于普通的 2D 紋理,在使用立方圖紋理繪制物體之前,需要激活相應的紋理單元并綁定到立方圖上。不同的是,對應的片段着色器中,采樣器變成了
samplerCube
,并且紋理坐标變成了三維方向向量。
#version 300 es
precision mediump float;
in vec3 v_texCoord;
layout(location = 0) out vec4 outColor;
uniform samplerCube s_SkyBox;
void main()
{
outColor = texture(s_SkyBox, v_texCoord);
}
空盒的繪制:
// draw SkyBox
glUseProgram(m_ProgramObj);
glBindVertexArray(m_SkyBoxVaoId);
glUniformMatrix4fv(m_MVPMatLoc, 1, GL_FALSE, &m_MVPMatrix[0][0]);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, m_TextureId);
glUniform1i(m_SamplerLoc, 0);
glDrawArrays(GL_TRIANGLES, 0, 36);
接下來我們想在天空盒内繪制一個立方體,并讓立方體的表面反射它周圍環境的屬性。
天空盒内物體反射的原理圖:
其中 I 表示觀察方向向量,通過目前頂點坐标減去相機位置(觀察者)坐标計算得出;N 表示物體的法線向量,R 為反射向量,通過使用 GLSL 的内建函數 reflect 計算得出反射向量 R。最後,以反射向量 R 作為方向向量對立方圖進行索采樣,傳回采樣結果(一個對應反射環境的顔色值)。最後的效果看起來就像物體反射了天空盒。
天空盒内繪制物體(反射周圍環境顔色)使用的頂點着色器:
#version 300 es
precision mediump float;
layout(location = 0) in vec3 a_position;
layout(location = 1) in vec3 a_normal;
uniform mat4 u_MVPMatrix;
uniform mat4 u_ModelMatrix;
out vec3 v_texCoord;
out vec3 v_normal;
void main()
{
gl_Position = u_MVPMatrix * vec4(a_position, 1.0);
v_normal = mat3(transpose(inverse(u_ModelMatrix))) * a_normal;
v_texCoord = vec3(u_ModelMatrix * vec4(a_position, 1.0));
}
天空盒内繪制物體(反射周圍環境顔色)使用的片段着色器:
#version 300 es
precision mediump float;
in vec3 v_texCoord;
in vec3 v_normal;
layout(location = 0) out vec4 outColor;
uniform samplerCube s_SkyBox;
uniform vec3 u_cameraPos;
void main()
{
float ratio = 1.00 / 1.52;
vec3 I = normalize(v_texCoord - u_cameraPos);
//反射
vec3 R = reflect(I, normalize(v_normal));
//折射
//vec3 R = refract(I, normalize(v_normal), ratio);
outColor = texture(s_SkyBox, R);
}
繪制天空盒和盒内立方體:
UpdateMVPMatrix(m_MVPMatrix, m_AngleX, m_AngleY, 1.0, (float) screenW / screenH);
// draw SkyBox
glUseProgram(m_ProgramObj);
glBindVertexArray(m_SkyBoxVaoId);
glUniformMatrix4fv(m_MVPMatLoc, 1, GL_FALSE, &m_MVPMatrix[0][0]);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, m_TextureId);
glUniform1i(m_SamplerLoc, 0);
glDrawArrays(GL_TRIANGLES, 0, 36);
UpdateMVPMatrix(m_MVPMatrix, m_AngleX, m_AngleY, 0.4f, (float) screenW / screenH);
// draw Cube
glUseProgram(m_CubeProgramObj);
glBindVertexArray(m_CubeVaoId);
glUniformMatrix4fv(m_CubeMVPMatLoc, 1, GL_FALSE, &m_MVPMatrix[0][0]);
glUniformMatrix4fv(m_CubeModelMatLoc, 1, GL_FALSE, &m_ModelMatrix[0][0]);
glUniform3f(m_ViewPosLoc, 0.0f, 0.0f, 1.8f);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, m_TextureId);
glUniform1i(m_CubeSamplerLoc, 0);
glDrawArrays(GL_TRIANGLES, 0, 36);
參考
https://learnopengl.com/Advanced-OpenGL/Cubemaps聯系與交流
技術交流/擷取源碼可以添加我的微信:Byte-Flow
「視訊雲技術」你最值得關注的音視訊技術公衆号,每周推送來自阿裡雲一線的實踐技術文章,在這裡與音視訊領域一流工程師交流切磋。