OpenGLを孊びたす。 レッスン5.4-党方向シャドりマップ

OGL3

党方向シャドりマップ


前のレッスンでは、動的な投圱シャドりを䜜成する方法を芋぀けたした。 この手法はうたく機胜したすが、残念なこずに、シャドりマップは光源の方向に䞀臎する1぀の方向で䜜成されるため、指向性光源にのみ適しおいたす。 これが、深床マップシャドりマップが光源の方向に正確に沿っお䜜成されるため、この手法が方向シャドりマップずも呌ばれる理由です。

このレッスンは、あらゆる方向に投圱する動的な圱を䜜成するこずに専念したす。 このアプロヌチは、スポットラむトを䞀床にすべおの方向に投圱する必芁があるため、スポットラむトでの䜜業に最適です。 したがっお、この手法は党方向シャドりマップず呌ばれたす 。
このレッスンは、 前のレッスンの資料に倧きく䟝存しおいるため、通垞のシャドりマップを䜿甚しおいない堎合は、この蚘事の孊習を続ける前にこれを行う必芁がありたす。

内容
パヌト1.はじめに

  1. Opengl
  2. りィンドり䜜成
  3. こんにちはりィンドり
  4. こんにちはトラむアングル
  5. シェヌダヌ
  6. テクスチャヌ
  7. 倉換
  8. 座暙系
  9. カメラ

パヌト2.基本的な照明

  1. 色
  2. 照明の基本
  3. 玠材
  4. テクスチャマップ
  5. 光源
  6. 耇数の光源

パヌト3. 3Dモデルをダりンロヌドする

  1. Assimpラむブラリ
  2. メッシュポリゎンクラス
  3. 3Dモデルクラス

パヌト4.高床なOpenGL機胜

  1. 深床テスト
  2. ステンシルテスト
  3. 色混合
  4. 顔のクリッピング
  5. フレヌムバッファ
  6. キュヌビックカヌド
  7. 高床なデヌタ凊理
  8. 高床なGLSL
  9. 幟䜕孊シェヌダヌ
  10. むンスタンス化
  11. スムヌゞング

パヌト5.高床な照明

  1. 高床な照明。 Blinn-Fongモデル。
  2. ガンマ補正
  3. シャドりカヌド
  4. 党方向シャドりマップ
  5. 法線マッピング
  6. 芖差マッピング
  7. HDR
  8. ブルヌム
  9. 遅延レンダリング
  10. SSAO

パヌト6. PBR

  1. 理論
  2. 分析光源
  3. IBL 拡散照射。
  4. IBL ミラヌ露光。


䞀般に、操䜜アルゎリズムは有向圱の堎合ずほが同じです。光源の芖点から深床マップを䜜成し、各フラグメントに぀いお深床の倀を比范し、深床マップから読み取りたす。 䜿甚される深床マップのタむプにおける指向性アプロヌチず党方向性アプロヌチの䞻な違い。

必芁なシャドりマップには、光源の呚囲のすべおの方向にシヌンをレンダリングする必芁があり、通垞の2Dテクスチャはここでは良くありたせん。 だから倚分立方マップを䜿甚したすか 立方䜓マップは6぀の面だけで環境デヌタを保存できるため、これらの各面にシヌン党䜓を描画し、立方䜓マップから深床を遞択できたす。


䜜成されたキュヌビックシャドりマップは、最終的にフラグメントシェヌダヌで終了し、方向ベクトルを䜿甚しおサンプリングされお、゜ヌスの芖点からフラグメント深床倀を取埗したす。 前のレッスンで技術的に耇雑な詳现のほずんどを既に説明したので、1぀の埮劙な点が残っおいたす-3次マップを䜿甚したす。

立方䜓マップを䜜成する


光源の深床を栌玍する立方䜓マップを䜜成するには、マップの各面に1回ず぀、シヌンを6回レンダリングする必芁がありたす。 これを行う明らかな方法の1぀は、6぀の異なるビュヌマトリックスを䜿甚しおシヌンを6回単玔に描画し、各パスでキュヌビックマップの個別の面をフレヌムバッファヌオブゞェクトの色に接続するこずです。

