Usiłuję uzyskać skybox współpracujący z OpenGL 3.3 i GLSL w wersji 330.
Nie mogłem znaleźć całkowicie nowoczesnego samouczka OGL skybox w dowolnym miejscu w sieci, więc zmodernizowałem starszy (używając glVertexAttribPointer()
zamiast gl_Vertex
wierzchołków itp.). Działa głównie, ale dla dwóch głównych szczegółów:
Skyboksy bardziej przypominają trójkąty nieba, a tekstury są mocno wypaczone i rozciągnięte (powinny to być pola gwiezdne, dostaję linie na czarnym tle). Jestem w 99% pewien, że dzieje się tak, ponieważ stare portale nie zostały całkowicie poprawnie przeniesione.
Oto moja klasa 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);
}
Shader Vertex:
#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);
}
Shader fragmentów:
#version 330 compatibility
uniform samplerCube SkyTexture;
in vec3 Position;
void main()
{
gl_FragColor = textureCube(SkyTexture, Position);
}
Oto przykład usterki. Jeśli ktoś mógłby rzucić okiem na kogoś, kto dobrze zna GLSL (wciąż się go uczę) lub skyboksów, byłbym wdzięczny za wszelką pomoc, którą możesz udzielić. Ponadto, jeśli możesz nauczyć mnie, jak używać nieaktualnych funkcji w module cieniującym fragmenty, więc nie muszę używać profilu zgodności glsl 330.
EDYCJA: Natychmiast znalazłem problem z rozciągającymi się teksturami: używałem Position = Vertex.xy
x
zamiast Position = Vertex.xy
z
w module cieniującym wierzchołki. Ups Ale błąd trójkąta nadal istnieje.
Odpowiedzi:
Chociaż ta odpowiedź nie mówi, co jest złego w twoim podejściu, przedstawia prostszy sposób renderowania skyboxów.
Tradycyjny sposób (sześcian teksturowany)
Prostym sposobem tworzenia skyboksów jest renderowanie teksturowanego sześcianu na środku kamery. Każda powierzchnia sześcianu składa się z dwóch trójkątów i tekstury 2D (lub części atlasu). Ze względu na współrzędne tekstury każda twarz wymaga własnych wierzchołków. Takie podejście ma problemy w szwach sąsiadujących ścian, gdzie wartości tekstury nie są poprawnie interpolowane.
Kostka o fakturze cubemap
Podobnie jak w tradycyjny sposób, wokół kamery renderowany jest teksturowany sześcian. Zamiast korzystać z sześciu tekstur 2D, używana jest pojedyncza tekstura mapy sześciennej. Ponieważ kamera jest wyśrodkowana wewnątrz sześcianu, współrzędne wierzchołków są odwzorowywane jeden na jeden za pomocą wektorów próbkowania mapy kostek. Dlatego współrzędne tekstury nie są potrzebne dla danych siatki, a wierzchołki mogą być dzielone między ścianami za pomocą bufora indeksu.
To podejście rozwiązuje również problem szwów, gdy GL_TEXTURE_CUBE_MAP_SEAMLESS jest włączony.
Prostszy (lepszy) sposób
Podczas renderowania sześcianu, w którym znajduje się kamera, cała rzutnia zostaje wypełniona. W dowolnym momencie można częściowo wyświetlić do pięciu twarzy skyboksa. Trójkąty ścian sześcianu są rzutowane i przycinane do rzutni, a wektory próbkowania mapy kubaturowej są interpolowane między wierzchołkami. Ta praca jest niepotrzebna.
Możliwe jest wypełnienie pojedynczego kwadratu wypełniającego całą rzutnię i obliczenie wektorów próbkowania mapy kostki w rogach. Ponieważ wektory próbkowania mapy sześciennej odpowiadają współrzędnym wierzchołka, można je obliczyć, odsuwając współrzędne rzutni do przestrzeni świata. Jest to przeciwieństwo rzutowania współrzędnych świata na rzutnię i można to osiągnąć poprzez odwrócenie matryc. Upewnij się także, że albo wyłączasz zapisywanie w buforze Z, albo zapisujesz wystarczająco dużą wartość.
Poniżej znajduje się moduł cieniujący wierzchołek, który to osiąga:
aPosition
to współrzędne wierzchołka{-1,-1; 1,-1; 1,1; -1,1}
. Moduł cieniujący obliczaeyeDirection
z odwrotnością macierzy widok-model-rzut. Inwersja jest jednak podzielona dla matryc projekcyjnych i świat-kamera. Wynika to z faktu, że do wyeliminowania pozycji kamery należy użyć tylko części matrycy kamery 3x3. Spowoduje to wyrównanie aparatu do środka skyboksa. Ponadto, ponieważ mój aparat nie ma żadnego skalowania ani ścinania, inwersję można uprościć do transpozycji. Odwrócenie macierzy projekcji jest kosztowną operacją i może być wstępnie obliczone, ale ponieważ ten kod jest wykonywany przez moduł cieniujący wierzchołki zwykle tylko cztery razy na ramkę, zwykle nie stanowi to problemu.Moduł cieniujący fragment wykonuje po prostu wyszukiwanie tekstury za pomocą
eyeDirection
wektora:Pamiętaj, że aby pozbyć się trybu zgodności, musisz zastąpić
textureCube
go parametrem justtexture
i samodzielnie określić zmienną wyjściową.źródło
inverse()