0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[Vulkan ray tracing] サンプルコードからの次の一歩 影と透過について

Posted at

概要

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シェーダだと思われるが、この関数はなんと冒頭の通り他のシェーダ内でも実行できる。
この関数を用いて、影や透過の判定を行う。

実施例

透明なキューブと色のある平面を描き、そこに左上から光を当てた絵。
image.png

影について

アイデアは非常にシンプルで、平面上の各点からlightに向けてtraceRayEXT関数を実行し、もしオブジェクトに当たればそこは影になると判断する。
image.png

rayhit.rchit
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;
  }
}
shadow.rmiss
void main()
{
  isShadowed = false;
}

traceRayEXT()を実行するが、ここでの目的はrayがmissするかどうかだけである。isShadowed変数にあらかじめtrueを入れておき、「missシェーダが呼ばれる=rayが当たらない」という状況であればmissシェーダ内でisShadowedをfalseに書き換えることで、呼び出し元の点が影になっているかどうかを判断する。

透過について

これは上記リンクのサンプルコードには書かれていないが、アイデアは同じである。

rayhit.rchit
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を飛ばして見える景色を描画するという思想なので、こういった影や透過に関しては、コードの記述が簡単になっているように感じる。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?