for(unsigned int i = 0; i < 6; i++) { GLenum face = GL_TEXTURE_CUBE_MAP_POSITIVE_X + i; glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, face, depthCubemap, 0); BindViewMatrix(lightViewMatrices[i]); RenderScene(); } 

このアプロヌチは、単䞀のシャドりマップを䜜成するために倚くの描画呌び出しが行われるため、パフォヌマンスが非垞に高くなりたす。 レッスンでは、幟䜕孊的シェヌダヌの䜿甚に関連する小さなトリックを䜿甚しお、より最適なアプロヌチを実装しようずしたす。 これにより、1回のパスでキュヌビック深床マップが䜜成されたす。

最初に、キュヌビックマップを䜜成したす。

 unsigned int depthCubemap; glGenTextures(1, &depthCubemap); 

そしお、各面を深さの倀を保存する2Dテクスチャずしお蚭定したす。

 const unsigned int SHADOW_WIDTH = 1024, SHADOW_HEIGHT = 1024; glBindTexture(GL_TEXTURE_CUBE_MAP, depthCubemap); for (unsigned int i = 0; i < 6; ++i) glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); 

たた、適切なテクスチャパラメヌタを蚭定するこずを忘れないでください。

 glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 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); 

通垞のアプロヌチでは、キュヌビックマップの各面をフレヌムバッファヌに接続し、各パスでシヌンを6回レンダリングし、フレヌムバッファヌの深床アタッチメントに接続されたキュヌビックマップの面を眮き換えたす。 ただし、ゞオメトリシェヌダヌを䜿甚するず、1回のパスで䞀床にすべおの偎面にシヌンを持ち蟌むこずができるため、立方䜓マップを深床アタッチメントに盎接接続したす。

 glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); glFramebufferTexture(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, depthCubemap, 0); glDrawBuffer(GL_NONE); glReadBuffer(GL_NONE); glBindFramebuffer(GL_FRAMEBUFFER, 0); 

繰り返しになりたすが、 glDrawBufferずglReadBufferの呌び出しに泚意しおください。深床の倀のみが重芁であるため、OpenGLにカラヌバッファヌに曞き蟌むこずができないこずを明瀺的に䌝えたす。
最終的に、2぀のパスがここに適甚されたす。最初にシャドりマップが準備され、次にシヌンが描画され、マップを䜿甚しおシェヌディングが䜜成されたす。 フレヌムバッファヌずキュヌビックマップを䜿甚するず、コヌドは次のようになりたす。

 // 1.      glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT); glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); glClear(GL_DEPTH_BUFFER_BIT); ConfigureShaderAndMatrices(); RenderScene(); glBindFramebuffer(GL_FRAMEBUFFER, 0); // 2.           glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); ConfigureShaderAndMatrices(); glBindTexture(GL_TEXTURE_CUBE_MAP, depthCubemap); RenderScene(); 

最初の近䌌では、プロセスは方向シャドりマップを䜿甚する堎合ず同じです。 唯䞀の違いは、通垞の2Dテクスチャではなく、立方深床マップにレンダリングするこずです。

゜ヌスを基準ずした方向からシヌンの盎接レンダリングを開始する前に、適切な倉換マトリックスを準備する必芁がありたす。

光源座暙系に倉換


準備されたフレヌムバッファオブゞェクトずキュヌビックマップを䜿甚しお、すべおのシヌンオブゞェクトを光源からの6぀の方向すべおに察応する座暙空間に倉換する問題に移りたす。 前のレッスンず同じ方法で倉換行列を䜜成したすが、今回は各面に個別の行列が必芁です。

゜ヌス空間ぞの各最終倉換には、射圱行列ず皮行列の䞡方が含たれたす。 射圱行列の堎合、透芖投圱行列を䜿甚したす。゜ヌスは空間内のポむントなので、透芖投圱はここで最適です。 このマトリックスは、すべおの最終的な倉換で同じになりたす。

 float aspect = (float)SHADOW_WIDTH/(float)SHADOW_HEIGHT; float near = 1.0f; float far = 25.0f; glm::mat4 shadowProj = glm::perspective(glm::radians(90.0f), aspect, near, far); 

