NDK OpenGL ES 3.0 開發(十五):立方體貼圖(天空盒)

該原創文章首發於微信公衆號:字節流動

OpenGL 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。

紋理目標(Texture target) 方位
GL_TEXTURE_CUBE_MAP_POSITIVE_X
GL_TEXTURE_CUBE_MAP_NEGATIVE_X
GL_TEXTURE_CUBE_MAP_POSITIVE_Y
GL_TEXTURE_CUBE_MAP_NEGATIVE_Y
GL_TEXTURE_CUBE_MAP_POSITIVE_Z
GL_TEXTURE_CUBE_MAP_NEGATIVE_Z

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

實現代碼路徑:
NDK_OpenGLES_3_0

參考

https://learnopengl.com/Advanced-OpenGL/Cubemaps

聯繫與交流

我的公衆號
在這裏插入圖片描述
我的微信
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章