8

C++ と OpenGL 3.2 で SFML を使用して 3D ゲームを作成しています。ポイント ライト シャドウ マッピングの実装に苦労しています。私がこれまでに行ってきたことは、私が学んだことや見た例と一致しているように見えますが、それでも影はありません.

私が行ったことは、使用するすべてのコードの単純化されたリストを、使用する正確な順序で作成することですが、完全なソース コードとしてではなく、関連するコードのみを記述します (私のプロジェクトはいくつかのクラスに分割されているため)。

Omnidirectional shadow mapping


C++
- Initialization
-- Use shadow pass shader program
-- Generate + bind the shadow frame buffer
glGenFramebuffers(1, &shadowFrameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, shadowFrameBuffer);


-- Generate a texture
glGenTextures(1, &shadowMap);

-- Bind texture as cubemap
glBindTexture(GL_TEXTURE_CUBE_MAP);

-- Set texture parameters
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);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

-- Generate empty 1024 x 1024 for every face of the cube
for (int face = 0; face < 6; face++)
        glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + face, 0, GL_DEPTH_COMPONENT32F , 1024, 1024, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);

-- Attach the cubemap to the framebuffer
glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, shadowMap, 0);

-- Only draw depth to framebuffer
glDrawBuffer(GL_NONE);

- Every frame
-- Clear screen
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

-- Render shadow map
--- Bind shadow frame buffer
glBindFramebuffer(GL_FRAMEBUFFER, shadowFrameBuffer);

--- Set the viewport to the size of the shadow map
glViewport(0, 0, 1024, 1024);

-- Cull front faces
glCullFace(GL_FRONT);

-- Use shadow mapping program
--- Define projection matrix for rendering each face
glm::mat4 depthProjectionMatrix = glm::perspective(90.0f, 1.0f, 1.0f, 10.0f);

--- Define view matrices for all six faces
std::vector<glm::mat4> depthViewMatrices;

depthViewMatrices.push_back(glm::lookAt(lightInvDir, glm::vec3(1,0,0),  glm::vec3(0,-1,0) )); // +X
depthViewMatrices.push_back(glm::lookAt(lightInvDir, glm::vec3(-1,0,0), glm::vec3(0,1,0) )); // -X
depthViewMatrices.push_back(glm::lookAt(lightInvDir, glm::vec3(0,1,0),  glm::vec3(0,0,1)  )); // +Y
depthViewMatrices.push_back(glm::lookAt(lightInvDir, glm::vec3(0,-1,0), glm::vec3(0,0,-1) )); // -Y
depthViewMatrices.push_back(glm::lookAt(lightInvDir, glm::vec3(0,0,1),  glm::vec3(0,-1,0) )); // +Z
depthViewMatrices.push_back(glm::lookAt(lightInvDir, glm::vec3(0,0,-1), glm::vec3(0,1,0)  )); // -Z

--- For every object in the scene
---- Bind the VBO of the object
---- Define the model matrix for the object based on its position and orientation
---- For all six sides of the cube
----- Set the correct side to render to
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, shadowMap, 0);

----- Clear depth buffer
glClear(GL_DEPTH_BUFFER_BIT);

----- Send model, view and projection matrices to shadow mapping shader
glUniformMatrix4fv(glGetUniformLocation(shadowMapper, "lightModelMatrix"), 1, GL_FALSE, glm::value_ptr(depthModelMatrix));
glUniformMatrix4fv(glGetUniformLocation(shadowMapper, "lightViewMatrix"), 1, GL_FALSE, glm::value_ptr(depthViewMatrices[i]));
glUniformMatrix4fv(glGetUniformLocation(shadowMapper, "lightProjectionMatrix"), 1, GL_FALSE, glm::value_ptr(depthProjectionMatrix));

----- Draw the object
glDrawElements(....);
- END SHADOW MAP DRAW
-- Cull back faces
glCullFace(GL_BACK);

-- Use standard shader program
-- Bind default framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, 0);

-- Activate cubemap texture
glActiveTexture(GL_TEXTURE1);

-- Bind cubemap texture
glBindTexture(GL_TEXTURE_CUBE_MAP, shadowMap);

-- Tell shader to use first texture
glUniform1i(glGetUniformLocation(currentProgram->id, "shadowmap"), 1);

