Ich versuche, eine Skybox mit OpenGL 3.3 und GLSL Version 330 zum Laufen zu bringen.
Ich konnte nirgendwo im Web ein vollständig modernes OGL-Skybox-Tutorial finden, also habe ich ein älteres modernisiert ( glVertexAttribPointer()
statt gl_Vertex
für Vertices usw.). Es funktioniert meistens, aber für 2 wichtige Details:
Die Skyboxes sind eher wie Himmelsdreiecke, und die Texturen sind stark verzogen und gedehnt (sie sollen Sternfelder sein, ich bekomme dabei Linien auf schwarzem Hintergrund). Ich bin mir zu 99% sicher, dass dies daran liegt, dass ich die alten Tutorials nicht richtig portiert habe.
Hier ist meine Skybox-Klasse:
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);
}
Vertex Shader:
#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);
}
Fragment Shader:
#version 330 compatibility
uniform samplerCube SkyTexture;
in vec3 Position;
void main()
{
gl_FragColor = textureCube(SkyTexture, Position);
}
Hier ist ein Beispiel für die Störungen. Wenn jemand einen Blick auf GLSL (ich lerne es noch) oder Skyboxes werfen könnte, wäre ich für jede Hilfe dankbar. Außerdem ein großes Lob, wenn Sie mir beibringen können, wie man nicht veraltete Funktionen im Fragment-Shader verwendet, damit ich nicht das Kompatibilitätsprofil von glsl 330 verwenden muss.
EDIT: gefunden sofort das Problem mit den Strecken Texturen: Ich wurde mit Position = Vertex.xy
x
statt Position = Vertex.xy
z
in den Vertex - Shader. Hoppla. Der Dreiecksfehler besteht jedoch weiterhin.
Antworten:
Diese Antwort sagt zwar nicht, was an Ihrem Ansatz falsch ist, bietet jedoch eine einfachere Möglichkeit, Skyboxes zu rendern.
Traditionelle Art (strukturierter Würfel)
Eine einfache Möglichkeit zum Erstellen von Skyboxes ist das Rendern eines strukturierten Würfels in der Mitte der Kameraposition. Jede Fläche des Würfels besteht aus zwei Dreiecken und einer 2D-Textur (oder einem Teil eines Atlas). Aufgrund der Texturkoordinaten benötigt jede Fläche eigene Eckpunkte. Dieser Ansatz weist Probleme in den Nähten benachbarter Flächen auf, in denen die Texturwerte nicht richtig interpoliert werden.
Würfel mit Cubemap Textur
Wie auf herkömmliche Weise wird ein strukturierter Würfel um die Kamera herum gerendert. Anstelle von sechs 2D-Texturen wird eine einzelne Cubemap-Textur verwendet. Da die Kamera im Würfel zentriert ist, werden die Scheitelpunktkoordinaten eins zu eins mit den Abtastvektoren der Cubemap zugeordnet. Daher werden für die Netzdaten keine Texturkoordinaten benötigt und die Eckpunkte können mithilfe des Indexpuffers zwischen Flächen geteilt werden.
Dieser Ansatz behebt auch das Problem von Nähten, wenn GL_TEXTURE_CUBE_MAP_SEAMLESS aktiviert ist.
Einfacher (besser) Weg
Wenn Sie einen Würfel rendern und die Kamera darin liegt, wird das gesamte Ansichtsfenster gefüllt. Bis zu fünf Gesichter der Skybox können jederzeit teilweise sichtbar sein. Die Dreiecke der Würfelflächen werden projiziert und im Ansichtsfenster abgeschnitten, und die Stichprobenvektoren der Würfelkarte werden zwischen den Eckpunkten interpoliert. Diese Arbeit ist unnötig.
Es ist möglich, ein einzelnes Quad zu füllen, das das gesamte Ansichtsfenster ausfüllt, und die Cubemap-Abtastvektoren in den Ecken zu berechnen. Da die Abtastvektoren der Cubemap mit den Scheitelpunktkoordinaten übereinstimmen, können sie berechnet werden, indem die Ansichtsfensterkoordinaten in den Weltraum projiziert werden. Dies ist das Gegenteil der Projektion von Weltkoordinaten auf das Ansichtsfenster und kann durch Invertieren der Matrizen erreicht werden. Stellen Sie außerdem sicher, dass Sie entweder den Z-Buffer-Schreibvorgang deaktivieren oder einen Wert schreiben, der weit genug ist.
Unten sehen Sie den Vertex-Shader, mit dem dies erreicht wird:
aPosition
ist die Scheitelpunktkoordinaten{-1,-1; 1,-1; 1,1; -1,1}
. Der Shader berechneteyeDirection
mit der Umkehrung der Modellansicht-Projektionsmatrix. Die Inversion ist jedoch für Projektions- und Welt-zu-Kamera-Matrizen aufgeteilt. Dies liegt daran, dass nur der 3x3-Teil der Kameramatrix verwendet werden sollte, um die Position der Kamera zu beseitigen. Dadurch wird die Kamera auf die Mitte der Skybox ausgerichtet. Da meine Kamera keine Skalierung oder Scherung aufweist, kann die Inversion zur Transposition vereinfacht werden. Die Inversion der Projektionsmatrix ist eine kostspielige Operation und könnte vorberechnet werden. Da dieser Code jedoch vom Vertex-Shader in der Regel nur viermal pro Frame ausgeführt wird, ist dies normalerweise kein Problem.Der Fragment-Shader führt einfach eine Textur-Suche mit
eyeDirection
vector durch:Beachten Sie , dass der Kompatibilitätsmodus , um loszuwerden , ersetzen müssen
textureCube
mit nurtexture
und geben Sie die Ausgangsgröße selbst.quelle
inverse()