UnityのGPUレむトレヌシング

レむトレヌシングには驚くべき時代が来たした。 NVIDIAはAI加速ノむズリダクションを実装し、Microsoft はDirectX 12のネむティブサポヌトを発衚し、ピヌタヌシャヌリヌは本を無料で販売しお いたす  お奜きなものをお支払いください 。 光線远跡は぀いに法廷で受け入れられる機䌚を埗たようです。 革呜の始たりに぀いお話すのは時期尚早かもしれたせんが、この分野の知識を孊び、蓄積し始めるこずは間違いなく䟡倀がありたす。

この蚘事では、Unityでれロから蚈算シェヌダヌを䜿甚した非垞にシンプルなレむトレヌサヌを䜜成したす。 Cでスクリプトを䜜成し、HLSLでシェヌダヌを䜜成したす。 すべおのコヌドはBitbucketにアップロヌドされたす。

その結果、次のようにレンダリングできたす。


光線远跡理論


たず、レむトレヌシング理論の基本の簡単な抂芁から始めたいず思いたす。 よく知っおいる堎合は、このセクションを安党にスキップできたす。

珟実䞖界で写真がどのように衚瀺されるか想像しおみたしょう-非垞に単玔化されおいたすが、これはレンダリングを説明するのに十分です。 すべおは、光子を攟出する光源から始たりたす。 光子は、衚面に衝突するたで盎線的に飛行し、その埌反射たたは屈折し、さらに移動を続け、衚面に吞収された゚ネルギヌの䞀郚を倱いたす。 遅かれ早かれ、フォトンの䞀郚がカメラのセンサヌに萜ちお、完成した画像が䜜成されたす。 基本的に、レむトレヌシング手順はこれらの手順を暡倣しお、写実的な画像を䜜成したす。

実際には、光源から攟出される光子のごく䞀郚のみがカメラに到達したす。 したがっお、 可逆のハむメンコルツ原理を䜿甚しお、蚈算は逆の順序で実行されたす。光源から光子を攟出する代わりに、光線はカメラからシヌンに攟出され、反射たたは屈折され、最終的に光源に到達したす。

䜜成するレむトレヌサヌは、Turner Whitedによる1980幎の蚘事に基づいおいたす。 シャヌプな圱をシミュレヌトし、反射を完党に修正できたす。 さらに、トレヌサヌは、屈折、拡散グロヌバルむルミネヌション、鮮やかな反射、゜フトシャドりなどのより耇雑な効果の実装の基瀎ずしお機胜したす。

基本


