18

バックグラウンド

私はC++と最新のOpenGL(3.3)を使用して3Dゲームに取り組んでいます。現在、ライティングとシャドウレンダリングに取り組んでおり、指向性シャドウマッピングを正常に実装できました。ゲームの要件を読んだ後、ポイントライトシャドウマッピングが必要になると判断しました。いくつかの調査を行った後、全方向性シャドウマッピングを実行するには、方向性シャドウマッピングと同様のことを実行しますが、代わりにキューブマップを使用することを発見しました。

私はキューブマップについてこれまでの知識はありませんが、キューブマップは6つのテクスチャであり、シームレスに接続されていることを理解しています。私は周りを見回しましたが、残念ながら、現代のOpenGLの主題に関する決定的な「チュートリアル」を見つけるのに苦労しました。ソースコードの抜粋や概念だけから学ぶのに真剣に苦労したので、最初から最後まで説明するチュートリアルを最初に探しますが、試してみました。

現在の理解

これが私の一般的な考え方ですが、専門性はありません。訂正してください。

  • ポイントライトごとに、指向性シャドウマッピングのようにフレームバッファが設定されます
  • 次に、単一のキューブマップテクスチャが生成され、。でバインドされglBindTexture(GL_TEXTURE_CUBE_MAP, shadowmap)ます。
  • キューブマップは、次の属性で設定されます。

    glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    

(これも指向性シャドウマッピングに似ています)

  • glTexImage2D()これで、顔ごとに1回ずつ、6回繰り返されます。私はこのようにします:

     for (int face = 0; face < 6; face++) // Fill each face of the shadow cubemap
         glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, GL_DEPTH_COMPONENT32F , 1024, 1024, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
    
  • テクスチャは、への呼び出しでフレームバッファにアタッチされます

    glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, shadowmap, 0);
    
  • シーンをレンダリングする場合、指向性シャドウマッピングのように、2つのパスでレンダリングされます。

  • まず、シャドウフレームバッファがバインドされ、ビューポートがシャドウマップのサイズ(この場合は1024 x 1024)に調整されます。
  • カリングは前面に設定されていますglCullFace(GL_FRONT)
  • アクティブなシェーダープログラムは、頂点とフラグメントのシャドウシェーダーに切り替えられます。これらのシェーダーは、さらに下のソースを提供します。
  • 6つのビューすべてのライトビューマトリックスが計算されます。これを行うには、glm::mat4とpush_back()行列のベクトルを次のように作成します。

    // Create the six view matrices for all six sides
    for (int i = 0; i < renderedObjects.size(); i++) // Iterate through all rendered objects
    {
        renderedObjects[i]->bindBuffers(); // Bind buffers for rendering with it
    
        glm::mat4 depthModelMatrix = renderedObjects[i]->getModelMatrix(); // Set up model matrix
    
        for (int i = 0; i < 6; i++) // Draw for each side of the light
        {
            glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, shadowmap, 0);
            glClear(GL_DEPTH_BUFFER_BIT); // Clear depth buffer
    
            // Send MVP for shadow map
            glm::mat4 depthMVP = depthProjectionMatrix * depthViewMatrices[i] * depthModelMatrix;
            glUniformMatrix4fv(glGetUniformLocation(shadowMappingProgram, "depthMVP"), 1, GL_FALSE, glm::value_ptr(depthMVP));
    
            glUniformMatrix4fv(glGetUniformLocation(shadowMappingProgram, "lightViewMatrix"), 1, GL_FALSE, glm::value_ptr(depthViewMatrices[i]));
            glUniformMatrix4fv(glGetUniformLocation(shadowMappingProgram, "lightProjectionMatrix"), 1, GL_FALSE, glm::value_ptr(depthProjectionMatrix));
            glDrawElements(renderedObjects[i]->getDrawType(), renderedObjects[i]->getElementSize(), GL_UNSIGNED_INT, 0);
        }
    }
    
  • デフォルトのフレームバッファがバインドされ、シーンは正常に描画されます。

問題

