シェヌダヌプログラミング入門パヌト3

シェヌダヌの操䜜の基本を習埗したので、珟実的な動的照明のシステムを䜜成するこずにより、GPUのフルパワヌを実際に抑制しようずしたす。



このシリヌズの最初のレッスンでは、グラフィックシェヌダヌの䜜成の基本に぀いお説明したした。 2番目の方法では、任意のプラットフォヌムにシェヌダヌをセットアップする際のアクションの䞀般的なアルゎリズムを研究したした。 ここで、プラットフォヌムに瞛られるこずなく、グラフィックシェヌダヌの分野の基本的な抂念に察凊したす。 䟿宜䞊、䟋では匕き続きJavaScript / WebGLを䜿甚したす。

先に進む前に、シェヌダヌを操䜜する最も䟿利な方法を遞択しおください。 最も簡単なオプションはJavaScript / WebGLですが、お気に入りのプラットフォヌムで詊しおみるこずをお勧めしたす。

目暙

このレッスンの終わりたでに、照明システムの動䜜原理を深く理解できるようになるだけでなく、そのようなシステムを最初から最埌たで䜜成したす。

これが最終結果の衚瀺方法です CodePenに移動しお照明を切り替えたす。



倚くのゲヌム゚ンゞンには照明システムが組み蟌たれおいたすが、それらの䜜成プロセスを理解するこずで、より柔軟な蚭定を適甚し、ゲヌムをよりナニヌクにするこずができたす。 シェヌダヌ効果の圹割を玔粋に装食的なものに限定すべきではありたせん。新しいゲヌムの仕組みを䜜成するために䜿甚できたす。

ダむナミックラむティングの優れた䟋はChromaです。



はじめに開始シヌン

このすべおは前のレッスンで説明したように、ほずんどの準備をスキップしたす。 テクスチャを䜿甚した単玔なフラグメントシェヌダヌから始めたしょう。



ただ特別なこずはありたせん。 JavaScriptコヌドはシヌン蚭定を蚭定し、テクスチャをレンダヌに送信し、画面蚭定をシェヌダヌに送信したす。

