GLSL sürüm 330 ile bir skybox uygulama


14

OpenGL 3.3 ve GLSL sürüm 330 ile çalışan bir skybox almaya çalışıyorum.

Web'in herhangi bir yerinde tamamen modern bir OGL skybox öğretici bulamadım, bu yüzden daha eski bir tane modernleştirdim ( köşe noktaları glVertexAttribPointer()yerine kullanarak gl_Vertex). Çoğunlukla işe yarıyor, ancak 2 ana ayrıntı için:

Gökyüzü kutuları daha çok gökyüzü üçgenlerine benzer ve dokular kötü bir şekilde bükülmüş ve gerilmiş (yıldız alanları olması gerekiyordu, siyah bir arka plan üzerinde çizgiler elde ediyorum). Ben% 99 eminim çünkü ben eski öğreticiler tamamen doğru port değildi.

İşte benim skybox sınıfım:

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);
}

Köşe Gölgelendirici:

#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);
}

Fragman Gölgelendiricisi:

#version 330 compatibility

uniform samplerCube SkyTexture;

in vec3 Position;

void main()
{
    gl_FragColor = textureCube(SkyTexture, Position);
}

Buraya aksaklıklara bir örnek. Herkes GLSL'yi (hala öğreniyorum) veya skybox'ları bilen bir göz atabilirse, verebileceğiniz herhangi bir yardım için minnettar olurum. Ayrıca, gudl 330'un uyumluluk profilini kullanmak zorunda kalmam için parça gölgelendiricide kullanım dışı işlevlerin nasıl kullanılacağını bana öğretebilirseniz kudos.


DÜZENLEME: Hemen germe dokuları ile ilgili sorun bulundu: Köşe gölgelendirici Position = Vertex.xyxyerine kullanıyordum Position = Vertex.xyz. Hata. Ancak üçgen hatası hala var.


1
Cubemap dokuya sahip bir skybox oluşturmak için sadece 4 köşeye (tam ekran dörtlü) ihtiyacınız var. Sadece kamera ve projeksiyona göre doğru doku koordinatlarını hesaplayan bir tepe gölgelendiriciye ihtiyacınız var.
msell

Bir itlaf sorunu olabilir. Kutunun tamamını alıp almadığınızı görmek için arka yüz kaldırma işlemini devre dışı bırakmayı denediniz mi?
pwny

@pwny, bunu düşünmedim. Denedim ve işe yaramadı, ama bunun nasıl fırlattığını görebiliyorum. Önerin için teşekkürler.
sm81095

@msell, bu yaklaşımı duydum, ancak bunun için çevrimiçi bir eğitim bulamadım ve hala glsl öğrenme sürecindeyim. Bunu nasıl yapacağınıza dair bir örnek veya bir bağlantı verebilseydiniz, bunu çok takdir ediyorum.
sm81095

Yanıtlar:


29

Bu cevap yaklaşımınızda neyin yanlış olduğunu söylemese de, gökyüzü kutularını oluşturmanın daha basit bir yolunu sunar.

Geleneksel yol (dokulu küp)

Skybox'lar oluşturmanın basit bir yolu, kamera konumuna ortalanmış dokulu bir küp oluşturmaktır. Küpün her yüzü iki üçgen ve bir 2B dokudan (veya bir atlasın parçası) oluşur. Doku koordinatları nedeniyle her yüz kendi köşelerini gerektirir. Bu yaklaşım, doku değerlerinin doğru bir şekilde enterpole edilmediği bitişik yüzlerin dikişlerinde sorunlara sahiptir.

Cubemap doku ile küp

Geleneksel şekilde olduğu gibi, kameranın etrafında dokulu bir küp oluşturulur. Altı 2D doku kullanmak yerine tek bir kübapap dokusu kullanılır. Kamera küpün içinde ortalandığından tepe noktası, kübemap örnekleme vektörleriyle bire bir eşleşir. Bu nedenle, mesh verileri için doku koordinatlarına gerek yoktur ve köşeler, dizin arabelleği kullanılarak yüzler arasında paylaşılabilir.

