OpenGLを孊びたす。 レッスン5.3-シャドりマップ


圱には光がありたせん。 光源からの光線が他のオブゞェクトに吞収されるため、オブゞェクトに圓たらない堎合、最初のオブゞェクトは圱の䞭にありたす。 圱は画像にリアリズムを远加し、オブゞェクトの盞察的な䜍眮を確認できるようにしたす。 圌らのおかげで、シヌンは「深さ」を垯びおいたす。 次のシヌン画像を圱ありず圱なしで比范したす。


with_shadows_and_without


ご芧のように、圱により、オブゞェクトが互いにどのように配眮されおいるかがより明確になりたす。 圱のおかげで、立方䜓の1぀が空䞭に芋えたす。


特に完党な圱のリアルタむムアルゎリズムがただ発明されおいないため、圱の実装は困難です。 圱の蚈算を近䌌するためのいく぀かの良い方法がありたすが、それらはすべお考慮に入れなければならない独自の特性を持っおいたす。


方法の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. モデルクラスモデル

パヌト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 ミラヌ露光。

翻蚳者の泚意-将来、ロシア語版のいく぀かの甚語は、定評のある英語版ず重耇したす。゚ラヌがなく、わからない-元の甚語ず䞀緒に芚えおおいおください。



シャドりカヌド


シャドりマップの基瀎ずなるアむデアは非垞に単玔です。光源の芖点からシヌンを描画したす。 私たちが芋るものはすべお照らされ、残りは日陰になっおいたす。 床ず光源の間に倧きな立方䜓がある床を想像しおください。 光源は床の䞀郚ではなく立方䜓を「芋る」ため、床のこの郚分は芋えなくなりたす。


shadow_mapping_theory


䞊の図で、青い線は光源が芋える衚面を瀺しおいたす。 閉じた衚面は黒く塗られたす-陰圱が付けられたす。 光源から䞀番右の立方䜓の䞊郚に線光線を匕くず、最初に空䞭にぶら䞋がっおいる立方䜓を暪切りたす。 このため、右の立方䜓ずは異なり、ぶら䞋がっおいる立方䜓の巊面が照らされたす。


光線ず衚面の最初の亀差点を芋぀けお、他の亀差点ず比范したいず思いたす。 光線ずサヌフェスの亀点が最も近い亀点ず䞀臎しない堎合、圱の䞭にありたす。 ゜ヌスからの䜕千もの異なる光線に察しおこの操䜜を繰り返すこずは非垞に非効率的であり、ゲヌムのすべおのフレヌムで描画するのには適しおいたせん。


おそらく深さテストに぀いおはすでに読んでいたす ハブの翻蚳 、 元の 。 深床バッファの倀は、カメラの芖点からのフラグメントの深床であり、0〜1の倀によっお制限されたす。光源の芖点からシヌンをレンダリングし、テクスチャに深床倀を栌玍するずどうなりたすか したがっお、光源の芖点から芋るこずができる最小の深さを取埗したす。 さらに、深床倀は、光源に最も近いサヌフェスを瀺したす。 このテクスチャは、 深床マップたたはシャドりマップず呌ばれたす 。


shadow_mapping_theory_spaces


巊の写真は、立方䜓の䞋の衚面に圱を萜ずす指向性光源すべおの光線が平行を瀺しおいたす。 テクスチャに保存されおいる深床倀を䜿甚しお、゜ヌスに最も近いサヌフェスを芋぀け、その助けを借りお、圱の䞭にあるものを刀断したす。 光源に察応する行列を䜿甚したビュヌおよび投圱行列ずしお、シヌンレンダリングを䜿甚しお深床マップを䜜成したす。


平行光線を䜿甚した指向性ラむトには䜍眮がなく、「無限に遠く」ありたす。 ただし、シャドりマップを䜜成するには、光の線䞊の䜍眮からシヌンを描画する必芁がありたす。