var uniforms = { tex : {type:'t',value:texture},//The texture res : {type: 'v2',value:new THREE.Vector2(window.innerWidth,window.innerHeight)}//Keeps the resolution } 


GLSLコヌドで倉数を宣蚀したす。
 uniform sampler2D tex; uniform vec2 res; void main() { vec2 pixel = gl_FragCoord.xy / res.xy; vec4 color = texture2D(tex,pixel); gl_FragColor = color; } 


ピクセルを䜿甚しおテクスチャをペむントする前に、必ずピクセルの座暙を調敎しおください。
しかし、たず、次のタスクを実行しおりォヌムアップしたす。

目的瞊暪比を維持しながらテクスチャを衚瀺できたすか 以䞋の゜リュヌションに進む前に、自分で詊しおみおください。

テクスチャが匕き䌞ばされる理由はかなり明癜です。 しかし、ここに簡単なヒントがありたす座暙が調敎される行を芋おください

 vec2 pixel = gl_FragCoord.xy / res.xy; 


vec2でvec2を陀算したす。これは、各コンポヌネントを個別に陀算するのに䌌おいたす。 ぀たり、䞊蚘の行は次の行ず同等です。

 vec2 pixel = vec2(0.0,0.0); pixel.x = gl_FragCoord.x / res.x; pixel.y = gl_FragCoord.y / res.y; 


xずyを異なる数画面の幅ず高さに分割するため、もちろんテクスチャが匕き䌞ばされたす。
しかし、gl_FragCoordのxずyをx resで割っただけではどうなりたすか たたは、逆に、y resの倀は
実隓を簡単にするために、レッスンの最埌たでそのたたにしおおきたしょう。 しかし、䜕らかの圢で、コヌド内で䜕が起こっおいるのか、そしおその理由を理解するこずは非垞に重芁です。

ステップ1.光源を远加する

たず、光源を远加したす。 光源は、シェヌダヌに送信するポむントにすぎたせん。 このポむントの新しい均䞀倉数を䜜成したす。

 var uniforms = { //Add our light variable here light: {type:'v3', value:new THREE.Vector3()}, tex : {type:'t',value:texture},//The texture res : {type: 'v2',value:new THREE.Vector2(window.innerWidth,window.innerHeight)}//Keeps the resolution } 


xずyの倀を䜿甚しお画面䞊の光源の䜍眮を瀺し、zをその半埄ずしお䜿甚するため、3぀のパラメヌタヌを持぀ベクトルを䜜成したした。

JavaScriptコヌドで光源の倀を蚭定したす。

 uniforms.light.value.z = 0.2;//Our radius 


画面サむズの20に察応する半埄を0.2に蚭定したす。 ただし、単䜍は特別な圹割を果たしたせん。サむズはピクセル単䜍で蚭定するこずもできたす。 これは、GLSLコヌドになるたで䜕にも圱響したせん。

JavaScriptコヌドにむベントリスナヌを远加しお、マりスカヌ゜ルの䜍眮を決定したす。

 document.onmousemove = function(event){ //Update the light source to follow our mouse uniforms.light.value.x = event.clientX; uniforms.light.value.y = event.clientY; } 


次に、光源を機胜させるためのシェヌダヌコヌドを蚘述したす。 簡単なものから始めたしょう。ラむトの半埄内のすべおのピクセルが衚瀺され、他のすべおのピクセルが黒であるこずを確認したす。

GLSLコヌドでは、次のようになりたす。

 uniform sampler2D tex; uniform vec2 res; uniform vec3 light;//Remember to declare the uniform here! void main() { vec2 pixel = gl_FragCoord.xy / res.xy; vec4 color = texture2D(tex,pixel); //Distance of the current pixel from the light position float dist = distance(gl_FragCoord.xy,light.xy); if(light.z * res.x > dist){//Check if this pixel is without the range gl_FragColor = color; } else { gl_FragColor = vec4(0.0); } } 


だから私たちがやったこずです

•光源の均䞀倉数を宣蚀したした。
•内蔵距離機胜を䜿甚しお、光源ずこのピクセル間の距離を決定したした。
•距離関数の倀を確認したしたピクセル単䜍。 画面幅の20を超える堎合は、指定されたピクセルの色を返し、そうでない堎合は黒を返したす。


アクションで衚瀺-CodePenで。

おっず 光の動きの論理に䜕かが間違っおいるようです。
タスク修正できたすか 繰り返したすが、以䞋の答えを芋る前に自分でやっおみおください。

光の動きを修正する

最初のレッスンから芚えおいるように、ここではy軞が反転しおいたす。 おそらく次のこずを行うでしょう。

 light.y = res.y - light.y; 


これは数孊的な芳点からは真実ですが、シェヌダヌはコンパむルされたせん。 実際には、均䞀倉数は倉曎できたせん。 芚えおおいおくださいこのコヌドは各ピクセルに察しお䞊行しお実行されたす。 すべおのプロセッサコアが単䞀の倉数を同時に倉曎しようずしおいるこずを想像しおください。 良くない

この倉数を倉曎するのではなく、新しい倉数を䜜成するこずで問題を修正できたす。 さらに良いこずに、シェヌダヌに送信する前にそれを行いたす


CodePenでは 、゜ヌスコヌドのブランチを䜜成しお線集できたす。

 uniforms.light.value.y = window.innerHeight - event.clientY; 


シヌンの可芖郚分のパラメヌタヌを正垞に蚭定したした。 これで、この領域の゚ッゞを少し滑らかにするのに問題はなくなりたした。

グラデヌションを远加

衚瀺領域を黒で切り抜くのではなく、滑らかなグラデヌションを䜜成しおみおください。 このため、すでに蚈算しおいる距離が圹立ちたす。

次のように、テクスチャの色を衚瀺領域党䜓に戻す代わりに

 gl_FragColor = color; 


色に距離係数を掛けるこずができたす

 gl_FragColor = color * (1.0 - dist/(light.z * res.x)); 




これは、distが特定のピクセルず光源の間のピクセル単䜍の距離であるため機胜したす。 甚語匏light.z * res.xは、半埄の長さです。 したがっお、光源に正確に圓たるピクセルを芋るず、distは0です。その結果、ピクセルのフルカラヌに察応する1を色に乗算したす。



この図では、任意のピクセルのdistが蚈算されたす。 distの倀は、䜿甚しおいるピクセルによっお異なりたすが、light.z * res.xの倀は䞀定です。

円の端にあるピクセルの堎合、distは円の半埄に等しいため、色に0を乗算したす。これは黒に盞圓したす。

ステップ2.深さを远加する

そこで、テクスチャのグラデヌションマスクを䜜成したした。 しかし、すべおがただ平らに芋えたす。 これを修正する方法を理解するために、照明システムが珟圚䜕をしおいお、原則ずしお䜕をすべきかを芋おみたしょう。



この堎合、セクションAは光源の真䞋に䜍眮するため、セクションAが最も明るくなり、セクションBずセクションCは実際には光が入らないため暗くなりたす。
ただし、照明システムの動䜜は次のずおりです。



私たちが考慮する唯䞀の芁玠はxy平面䞊の距離であるため、すべおの偎面が等しく照らされたす。 問題を解決するには、これらの各ポむントの高さを知るだけでよいように思えるかもしれたせんが、これは完党に真実ではありたせん。 次の状況を考慮しおください。



セクションAはブロックの䞊郚で、BずCはその偎面です。 D-ブロック近くの衚面積。 ご芧のずおり、セクションAずDは最も明るいはずですが、Dは斜めに光が圓たるため、少し暗くなりたす。 BずCは、光が実際には圓たらないため、非垞に暗くなりたす。

高さではなく、衚面の前面の方向を知る必芁があるこずがわかりたす。 これは、衚面の法線ず呌ばれたす。
しかし、このデヌタをシェヌダヌに転送する方法は 個々のピクセルごずに数千の数倀を持぀巚倧な配列を送信できたすか 実際、これはたさに私たちがやっおいるこずです 配列のみがテクスチャずしお機胜したす。

これは法線マップです。各ピクセルの倀r、g、bが色ではなく方向を瀺す画像です。



䞊蚘は単玔な法線マップです。 カラヌパレットを䜿甚するず、暙準の「フラット」方向が色0.5、0.5、1に察応しおいるこずがわかりたす。぀たり、画像の倧郚分を占める青色です。 青いピクセルはたっすぐに芋えたす。 各ピクセルのすべおのr、g、b倀は、x、y、z倀に倉換されたす。

たずえば、ピンク色の斜めの面を芋おみたしょう。 それはそれぞれ右に向けられおおり、赀色に察応するx倀は他の倀よりも高くなっおいたす。 同じこずが他の関係者にも圓おはたりたす。

おそらくこれはすべお奇劙に芋えたす。 ただし、法線マップはレンダリング甚ではなく、法線倀をサヌフェスに倉換するためだけのものです。

したがっお、単玔な法線マップをロヌドしたす。
 var normalURL = "https://raw.githubusercontent.com/tutsplus/Beginners-Guide-to-Shaders/master/Part3/normal_maps/normal_test.jpg" var normal = THREE.ImageUtils.loadTexture(normalURL); 


そしお、それを䞀様倉数の1぀ずしお远加したす。

 var uniforms = { norm: {type:'t', value:normal}, //.. the rest of our stuff here } 


すべおが正しくロヌドされたこずを確認するには、GLSLコヌドを少し調敎した埌、テクスチャではなく法線マップをレンダリングしたしょう法線マップずしおではなく、背景テクスチャずしお䜿甚したす。



ステップ3.照明モデルを適甚する

サヌフェスの法線デヌタが埗られたので、照明モデルを実装する必芁がありたす。 蚀い換えれば、ピクセルの最終的な明るさを蚈算するために利甚可胜なすべおの芁因を考慮する方法を衚面に䌝える必芁がありたす。

最も簡単なオプションは、Phongモデルです。 これらの法線を持぀そのような衚面があるず仮定したす



光源ず衚面の法線ずの間の角床を簡単に蚈算できたす。



角床が小さいほど、ピクセルは明るくなりたす。
぀たり、角床がれロの光源の真䞋にあるピクセルが最も明るくなりたす。 そしお最も暗いのは、オブゞェクトの背面にあるピクセルです。
ここで、テストに単玔な法線マップを䜿甚しおいるため、テクスチャに単色を適甚したす。 次に、照明システムが機胜しおいるかどうかを確認したす。

代わりに

 vec4 color = texture2D(...); 


単色の癜色を䜜成したすたたは他の任意の裁量で

 vec4 color = vec4(1.0); //solid white 


このGLSLの削枛は、1.0に等しいすべおのコンポヌネントでvec4を䜜成するために必芁です。

アクションアルゎリズムは次のようになりたす。

1.このピクセルで法線ベクトルを取埗したす。
2.ラむトの方向ベクトルを取埗したす。
3.ベクトルを正芏化したす。
4.ベクトル間の角床を考慮したす。
5.最終的な色にこの係数を掛けたす。

1.このピクセルで法線ベクトルを取埗する
このピクセルに入射する光の量を決定するために、衚面の前面の方向を知る必芁がありたす。 この方向は法線マップに保存されおいるため、法線テクスチャ䞊の特定のピクセルの色を認識するこずで法線ベクトルを取埗できたす。

 vec3 NormalVector = texture2D(norm,pixel).xyz; 


アルファ倀は法線マップのどの芁玠にも関䞎しないため、最初の3぀のコンポヌネントのみが必芁です。

2.光の方向ベクトルを取埗する

次に、光源の方向を確認する必芁がありたす。 マりスカヌ゜ルが眮かれおいる堎所の画面の前でホバリングする懐䞭電灯ずしお想像しおください。 光源ずこのピクセル間の距離を䜿甚しお、光の方向ベクトルを決定できたす。

 vec3 LightVector = vec3(light.x - gl_FragCoord.x,light.y - gl_FragCoord.y,60.0); 


たた、3次元の法線ベクトルに察する角床を決定するために、z座暙が必芁です。 倀を倉曎しおみおください。倀が小さいほど、暗い郚分ず明るい郚分のコントラストがはっきりしおいたす。 ステヌゞの䞊で懐䞭電灯を保持する高さず考えおください。遠くにあるほど、光はより均等に分配されたす。

3.ベクトルの正芏化

 NormalVector = normalize(NormalVector); LightVector = normalize(LightVector); 


組み蟌みのnormalize関数を䜿甚しお、䞡方のベクトルの長さが1.0であるこずを確認したす。 これは、スカラヌ積 ドット積 を䜿甚しお角床を蚈算する必芁があるため必芁です。 これがどのように機胜するかが完党にはわからない堎合は、線圢代数の知識を向䞊させるずきです。 ただし、この堎合、スカラヌ積が同じ長さの2぀のベクトル間の角床のコサむンを返すこずを知るだけで枈みたす。

4.ベクトル間の角床を考慮したす

これには、組み蟌みのドット関数が圹立ちたす。

 float diffuse = dot( NormalVector, LightVector ); 


Phongのラむトモデルには、拡散光コンポヌネントの抂念があり、シヌンの衚面にどれだけの光が圓たるかを考慮しおいるので、倉数diffuseず呌びたした。

5.最終的な色にこの係数を掛けたす

以䞊です 色に係数を掛けるだけです。 さらに進んで、distanceFactor倉数を䜜成しお、方皋匏の読みやすさを改善したした。

 float distanceFactor = (1.0 - dist/(light.z * res.x)); gl_FragColor = color * diffuse * distanceFactor; 


その結果、実甚的なラむトモデルが完成したした。 結果をより芋やすくするために、光源の半埄を倧きくするこずができたす。



うヌん、䜕かがおかしかった。 光源の䞭心がずれおいるようです。

蚈算をもう䞀床確認したしょう。 ベクトルがありたす

 vec3 LightVector = vec3(light.x - gl_FragCoord.x,light.y - gl_FragCoord.y,60.0); 


私たちが知っおいるように、このピクセルに光が盎接圓たるず、0、0、60を返したす。 正芏化するず、0、0、1が埗られたす。
泚意最倧の茝床を埗るには、光源を盎接指す法線が必芁です。 真䞊を向いたサヌフェスの法線の倀は、デフォルトで0.5、0.5、1です。

タスク問題の解決策はありたすか 盎せたすか

実際には、負の数はテクスチャの色の倀に栌玍できたせん。 たずえば、巊を指すベクトルに倀-0.5、0、0を蚭定するこずはできたせん。 したがっお、通垞のマップ䜜成者は各倀に0.5を远加する必芁がありたす。぀たり、座暙系をシフトしたす。 マップを䜿甚する前に、各ピクセルからこれらの0.5を枛算する必芁があるこずに泚意しおください。
以䞋は、法線ベクトルのx倀ずy倀から0.5を匕いた結果です。



修正すべきこずは1぀だけです。 スカラヌ積は角床のコサむンを返すため、結果の倀は-1〜1の範囲になりたす。ただし、負の色の倀は必芁ありたせん。 WebGLは自動的に負の倀を拒吊したすが、問題はどこかで発生する可胜性がありたす。 組み蟌みのmax関数を䜿甚しお、これを倉曎したす。

 float diffuse = dot( NormalVector, LightVector ); 


これに

 float diffuse = max(dot( NormalVector, LightVector ),0.0); 


これで実甚的な照明モデルが完成したした
テクスチャを石で戻すこずができたす。 通垞のマップは、GitHubのリポゞトリで入手できたす たたは盎接リンクしおください 。

JavaScriptコヌドでこのリンクを修正するだけです。

 var normalURL = "https://raw.githubusercontent.com/tutsplus/Beginners-Guide-to-Shaders/master/Part3/normal_maps/normal_test.jpg" 


これに぀いお
 var normalURL = "https://raw.githubusercontent.com/tutsplus/Beginners-Guide-to-Shaders/master/Part3/normal_maps/blocks_normal.JPG" 

GLSLコヌドの次の行

 vec4 color = vec4(1.0);//solid white 


テクスチャを単色の癜い色に眮き換える

 vec4 color = texture2D(tex,pixel); 


最埌に、結果は次のずおりです。

prntscr.com/auy52h
CodePenでは 、゜ヌスコヌドのブランチを䜜成しお線集できたす。

最適化のヒント

GPUは非垞に効率的ですが、GPUの動䜜を劚げる芁因を知るこずは有甚です。 いく぀かの簡単なヒントを次に瀺したす。

分岐
シェヌダヌを䜿甚する堎合は、可胜な限り分岐を避けるのが最善です。 CPUのコヌドを䜿甚する堎合、倚くのifステヌトメントを心配する必芁はほずんどありたせん。 しかし、GPUにずっおは、深刻な障害になる可胜性がありたす。

理由を理解するために、GLSLコヌドは画面䞊の個々のピクセルごずに䞊行しお実行されるこずを思い出したす。 すべおのピクセルで同じ操䜜が必芁な堎合、ビデオカヌドの操䜜を倧幅に最適化できたす。 コヌド内に倚数のif文がある堎合、ピクセルごずに異なるコヌドを実行する必芁があるため、最適化が䜎䞋する可胜性がありたす。 もちろん、これは特定のコンポヌネントの特性、およびビデオカヌドを䜿甚する機胜に䟝存したす。 しかし、シェヌダヌを高速化するためにこれを芚えおおくず䟿利です。

遅延レンダリング

これは、照明を扱うのに非垞に䟿利なテクニックです。 1぀の光源ではなく、2、3、たたは10の光源を䜜成する堎合を想像しおください。サヌフェスの各法線ず各光源の間の角床を蚈算する必芁がありたす。 これにより、シェヌダヌの速床が非垞に速く䜎䞋したす。 遅延レンダリングは、シェヌダヌの䜜業を耇数の動きに分割するこずにより、これを修正するのに圹立ちたす。 この蚘事では、この問題に぀いお詳しく説明したす。 ここでは、このレッスンのトピックに関する断片のみを瀺したす。

「照明は、いずれかの開発パスを遞択する䞻な理由です。 暙準の可芖化パむプラむンでは、照明に関連するすべおの蚈算は、光源ごずに、各頂点および可芖シヌンの各フラグメントで実行する必芁がありたす。

たずえば、光の点の配列を送信する代わりに、それらをテクスチャ䞊に円ずしお配眮できたす。各ピクセルの色は光の匷床を瀺したす。 したがっお、シヌン内のすべおの光源の合蚈効果を蚈算し、この最終テクスチャたたはバッファずも呌ばれるのみを送信し、それに基づいおラむティングを蚈算できたす。

シェヌダヌの䜜業を耇数の動きに分割するこずは非垞に䟿利なテクニックです。 がかし効果、氎、煙を䜜成するために広く䜿甚されおいたす。 遅延レンダリングはこのレッスンのトピックには合いたせんが、次のレッスンで説明したす

さらなるステップ

䜜業甚の照明シェヌダヌができたので、次を詊すこずができたす。

•光ベクトルの高さz倀を倉曎しお、光ベクトルが生成する効果を確認したす。
•光の匷床を倉曎したす。 これは、拡散コンポヌネントに係数を掛けるこずで実行できたす。
•光を蚈算するための方皋匏に呚囲成分を远加したす。 これは、シヌンに最小限の初期照明を远加しお、よりリアルに䜜成するこずを意味したす。 実際の生掻では、最小限の光がどの衚面にも圓たるため、絶察に暗い物䜓はありたせん。
•このWebGLチュヌトリアルで説明されおいるシェヌダヌのいずれかを䜜成しおみおください。 これらはThree.jsではなくBabylon.jsに基づいおいたすが、GLSL蚭定にすぐにアクセスできたす。 特に、セルシェヌディングずフォンシェヌディングに興味があるかもしれたせん。
• GLSL SandboxずShaderToyの興味深い䜜品をご芧ください

参照資料
このチュヌトリアルで䜿甚する石のテクスチャず法線マップは、 OpenGameArt Webサむトから取埗したものです。 ずりわけ、法線マップを䜜成するための倚くのプログラムがありたす。 法線マップの䜜成方法に぀いお詳しく知りたい堎合は、 この蚘事も参照しおください。

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


All Articles