-- Send standard MVPs and draw objects
glDrawElements(...);

- END C++

=================================
GLSL

shadowpass vertex shader source

#version 150

in vec3 position;
out vec3 worldPosition;

uniform mat4 lightModelMatrix;
uniform mat4 lightViewMatrix;
uniform mat4 lightProjectionMatrix;

void main()
{
    gl_Position = lightProjectionMatrix * lightViewMatrix * lightModelMatrix * vec4(position, 1.0);
    worldPosition = (lightModelMatrix * vec4(position, 1.0)).xyz; // Send world position of vertex to fragment shader
}


shadowpass fragment shader source

#version 150

in vec3 worldPosition; // Vertex position in world space
out float distance; // Distance from vertex position to light position
vec3 lightWorldPosition = vec3(0.0, 0.0, 0.0); // Light position in world space

void main()
{
    distance = length(worldPosition - lightWorldPosition); // Distance from point to light
    // Distance will be written to the cubemap
}

standard vertex shader source

#version 150

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

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 = vec4(position, 1.0); // Position of vertex in object space
    fragtexcoord = texcoord;
    fragnormaldirection = normalize(modelInverseTranspose * normal);
    fragnormal = normalize(normal);


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

standard fragment shader source

#version 150

out vec4 outColour;

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

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

uniform sampler2D tex;
uniform samplerCube shadowmap;

void main()
{   
    vec3 lightpos = vec3(0.0, 0.0, 0.0);

    vec3 pointToLight = (fragposition * modelMatrix).xyz - lightpos; // Get vector between this point and the light

    float dist = texture(shadowmap, pointToLight).x; // Get distance written in texture

    float shadowfactor = 1.0;

    if (length(pointToLight) > dist) // Is it occluded?
        shadowfactor = 0.5;

    outColour = texture(tex, fragtexcoord) * shadowfactor;
}

これが私のコードが今何をするかの写真です:

壊れたシャドウ マッピング

これは奇妙な効果ですが、私が意図したものに近いようです。0, 0, 0 の光にさらされたサーフェスには、その中心に影のない円があり、他のすべては影がないように見えます。

4

1 に答える 1

4

シャドウ マップをデバッグする非常に便利な方法の 1 つは、シャドウ マップの内容を画面上にクワッドとして表示する方法です。キューブ シャドウ マップの場合は 6 つのクワッド。これは、画面全体に完全なテクスチャを表示して「次の面に移動」できるデバッグ イースター エッグとして実装できるため、別のキー コンボで 6 つの面を滑らせることができます。

次に、キュービック シャドウ マップで最も重要なことの 1 つは深度範囲です。ポイント ライトの範囲は無限ではないため、通常は、ライトの範囲に合わせて深度ストレージをスケーリングします。

浮動小数点 16 ビットの輝度 (または赤チャンネル) テクスチャを使用してワールド深度を格納できます (球体、つまりピクセル シェーダーで少し計算して真の長さ (光線から交差まで) を意味します) または、線形深度 (正規化されたデバイス座標の深さである、従来の ZBuffer に格納されているものと同じ種類. これは、射影行列の後の深さです. この場合、ライティング シェーダー (次のパス) で一度ワールド位置を再構築するには、問題camera-cube-face inverse view*projection を掛けた後、必ず w で割ります。

シャドウ マップのデバッグの鍵は、すべてシェーダーをいじることにあります。まず、色を使用して、シャドウ マップに保存されている深さを、世界のピクセルによって認識されるように視覚化します。私の会社のエンジンでポイント シャドウ マップを修正するのに役立つ唯一の方法でした。青は 0 から 0.3 まで、赤は 0.3 から 0.6 まで、緑は 0.6 から 1 までのように、ミックスとクランプの組み合わせを使用してカラー コードを作成できます。カラーコード。同じ関数を使用しますが、距離を予想される世界範囲で割ります。

そのビズ スキームを使用すると、シャドウ ゾーンがすべて同じ色を帯びているため、すぐに見ることができます (「光線」がより近いサーフェスによって遮られたため)。その時点に到達したら。残りはすべてスムーズに進みます。

幸運を :)

于 2013-02-02T02:52:32.913 に答える