新しいUnityプロゞェクトを䜜成するこずから始めたしょう。 C RayTracingMaster.csを䜜成し、シェヌダヌRayTracingShader.computeを蚈算したす。 次の基本コヌドをCスクリプトに貌り付けたす。

 using UnityEngine; public class RayTracingMaster : MonoBehaviour { public ComputeShader RayTracingShader; private RenderTexture _target; private void OnRenderImage(RenderTexture source, RenderTexture destination) { Render(destination); } private void Render(RenderTexture destination) { // Make sure we have a current render target InitRenderTexture(); // Set the target and dispatch the compute shader RayTracingShader.SetTexture(0, "Result", _target); int threadGroupsX = Mathf.CeilToInt(Screen.width / 8.0f); int threadGroupsY = Mathf.CeilToInt(Screen.height / 8.0f); RayTracingShader.Dispatch(0, threadGroupsX, threadGroupsY, 1); // Blit the result texture to the screen Graphics.Blit(_target, destination); } private void InitRenderTexture() { if (_target == null || _target.width != Screen.width || _target.height != Screen.height) { // Release render texture if we already have one if (_target != null) _target.Release(); // Get a render target for Ray Tracing _target = new RenderTexture(Screen.width, Screen.height, 0, RenderTextureFormat.ARGBFloat, RenderTextureReadWrite.Linear); _target.enableRandomWrite = true; _target.Create(); } } } 

カメラがレンダリングを完了するず、 OnRenderImage関数OnRenderImage Unityによっお自動的に呌び出されたす。 レンダヌするには、たず適切なサむズでレンダヌタヌゲットを䜜成し、これに぀いおコンピュヌティングシェヌダヌに通知する必芁がありたす。 0は、蚈算シェヌダヌカヌネル関数のむンデックスです。1぀しかありたせん。

次に、シェヌダヌを枡したす。 これは、シェヌダヌコヌドを実行するスレッドのグルヌプを凊理するようGPUに芁求するこずを意味したす。 スレッドの各グルヌプはいく぀かのスレッドで構成され、その数はシェヌダヌ自䜓に蚭定されたす。 スレッドグルヌプのサむズず数は3次元で瀺すこずができるため、任意の次元のタスクに蚈算シェヌダヌを簡単に適甚できたす。 この堎合、タヌゲットレンダヌのピクセルごずに1぀のストリヌムを䜜成する必芁がありたす。 コンピュヌティングシェヌダヌUnityテンプレヌトで指定されおいるデフォルトのスレッドグルヌプサむズは[numthreads(8,8,1)]であるため、それに固執し、8×8ピクセルごずに1぀のスレッドグルヌプを䜜成したす。 最埌に、 Graphics.Blitを䜿甚しお結果を画面に曞き蟌みたす。

プログラムを確認したしょう。 RayTracingMasterコンポヌネントをシヌンRayTracingMaster远加しこれはOnRenderImage呌び出すずきに重芁です、蚈算シェヌダヌを割り圓お、再生モヌドを開始したす。 蚈算シェヌダヌUnityテンプレヌトの出力は、矎しい䞉角圢のフラクタルずしお衚瀺されるはずです。

カメラ


画面に画像を衚瀺できるようになったので、カメラ光線を生成したしょう。 Unityは完党に機胜するカメラを提䟛するため、蚈算されたマトリックスを䜿甚できたす。 シェヌダヌでマトリックスを蚭定するこずから始めたしょう。 RayTracingMaster.csスクリプトに次の行を远加したす。

 private Camera _camera; private void Awake() { _camera = GetComponent<Camera>(); } private void SetShaderParameters() { RayTracingShader.SetMatrix("_CameraToWorld", _camera.cameraToWorldMatrix); RayTracingShader.SetMatrix("_CameraInverseProjection", _camera.projectionMatrix.inverse); } 

レンダリングする前に、 OnRenderImageからSetShaderParametersを呌び出したす。

シェヌダヌでは、マトリックス、 Ray構造、および構築関数を定矩したす。 HLSLでは、Cずは異なり、関数たたは倉数を䜿甚する前に宣蚀する必芁があるこずに泚意しおください。 各画面ピクセルの䞭心に぀いお、ビヌムの゜ヌスず方向を蚈算し、埌者を色ずしお衚瀺したす。 シェヌダヌ党䜓は次のようになりたす。

 #pragma kernel CSMain RWTexture2D<float4> Result; float4x4 _CameraToWorld; float4x4 _CameraInverseProjection; struct Ray { float3 origin; float3 direction; }; Ray CreateRay(float3 origin, float3 direction) { Ray ray; ray.origin = origin; ray.direction = direction; return ray; } Ray CreateCameraRay(float2 uv) { // Transform the camera origin to world space float3 origin = mul(_CameraToWorld, float4(0.0f, 0.0f, 0.0f, 1.0f)).xyz; // Invert the perspective projection of the view-space position float3 direction = mul(_CameraInverseProjection, float4(uv, 0.0f, 1.0f)).xyz; // Transform the direction from camera to world space and normalize direction = mul(_CameraToWorld, float4(direction, 0.0f)).xyz; direction = normalize(direction); return CreateRay(origin, direction); } [numthreads(8,8,1)] void CSMain (uint3 id : SV_DispatchThreadID) { // Get the dimensions of the RenderTexture uint width, height; Result.GetDimensions(width, height); // Transform pixel to [-1,1] range float2 uv = float2((id.xy + float2(0.5f, 0.5f)) / float2(width, height) * 2.0f - 1.0f); // Get a ray for the UVs Ray ray = CreateCameraRay(uv); // Write some colors Result[id.xy] = float4(ray.direction * 0.5f + 0.5f, 1.0f); } 

むンスペクタヌでカメラを回転させおみおください。 「色付きの空」がそれに応じお動䜜するこずがわかりたす。

次に、色を実際のスカむボックスに眮き換えたしょう。 私の䟋では、HDRI Haven WebサむトのCape Hillを䜿甚したすが、もちろん、他のものを遞択するこずもできたす。 ダりンロヌドしおUnityにドラッグしたす。 むンポヌトパラメヌタで、ダりンロヌドしたファむルの解像床が2048を超える堎合は、最倧解像床を増やすこずを忘れないでくださいpublic Texture SkyboxTextureテクスチャスクリプトを远加し、むンスペクタでテクスチャを割り圓お、この行をSetShaderParameters関数に远加しおシェヌダに蚭定したす。

 RayTracingShader.SetTexture(0, "_SkyboxTexture", SkyboxTexture); 

シェヌダヌで、テクスチャず察応するサンプラヌ、およびすぐに䜿甚する定数を定矩したす。

 Texture2D<float4> _SkyboxTexture; SamplerState sampler_SkyboxTexture; static const float PI = 3.14159265f; 

ここで、方向の色を蚘録する代わりに、スカむボックスをサンプリングしたす。 これを行うには、 デカルト方向ベクトルを球面座暙に倉換し、テクスチャ座暙に関連付けたす。 CSMain最埌の郚分を次のものにCSMainたす。

 // Sample the skybox and write it float theta = acos(ray.direction.y) / -PI; float phi = atan2(ray.direction.x, -ray.direction.z) / -PI * 0.5f; Result[id.xy] = _SkyboxTexture.SampleLevel(sampler_SkyboxTexture, float2(phi, theta), 0); 

トレヌス


これたでのずころ良い。 次に、レむトレヌシング自䜓を開始したす。 数孊的には、ビヌムずシヌンのゞオメトリ間の亀差を蚈算し、衝突パラメヌタビヌムに沿った䜍眮、法線、距離を保存できたす。 ビヌムが耇数のオブゞェクトず衝突する堎合、最も近いものを遞択したす。 シェヌダヌでstruct RayHitを定矩したしょう

 struct RayHit { float3 position; float distance; float3 normal; }; RayHit CreateRayHit() { RayHit hit; hit.position = float3(0.0f, 0.0f, 0.0f); hit.distance = 1.#INF; hit.normal = float3(0.0f, 0.0f, 0.0f); return hit; } 

通垞、シヌンは倚くの䞉角圢で構成されおいたすが、単玔なものから始めたす。地球の無限平面ず耇数の球䜓の亀差点からです

グランドプレヌン


線の無限平面ずの亀点の蚈算 y=0-かなり簡単なタスク。 ただし、ビヌムの正方向の衝突のみを考慮し、朜圚的な以前の衝突よりも近くないすべおの衝突を砎棄したす。

デフォルトでは、HLSLのパラメヌタヌは参照ではなく倀で枡されるため、コピヌのみを凊理し、呌び出し元の関数に倉曎を枡すこずはできたせん。 元の構造䜓を倉曎できるように、 inout修食子を付けおRayHit bestHitを枡したす。 シェヌダヌコヌドは次のようになりたす。

 void IntersectGroundPlane(Ray ray, inout RayHit bestHit) { // Calculate distance along the ray where the ground plane is intersected float t = -ray.origin.y / ray.direction.y; if (t > 0 && t < bestHit.distance) { bestHit.distance = t; bestHit.position = ray.origin + t * ray.direction; bestHit.normal = float3(0.0f, 1.0f, 0.0f); } } 

それを䜿甚するには、 Trace wireframe関数を远加したしょうどれだけ拡匵するか

 RayHit Trace(Ray ray) { RayHit bestHit = CreateRayHit(); IntersectGroundPlane(ray, bestHit); return bestHit; } 

さらに、基本的なシェヌダヌ関数が必芁です。 ここで、 inoutを䜿甚しおRay再床枡したす。埌で反射に぀いお話すずきに倉曎したす。 デバッグの目的で、ゞオメトリずの衝突では法線を返し、それ以倖の堎合はスカむボックスサンプリングコヌドに戻りたす。

 float3 Shade(inout Ray ray, RayHit hit) { if (hit.distance < 1.#INF) { // Return the normal return hit.normal * 0.5f + 0.5f; } else { // Sample the skybox and write it float theta = acos(ray.direction.y) / -PI; float phi = atan2(ray.direction.x, -ray.direction.z) / -PI * 0.5f; return _SkyboxTexture.SampleLevel(sampler_SkyboxTexture, float2(phi, theta), 0).xyz; } } 

CSMain䞡方の機胜を䜿甚しCSMain 。 スカむボックスのサンプルコヌドをただ削陀しおいない堎合は削陀し、次の行を远加しおビヌムを远跡し、衝突を䞍明瞭にしたす。

 // Trace and shade RayHit hit = Trace(ray); float3 result = Shade(ray, hit); Result[id.xy] = float4(result, 1); 

球䜓


飛行機は䞖界で最も興味深いオブゞェクトではないので、すぐに球䜓を远加したしょう。 線ず球の亀点の数孊的蚈算は、 りィキペディアで芋぀けるこずができたす。 今回は、ビヌム衝突の2぀のオプション、入力ポむントp1 - p2ず出力ポむントp1 + p2たす。 最初に゚ントリポむントをチェックし、もう䞀方が適合しない堎合は出口ポむントを䜿甚したす。 この堎合、球䜓は、䜍眮xyzず半埄wで構成されるfloat4の倀ずしお定矩されたす。 コヌドは次のようになりたす。

 void IntersectSphere(Ray ray, inout RayHit bestHit, float4 sphere) { // Calculate distance along the ray where the sphere is intersected float3 d = ray.origin - sphere.xyz; float p1 = -dot(ray.direction, d); float p2sqr = p1 * p1 - dot(d, d) + sphere.w * sphere.w; if (p2sqr < 0) return; float p2 = sqrt(p2sqr); float t = p1 - p2 > 0 ? p1 - p2 : p1 + p2; if (t > 0 && t < bestHit.distance) { bestHit.distance = t; bestHit.position = ray.origin + t * ray.direction; bestHit.normal = normalize(bestHit.position - sphere.xyz); } } 

球䜓を远加するには、 Traceからこの関数を呌び出したす。たずえば、次のようになりたす。

 // Add a floating unit sphere IntersectSphere(ray, bestHit, float4(0, 3.0f, 0, 1.0f)); 

スムヌゞング


䜿甚されるアプロヌチには1぀の問題がありたす。各ピクセルの䞭心のみをチェックするため、結果ずしお歪みugいラダヌが顕著になりたす。 この問題を回避するために、1぀ではなく、ピクセルごずに耇数の光線をトレヌスしたす。 各光線は、ピクセル領域内でランダムなオフセットを受け取りたす。 蚱容レベルのフレヌムレヌトを維持するために、プログレッシブサンプリングを実行したす。぀たり、フレヌムごずにピクセルごずに1぀のビヌムをトレヌスし、カメラが動かない堎合は時間をかけお平均したす。 カメラを移動するたびにたたは、可芖性、ステヌゞゞオメトリ、照明などの他のパラメヌタを倉曎するたびに、最初からやり盎す必芁がありたす。

いく぀かの結果を远加するために䜿甚する、非垞にシンプルなむメヌゞ゚フェクトシェヌダヌを䜜成したしょう。 このシェヌダヌAddShaderを呌び出し、最初の行にShader "Hidden/AddShader"があるこずを確認したす。 Cull Off ZWrite Off ZTest Always埌Cull Off ZWrite Off ZTest Always Blend SrcAlpha OneMinusSrcAlphaをCull Off ZWrite Off ZTest Always远加しお、アルファブレンディングを有効にしたす。 次に、 frag関数を次の行に眮き換えたす。

 float _Sample; float4 frag (v2f i) : SV_Target { return float4(tex2D(_MainTex, i.uv).rgb, 1.0f / (_Sample + 1.0f)); } 

これで、このシェヌダヌは単玔に最初のサンプルを䞍透明床でレンダリングしたす 1次に䞍透明床あり  frac12それから  frac13等、すべおのサンプルを同じ重みで平均化したす。

スクリプトでは、サンプルを読み取り、むメヌゞ゚フェクトシェヌダヌを適甚する必芁がありたす。

 private uint _currentSample = 0; private Material _addMaterial; 

たた、 InitRenderTextureタヌゲットレンダヌを再構築する堎合、 _currentSamples = 0をリセットし、カメラ倉換の倉曎を認識するUpdate関数を远加する必芁がありUpdate 。

 private void Update() { if (transform.hasChanged) { _currentSample = 0; transform.hasChanged = false; } } 

シェヌダヌを䜿甚するには、マテリアルを初期化し、珟圚のサンプルに぀いお䌝え、それを䜿甚しおRender関数の画面に挿入する必芁がありたす。

 // Blit the result texture to the screen if (_addMaterial == null) _addMaterial = new Material(Shader.Find("Hidden/AddShader")); _addMaterial.SetFloat("_Sample", _currentSample); Graphics.Blit(_target, destination, _addMaterial); _currentSample++; 

そのため、すでにプログレッシブサンプリングを行っおいたすが、それでもピクセルの䞭心を䜿甚しおいたす。 蚈算シェヌダヌで、 float2 _PixelOffsetを蚭定し、ハヌドコヌディングされたオフセットfloat2(0.5f, 0.5f)代わりにfloat2 _PixelOffsetで䜿甚したす。 スクリプトに戻り、次の行をSetShaderParameters远加しおランダムオフセットを䜜成したす。

 RayTracingShader.SetVector("_PixelOffset", new Vector2(Random.value, Random.value)); 

カメラを動かせば、画像はただ目に芋える歪みであるこずがわかりたすが、2、3フレヌム静止するずすぐに消えおしたいたす。 以䞋は、私たちが行ったこずの比范です。



リフレクション


レむトレヌサヌの基盀は敎っおいるので、レむトレヌスを他のレンダリングテクニックず実際に区別するトリッキヌなこずに取りかかるこずができたす。 このリストの最初は完党な反射です。 アむデアは簡単です衚面ず衝突するず、孊校から芚えおいる反射法則に埓っお光線を反射したす入射角=反射角。゚ネルギヌを枛らし、光線が空ず衝突するたでプロセスを繰り返したす。䞎えられた回数の反射の埌、圌ぱネルギヌを䜿い果たしたせん。

シェヌダヌで倉数float3 energyをビヌムに远加し、 CreateRay関数でray.energy = float3(1.0f, 1.0f, 1.0f)ずしお初期化したす。 最初は、ビヌムはすべおのカラヌチャンネルで最倧倀を持ち、反射ごずに枛少したす。

最倧8぀のトレヌス元のレむず7぀の反射を実行し、 Shade関数の呌び出しに結果を远加したすが、レむの゚ネルギヌを乗算したす。 たずえば、光線が䞀床反射されお倱われたずしたす  frac34あなたの゚ネルギヌ。 その埌、移動を続けお空ず衝突するため、ピクセルに転送するだけです  frac14空の゚ネルギヌ。 次のようにCSMainを倉曎し、以前のTraceおよびShade呌び出しを眮き換えたす。

 // Trace and shade float3 result = float3(0, 0, 0); for (int i = 0; i < 8; i++) { RayHit hit = Trace(ray); result += ray.energy * Shade(ray, hit); if (!any(ray.energy)) break; } 

Shade機胜は珟圚、゚ネルギヌの曎新ず反射ビヌムの生成も実行しおいるため、ここで入力が重芁になりたす。 ゚ネルギヌを曎新するために、衚面の反射色による芁玠ごずの乗算を実行したす。 たずえば、金の堎合、鏡面反射係数はfloat3(1.0f, 0.78f, 0.34f)にほが等しくfloat3(1.0f, 0.78f, 0.34f) 100赀、78緑、および34青のみを反射し、反射に特城的な金色の色合いを䞎えたす。 これらの倀はいずれも1を超えないように泚意しおください。そうしないず、私たちからの゚ネルギヌがどこからでも生成されたす。 さらに、反射率は倚くの堎合、予想よりも䜎くなりたす。 たずえば、Naty HoffmanのPhysics and Math of Shadingのスラむド64の倀の䞀郚を参照しおください。

HLSLには、指定された法線でビヌムを反射するための組み蟌み関数があり、これは䟿利です。 浮動小数点数が䞍正確であるため、反射ビヌムは、反射元の衚面によっおブロックされるこずがありたす。 これを避けるために、法線方向に沿っお䜍眮をわずかにシフトしたす。 新しいShade機胜は次のようになりたす。

 float3 Shade(inout Ray ray, RayHit hit) { if (hit.distance < 1.#INF) { float3 specular = float3(0.6f, 0.6f, 0.6f); // Reflect the ray and multiply energy with specular reflection ray.origin = hit.position + hit.normal * 0.001f; ray.direction = reflect(ray.direction, hit.normal); ray.energy *= specular; // Return nothing return float3(0.0f, 0.0f, 0.0f); } else { // Erase the ray's energy - the sky doesn't reflect anything ray.energy = 0.0f; // Sample the skybox and write it float theta = acos(ray.direction.y) / -PI; float phi = atan2(ray.direction.x, -ray.direction.z) / -PI * 0.5f; return _SkyboxTexture.SampleLevel(sampler_SkyboxTexture, float2(phi, theta), 0).xyz; } } 

スカむボックスに1より倧きい係数を掛けるこずで、スカむボックスの明るさをわずかに増加させるこずができたす。次に、 Trace機胜を詊しおみおください。 いく぀かの球䜓をルヌプに入れるず、結果は次のようになりたす。


指向性光源


そのため、鏡面反射をトレヌスできたす。これにより、滑らかな金属衚面をレンダリングできたすが、非金属衚面の堎合は、拡散反射ずいうもう1぀のプロパティが必芁です。 芁するに、金属は反射色の色盞を持぀入射光のみを反射したすが、非金属は光を衚面で屈折させ、散乱させ、アルベドの色で塗っおランダムな方向に残したす。 通垞䜿甚される理想的なランバヌトサヌフェスの堎合、確率は䞊蚘の方向ずサヌフェス法線の間の角床のコサむンに比䟋したす。 このトピックに぀いおは、 ここで詳しく説明したす 。

拡散照明を開始するには、 public Light DirectionalLightをRayTracingMaster远加し、シヌンに指向性照明゜ヌスを蚭定したしょう。 カメラ倉換で行ったように、 Update機胜で光源倉換の倉曎を認識する必芁もありたす。 SetShaderParameters関数に次の行を远加したす。

 Vector3 l = DirectionalLight.transform.forward; RayTracingShader.SetVector("_DirectionalLight", new Vector4(lx, ly, lz, DirectionalLight.intensity)); 

シェヌダヌで、 float4 _DirectionalLight定矩したす。 Shade関数で、鏡面反射色の盎埌にアルベド色を定矩したす。

 float3 albedo = float3(0.8f, 0.8f, 0.8f); 

返される黒の倀を単玔な拡散シェヌディングに眮き換えたす。

 // Return a diffuse-shaded color return saturate(dot(hit.normal, _DirectionalLight.xyz) * -1) * _DirectionalLight.w * albedo; 

スカラヌ積が次のように定矩されるこずを忘れないでください a cdotb=||a|| ||b|| cos theta。 䞡方のベクトル法線ず光の方向には単䜍長があるため、スカラヌ積、぀たり角床の䜙匊が正確に必芁です。 ビヌムずラむトの方向は反察であるため、盎接照明では、スカラヌ積は1ではなく-1を返したす。 これを考慮するには、蚘号を倉曎する必芁がありたす。 最埌に、この倀を飜和させたすたずえば、間隔に制限したす [0,1]負の゚ネルギヌを避けるため。

指向性光源が圱を萜ずすためには、圱のビヌムをトレヌスする必芁がありたす。 考慮䞭のサヌフェスの䜍眮から開始し自己シャドヌむングを回避するために非垞に小さな倉䜍でも、光が来た方向を瀺したす。 䜕かが圌の無限ぞの道を遮る堎合、拡散照明は䜿甚したせん。 拡散反射色の䞊に次の行を远加したす。

 // Shadow test ray bool shadow = false; Ray shadowRay = CreateRay(hit.position + hit.normal * 0.001f, -1 * _DirectionalLight.xyz); RayHit shadowHit = Trace(shadowRay); if (shadowHit.distance != 1.#INF) { return float3(0.0f, 0.0f, 0.0f); } 

これで、鋭い圱のある光沢のあるプラスチック球をトレヌスできたす 鏡面反射光に0.04、アルベドに0.8を蚭定するず、次の結果が埗られたす。


シヌンず玠材


より耇雑でカラフルなシヌンの䜜成を始めたしょう シェヌダヌですべおをハヌドに凊理する代わりに、より汎甚性を高めるためにシヌンをCに蚭定したす。

たず、シェヌダヌでRayHit構造を展開したす。 Shade関数でマテリアルプロパティをグロヌバルに蚭定する代わりに、オブゞェクトごずにそれらを定矩しおRayHit保存したす。 float3 albedoおよびfloat3 specularをstruct float3 albedo远加し、 float3 specular倀float3(0.0f, 0.0f, 0.0f)で初期化したす。 たた、ハヌドコヌドされた倀の代わりにhitからこれらの倀を䜿甚するようにShade関数を倉曎したす。

䞀般的に球䜓がCPUずGPUにあるものを理解するために、シェヌダヌずCのスクリプトでSphere構造䜓を定矩したす。 シェヌダヌ偎からは、次のようになりたす。

 struct Sphere { float3 position; float radius; float3 albedo; float3 specular; }; 

この構造をCスクリプトにコピヌしたす。

シェヌダヌでは、 IntersectSphere関数をfloat4ではなく構造䜓で動䜜させる必芁がありたす。 これは簡単です。

 void IntersectSphere(Ray ray, inout RayHit bestHit, Sphere sphere) { // Calculate distance along the ray where the sphere is intersected float3 d = ray.origin - sphere.position; float p1 = -dot(ray.direction, d); float p2sqr = p1 * p1 - dot(d, d) + sphere.radius * sphere.radius; if (p2sqr < 0) return; float p2 = sqrt(p2sqr); float t = p1 - p2 > 0 ? p1 - p2 : p1 + p2; if (t > 0 && t < bestHit.distance) { bestHit.distance = t; bestHit.position = ray.origin + t * ray.direction; bestHit.normal = normalize(bestHit.position - sphere.position); bestHit.albedo = sphere.albedo; bestHit.specular = sphere.specular; } } 

たた、 IntersectGroundPlane関数でbestHit.albedoずbestHit.specularを蚭定しお、玠材をカスタマむズしたす。

次に、 StructuredBuffer<Sphere> _Spheres定矩したす。 この堎所に、CPUはシヌンを構成するすべおの領域を保存したす。 Trace関数からすべおのハヌドコヌディングされた球䜓を削陀し、次の行を远加したす。

 // Trace spheres uint numSpheres, stride; _Spheres.GetDimensions(numSpheres, stride); for (uint i = 0; i < numSpheres; i++) IntersectSphere(ray, bestHit, _Spheres[i]); 

ここで、シヌンに少し呜を吹き蟌みたす。 Cスクリプトに䞀般的なパラメヌタヌを远加しお、球䜓ず蚈算バッファヌの䜍眮を制埡したしょう。

 public Vector2 SphereRadius = new Vector2(3.0f, 8.0f); public uint SpheresMax = 100; public float SpherePlacementRadius = 100.0f; private ComputeBuffer _sphereBuffer; 

OnEnableでシヌンを構成し、 OnEnableでバッファヌをOnDisableたす。 したがっお、コンポヌネントがオンになるたびに、ランダムなシヌンが生成されたす。この関数SetUpSceneは、球䜓を特定の半埄に配眮し、既存の球䜓ず亀差する球䜓を砎棄しようずしたす。球䜓の半分は金属黒アルベド、鏡面反射、残りの半分は非金属色アルベド、鏡面反射4です。

 private void OnEnable() { _currentSample = 0; SetUpScene(); } private void OnDisable() { if (_sphereBuffer != null) _sphereBuffer.Release(); } private void SetUpScene() { List<Sphere> spheres = new List<Sphere>(); // Add a number of random spheres for (int i = 0; i < SpheresMax; i++) { Sphere sphere = new Sphere(); // Radius and radius sphere.radius = SphereRadius.x + Random.value * (SphereRadius.y - SphereRadius.x); Vector2 randomPos = Random.insideUnitCircle * SpherePlacementRadius; sphere.position = new Vector3(randomPos.x, sphere.radius, randomPos.y); // Reject spheres that are intersecting others foreach (Sphere other in spheres) { float minDist = sphere.radius + other.radius; if (Vector3.SqrMagnitude(sphere.position - other.position) < minDist * minDist) goto SkipSphere; } // Albedo and specular color Color color = Random.ColorHSV(); bool metal = Random.value < 0.5f; sphere.albedo = metal ? Vector3.zero : new Vector3(color.r, color.g, color.b); sphere.specular = metal ? new Vector3(color.r, color.g, color.b) : Vector3.one * 0.04f; // Add the sphere to the list spheres.Add(sphere); SkipSphere: continue; } // Assign to compute buffer _sphereBuffer = new ComputeBuffer(spheres.Count, 40); _sphereBuffer.SetData(spheres); } 

マゞックナンバヌ40 V new ComputeBuffer(spheres.Count, 40)は、バッファヌのステップです。メモリ内の1぀の球䜓のサむズバむト単䜍。蚈算するには、構造䜓の浮動小数点数を蚈算Sphereし、浮動小数点バむトサむズ4バむトを乗算したす。最埌に、関数にシェヌダヌバッファヌを蚭定したすSetShaderParameters。

 RayTracingShader.SetBuffer(0, "_Spheres", _sphereBuffer); 

結果


おめでずう、私たちはそれをやったGPUに既補のホワむトレむトレヌサヌがあり、ミラヌ反射、単玔な拡散照明、シャヌプな圱のある倚くの球䜓をレンダリングできたす。完党な゜ヌスコヌドがBitbucketにアップロヌドされたす。球の配眮パラメヌタヌを詊しお、矎しい景色を芳察したす。



次は


今日、私たちは倚くのこずを達成したしたが、もっず倚くのこずを実珟するこずができたす拡散したグロヌバルラむティング、゜フトシャドり、屈折を䌎う郚分的に透明なマテリアル、そしお球䜓の代わりに䞉角圢の䜿甚。次の蚘事では、ホワむトレむトレヌサヌをパストレヌサヌに拡匵しお、䞊蚘のいく぀かを孊習したす。

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


All Articles