ご泚意 トランスレヌタ-openGLは、遠すぎるz> 1たたは近すぎるz <0たたはz <-1の蚭定に応じおサヌフェスを切り取りたす。 カメラマトリックスは、シヌン䞊のオブゞェクトのz座暙がこの間隔になるように遞択されたす。そうでない堎合、それらは衚瀺されたせん。 数孊的な芳点からは、䜍眮はありたせんが、実際には、カメラのポむントは、描画時に画面の䞭倮のマスキング的に近いポむントに衚瀺されるポむントず芋なすこずができたす


右偎の写真では、同じ光、立方䜓、芳察者が芋えたす。 ポむントPでサヌフェスのフラグメントを描画し、それが圱にあるかどうかを刀断する必芁がありたす。 これを行うには、 Pを光源T(P)の座暙空間に倉換したす。 点P光の芖点からは芋えないため、この䟋の座暙zは0.9たす。 ポむントx,の座暙に基づいお、深床マップを調べるず、光源に最も近いポむントが深床0.4のであるこずがわかりたす。この倀はポむントP倀よりも小さいため、ポむントPはシャドり内にありたす。


シャドり描画は2぀のパスで構成されたす。最初に深床マップを描画し、2番目のパスで通垞どおりワヌルドを描画したす。深床マップを䜿甚しお、衚面のどの断片がシャドりにあるかを刀断したす。 耇雑に思えるかもしれたせんが、すべおを段階的に進めるず、すべおが明確になりたす。


深床マップ


最初のパスでは、深床マップを生成したす。 深床マップは、光源の芳点からレンダリングされた深床倀を持぀テクスチャです。 次に、それを䜿甚しお圱を蚈算したす。 レンダリングされた結果をテクスチャに保存するには、ハブの framebuffer  translationが必芁です。


最初に、深床マップを描画するためのフレヌムバッファヌを䜜成したす。


 unsigned int depthMapFBO; glGenFramebuffers(1, &depthMapFBO); 

フレヌムバッファの深床バッファずしお䜿甚する2Dテクスチャを䜜成した埌。


 const unsigned int SHADOW_WIDTH = 1024, SHADOW_HEIGHT = 1024; unsigned int depthMap; glGenTextures(1, &depthMap); glBindTexture(GL_TEXTURE_2D, depthMap); glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT, SHADOW_WIDTH, SHADOW_HEIGHT, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); 

深床マップの䜜成は耇雑に芋えたせん。 深さの倀にのみ関心があるため r,g,b,a色には関心がない、テクスチャ圢匏GL_DEPTH_COMPONENTを指定したす。 テクスチャの高さず幅を1024 * 1024に蚭定したす-これは深床マップのサむズになりたす。


次に、深床バッファずしお深床テクスチャをフレヌムバッファにアタッチしたす。


 glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_TEXTURE_2D, depthMap, 0); glDrawBuffer(GL_NONE); glReadBuffer(GL_NONE); glBindFramebuffer(GL_FRAMEBUFFER, 0); 

光源の芖点からシヌンを描画する堎合、深さにのみ関心があり、カラヌバッファは必芁ありたせん。 フレヌムバッファヌはカラヌバッファヌがないず䞍完党になるため、色をレンダリングしないこずを明瀺的に瀺す必芁がありたす。 これを行うために、 glDrawBufferずglReadBufferにglDrawBufferを蚭定したす。


これで、テクスチャに深床倀を曞き蟌むフレヌムバッファが適切に構成され、深床マップをレンダリングできたす。 䞡方のレンダリングパスの完党な実装は次のようになりたす。


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

このコヌドには詳现は含たれおいたせんが、シャドりマップの䞀般的な抂念を瀺しおいたす。 glViewport呌び出しに泚意しおglViewport 。通垞、深床マップのサむズは、画面たたは最終画像がレンダリングされるテクスチャのサむズずは異なりたす。 倉曎を忘れるず、画面サむズの正方圢のピヌスのみが深床テクスチャに曎新されるか、テクスチャが小さい堎合情報の䞀郚が衚瀺されたせん゚ッゞの倖偎に残りたす。


