ã¬ã€ãã¬ãŒã·ã³ã°ã«ã¯é©ãã¹ãæ代ãæ¥ãŸããã 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) {
ã«ã¡ã©ãã¬ã³ããªã³ã°ãå®äºãããšã
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) {
ã€ã³ã¹ãã¯ã¿ãŒã§ã«ã¡ã©ãå転ãããŠã¿ãŠãã ããã ãè²ä»ãã®ç©ºããããã«å¿ããŠåäœããããšãããããŸãã
次ã«ãè²ãå®éã®ã¹ã«ã€ããã¯ã¹ã«çœ®ãæããŸãããã ç§ã®äŸã§ã¯ã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
ãŸãã
ãã¬ãŒã¹
ãããŸã§ã®ãšããè¯ãã 次ã«ãã¬ã€ãã¬ãŒã·ã³ã°èªäœãéå§ããŸãã æ°åŠçã«ã¯ãããŒã ãšã·ãŒã³ã®ãžãªã¡ããªéã®äº€å·®ãèšç®ããè¡çªãã©ã¡ãŒã¿ïŒããŒã ã«æ²¿ã£ãäœçœ®ãæ³ç·ãè·é¢ïŒãä¿åã§ããŸãã ããŒã ãè€æ°ã®ãªããžã§ã¯ããšè¡çªããå Žåãæãè¿ããã®ãéžæããŸãã ã·ã§ãŒããŒã§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; }
éåžžãã·ãŒã³ã¯å€ãã®äžè§åœ¢ã§æ§æãããŠããŸãããåçŽãªãã®ããå§ããŸããå°çã®ç¡éå¹³é¢ãšè€æ°ã®çäœã®äº€å·®ç¹ããã§ãïŒ
ã°ã©ã³ããã¬ãŒã³
ç·ã®ç¡éå¹³é¢ãšã®äº€ç¹ã®èšç®
-ããªãç°¡åãªã¿ã¹ã¯ã ãã ããããŒã ã®æ£æ¹åã®è¡çªã®ã¿ãèæ
®ããæœåšçãªä»¥åã®è¡çªãããè¿ããªããã¹ãŠã®è¡çªãç Žæ£ããŸãã
ããã©ã«ãã§ã¯ã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) {
çäœãè¿œå ããã«ã¯ã
Trace
ãããã®é¢æ°ãåŒã³åºããŸããããšãã°ã次ã®ããã«ãªããŸãã
ã¹ã ãŒãžã³ã°
䜿çšãããã¢ãããŒãã«ã¯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)); }
ããã§ããã®ã·ã§ãŒããŒã¯åçŽã«æåã®ãµã³ãã«ãäžéæ床ã§ã¬ã³ããªã³ã°ããŸã
次ã«äžéæ床ãã
ãããã
çããã¹ãŠã®ãµã³ãã«ãåãéã¿ã§å¹³ååããŸãã
ã¹ã¯ãªããã§ã¯ããµã³ãã«ãèªã¿åããã€ã¡ãŒãžãšãã§ã¯ãã·ã§ãŒããŒãé©çšããå¿
èŠããããŸãã
private uint _currentSample = 0; private Material _addMaterial;
ãŸãã
InitRenderTexture
ã¿ãŒã²ããã¬ã³ããŒãåæ§ç¯ããå Žåã
_currentSamples = 0
ããªã»ããããã«ã¡ã©å€æã®å€æŽãèªèãã
Update
é¢æ°ãè¿œå ããå¿
èŠããã
Update
ã
private void Update() { if (transform.hasChanged) { _currentSample = 0; transform.hasChanged = false; } }
ã·ã§ãŒããŒã䜿çšããã«ã¯ããããªã¢ã«ãåæåããçŸåšã®ãµã³ãã«ã«ã€ããŠäŒããããã䜿çšããŠ
Render
é¢æ°ã®ç»é¢ã«æ¿å
¥ããå¿
èŠããããŸãã
ãã®ããããã§ã«ããã°ã¬ãã·ããµã³ããªã³ã°ãè¡ã£ãŠããŸãããããã§ããã¯ã»ã«ã®äžå¿ã䜿çšããŠããŸãã èšç®ã·ã§ãŒããŒã§ã
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
é¢æ°ã®åŒã³åºãã«çµæãè¿œå ããŸãããã¬ã€ã®ãšãã«ã®ãŒãä¹ç®ããŸãã ããšãã°ãå
ç·ãäžåºŠåå°ãããŠå€±ããããšããŸã
ããªãã®ãšãã«ã®ãŒã ãã®åŸã移åãç¶ããŠç©ºãšè¡çªããããããã¯ã»ã«ã«è»¢éããã ãã§ã
空ã®ãšãã«ã®ãŒã 次ã®ããã«
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;
ã¹ã«ã©ãŒç©ã次ã®ããã«å®çŸ©ãããããšãå¿ããªãã§ãã ãã
ã äž¡æ¹ã®ãã¯ãã«ïŒæ³ç·ãšå
ã®æ¹åïŒã«ã¯åäœé·ããããããã¹ã«ã©ãŒç©ãã€ãŸãè§åºŠã®äœåŒŠãæ£ç¢ºã«å¿
èŠã§ãã ããŒã ãšã©ã€ãã®æ¹åã¯å察ã§ãããããçŽæ¥ç
§æã§ã¯ãã¹ã«ã©ãŒç©ã¯1ã§ã¯ãªã-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
é¢æ°ãããã¹ãŠã®ããŒãã³ãŒãã£ã³ã°ãããçäœãåé€ãã次ã®è¡ãè¿œå ããŸãã
ããã§ãã·ãŒã³ã«å°ãåœãå¹ã蟌ã¿ãŸãã 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>();
ããžãã¯ãã³ããŒ40 V new ComputeBuffer(spheres.Count, 40)
ã¯ããããã¡ãŒã®ã¹ãããã§ããã¡ã¢ãªå
ã®1ã€ã®çäœã®ãµã€ãºïŒãã€ãåäœïŒãèšç®ããã«ã¯ãæ§é äœã®æµ®åå°æ°ç¹æ°ãèšç®Sphere
ããæµ®åå°æ°ç¹ãã€ããµã€ãºïŒ4ãã€ãïŒãä¹ç®ããŸããæåŸã«ãé¢æ°ã«ã·ã§ãŒããŒãããã¡ãŒãèšå®ããŸãSetShaderParameters
ã RayTracingShader.SetBuffer(0, "_Spheres", _sphereBuffer);
çµæ
ããã§ãšããç§ãã¡ã¯ããããã£ãïŒGPUã«æ¢è£œã®ãã¯ã€ãã¬ã€ãã¬ãŒãµãŒãããããã©ãŒåå°ãåçŽãªæ¡æ£ç
§æãã·ã£ãŒããªåœ±ã®ããå€ãã®çäœãã¬ã³ããªã³ã°ã§ããŸããå®å
šãªãœãŒã¹ã³ãŒããBitbucketã«ã¢ããããŒããããŸããçã®é
眮ãã©ã¡ãŒã¿ãŒãè©ŠããŠãçŸããæ¯è²ã芳å¯ããŸãã次ã¯ïŒ
ä»æ¥ãç§ãã¡ã¯å€ãã®ããšãéæããŸãããããã£ãšå€ãã®ããšãå®çŸããããšãã§ããŸãïŒæ¡æ£ããã°ããŒãã«ã©ã€ãã£ã³ã°ããœããã·ã£ããŠãå±æã䌎ãéšåçã«éæãªãããªã¢ã«ããããŠçäœã®ä»£ããã«äžè§åœ¢ã®äœ¿çšã次ã®èšäºã§ã¯ããã¯ã€ãã¬ã€ãã¬ãŒãµãŒããã¹ãã¬ãŒãµãŒã«æ¡åŒµããŠãäžèšã®ããã€ããåŠç¿ããŸãã