Bu yaklaşım, GL_TEXTURE_CUBE_MAP_SEAMLESS etkinleştirildiğinde dikiş sorununu da giderir.

Daha basit (daha iyi) yol

Bir küpü oluştururken ve kamera onun içinde yer alırken, tüm görünüm alanı doldurulur. Skybox'ın en fazla beş yüzü her zaman kısmen görülebilir. Küp yüzlerin üçgenleri projektöre yansıtılır ve kırpılır ve kübap örnekleme vektörleri köşeler arasında enterpole edilir. Bu iş gereksiz.

Tüm manzarayı dolduran tek bir dörtlü doldurmak ve köşelerdeki kübap örnekleme vektörlerini hesaplamak mümkündür. Cubemap örnekleme vektörleri tepe koordinatlarıyla eşleştiğinden, görünüm alanı koordinatlarının dünya boşluğuna çıkarılmasıyla hesaplanabilir. Bu, dünya koordinatlarını görüntüye yansıtmanın tersidir ve matrisleri ters çevirerek elde edilebilir. Ayrıca z-buffer yazmayı devre dışı bıraktığınızdan veya yeterince uzak bir değer yazdığınızdan emin olun.

Bunu başaran köşe gölgelendirici aşağıdadır:

#version 330
uniform mat4 uProjectionMatrix;
uniform mat4 uWorldToCameraMatrix;

in vec4 aPosition;

smooth out vec3 eyeDirection;

void main() {
    mat4 inverseProjection = inverse(uProjectionMatrix);
    mat3 inverseModelview = transpose(mat3(uWorldToCameraMatrix));
    vec3 unprojected = (inverseProjection * aPosition).xyz;
    eyeDirection = inverseModelview * unprojected;

    gl_Position = aPosition;
} 

aPositionköşe koordinatlarıdır {-1,-1; 1,-1; 1,1; -1,1}. GölgelendiricieyeDirection , model-görünüm-projeksiyon matrisinin tersi ile . Ancak, projeksiyon ve dünyadan kameraya matrisler için tersine bölünme vardır. Bunun nedeni, kameranın konumunu ortadan kaldırmak için kamera matrisinin yalnızca 3x3 kısmının kullanılması gerektiğidir. Bu, kamerayı skybox'ın merkezine hizalar. Buna ek olarak, fotoğraf makinemde herhangi bir ölçeklendirme veya kırpma bulunmadığından, tersine çevirme işlemi basitleştirilebilir. Yansıtma matrisinin ters çevrilmesi maliyetli bir işlemdir ve önceden hesaplanabilir, ancak bu kod köşe gölgeleyici tarafından genellikle kare başına yalnızca dört kez yürütüldüğünden, genellikle sorun olmaz.

Parça gölgelendirici, eyeDirectionvektörü kullanarak bir doku araması yapar :

#version 330
uniform samplerCube uTexture;

smooth in vec3 eyeDirection;

out vec4 fragmentColor;

void main() {
    fragmentColor = texture(uTexture, eyeDirection);
}

Uyumluluk modundan kurtulmak için textureCubesadece ile değiştirmeniz textureve çıkış değişkenini kendiniz belirtmeniz gerektiğini unutmayın.


Ayrıca, matris inversiyonunun maliyetli bir süreç olduğunu da belirtmelisiniz, bu yüzden istemci tarafı kodunda daha iyi gerçekleşir.
akaltar

1
Tam ekran dörtlü 4 ver için, tersine çevirme maliyeti hakkında çok fazla endişelenmemiz gerektiğini düşünmüyorum (özellikle GPU'nun 4 kez yaptığı CPU'nun bir kez yaptığı işlemden daha hızlı olacağı gibi).
Maximus Minimus

1
Sadece millet için yararlı bir not, GLSL ES 1.0 (GL ES 2.0 için kullanılır) uygulanmazinverse()
Steven Lu

dönüşüm nesnesinin MVP uWorldToCameraMatrix nedir?
Sidar

@Sidar Hayır, sadece ModelView matrisi, Projeksiyon ayrı.
msell
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.