光源スペヌス


䞊蚘のコヌドで䞍明なのは、 ConfigureShaderAndMatrices()関数が䜕をするかだけです。
2番目のパスでは、通垞どおりに動䜜したす。カメラに適切なビュヌず投圱行列を蚭定し、オブゞェクトにモデル行列を蚭定したす。 ただし、最初のパスでは、投圱ずビュヌに他のマトリックスを䜿甚したす。光源の芖点からシヌンを描画するためです。


指向性光源をシミュレヌトするため、すべおの光線は平行です。 このため、光源に正投圱図法を䜿甚したす過床の歪みはありたせん。


 float near_plane = 1.0f, far_plane = 7.5f; glm::mat4 lightProjection = glm::ortho(-10.0f, 10.0f, -10.0f, 10.0f, near_plane, far_plane); 

これは、この蚘事のデモで䜿甚される正投圱図の䟋です。 射圱行列はオブゞェクトを芋る距離を決定するので぀たり、ビデオカヌドはオブゞェクトをあたりにも近くたたは遠ざけるこずはありたせん、クリッピング領域のサむズには、深床マップに衚瀺するすべおのオブゞェクトが含たれおいるこずを確認する必芁がありたす。


すべおのオブゞェクトが光源の芖点から芋えるビュヌ行列を䜜成するには、人気のない関数glm::lookAtを䜿甚したす。光源はシヌンの䞭心を「芋る」ようになりたした。


 glm::mat4 lightView = glm::lookAt(glm::vec3(-2.0f, 4.0f, -1.0f), glm::vec3( 0.0f, 0.0f, 0.0f), glm::vec3( 0.0f, 1.0f, 0.0f)); 

泚車線-最初のベクトルはカメラの䜍眮、2番目は芋おいる堎所、3番目は芋䞊げる方向です


これら2぀の行列の組み合わせにより、䞖界の座暙から光源が䞖界を「芋る」座暙ぞの倉換行列が埗られたす。 これは、デプスマップをレンダリングするために必芁なものです。


 glm::mat4 lightSpaceMatrix = lightProjection * lightView; 

lightSpaceMatrixマトリックスは、䞊蚘でTずしお指定したものずたったく同じですT このマトリックスを䜿甚するず、埓来のカメラのビュヌおよび投圱マトリックスの代わりに䜿甚しお、通垞どおりシヌンを平均化できたす。 ただし、深さの倀にのみ関心があり、未䜿甚の色に぀いお䞍必芁な蚈算を行わずにパフォヌマンスを維持したいず考えおいたす。 したがっお、深床マップのみを描画する最も単玔なシェヌダヌを䜜成したす。


デプスマップレンダリング


光源のシヌンをレンダリングするずき、頂点の座暙のみが必芁で、それ以䞊は必芁ありたせん。 このような単玔なシェヌダヌ simpleDepthShaderず呌びたしょうの堎合、頂点シェヌダヌを蚘述したす。


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

このシェヌダヌは、モデルごずに、 lightSpaceMatrixを䜿甚しおモデルの頂点を光源の空間にlightSpaceMatrixたす。
シャドりのフレヌムバッファヌにはカラヌバッファヌがないため、フラグメントシェヌダヌは蚈算を必芁ずせず、空のたたにできたす。


 #version 330 core void main() { // gl_FragDepth = gl_FragCoord.z; } 

空のフラグメントシェヌダヌは䜕も実行せず、シェヌダヌの最埌に曎新された深床バッファヌを取埗したす。 このコヌド行のコメントを倖すこずができたすが、実際には深さはずにかく蚈算されたす。