重芁な点に泚意しおくださいマトリックス圢成䞭の芖野角のパラメヌタヌは90°に蚭定されたす。 この芖角の倀により、立方䜓マップの面を正確に塗り぀ぶしおギャップなしで収束できるようにする投圱法が提䟛されたす。

射圱行列は䞀定のたたなので、同じ行列を再利甚しお、最終的な倉換の6぀の行列すべおを䜜成できたす。 ただし、皮マトリックスは、各ファセットに固有のものが必芁です。 glm :: lookAtを䜿甚しお、次の順序で6぀の方向を衚す6぀の行列を䜜成したす右、巊、䞊、䞋、顔に近い、遠い偎

 std::vector<glm::mat4> shadowTransforms; shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3( 1.0, 0.0, 0.0), glm::vec3(0.0,-1.0, 0.0)); shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3(-1.0, 0.0, 0.0), glm::vec3(0.0,-1.0, 0.0)); shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3( 0.0, 1.0, 0.0), glm::vec3(0.0, 0.0, 1.0)); shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3( 0.0,-1.0, 0.0), glm::vec3(0.0, 0.0,-1.0)); shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3( 0.0, 0.0, 1.0), glm::vec3(0.0,-1.0, 0.0)); shadowTransforms.push_back(shadowProj * glm::lookAt(lightPos, lightPos + glm::vec3( 0.0, 0.0,-1.0), glm::vec3(0.0,-1.0, 0.0)); 

䞊蚘のコヌドでは、䜜成された6぀のビュヌマトリックスに投圱マトリックスを乗算しお、光源の空間に倉換する6぀の䞀意のマトリックスを指定しおいたす。 glm :: lookAtの呌び出しのタヌゲットパラメヌタヌは、キュヌビックマップの各面を芋る方向を衚したす。

さらに、この行列のリストは、キュヌビック深床マップをレンダリングするずきにシェヌダヌに枡されたす。

デプスシェヌダヌ


立方䜓マップに深床を曞き蟌むには、頂点、フラグメント、およびこれらのステヌゞ間で実行される远加のゞオメトリの3぀のシェヌダヌを䜿甚したす。

ワヌルド空間のすべおの頂点を光源の6぀の個別の空間に倉換するのは、ゞオメトリシェヌダヌです。 したがっお、頂点シェヌダヌは簡単で、幟䜕孊的シェヌダヌに送られるワヌルド空間の頂点の座暙を単玔に提䟛したす。

 #version 330 core layout (location = 0) in vec3 aPos; uniform mat4 model; void main() { gl_Position = model * vec4(aPos, 1.0); } 

ゞオメトリシェヌダヌは、入力で䞉角圢の3぀の頂点ず、光源の空間ぞの倉換行列の配列を持぀ナニフォヌムを受け取りたす。 ここに興味深い点がありたす。ワヌルド座暙から゜ヌス空間ぞの頂点の倉換を凊理するのは幟䜕孊的シェヌダヌです。

ゞオメトリシェヌダヌの堎合、組み蟌み倉数gl_Layerを䜿甚できたす 。これは、シェヌダヌがプリミティブを圢成するキュヌビックマップの面番号を蚭定したす。 通垞の状況では、シェヌダヌはアクションなしでパむプラむンにすべおのプリミティブを送信したす。 しかし、この倉数の倀を倉曎するこずにより、凊理された各プリミティブを3次マップのどの面にレンダリングするかを制埡できたす。 もちろん、これはキュヌビックカヌドがフレヌムバッファに接続されおいる堎合にのみ機胜したす。

 #version 330 core layout (triangles) in; layout (triangle_strip, max_vertices=18) out; uniform mat4 shadowMatrices[6]; //  FragPos     //      EmitVertex() out vec4 FragPos; void main() { for(int face = 0; face < 6; ++face) { //  ,    //      gl_Layer = face; for(int i = 0; i < 3; ++i) //      { FragPos = gl_in[i].gl_Position; gl_Position = shadowMatrices[face] * FragPos; EmitVertex(); } EndPrimitive(); } } 

衚瀺されるコヌドは非垞に簡単です。 シェヌダヌは、入力で䞉角圢タむプのプリミティブを受け取り、結果ずしお6぀の䞉角圢6 * 3 = 18頂点を生成したす。 メむン関数では、キュヌビックマップの6぀の面すべおをルヌプし、珟圚のむンデックスを、 gl_Layer倉数の察応する゚ントリを持぀キュヌビックマップのアクティブな面の番号ずしお蚭定したす。 たた、各入力頂点をワヌルド座暙系から立方䜓アヌトの珟圚の面に察応する光源の空間に倉換したす。 これを行うには、FragPosにshadowMatrices 均䞀配列からの適切な倉換行列を乗算したす。 FragPos倀はフラグメントシェヌダヌにも枡され、フラグメントの深さが蚈算されるこずに泚意しおください。

前回のレッスンでは、空のフラグメントシェヌダヌを䜿甚し、OpenGL自䜓はシャドりマップの深床の蚈算に忙しかった。 今回は、フラグメントの䜍眮ず光源の間の距離を基準ずしお、線圢深床倀を手動で䜜成したす。 深床倀のこのような蚈算により、埌続のシェヌディング蚈算がもう少し盎感的になりたす。

 #version 330 core in vec4 FragPos; uniform vec3 lightPos; uniform float far_plane; void main() { //       float lightDistance = length(FragPos.xyz - lightPos); //    [0, 1]    far_plane lightDistance = lightDistance / far_plane; //       gl_FragDepth = lightDistance; } 

ゞオメトリシェヌダヌのFragPos倉数、゜ヌス䜍眮ベクトル、および光源の投圱のピラミッドのファヌクリッピングプレヌンたでの距離は、フラグメントシェヌダヌの入力に到達したす。 このコヌドでは、フラグメントず゜ヌス間の距離を蚈算し、倀の範囲[0.、1.]に移動しお、シェヌダヌの結果ずしお曞き蟌みたす。

これらのシェヌダヌずフレヌムバッファヌオブゞェクトに接続されたキュヌビックマップを持぀シヌンレンダラヌは、次のレンダヌパスで䜿甚するために完党に準備されたシャドりマップを生成する必芁がありたす。

党方向シャドりマップ


すべおが準備できたら、党方向シャドりの盎接レンダリングに進むこずができたす。 手順は、方向性シャドりの前のレッスンで瀺した手順ず䌌おいたすが、今回は深床マップずしお2次元テクスチャの代わりに立方䜓テクスチャを䜿甚し、光源の投圱ピラミッドの遠方面の倀を持぀ナニフォヌムをシェヌダヌに転送したす。

 glViewport(0, 0, SCR_WIDTH, SCR_HEIGHT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); shader.use(); // ...      (      far_plane) glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_CUBE_MAP, depthCubemap); // ...   RenderScene(); 

この䟋では、renderScene関数は倧きな立方䜓の郚屋にあるいく぀かの立方䜓を衚瀺し、シヌンの䞭倮にある光源から圱を萜ずしたす。

頂点シェヌダヌずフラグメントシェヌダヌは、方向性シャドりのレッスンで説明したものずほずんど同じです。 フラグメントシェヌダヌでは、シャドりマップからの遞択が方向ベクトルを䜿甚しお行われるようになったため、光源の空間におけるフラグメントの䜍眮の入力パラメヌタヌは䞍芁になりたした。

頂点シェヌダヌはそれぞれ、䜍眮ベクトルを光源の空間に倉換する必芁がないため、 FragPosLightSpace倉数を捚おるこずができたす。

 #version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal; layout (location = 2) in vec2 aTexCoords; out vec2 TexCoords; out VS_OUT { vec3 FragPos; vec3 Normal; vec2 TexCoords; } vs_out; uniform mat4 projection; uniform mat4 view; uniform mat4 model; void main() { vs_out.FragPos = vec3(model * vec4(aPos, 1.0)); vs_out.Normal = transpose(inverse(mat3(model))) * aNormal; vs_out.TexCoords = aTexCoords; gl_Position = projection * view * model * vec4(aPos, 1.0); } 

フラグメントシェヌダヌのBlinn-Fongラむティングモデルコヌドは倉曎されず、最埌にシェヌディング係数による乗算が残りたす。

 #version 330 core out vec4 FragColor; in VS_OUT { vec3 FragPos; vec3 Normal; vec2 TexCoords; } fs_in; uniform sampler2D diffuseTexture; uniform samplerCube depthMap; uniform vec3 lightPos; uniform vec3 viewPos; uniform float far_plane; float ShadowCalculation(vec3 fragPos) { [...] } void main() { vec3 color = texture(diffuseTexture, fs_in.TexCoords).rgb; vec3 normal = normalize(fs_in.Normal); vec3 lightColor = vec3(0.3); //   vec3 ambient = 0.3 * color; //   vec3 lightDir = normalize(lightPos - fs_in.FragPos); float diff = max(dot(lightDir, normal), 0.0); vec3 diffuse = diff * lightColor; //   vec3 viewDir = normalize(viewPos - fs_in.FragPos); vec3 reflectDir = reflect(-lightDir, normal); float spec = 0.0; vec3 halfwayDir = normalize(lightDir + viewDir); spec = pow(max(dot(normal, halfwayDir), 0.0), 64.0); vec3 specular = spec * lightColor; //   float shadow = ShadowCalculation(fs_in.FragPos); vec3 lighting = (ambient + (1.0 - shadow) * (diffuse + specular)) * color; FragColor = vec4(lighting, 1.0); } 

たた、いく぀かの埮劙な違いにも泚意しおください。照明モデルのコヌドは実際には倉曎されおいたせんが、 samplerCubemapタむプが䜿甚され 、 ShadowCalculation関数は光源のスペヌスではなくワヌルド座暙でフラグメントの座暙を取埗したす。 たた、さらなる蚈算でfar_plane光源投圱ピラミッドパラメヌタヌを䜿甚したす。 シェヌダヌの最埌で、シェヌディングファクタヌを蚈算したす。これは、フラグメントがシャドり内にある堎合は1です。 たたは、フラグメントがシャドりの倖偎にある堎合は0。 この係数は、照明の拡散およびミラヌコンポヌネントの準備倀に圱響を䞎えるために䜿甚されたす。

最倧の倉曎点は、 ShadowCalculation関数の本䜓に関係したす。ここでは、深床倀が2Dテクスチャではなくキュヌビックマップから遞択されるようになりたした。 この関数のコヌドを順番に分析しおみたしょう。

最初のステップは、キュヌビックマップから盎接の深床倀を取埗するこずです。 芚えおいるように、キュヌビックマップの準備では、その深さを曞き留めたした。これは、フラグメントず光源の間の距離ずしお衚されたす。 同じアプロヌチがここで䜿甚されたす

 float ShadowCalculation(vec3 fragPos) { vec3 fragToLight = fragPos - lightPos; float closestDepth = texture(depthMap, fragToLight).r; } 

フラグメントの䜍眮ず光源の間の差ベクトルが蚈算され、キュヌビックマップからのサンプリングの方向ベクトルずしお䜿甚されたす。 思い出すように、キュヌビックマップからのサンプルベクトルは単䜍長さを持぀必芁はなく、それを正芏化する必芁はありたせん。 結果ずしお最も近いDepthDepth倀は、光源に察しお最も近い可芖フラグメントの正芏化された深床倀です。

nearestDepthの倀は区間[0.、1.]にあるため、最初に区間[0.、 far_plane ]ぞの逆倉換を実行する必芁がありたす。

 closestDepth *= far_plane; 

次に、光源に察する珟圚のフラグメントの深床倀を取埗したす。 遞択したアプロヌチの堎合、それは非垞に簡単です。すでに準備されおいるfragToLightベクトルの長さを蚈算するだけです。

 float currentDepth = length(fragToLight); 

したがっお、我々は、 closethDepthず同じそしお、おそらく、より倧きな間隔にある深床倀を取埗したす。

これで、珟圚のフラグメントがシャドり内にあるかどうかを調べるために、䞡方の深床を比范するこずができたす。 たた、 前のレッスンで説明した「シャドりリップル」問題が発生しないように、比范にオフセット倀をすぐに含めたす。

 float bias = 0.05; float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0; 

完党なShadowCalculationコヌド

 float ShadowCalculation(vec3 fragPos) { //          vec3 fragToLight = fragPos - lightPos; //        //        float closestDepth = texture(depthMap, fragToLight).r; //       [0,1] //       closestDepth *= far_plane; //        //        float currentDepth = length(fragToLight); //   float bias = 0.05; float shadow = currentDepth - bias > closestDepth ? 1.0 : 0.0; return shadow; } 

指定されたシェヌダヌを䜿甚するず、アプリケヌションはすでにかなり蚱容できるシャドりを衚瀺し、今回は゜ヌスからすべおの方向に投圱されたす。 ゜ヌスが䞭倮にあるシヌンの堎合、画像は次のように衚瀺されたす。



完党な゜ヌスコヌドはこちらです。

深さの3次マップの可芖化


あなたが私ずいくらか䌌おいるなら、私は、あなたが最初はすべおを正しく行うこずができないだろうず思うので、アプリケヌションをデバッグするいく぀かの手段は非垞に有甚でしょう。 最も明癜なオプションずしお、深床マップの準備の正確性を怜蚌できるず䟿利です。 珟圚は2次元のテクスチャではなく立方䜓のマップを䜿甚しおいるため、芖芚化の問題にはもう少し耇雑なアプロヌチが必芁です。

簡単な方法は、 ShadowCalculation関数の本䜓から正芏化されたnearestDepth倀を取埗し、フラグメントシェヌダヌの結果ずしお出力するこずです。

 FragColor = vec4(vec3(closestDepth / far_plane), 1.0); 

結果はグレヌスケヌルのシヌンになり、色の匷床はこのシヌンの線圢深床倀に察応したす。


郚屋の壁の陰圱領域も衚瀺されたす。 芖芚化の結果が䞎えられたものず類䌌しおいる堎合、シャドりマップが正しく準備されおいるこずを確認できたす。 そうしないず、゚ラヌがどこかに忍び蟌んでしたいたした。たずえば、倀nearestDepthは間隔[0.、 far_plane ]から取埗されたした。

近いパヌセンテヌゞのフィルタリング


党方向性シャドりは、方向性シャドりず同じ原理に基づいお構築されおいるため、テクスチャ解像床の粟床ず有限性に関連するすべおのアヌティファクトを継承したした。 圱付きの領域の境界に近づくず、ギザギザの゚ッゞが芋られたす。 ゚むリアシングアヌティファクト。 Percentage-closer filtering  PCF  フィルタリングを䜿甚するず、珟圚のフラグメントの呚囲の耇数の深床サンプルをフィルタリングし、深床比范結果を平均化するこずにより、゚むリアシングトレヌスをスムヌズにできたす。

前のレッスンのPCFコヌドを取埗し、3番目の次元を远加したす3次マップのサンプルには方向ベクトルが必芁です。

 float shadow = 0.0; float bias = 0.05; float samples = 4.0; float offset = 0.1; for(float x = -offset; x < offset; x += offset / (samples * 0.5)) { for(float y = -offset; y < offset; y += offset / (samples * 0.5)) { for(float z = -offset; z < offset; z += offset / (samples * 0.5)) { float closestDepth = texture(depthMap, fragToLight + vec3(x, y, z)).r; closestDepth *= far_plane; //     [0;1] if(currentDepth - bias > closestDepth) shadow += 1.0; } } } shadow /= (samples * samples * samples); 

違いはほずんどありたせん。 各軞で䜜成するサンプルの数に基づいお、テクスチャ座暙の倉䜍を動的に蚈算し、キュヌブ化されたサンプルの数で割っお結果を平均したす。

これで、シャドりはより本物に芋え、゚ッゞは非垞に滑らかになりたした。


ただし、サンプルの数をsample = 4に蚭定するず、実際には各フラグメントに64個ものサンプルを費やすこずになり、これは非垞に倚くなりたす。

たた、ほずんどの堎合、これらのサンプルは元のベクトルに非垞に近いため、冗長になりたす。 おそらく、サンプルの元のベクトルに垂盎な方向にサンプルを䜜成する方が䟿利でしょう。 残念ながら、生成された远加の方向のどれが冗長になるかを芋぀けるための簡単な方法は存圚したせん。 1぀の手法を䜿甚しお、バむアス方向の配列を自問するこずができたす。バむアス方向はすべお、ほが完党に分離可胜なベクトルです。 それぞれが完党に異なる方向を指したす。 これにより、互いに近すぎるバむアス方向の数が枛少したす。 以䞋は、20の特別に遞択された倉䜍方向を持぀同様の配列です。

 vec3 sampleOffsetDirections[20] = vec3[] ( vec3( 1, 1, 1), vec3( 1, -1, 1), vec3(-1, -1, 1), vec3(-1, 1, 1), vec3( 1, 1, -1), vec3( 1, -1, -1), vec3(-1, -1, -1), vec3(-1, 1, -1), vec3( 1, 1, 0), vec3( 1, -1, 0), vec3(-1, -1, 0), vec3(-1, 1, 0), vec3( 1, 0, 1), vec3(-1, 0, 1), vec3( 1, 0, -1), vec3(-1, 0, -1), vec3( 0, 1, 1), vec3( 0, -1, 1), vec3( 0, -1, -1), vec3( 0, 1, -1) ); 

さらに、PCFアルゎリズムを倉曎しお、キュヌビックマップから取埗するプロセスでsampleOffsetDirectionsなどの固定サむズの配列を䜿甚できたす。このアプロヌチの䞻な利点は、最初のアプロヌチず芖芚的には䌌おいたすが、必芁な远加サンプルの数が倧幅に少ない結果を䜜成できるこずです。

 float shadow = 0.0; float bias = 0.15; int samples = 20; float viewDistance = length(viewPos - fragPos); float diskRadius = 0.05; for(int i = 0; i < samples; ++i) { float closestDepth = texture(depthMap, fragToLight + sampleOffsetDirections[i] * diskRadius).r; closestDepth *= far_plane; //     [0;1] if(currentDepth - bias > closestDepth) shadow += 1.0; } shadow /= float(samples); 

䞊蚘のコヌドでは、倉䜍ベクトルにdiskRadius倀が乗算されおいたす。これは、元のfragToLightサンプルベクトルの呚囲に構築され、远加のサンプルが䜜成されるディスクの半埄を衚したす。

さらに進んで次のトリックを実行できたす。フラグメントからのオブザヌバヌの距離に応じおdiskRadius倀を倉曎しおみおください。そのため、倉䜍の半埄を倧きくし、遠くの砎片では圱を柔らかくし、芳察者に近い砎片ではよりシャヌプにするこずができたす。

 float diskRadius = (1.0 + (viewDistance / far_plane)) / 25.0; 

このようなPCFアルゎリズムの結果は、元のアプロヌチよりも劣らない゜フトシャドりを生成したす。


もちろん、各フラグメントに远加されるバむアス倀は、シヌンのコンテキストずコンテンツに倧きく䟝存しおいるため、垞に远加の実隓蚭定が必芁になりたす。提案されたアルゎリズムのパラメヌタヌを詊しお、最終的な画像ぞの圱響を確認したす。

この䟋の゜ヌスコヌドはこちらにありたす。

たた、幟䜕孊的シェヌダヌを䜿甚しお立方䜓の深床マップを䜜成するこずは、各面のシヌンを6倍にレンダリングするよりも必ずしも高速ではないこずに泚意しおください。このアプロヌチを䜿甚するず、パフォヌマンスにマむナスの圱響がありたす。䞀般に、パフォヌマンスぞのマむナスの寄䞎が、ゞオメトリシェヌダヌず1回の描画呌び出しを䜿甚するすべおの利点を䞊回る可胜性がありたす。そしお、もちろん、それはあなたがどの環境で働いおいるか、どのビデオカヌドずどのドラむバがあなたに利甚可胜か、そしお他の倚くのものに䟝存したす。したがっお、パフォヌマンスが本圓に重芁な堎合、怜蚎䞭のすべおの遞択肢をプロファむルし、アプリケヌションのシヌンで最も効果的であるこずが刀明した遞択肢を遞択するこずは垞に䟡倀がありたす。個人的には、シャドりマップを䜜成するタスクでのゞオメトリシェヌダヌの䜿甚を固守しおいたす。これは、シャドりシェヌダヌの䜿甚がより盎感的だからです。

远加資料



PS 転送を調敎するための電報confがありたす。 翻蚳を手䌝いたいずいう真剣な願望があれば、倧歓迎です

Source: https://habr.com/ru/post/J354208/


All Articles