さて、シェーダーに。これは私の理解が枯渇するところです。私は自分が何をすべきか完全に確信が持てません。バージョンが異なるため、私の研究は互いに矛盾しているようです。結局、ランダムなソースからコードを無造作にコピーして貼り付け、それが黒い画面以外の何かを達成することを望んでいました。私はこれがひどいことを知っていますが、何をすべきかについて明確な定義がないようです。どのスペースで働いていますか?指向性ポイントライティングで使用したように、別のシャドウシェーダーも必要ですか?シャドウキューブマップのタイプとして何を使用しますか?samplerCube?samplerCubeShadow?上記のキューブマップを適切にサンプリングするにはどうすればよいですか?誰かが私のためにそれを片付けて、いい説明をしてくれることを願っています。シェーダー部分についての私の現在の理解は次のとおりです。-シーンがキューブマップにレンダリングされているとき、頂点シェーダーは、C ++コードで計算したdepthMVPユニフォームを取得し、それらによって入力頂点を変換します。-キューブマップパスのフラグメントシェーダーは、シングルアウト値をに割り当てるだけです。gl_FragCoord.z。(この部分は、指向性シャドウマッピングを実装したときから変更されていません。シェーダーはキューブマップと相互作用しないため、キューブマッピングでも同じだと思いました。OpenGLは、シェーダーからの出力をキューブマップにレンダリングするだけですよね?フレームバッファ?)

  • 通常のレンダリングの頂点シェーダーは変更されていません。
  • 通常のレンダリングのフラグメントシェーダーでは、頂点の位置は、ライトの投影とビューマトリックスを使用してライトの空間に変換されます。
  • これは、キューブマップテクスチャルックアップで何らかの形で使用されています。???
  • 魔法の手段を使用して深度が取得されると、指向性シャドウマッピングのように、頂点までのライトの距離と比較されます。それより少ない場合は、そのポイントをシャドウする必要があり、その逆も同様です。

それはあまり理解されていません。頂点がどのように変換され、キューブマップを検索するために使用されるかについては空白になっているので、人々がこれを明確にできることを期待して、シェーダーのソースを貼り付けます。このコードの多くはブラインドコピーと貼り付けであることに注意してください。理解を損なうことのないように、私は何も変更していません。

シャドウ頂点シェーダー:

#version 150

in vec3 position;

uniform mat4 depthMVP;

void main()
{
    gl_Position = depthMVP * vec4(position, 1);
}

シャドウフラグメントシェーダー:

#version 150

out float fragmentDepth;

void main()
{
    fragmentDepth = gl_FragCoord.z;
}

標準の頂点シェーダー:

#version 150

in vec3 position;
in vec3 normal;
in vec2 texcoord;

uniform mat3 modelInverseTranspose;
uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;

out vec3 fragnormal;
out vec3 fragnormaldirection;
out vec2 fragtexcoord;
out vec4 fragposition;
out vec4 fragshadowcoord;

void main()
{
    fragposition = modelMatrix * vec4(position, 1.0);
    fragtexcoord = texcoord;
    fragnormaldirection = normalize(modelInverseTranspose * normal);
    fragnormal = normalize(normal);
    fragshadowcoord = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);


    gl_Position = projectionMatrix * viewMatrix * modelMatrix * vec4(position, 1.0);
}

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

#version 150

out vec4 outColour;

in vec3 fragnormaldirection;
in vec2 fragtexcoord;
in vec3 fragnormal;
in vec4 fragposition;
in vec4 fragshadowcoord;

uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform mat4 viewMatrixInversed;

uniform mat4 lightViewMatrix;
uniform mat4 lightProjectionMatrix;

uniform sampler2D tex;
uniform samplerCubeShadow shadowmap;

float VectorToDepthValue(vec3 Vec)
{
    vec3 AbsVec = abs(Vec);
    float LocalZcomp = max(AbsVec.x, max(AbsVec.y, AbsVec.z));

    const float f = 2048.0;
    const float n = 1.0;
    float NormZComp = (f+n) / (f-n) - (2*f*n)/(f-n)/LocalZcomp;
    return (NormZComp + 1.0) * 0.5;
}

float ComputeShadowFactor(samplerCubeShadow ShadowCubeMap, vec3 VertToLightWS)
{   
    float ShadowVec = texture(ShadowCubeMap, vec4(VertToLightWS, 1.0));
    if (ShadowVec + 0.0001 > VectorToDepthValue(VertToLightWS)) // To avoid self shadowing, I guess
        return 1.0;

    return 0.7;
}

