GLSLバージョン330でスカイボックスを実装する


14

OpenGL 3.3およびGLSLバージョン330で動作するスカイボックスを取得しようとしています。

ウェブ上のどこにも完全に最新のOGLスカイボックスチュートリアルが見つからなかったため、古いものを最新化しました(頂点などのglVertexAttribPointer()代わりに使用gl_Vertex)。それはほとんど動作していますが、2つの主要な詳細:

スカイボックスは空の三角形に似ており、テクスチャはひどくゆがんで伸びています(それらはスターフィールドであると想定されていますが、黒の背景に線が描かれています)。これは、古いチュートリアルを完全に正しく移植しなかったためだと99%確信しています。

これが私のスカイボックスクラスです。

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

頂点シェーダー:

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

フラグメントシェーダー:

#version 330 compatibility

uniform samplerCube SkyTexture;

in vec3 Position;

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

これがグリッチの例です。GLSLをよく知っている人(私はまだ学んでいます)やスカイボックスを見ることができるなら、あなたができる助けを感謝します。また、フラグメントシェーダーで非推奨ではない関数を使用する方法を教えていただければ幸いです。したがって、glsl 330の互換性プロファイルを使用する必要はありません。


編集:テクスチャのストレッチに関する問題がすぐに見つかりました:頂点シェーダーのPosition = Vertex.xyx代わりに使用していPosition = Vertex.xyzました。おっとっと。しかし、三角形のエラーはまだ存在しています。


1
キューブマップテクスチャを使用してスカイボックスをレンダリングするには、4つの頂点(フルスクリーンクワッド)のみが必要です。カメラと投影に基づいて正しいテクスチャ座標を計算する頂点シェーダーが必要です。
msell

カリングの問題かもしれません。フルボックスを取得するかどうかを確認するために、バックフェースカリングを無効にしてみましたか?
pwny

@pwny、私はそれを考えていませんでした。私はそれを試してみましたが、うまくいきませんでしたが、どのようにそれがうまくいかなかったかを見ることができます。提案をありがとう。
sm81095

@msell、私はこのアプローチを聞いたことがありますが、このためのオンラインでのチュートリアルは見つかりませんでした。これを行う方法についての例または例へのリンクを提供できれば、とても感謝しています。
sm81095

回答:


29

この答えはあなたのアプローチの何が悪いのかはわかりませんが、スカイボックスをレンダリングするより簡単な方法を提示します。

従来の方法(テクスチャキューブ)

スカイボックスを作成する簡単な方法は、カメラの位置を中心としたテクスチャ付きキューブをレンダリングすることです。立方体の各面は、2つの三角形と2Dテクスチャ(またはアトラスの一部)で構成されています。テクスチャ座標のため、各面には独自の頂点が必要です。このアプローチには、テクスチャ値が適切に補間されない隣接する面の継ぎ目に問題があります。

キューブマップテクスチャを持つキューブ

従来の方法と同様に、テクスチャ付きキューブがカメラの周りにレンダリングされます。6つの2Dテクスチャを使用する代わりに、単一のキューブマップテクスチャが使用されます。カメラはキューブの中心にあるため、頂点座標はキューブマップサンプリングベクトルと1対1でマッピングされます。したがって、メッシュデータにはテクスチャ座標は必要なく、インデックスバッファを使用して頂点を面間で共有できます。

このアプローチは、GL_TEXTURE_CUBE_MAP_SEAMLESSが有効な場合の継ぎ目の問題も修正します。

よりシンプルな(より良い)方法

キューブをレンダリングし、カメラがその内部にある場合、ビューポート全体が塗りつぶされます。スカイボックスの最大5つの面をいつでも部分的に表示できます。立方体の面の三角形が投影され、ビューポートにクリップされ、キューブマップのサンプリングベクトルが頂点間で補間されます。この作業は不要です。

ビューポート全体を埋める単一のクワッドを埋めて、コーナーでキューブマップサンプリングベクトルを計算することができます。キューブマップサンプリングベクトルは頂点座標と一致するため、ビューポート座標をワールド空間に投影解除することで計算できます。これは、世界座標をビューポートに投影することの反対であり、マトリックスを反転することで実現できます。また、z-buffer書き込みを無効にするか、十分な値を書き込むようにしてください。

以下は、これを実現する頂点シェーダーです。

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

aPositionは頂点座標{-1,-1; 1,-1; 1,1; -1,1}です。シェーダーeyeDirectionは、model-view-projectionマトリックスの逆で計算します。ただし、投影とワールドからカメラへのマトリックスの反転は分割されます。これは、カメラマトリックスの3x3部分のみを使用してカメラの位置を削除する必要があるためです。これにより、カメラがスカイボックスの中央に配置されます。さらに、私のカメラにはスケーリングやせん断がないため、反転を転置に単純化できます。射影行列の反転はコストのかかる操作であり、事前に計算することもできますが、このコードは通常、フレームあたり4回だけ頂点シェーダーによって実行されるため、通常は問題ではありません。

フラグメントシェーダーは、単にeyeDirectionベクターを使用してテクスチャルックアップを実行します。

#version 330
uniform samplerCube uTexture;

smooth in vec3 eyeDirection;

out vec4 fragmentColor;

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

互換モードを取り除くには、textureCube単に置き換えてtexture、出力変数を自分で指定する必要があることに注意してください。


マトリックスの反転はコストのかかるプロセスであるため、クライアント側のコードで実行する方が良いことにも言及する必要があると思います。
akaltar

1
フルスクリーンクワッドの4つの頂点については、反転のコストについてあまり心配する必要はないと思います(特に、GPUが4回実行すると、CPUが1回実行するよりも高速になる可能性が高いため)。
マキシマスミニマス

1
人々にだけ役立つノート、GLSL ES 1.0が実装されていない(GL ES 2.0を使用)inverse()
スティーブン・呂

uWorldToCameraMatrixはカメラ変換オブジェクトのMVPですか?
シダー

@Sidarいいえ、それは単なるModelViewマトリックスです。投影は別です。
msell
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.