天天看點

NDK OpenGL ES 3.0 開發(十五):立方體貼圖(天空盒)OpenGL ES 立方體貼圖參考聯系與交流

作者:位元組流動

來源:

https://blog.csdn.net/Kennethdroid/article/details/102991524

OpenGL ES 立方體貼圖

NDK OpenGL ES 3.0 開發(十五):立方體貼圖(天空盒)OpenGL ES 立方體貼圖參考聯系與交流

OpenGL ES 立方體貼圖本質上還是紋理映射,是一種 3D 紋理映射。立方體貼圖所使的紋理稱為立方圖紋理,它是由 6 個單獨的 2D 紋理組成,每個 2D 紋理是立方圖的一個面。

NDK OpenGL ES 3.0 開發(十五):立方體貼圖(天空盒)OpenGL ES 立方體貼圖參考聯系與交流

立方圖紋理的采樣通過一個 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。

NDK OpenGL ES 3.0 開發(十五):立方體貼圖(天空盒)OpenGL ES 立方體貼圖參考聯系與交流

将立方圖的 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);           

接下來我們想在天空盒内繪制一個立方體,并讓立方體的表面反射它周圍環境的屬性。

天空盒内物體反射的原理圖:

NDK OpenGL ES 3.0 開發(十五):立方體貼圖(天空盒)OpenGL ES 立方體貼圖參考聯系與交流

其中 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

「視訊雲技術」你最值得關注的音視訊技術公衆号,每周推送來自阿裡雲一線的實踐技術文章,在這裡與音視訊領域一流工程師交流切磋。
NDK OpenGL ES 3.0 開發(十五):立方體貼圖(天空盒)OpenGL ES 立方體貼圖參考聯系與交流

繼續閱讀