Estoy tratando de hacer que un skybox funcione con OpenGL 3.3 y GLSL versión 330.
No pude encontrar un tutorial de OGL skybox completamente moderno en ninguna parte de la web, así que modernicé uno más antiguo (usando en glVertexAttribPointer()
lugar de gl_Vertex
vértices, etc.). Funciona principalmente, pero para 2 detalles principales:
Los skyboxes son más como triángulos de cielo, y las texturas están muy deformadas y estiradas (se supone que son campos de estrellas, obtengo líneas sobre un fondo negro). Estoy 99% seguro de que esto se debe a que no porté los tutoriales antiguos completamente correctamente.
Aquí está mi clase de skybox:
static ShaderProgram* cubeMapShader = nullptr;
static const GLfloat vertices[] =
{
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
};
Skybox::Skybox(const char* xp, const char* xn, const char* yp, const char* yn, const char* zp, const char* zn)
{
if (cubeMapShader == nullptr)
cubeMapShader = new ShaderProgram("cubemap.vert", "cubemap.frag");
texture = SOIL_load_OGL_cubemap(xp, xn, yp, yn, zp, zn, SOIL_LOAD_AUTO, SOIL_CREATE_NEW_ID, SOIL_FLAG_MIPMAPS);
glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_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);
glGenVertexArrays(1, &vaoID);
glBindVertexArray(vaoID);
glGenBuffers(1, &vboID);
glBindBuffer(GL_ARRAY_BUFFER, vboID);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
glBindVertexArray(0);
scale = 1.0f;
}
Skybox::~Skybox()
{
}
void Skybox::Render()
{
ShaderProgram::SetActive(cubeMapShader);
glDisable(GL_DEPTH_TEST);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_CUBE_MAP, texture);
cubeMapShader->Uniform1i("SkyTexture", 0);
cubeMapShader->UniformVec3("CameraPosition", Camera::ActiveCameraPosition());
cubeMapShader->UniformMat4("MVP", 1, GL_FALSE, Camera::GetActiveCamera()->GetProjectionMatrix() * Camera::GetActiveCamera()->GetViewMatrix() * glm::mat4(1.0));
glBindVertexArray(vaoID);
glDrawArrays(GL_QUADS, 0, 24);
glBindVertexArray(0);
glBindTexture(GL_TEXTURE_CUBE_MAP, 0);
}
Sombreador de vértices:
#version 330
layout(location = 0) in vec3 Vertex;
uniform vec3 CameraPosition;
uniform mat4 MVP;
out vec3 Position;
void main()
{
Position = Vertex.xyz;
gl_Position = MVP * vec4(Vertex.xyz + CameraPosition, 1.0);
}
Fragmento Shader:
#version 330 compatibility
uniform samplerCube SkyTexture;
in vec3 Position;
void main()
{
gl_FragColor = textureCube(SkyTexture, Position);
}
Aquí hay un ejemplo de los problemas técnicos. Si alguien pudiera ver quién conoce bien GLSL (todavía lo estoy aprendiendo), o skyboxes, agradecería cualquier ayuda que pueda brindar. Además, felicitaciones si me pueden enseñar cómo usar funciones no obsoletas en el fragment shader para que no tenga que usar el perfil de compatibilidad de glsl 330.
EDITAR: Inmediatamente encontré el problema con las texturas de estiramiento: estaba usando en Position = Vertex.xy
x
lugar de Position = Vertex.xy
z
en el sombreador de vértices. Ups Pero el error del triángulo todavía existe.
Respuestas:
Si bien esta respuesta no dice qué hay de malo en su enfoque, presenta una forma más sencilla de renderizar skyboxes.
Forma tradicional (cubo texturizado)
Una forma sencilla de crear skyboxes es renderizar un cubo texturizado centrado en la posición de la cámara. Cada cara del cubo consta de dos triángulos y una textura 2D (o parte de un atlas). Debido a las coordenadas de textura, cada cara requiere vértices propios. Este enfoque tiene problemas en las costuras de las caras adyacentes, donde los valores de textura no se interpolan correctamente.
Cubo con textura cubemap
Como en la forma tradicional, se representa un cubo texturizado alrededor de la cámara. En lugar de usar seis texturas 2D, se usa una sola textura de mapa de cubos. Debido a que la cámara está centrada dentro del cubo, las coordenadas del vértice se asignan una a una con los vectores de muestreo del mapa de cubos. Por lo tanto, las coordenadas de textura no son necesarias para los datos de malla y los vértices se pueden compartir entre caras mediante el uso de un búfer de índice.
Este enfoque también corrige el problema de las costuras cuando GL_TEXTURE_CUBE_MAP_SEAMLESS está habilitado.
Forma más simple (mejor)
Al renderizar un cubo y la cámara se encuentra dentro de él, se llena toda la ventana gráfica. Hasta cinco caras del skybox pueden ser parcialmente visibles en cualquier momento. Los triángulos de las caras de los cubos se proyectan y recortan en la ventana gráfica y los vectores de muestreo del mapa de cubos se interpolan entre los vértices. Este trabajo es innecesario.
Es posible llenar un solo quad llenando toda la ventana gráfica y calcular los vectores de muestreo del mapa de cubos en las esquinas. Dado que los vectores de muestreo del mapa de cubos coinciden con las coordenadas del vértice, se pueden calcular desproyectando las coordenadas de la ventana gráfica al espacio mundial. Esto es lo contrario de proyectar coordenadas mundiales a la ventana gráfica y puede lograrse invirtiendo las matrices. También asegúrese de deshabilitar la escritura del búfer z o escribir un valor lo suficientemente lejos.
A continuación se muestra el sombreador de vértices que logra esto:
aPosition
son las coordenadas del vértice{-1,-1; 1,-1; 1,1; -1,1}
. El sombreador calculaeyeDirection
con el inverso de la matriz de modelo-vista-proyección. Sin embargo, la inversión se divide para la proyección y las matrices de mundo a cámara. Esto se debe a que solo se debe usar la parte 3x3 de la matriz de la cámara para eliminar la posición de la cámara. Esto alinea la cámara al centro del skybox. Además, como mi cámara no tiene escala ni cizallamiento, la inversión se puede simplificar a la transposición. La inversión de la matriz de proyección es una operación costosa y podría calcularse previamente, pero como este código es ejecutado por el sombreador de vértices, generalmente solo cuatro veces por cuadro, generalmente no es un problema.El sombreador de fragmentos simplemente realiza una búsqueda de textura usando el
eyeDirection
vector:Tenga en cuenta que para deshacerse del modo de compatibilidad, debe reemplazar
textureCube
con justtexture
y especificar la variable de salida usted mismo.fuente
inverse()