深床バッファに描画するず、次のコヌドになりたす。


 simpleDepthShader.use(); glUniformMatrix4fv(lightSpaceMatrixLocation, 1, GL_FALSE, glm::value_ptr(lightSpaceMatrix)); glViewport(0, 0, SHADOW_WIDTH, SHADOW_HEIGHT); glBindFramebuffer(GL_FRAMEBUFFER, depthMapFBO); glClear(GL_DEPTH_BUFFER_BIT); RenderScene(simpleDepthShader); glBindFramebuffer(GL_FRAMEBUFFER, 0); 

RenderScene関数はシェヌダヌを受け取り、描画に必芁な関数を呌び出し、必芁に応じおモデル行列をむンストヌルしたす。


その結果、光の点で最も近いフラグメントの深さを含む各ピクセルに察しお、深さバッファがいっぱいになりたす。 このテクスチャを画面サむズの長方圢に投圱しお衚瀺できたす。 フレヌムバッファを䜿甚した䟋の埌凊理の堎合ず同様です。 元の ハブでの倉換 。


shadow_mapping_depth_map


長方圢に深床マップを描画するには、次のシェヌダヌを䜿甚したす。


 #version 330 core out vec4 FragColor; in vec2 TexCoords; uniform sampler2D depthMap; void main() { float depthValue = texture(depthMap, TexCoords).r; FragColor = vec4(vec3(depthValue), 1.0); } 

シャドりをレンダリングするずきに、投圱マトリックスが盎亀ではなく遠近法になる堎合、深床は非線圢に倉化したす。 蚘事の最埌で、この違いに぀いお説明したす。


シヌンを深床マップにレンダリングするための゜ヌスコヌドは、 ここにありたす 。


圱絵


適切に䜜成された深床マップを䜿甚しお、圱を描画できたす。 フラグメントシェヌダヌを䜿甚しおフラグメントがシャドりにあるかどうかを確認したすが、頂点シェヌダヌで光源のスペヌスに倉換したす。


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

ここから新しいものは、頂点シェヌダヌの出力にある远加のベクトルFragPosLightSpaceです。 深さを描画するために最初のパスで䜿甚されたのず同じlightSpaceMatrixを受け入れ、その助けを借りお、ベクトルを光源の空間に倉換したす。 頂点シェヌダヌは、ワヌルドのスペヌス vs_out.FragPos ず光源のスペヌス vs_out.FragPos の䞡方の頂点の座暙を同時にフラグメントに転送したす。


Blinn-Fongラむトモデルに基づいたフラグメントシェヌダヌを䜿甚したす。 フラグメントシェヌダヌでは、 shadow倀を芋぀けたす。フラグメントがシャドり内にある堎合は1.0になり、ラむトがかかっおいる堎合は0.0になりたす。 結果ずしお生じるdiffuseおよびspecular色拡散および鏡面反射照明は(1.0 - shadow)で乗算されたす。 間接照明のために圱が完党に黒になるこずはめったにないため、圱に関係なく背景の照明が存圚したす。


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

これは、ほずんどの堎合、ラむティングの䟋で䜿甚したシェヌダヌのコピヌです ハブ䞊の倉換 、 高床なラむティング 。


ここではシャドり蚈算のみが远加されおいたす。 䜜業の倧郚分は、 ShadowCalculation関数によっお行われたす。 フラグメントシェヌダヌの最埌で、光の拡散反射ず鏡面反射からの寄䞎に1.0-シャドりを掛けたす-぀たり、フラグメントがシェヌディングされおいない量に䟝存したす。 さらに、この入力シェヌダヌは、光源のスペヌス内のフラグメントの䜍眮ず、深さの倀を持぀テクスチャヌ最初のパスでレンダリングされたをさらに受け入れたす。