void main()
{
    vec3 light_position = vec3(0.0, 0.0, 0.0);
    vec3 VertToLightWS = light_position - fragposition.xyz;
    outColour = texture(tex, fragtexcoord) * ComputeShadowFactor(shadowmap, VertToLightWS);
}

ComputerShadowFactorとVectorToDepthValueの関数コードがどこから来たのか思い出せません。今はラップトップで調べていたのですが、これはこれらのシェーダーの結果です。

これらのシェーダーの結果

影のある空間に囲まれた影のない空間の小さな正方形です。

チュートリアル以外から学ぶのは難しいので、このテーマに関する知識が不足しているため、おそらくシェーダーを中心に、ここで多くの間違いをしていることは明らかです。申し訳ありません。私が間違っていること、なぜそれが間違っているのか、どうすれば修正できるのか、そしておそらくいくつかのコードについても明確に説明して、誰かがこれに光を当てることができれば素晴らしいと思います。問題は私が間違った場所で働いていることが原因かもしれないと思います。

4

1 に答える 1

11

私はあなたの質問のいくつかに答えを提供したいと思っていますが、最初にいくつかの定義が必要です:

キューブマップとは何ですか?

これは、方向ベクトルから[顔、その顔の2d座標]のペアへのマップであり、仮想の立方体に方向ベクトルを投影することによって取得されます。

OpenGLキューブマップテクスチャとは何ですか?

6枚の「画像」のセットです。

GLSLキューブマップサンプラーとは何ですか?

これは、キューブマップサンプリングを実行できるサンプラープリミティブです。これは、通常のテクスチャ座標の代わりに方向ベクトルを使用してサンプリングされることを意味します。次に、ハードウェアは方向ベクトルを仮想の立方体に投影し、結果の[顔、2Dテクスチャ座標]ペアを使用して、適切な2D位置で適切な「画像」をサンプリングします。

GLSLシャドウサンプラーとは何ですか?

これは、NDC空間の深さの値を含むテクスチャにバインドされ、シャドウ固有のサンプリング関数を使用してサンプリングされると、NDC空間の深さ(シャドウマップの同じ空間内)間の「比較」を返すサンプラープリミティブです。明らかに)そして境界付きテクスチャ内に格納されたNDC空間の深さ。比較する深さは、サンプリング関数を呼び出すときにテクスチャ座標の追加要素として指定されます。シャドウサンプラーは使いやすさと速度を上げるために提供されていますが、シェーダーで「手動」で比較を行うことはいつでも可能であることに注意してください。


さて、あなたの質問のために:

OpenGLは単に[...]をキューブマップにレンダリングしますよね?

いいえ、OpenGLは、現在バインドされているフレームバッファ内の一連のターゲットにレンダリングします。

キューブマップの場合、キューブマップでレンダリングする通常の方法は次のとおりです。

  • それらを作成し、それらの6つの「画像」のそれぞれを同じフレームバッファにアタッチします(明らかに、異なるアタッチメントポイントで
  • 一度に1つのターゲットのみを有効にします(したがって、各キューブマップ面で個別にレンダリングします
  • キューブマップの顔に必要なものをレンダリングするには(顔固有の「ビュー」および「プロジェクション」マトリックスを使用する可能性があります

ポイントライトシャドウマップ

キューブマップについて述べられているすべてに加えて、ポイントライトシャドウマッピングを実装するためにキューブマップを使用することには多くの問題があるため、ハードウェアの深さの比較はめったに使用されません。

代わりに、一般的な慣習は次のとおりです。

  • NDC空間の深さを書き込む代わりに、ポイントライトからの半径方向の距離を書き込みます
  • シャドウマップをクエリする場合(下部のサンプルコードを参照):
    • ハードウェアの深さの比較を使用しないでください(samplerCubeShadowの代わりにsamplerCubeを使用してください)
    • 「立方体空間」でテストするポイントを変換します(投影はまったく含まれません)
    • キューブマップをサンプリングするためのルックアップ方向として「キューブスペース」ベクトルを使用します
    • キューブマップからサンプリングされた半径距離を、テストされたポイントの半径距離と比較します

サンプルコード

// sample radial distance from the cubemap
float radial_dist = texture(my_cubemap, cube_space_vector).x;

// compare against test point radial distance
bool shadowed = length(cube_space_vector) > radial_dist;
于 2012-12-23T00:00:45.247 に答える