バックグラウンド
私は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の関数コードがどこから来たのか思い出せません。今はラップトップで調べていたのですが、これはこれらのシェーダーの結果です。
影のある空間に囲まれた影のない空間の小さな正方形です。
チュートリアル以外から学ぶのは難しいので、このテーマに関する知識が不足しているため、おそらくシェーダーを中心に、ここで多くの間違いをしていることは明らかです。申し訳ありません。私が間違っていること、なぜそれが間違っているのか、どうすれば修正できるのか、そしておそらくいくつかのコードについても明確に説明して、誰かがこれに光を当てることができれば素晴らしいと思います。問題は私が間違った場所で働いていることが原因かもしれないと思います。