フラグメントがシャドり内にあるかどうかを確認するために、光源の空間の䜍眮を正芏化された座暙に移動したす。 頂点シェヌダヌで頂点の䜍眮をgl_Positionに戻すず、openGLはx,y,zをw自動的に分割し、パヌスペクティブが正しく機胜するようにしたす。 FragPosLightSpaceずしお枡されないため、この分割を自分で行う必芁がありたす。


 float ShadowCalculation(vec4 fragPosLightSpace) { // perform perspective divide vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; [...] } 

光源の空間内のフラグメントの䜍眮を取埗したす。


正投圱を䜿甚する堎合、座暙w=1.0は倉化せず、wによる陀算は䞍芁になりたす。 ただし、透芖投圱を䜿甚する堎合は分割が必芁であり、コヌドは䞡方の堎合に正しく機胜したす。

レヌンに泚意しおください-wによる分割は、フラグメントシェヌダヌで行う必芁がありたす。 この蚘事の最初の図は、テクスチャ座暙の線圢補間ず透芖補間の違いを瀺しおいたす。


テクスチャ座暙は[0,1]の範囲にあり、レンダリング時の可芖フラグメントの座暙は[-1,1]の倀を取りたす。 それらを間隔[0,1]に移動したす。


 projCoords = projCoords * 0.5 + 0.5; 

これらの座暙に基づいお、テクスチャの深床倀を確認できたす。これは、光源に最も近いオブゞェクトの深床になりたす。


 float closestDepth = texture(shadowMap, projCoords.xy).r; 

珟圚のフラグメントの深さを取埗するには、光源の空間でそのz座暙を取埗したす。


 float currentDepth = projCoords.z; 

その埌、 currentDepthずclosestDepth単玔な比范により、フラグメントが最も近いか、圱にあるかを刀断できたす。


 float shadow = currentDepth > closestDepth ? 1.0 : 0.0; 

ShadowCalculation関数のすべおのコヌドは次のずおりです。


 float ShadowCalculation(vec4 fragPosLightSpace) { // perform perspective divide vec3 projCoords = fragPosLightSpace.xyz / fragPosLightSpace.w; // transform to [0,1] range projCoords = projCoords * 0.5 + 0.5; // get closest depth value from light's perspective (using [0,1] range fragPosLight as coords) float closestDepth = texture(shadowMap, projCoords.xy).r; // get depth of current fragment from light's perspective float currentDepth = projCoords.z; // check whether current frag pos is in shadow float shadow = currentDepth > closestDepth ? 1.0 : 0.0; return shadow; } 

2番目のレンダヌパスでこのシェヌダヌをテクスチャず通垞のビュヌおよび投圱マトリックスず共に䜿甚するず、結果は図のようになりたす。


shadow_mapping_shadows


すべおを正しく行った堎合、床ず立方䜓に圱が芋えたすただし、いく぀かのアヌティファクトがありたす。 ゜ヌスコヌドデモ 。


シャドりマップの改善


シャドりマップの䜜業を完了できたしたが、画像にいく぀かのアヌティファクトを芋るこずができたす。 埌続のテキストは、それらの修正に専念したす。


モアレパタヌン


明らかに、䞋の画像で䜕かが間違っおいたす。 拡倧された画像は、 モアレパタヌンに䌌おいたす 。


shadow_mapping_acne


床党䜓は、はっきりず芋える亀互の黒いストラむプで芆われおいたす。 この効果は、単䞀の画像で説明できたす。


shadow_mapping_acne_diagram


レヌンに泚意しおください-䜜者は「 シャドりニキビ 」ずいう甚語を䜿甚しお効果を説明したす。確立された翻蚳は芋぀かりたせんでした。


シャドりマップの解像床は限られおいるため䞊​​蚘の䟋では、テクスチャ1024*1024を䜿甚したした、最終画像のいく぀かのピクセルは深床マップから同じ倀を取埗できたす。 䞊の図は、各傟斜郚分巊䞋から右ぞがデプスマップの1぀のテクセルを衚す床を瀺しおいたす。 テクセル-テクスチャピクセル


通垞、これは正垞ですが、䞊の䟋のように、光が衚面に察しお斜めに萜ちるず問題になる可胜性がありたす。 テクスチャから深床を受け取る䞀郚のフラグメントは、このフラグメントのフロアの実際の深床に察応しない倀を取埗したす。 このため、フラグメントの䞀郚は圱付きず芋なされ、ストラむプが衚瀺されたす。


この問題は小さなハックで解決できたす。぀たり、すべおのフラグメントが衚面より䞊になるように、深さの倀を少しだけシフトシャドりバむアスしたす。


shadow_mapping_acne_bias


レヌンに泚意しおください。-私は絵が正しく描かれおいないず匷く感じおいたす。フラグメントの陰圱を防ぐために、深床マップからの倀を蚘述するゞグザグ線は衚面の䞋にあるべきです


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

シヌン党䜓の0.005のシフトで問題は解決したすが、非垞に小さな角床で光が圓たる䞀郚のサヌフェスにはただ圱の瞞がありたす。 より深刻なアプロヌチは、光が衚面に圓たる角床に応じおシフトを倉曎するこずです。 スカラヌ積を䜿甚したす。


 float bias = max(0.05 * (1.0 - dot(normal, lightDir)), 0.005); 

したがっお、光線にほが垂盎な床タむプの衚面のシフトは非垞に小さくなりたす。 法線ず光の方向の間の角床が倧きいほど、シフトが倧きくなりたす。 次の図は、同じシヌンを瀺しおいたすが、シフトを䜿甚しおいたす。明らかに良く芋えたす。


shadow_mapping_with_bias


シフトごずに正しい倀を遞択するには、シヌンごずに異なる可胜性があるため、遞択する必芁がありたすが、これは通垞、アヌティファクトが消えるたでシフトを増やすだけで行われたす。


ピヌタヌパンの効果


ピットパン


深床のシフトを䜿甚するこずの欠点は、オブゞェクトの実際の深床にシフトを適甚するこずです。 その結果、このシフトは十分に倧きくなる可胜性があるため、䞋の図のように、オブゞェクトず圱の間に顕著な距離が衚瀺されたす誇匵しお倧きなシフトを䌎う。


shadow_mapping_peter_panning


圱が被写䜓から少し逃げるため、これはピヌタヌパン効果ず呌ばれたす。 ほずんどの問題を解決するために少しのトリックを䜿甚できたす。深床マップを描画するずきに、正面向きのポリゎンのクリッピングを䜿甚したす。 オリゞナル のHabréで顔を数えるこずに぀いお読んでください。
デフォルトでは、openGLは逆向きのポリゎンをカットしたす。 openGLを切り替えお反察のこずを行うこずができたす。


レンダリングの最初のパスでは、深さの倀のみが必芁であり、前面からたたは背面からどの深さを取るかは関係ありたせん。 オブゞェクト内に圱があるかどうかは関係ないので、䞍正確な結果に気付くこずはありたせん-それらはただ芋えたせん。


shadow_mapping_culling


Peter Panの効果を陀去するために、最初のパスでフロント゚ッゞを切り取りたした。 GL_CULL_FACE有効にする必芁があるこずに泚意しおください。


 glCullFace(GL_FRONT); RenderSceneToDepthMap(); glCullFace(GL_BACK); //     

これにより、Peter Pen゚フェクトの問題が解決されたすが、これはすべおの面にサヌフェスがあるオブゞェクトに察しおのみです。 この䟋では、これはキュヌブに察しおはうたく機胜したすが、前面のポリゎンをクリップするず床が完党に削陀されるため、床に察しおは機胜したせん。 この方法を䜿甚する堎合は、理にかなっおいる堎合にのみ、正面向きのポリゎンのクリッピングを䜿甚しおください。


車線に泚意しおください-特にこの䟋では、䞋にオブゞェクトがないため、床を完党に切断しおも問題はありたせん。䞋に圱があるかどうかは関係ありたせん。


オブゞェクトがシェヌディングされた衚面に近すぎるず、結果が正しく衚瀺されない堎合がありたす。 これが理にかなっおいるオブゞェクトに察しおのみ、前面のクリッピングを䜿甚したす。 ただし、適切な倀をシフトに䜿甚するこずにより、ピヌタヌペン効果を完党に回避できたす。


あなたが奜むかもしれないし奜たないかもしれないもう䞀぀の目に芋える欠点は、光源の範囲の倖偎のいく぀かの衚面が、理論的に光が圓たったずしおも陰圱で描かれるこずです。 これは、光源の参照フレヌム内のリモヌトポむントの堎合、座暙が1.0より倧きくなり、テクスチャ座暙が0.0から1.0にのみ倉化するためです。 衚面が光源から遠すぎる堎合、深床マップには倀がありたせん。


shadow_mapping_outside_frustum


䞊の画像では、光の領域を想像するこずができたす-それ以倖はすべお日陰です。 照らされた領域は、深床マップがどのように床に投圱されるかを瀺したす。 この動䜜の理由は、以前にGL_REPEATモヌドを深床テクスチャに蚭定したGL_REPEATです。


このようなフラグメントの深さ1.0を返したいず思いたす-これは、可芖オブゞェクトは1を超える深さを持぀こずができないためシャドりに決しお含たれないこずを意味したす。 "GL_CLAMP_TO_BORDER"


 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); float borderColor[] = { 1.0f, 1.0f, 1.0f, 1.0f }; glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor); 

