Saya mencoba membuat skybox bekerja dengan OpenGL 3.3 dan GLSL versi 330.
Saya tidak dapat menemukan tutorial OGL skybox yang sepenuhnya modern di mana pun di web, jadi saya memodernisasi tutorial yang lebih lama (menggunakan glVertexAttribPointer()
alih-alih gl_Vertex
untuk simpul, dll.). Sebagian besar berfungsi, tetapi untuk 2 detail utama:
Skybox lebih seperti segitiga langit, dan teksturnya sangat melengkung dan membentang (mereka seharusnya menjadi bidang bintang, saya dapatkan sementara garis-garis pada latar belakang hitam). Saya 99% yakin bahwa ini karena saya tidak mem-port-kan tutorial lama dengan benar.
Ini kelas skybox saya:
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);
}
Shader fragmen:
#version 330 compatibility
uniform samplerCube SkyTexture;
in vec3 Position;
void main()
{
gl_FragColor = textureCube(SkyTexture, Position);
}
Ini adalah contoh dari gangguan. Jika ada yang bisa melihat siapa yang mengenal GLSL dengan baik (saya masih mempelajarinya), atau skybox, saya sangat menghargai bantuan yang bisa Anda berikan. Juga, pujian jika Anda bisa mengajari saya cara menggunakan fungsi yang tidak usang dalam shader fragmen jadi saya tidak harus menggunakan profil kompatibilitas glsl 330.
EDIT: Segera menemukan masalah dengan tekstur peregangan: Saya menggunakan Position = Vertex.xy
x
bukan Position = Vertex.xy
z
di vertex shader. Ups. Namun kesalahan segitiga masih ada.
Jawaban:
Meskipun jawaban ini tidak memberi tahu apa yang salah dengan pendekatan Anda, jawaban ini menyajikan cara yang lebih sederhana untuk membuat skybox.
Cara tradisional (kubus bertekstur)
Cara mudah untuk membuat skybox adalah dengan membuat kubus bertekstur yang berpusat ke posisi kamera. Setiap permukaan kubus terdiri dari dua segitiga dan tekstur 2D (atau bagian dari atlas). Karena koordinat tekstur setiap wajah memerlukan simpul sendiri. Pendekatan ini memiliki masalah pada lapisan wajah yang berdekatan, di mana nilai tekstur tidak diinterpolasi dengan benar.
Kubus dengan tekstur cubemap
Seperti cara tradisional, kubus bertekstur ditampilkan di sekitar kamera. Alih-alih menggunakan enam tekstur 2D, tekstur cubemap tunggal digunakan. Karena kamera dipusatkan di dalam kubus, titik koordinat koordinat satu ke satu dengan vektor pengambilan sampel cubemap. Jadi koordinat tekstur tidak diperlukan untuk data mesh dan simpul dapat dibagi antara wajah dengan menggunakan buffer indeks.
Pendekatan ini juga memperbaiki masalah jahitan ketika GL_TEXTURE_CUBE_MAP_SEAMLESS diaktifkan.
Cara yang lebih sederhana (lebih baik)
Saat merender kubus dan kamera ada di dalamnya, seluruh viewport terisi. Hingga lima wajah skybox dapat terlihat sebagian kapan saja. Segitiga dari permukaan kubus diproyeksikan dan dipotong ke viewport dan vektor sampel kubemap diinterpolasi di antara simpul. Pekerjaan ini tidak perlu.
Dimungkinkan untuk mengisi satu quad yang mengisi seluruh viewport dan menghitung vektor pengambilan sampel cubemap di sudut-sudut. Karena vektor pengambilan sampel cubemap cocok dengan koordinat titik, mereka dapat dihitung dengan tidak memproyeksikan koordinat viewport ke ruang dunia. Ini adalah kebalikan dari memproyeksikan koordinat dunia ke viewport dan dapat dicapai dengan membalikkan matriks. Pastikan juga Anda menonaktifkan z-buffer write atau menulis nilai yang cukup jauh.
Di bawah ini adalah vertex shader yang melakukan ini:
aPosition
adalah koordinat titik{-1,-1; 1,-1; 1,1; -1,1}
. Shader menghitungeyeDirection
dengan kebalikan dari model-view-projection matrix. Namun inversi dibagi untuk proyeksi dan matriks dunia-ke-kamera. Ini karena hanya bagian 3x3 dari matriks kamera yang harus digunakan untuk menghilangkan posisi kamera. Ini menyelaraskan kamera ke pusat skybox. Selain karena kamera saya tidak memiliki penskalaan atau geser, inversinya dapat disederhanakan menjadi transposisi. Inversi dari matriks proyeksi adalah operasi yang mahal dan dapat dihitung ulang, tetapi karena kode ini dieksekusi oleh vertex shader biasanya hanya empat kali per frame, biasanya bukan masalah.Shader fragmen hanya melakukan pencarian tekstur menggunakan
eyeDirection
vektor:Perhatikan bahwa untuk menyingkirkan mode kompatibilitas, Anda perlu mengganti
textureCube
dengan adiltexture
dan tentukan sendiri variabel keluarannya.sumber
inverse()