第一部分 概念:
1) 引用
OpenGL ES 立方体贴图本质上还是纹理映射,是一种 3D 纹理映射。立方体贴图所使的纹理称为立方图纹理,它是由 6 个单独的 2D 纹理组成,每个 2D 纹理是立方图的一个面。
立方图纹理的采样通过一个 3D 向量(s, t, r)作为纹理坐标,这个 3D 向量只作为方向向量使用,OpenGL ES 获取方向向量触碰到立方图表面上的纹理像素作为采样结果。方向向量触碰到立方图表面对应的纹理位置作为采样点,要求立方图的中心必须位于原点。
立方图各个面的指定方法与 2D 纹理基本相同,且每个面必须为正方形(宽度和高度必须相同)。
2)应用
3D纹理跟2D纹理差不多,都是要先生成一个纹理,再绑定,只是2D的是绑定到GL_TEXTURE_2D,而3D的是绑定到**GL_TEXTURE_CUBE_MAP
**
GLuint textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_CUBE_MAP, textureID);
由于立方图包含 6 个纹理,每个面对应一个纹理,需要调用glTexImage2D
函数 6 次,OpenGL ES 为立方图提供了 6 个不同的纹理目标,对应立方图的 6 个面,且 6 个纹理目标按顺序依次增 1。
glGenTextures(1, &m_TextureId);
glBindTexture(GL_TEXTURE_CUBE_MAP, m_TextureId);
for (int i = 0; i < m_vcImage.size(); ++i)
{
//6个面的纹理是挨个+1
glTexImage2D(
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0,
GL_RGBA, m_vcImage[i].width, m_vcImage[i].height, 0, GL_RGBA, GL_UNSIGNED_BYTE,
m_vcImage[i].data);
LOGD("glTexImage2D %d",i);
}
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);
片段着色器也需要修改,采样器变成了 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);
}
第二部分 实践
天空盒内绘制一个立方体,并让立方体的表面反射它周围环境的属性
天空之盒的顶点着色器vSkyBox.vs
#version 300 es
precision mediump float;
layout(location = 0) in vec3 a_position;
uniform mat4 u_MVPMatrix;
out vec3 v_texCoord;
void main()
{
gl_Position = u_MVPMatrix * vec4(a_position, 1.0);
v_texCoord = a_position;
}
天空之盒的片段着色器fSkyBox.fs
#version 300 es
precision mediump float;
in vec3 v_texCoord;
layout(location = 0) out vec4 outColor;
uniform samplerCube s_SkyBox;//3D纹理为samplerCube类型
void main()
{
outColor = texture(s_SkyBox, v_texCoord);
}
立方体的顶点着色器vCube.vs
#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));
}
立方体的片段着色器fCube.fs
#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);
}
Skybox.h文件
//
// Created by CreatWall_zhouwen on 2023/5/23.
//
#ifndef ELEVENSKYBOX_SKYBOX_H
#define ELEVENSKYBOX_SKYBOX_H
#include <GLES3/gl3.h>
#include <detail/type_mat.hpp>
#include <detail/type_mat4x4.hpp>
#include <vector>
#include <map>
#include "Const.h"
#define MATH_PI 3.1415926535897932384626433832802
class Skybox {
public:
Skybox(){
m_SamplerLoc = GL_NONE;
m_MVPMatLoc = GL_NONE;
m_TextureId = GL_NONE;
m_SkyBoxVaoId = GL_NONE;
m_AngleX = 0;
m_AngleY = 0;
m_ScaleX = 1.0f;
m_ScaleY = 1.0f;
m_ModelMatrix = glm::mat4(0.0f);
};
~Skybox(){};
void CreateProgram(const char *ver, const char *frag,const char *curver, const char *curfrag);
void Draw();
static Skybox* GetInstance();
static void DestroyInstance();
void OnSurfaceChanged(int width, int height);
void UpdateTransformMatrix(float rotateX, float rotateY, float scaleX, float scaleY);
void UpdateMVPMatrix(glm::mat4 &mvpMatrix, int angleX, int angleY, float scale, float ratio);
void getTexturedata(std::vector<struct ImageTyep> vcImagetemp);
private:
GLuint m_TextureId;//纹理ID
GLuint m_CubeProgramObj;//立方体工程
GLuint m_ProgramObj;//天空盒工程
GLuint m_VertexShader;
GLuint m_FragmentShader;
GLint m_SamplerLoc;//天空盒着色器中uinform变量
GLint m_MVPMatLoc;
GLint m_CubeSamplerLoc;//立方体着色器中uinform变量
GLint m_CubeMVPMatLoc;
GLint m_CubeModelMatLoc;
GLint m_ViewPosLoc;
GLuint m_CubeVaoId;
GLuint m_CubeVboId;
GLuint m_SkyBoxVaoId;
GLuint m_SkyBoxVboId;
std::vector<struct ImageTyep> m_vcImage;//存放纹理数据
int srceenWidth, srceenHeight;//屏幕宽高
glm::mat4 m_MVPMatrix;
glm::mat4 m_ModelMatrix;
int m_AngleX;
int m_AngleY;
float m_ScaleX;
float m_ScaleY;
};
#endif //ELEVENSKYBOX_SKYBOX_H
Skybox.cpp文件
//
// Created by CreatWall_zhouwen on 2023/5/23.
//
#include "Skybox.h"
#include "Util.h"
#include "GLUtil.h"
#include <gtc/matrix_transform.hpp>
Skybox* m_pContext = nullptr;
#define TAG "DRAWTEXTURE"
GLfloat cubeVertices[] = {
//position //normal
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
-0.5f, -0.5f, -0.5f, 0.0f, 0.0f, -1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, -0.5f, 0.5f, 0.0f, 0.0f, 1.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, -0.5f, 1.0f, 0.0f, 0.0f,
0.5f, -0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
0.5f, 0.5f, 0.5f, 1.0f, 0.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,
0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,
-0.5f, -0.5f, 0.5f, 0.0f, -1.0f, 0.0f,
-0.5f, -0.5f, -0.5f, 0.0f, -1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, 0.5f, 0.0f, 1.0f, 0.0f,
-0.5f, 0.5f, -0.5f, 0.0f, 1.0f, 0.0f,
};
GLfloat skyboxVertices[] = {
// Positions
-2.0f, 2.0f, -2.0f,
-2.0f, -2.0f, -2.0f,
2.0f, -2.0f, -2.0f,
2.0f, -2.0f, -2.0f,
2.0f, 2.0f, -2.0f,
-2.0f, 2.0f, -2.0f,
-2.0f, -2.0f, 2.0f,
-2.0f, -2.0f, -2.0f,
-2.0f, 2.0f, -2.0f,
-2.0f, 2.0f, -2.0f,
-2.0f, 2.0f, 2.0f,
-2.0f, -2.0f, 2.0f,
2.0f, -2.0f, -2.0f,
2.0f, -2.0f, 2.0f,
2.0f, 2.0f, 2.0f,
2.0f, 2.0f, 2.0f,
2.0f, 2.0f, -2.0f,
2.0f, -2.0f, -2.0f,
-2.0f, -2.0f, 2.0f,
-2.0f, 2.0f, 2.0f,
2.0f, 2.0f, 2.0f,
2.0f, 2.0f, 2.0f,
2.0f, -2.0f, 2.0f,
-2.0f, -2.0f, 2.0f,
-2.0f, 2.0f, -2.0f,
2.0f, 2.0f, -2.0f,
2.0f, 2.0f, 2.0f,
2.0f, 2.0f, 2.0f,
-2.0f, 2.0f, 2.0f,
-2.0f, 2.0f, -2.0f,
-2.0f, -2.0f, -2.0f,
-2.0f, -2.0f, 2.0f,
2.0f, -2.0f, -2.0f,
2.0f, -2.0f, -2.0f,
-2.0f, -2.0f, 2.0f,
2.0f, -2.0f, 2.0f
};
void Skybox::CreateProgram(const char *ver, const char *frag,const char *curver, const char *curfrag) {
LOGD("CreateProgram Enter");
m_ProgramObj = CreateGLProgram(ver, frag, m_VertexShader, m_FragmentShader);
if (m_ProgramObj)
{
m_SamplerLoc = glGetUniformLocation(m_ProgramObj, "s_SkyBox");
m_MVPMatLoc = glGetUniformLocation(m_ProgramObj, "u_MVPMatrix");
}
else
{
LOGD("SkyBoxSample::Init create m_ProgramObj fail");
return;
}
m_CubeProgramObj = CreateGLProgram(curver, curfrag, m_VertexShader, m_FragmentShader);
if (m_CubeProgramObj)
{
m_CubeSamplerLoc = glGetUniformLocation(m_CubeProgramObj, "s_SkyBox");
m_CubeMVPMatLoc = glGetUniformLocation(m_CubeProgramObj, "u_MVPMatrix");
m_CubeModelMatLoc = glGetUniformLocation(m_CubeProgramObj, "u_ModelMatrix");
m_ViewPosLoc = glGetUniformLocation(m_CubeProgramObj, "u_cameraPos");
}
else
{
LOGD("SkyBoxSample::Init create m_CubeProgramObj fail");
return;
}
LOGD("CreateProgram Leave");
//创建天空盒的VBO 以及绑定VAO
glGenBuffers(1, &m_SkyBoxVboId);
glBindBuffer(GL_ARRAY_BUFFER, m_SkyBoxVboId);
glBufferData(GL_ARRAY_BUFFER, sizeof(skyboxVertices), skyboxVertices, GL_STATIC_DRAW);
glGenVertexArrays(1, &m_SkyBoxVaoId);//创建VAO
glBindVertexArray(m_SkyBoxVaoId);
glBindBuffer(GL_ARRAY_BUFFER, m_SkyBoxVboId);//绑定VBO
glEnableVertexAttribArray(0);//绑定VBO数据
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (const void *) 0);
glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
glBindVertexArray(GL_NONE);
//创建立方体的VBO 以及绑定VAO
glGenBuffers(1, &m_CubeVboId);
glBindBuffer(GL_ARRAY_BUFFER, m_CubeVboId);
glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), cubeVertices, GL_STATIC_DRAW);
glGenVertexArrays(1, &m_CubeVaoId);//创建VAO
glBindVertexArray(m_CubeVaoId);
glBindBuffer(GL_ARRAY_BUFFER, m_CubeVboId);//绑定VBO
glEnableVertexAttribArray(0);//属性O的数据
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (const void *) 0);
glEnableVertexAttribArray(1);//属性1的数据 对应顶点着色器的location 1
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat)));
glBindBuffer(GL_ARRAY_BUFFER, GL_NONE);
glBindVertexArray(GL_NONE);
}
void Skybox::Draw() {
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
glClearColor(0.2f, 0.9f, 0.3f, 1.0f);
glEnable(GL_DEPTH_TEST);
UpdateMVPMatrix(m_MVPMatrix, m_AngleX, m_AngleY, 1.0, (float) srceenWidth / srceenHeight);
if (!m_TextureId)
{
//create RGBA texture
glGenTextures(1, &m_TextureId);
glBindTexture(GL_TEXTURE_CUBE_MAP, m_TextureId);//绑定立体纹理
for (int i = 0; i < m_vcImage.size(); ++i)
{
//6个面的纹理是挨个+1
glTexImage2D(
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0,
GL_RGBA, m_vcImage[i].width, m_vcImage[i].height, 0, GL_RGBA, GL_UNSIGNED_BYTE,
m_vcImage[i].data);
LOGD("glTexImage2D %d",i);
}
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);
LOGD("sizeof(m_vcImage) / sizeof(ImageTyep) = %d ", sizeof(m_vcImage) / sizeof(ImageTyep));
}
//画天空盒
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) srceenWidth / srceenHeight);
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);
}
Skybox *Skybox::GetInstance() {
if (m_pContext == nullptr)
{
m_pContext = new Skybox();
}
return m_pContext;
}
void Skybox::DestroyInstance() {
if (m_pContext)
{
delete m_pContext;
m_pContext = nullptr;
}
}
void Skybox::OnSurfaceChanged(int width, int height) {
glViewport(0, 0, width, height);
srceenWidth = width;
srceenHeight = height;
LOGD("OnSurfaceChanged Srceenwidth = %d, Srceenheight = %d, ratio = %f", width,height);
}
void Skybox::UpdateTransformMatrix(float rotateX, float rotateY, float scaleX, float scaleY) {
m_AngleX = static_cast<int>(rotateX);
m_AngleY = static_cast<int>(rotateY);
m_ScaleX = scaleX;
m_ScaleY = scaleY;
}
void Skybox::UpdateMVPMatrix(glm::mat4 &mvpMatrix, int angleX, int angleY, float scale, float ratio) {
LOGD("SkyBoxSample::UpdateMVPMatrix angleX = %d, angleY = %d, ratio = %f", angleX,
angleY, ratio);
angleX = angleX % 360;
angleY = angleY % 360;
//转化为弧度角
float radiansX = static_cast<float>(MATH_PI / 180.0f * angleX);
float radiansY = static_cast<float>(MATH_PI / 180.0f * angleY);
// Projection matrix
//glm::mat4 Projection = glm::ortho(-ratio, ratio, -1.0f, 1.0f, 0.0f, 100.0f);
//glm::mat4 Projection = glm::frustum(-ratio, ratio, -1.0f, 1.0f, 4.0f, 100.0f);
glm::mat4 Projection = glm::perspective(45.0f, ratio, 0.1f, 100.f);
// View matrix
glm::mat4 View = glm::lookAt(
glm::vec3(0, 0, 1.8), // Camera is at (0,0,1), in World Space
glm::vec3(0, 0, -1), // and looks at the origin
glm::vec3(0, 1, 0) // Head is up (set to 0,-1,0 to look upside-down)
);
// Model matrix
glm::mat4 Model = glm::mat4(1.0f);
Model = glm::scale(Model, glm::vec3(scale, scale, scale));
Model = glm::rotate(Model, radiansX, glm::vec3(1.0f, 0.0f, 0.0f));
Model = glm::rotate(Model, radiansY, glm::vec3(0.0f, 1.0f, 0.0f));
Model = glm::translate(Model, glm::vec3(0.0f, 0.0f, 0.0f));
m_ModelMatrix = Model;
mvpMatrix = Projection * View * Model;
}
void Skybox::getTexturedata(std::vector<struct ImageTyep> vcImagetemp) {
m_vcImage = vcImagetemp;
}