, [0,1], 1.0, shadow 0.0. :


shadow_mapping_clamp_edge


, . , . , .


z , 1.0, GL_CLAMP_TO_BORDER . ( 1.0) ( 1.0) — .


— , z .


 float ShadowCalculation(vec4 fragPosLightSpace) { [...] if(projCoords.z > 1.0) shadow = 0.0; return shadow; } 

1.0 .


shadow_mapping_over_sampling_fixed


, , , . , , .


PCF


Percentage-closer filtering


, , . , .


shadow_mapping_zoom.png


, . .


, .


() — PCF (Percentage-closer filtering), , . , — . , . .


PCF — :


 float shadow = 0.0; vec2 texelSize = 1.0 / textureSize(shadowMap, 0); for(int x = -1; x <= 1; ++x) { for(int y = -1; y <= 1; ++y) { float pcfDepth = texture(shadowMap, projCoords.xy + vec2(x, y) * texelSize).r; shadow += currentDepth - bias > pcfDepth ? 1.0 : 0.0; } } shadow /= 9.0; 

textureSize — , . , , . , . 9 (x,y) , , .


/ texelSize , . PCF :


shadow_mapping_soft_shadows


. , , (, 9 ). , PCF .



, PCF, . , .


vs


. , , . . .


shadow_mapping_projection
( ). , — .


— . , , . , , . : , .


 #version 330 core out vec4 FragColor; in vec2 TexCoords; uniform sampler2D depthMap; uniform float near_plane; uniform float far_plane; float LinearizeDepth(float depth) { float z = depth * 2.0 - 1.0; // Back to NDC return (2.0 * near_plane * far_plane) / (far_plane + near_plane - z * (far_plane - near_plane)); } void main() { float depthValue = texture(depthMap, TexCoords).r; FragColor = vec4(vec3(LinearizeDepth(depthValue) / far_plane), 1.0); // perspective // FragColor = vec4(vec3(depthValue), 1.0); // orthographic } 

このコヌドは、正投圱で芋たものず同様の深床倀を瀺しおいたす。これはデバッグ専甚であるこずに泚意しおください。非線圢倉換は単調であり、2぀の深床倀を比范するず、線圢か非線圢かに関係なく同じ結果が埗られたす。


远加のリ゜ヌス



オリゞナル蚘事

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


All Articles