概要
ray tracingのAPIでは、シンプルなものなら影と透過(ガラスなどのオブジェクト)は比較的簡単に実装できる。
サンプルコード
NVIDIA Vulkan Ray Tracing Tutorial
記事の最後で、shadowについての記述がある。
https://nvpro-samples.github.io/vk_raytracing_tutorial_KHR/
traceRayEXT関数
raygenシェーダ、missシェーダ、rchitシェーダで使える超便利関数。originからdirection方向にrayを飛ばし、オブジェクトにhitすれば指定したrchitシェーダを、ヒットしなければ指定したmissシェーダを実行する。
traceRayEXT関数を初めて見るのはraygenシェーダだと思われるが、この関数はなんと冒頭の通り他のシェーダ内でも実行できる。
この関数を用いて、影や透過の判定を行う。
実施例
透明なキューブと色のある平面を描き、そこに左上から光を当てた絵。
影について
アイデアは非常にシンプルで、平面上の各点からlightに向けてtraceRayEXT関数を実行し、もしオブジェクトに当たればそこは影になると判断する。
if(objId == 0 && dot(normal, L) > 0)
{
float tMin = 0.001;
float tMax = lightDistance;
vec3 origin = worldPos;
vec3 rayDir = L;
uint flags = gl_RayFlagsTerminateOnFirstHitEXT | gl_RayFlagsOpaqueEXT
| gl_RayFlagsSkipClosestHitShaderEXT;
isShadowed = true;
traceRayEXT(topLevelAS, flags, 0xFF, 0, 0, 1, origin, tMin, rayDir, tMax, 1);
if(isShadowed)
{
attenuation = 0.3;
}
}
void main()
{
isShadowed = false;
}
traceRayEXT()を実行するが、ここでの目的はrayがmissするかどうかだけである。isShadowed変数にあらかじめtrueを入れておき、「missシェーダが呼ばれる=rayが当たらない」という状況であればmissシェーダ内でisShadowedをfalseに書き換えることで、呼び出し元の点が影になっているかどうかを判断する。
透過について
これは上記リンクのサンプルコードには書かれていないが、アイデアは同じである。
if(alpha < 1.0)
{
float tMin = 0.01;
float tMax = 100.0;
pldBlend.pos = worldPos;
pldBlend.hit = false;
pldBlend.color = pushC.clearColor.xyz;
vec4 cameraPos = cam.viewInverse * vec4(0, 0, 0, 1);
vec3 direction = normalize(worldPos - cameraPos.xyz);
uint flags = gl_RayFlagsOpaqueEXT;
for(uint i = 0; i < 4; i++)
{
traceRayEXT(topLevelAS, flags, 0xFF, 1, 0, 2, pldBlend.pos, tMin, direction, tMax, 2);
if(pldBlend.hit)
break;
}
color = mix(pldBlend.color, color, alpha);
}
判断はobjIdでも構わないが、rchitが呼ばれた点(worldPos)がガラスキューブ上である場合にこの処理を行う。worldPosをoriginとし、directionはcameraから見たworldPos方向とする。初期の色を背景の色としておき、この方向に向けて複数回traceRayEXT関数を実行し、平面にhitすればその場所の色をとってくる。注意点として、originは呼ぶたびに次のhitした地点の座標を入れる必要がある。
最後に、とってきた色とガラスキューブの色をmix関数でblendを行う。
最後に
ray tracingでは従来のrasterizer型のパイプラインとはレンダリングのアプローチの仕方が異なるように思える。従来のパイプラインだと各頂点ごとに動作を記述しているので、他の頂点にあわせてこうするといった記述は難しく、例えばシャドウマップなどの工夫が必要だったとだろうと思う。一方ray tracingではrayを飛ばして見える景色を描画するという思想なので、こういった影や透過に関しては、コードの記述が簡単になっているように感じる。