Aqoole_Hateenaの技術日記

vulkan+raytraceで色々描いてます

Simple Water Caustics

youtu.be


youtu.be


youtu.be



Vulkan Ray TracingでWater Causticsを書いてみました。Water Causticsとは水面の底にできる光の模様のことです。正確には光りの屈折でできあがる様々な模様のことのようです。
コースティクス - Wikipedia

水面の波の式

様々な波を試してみましたが、最も綺麗な模様を描けるのは円形波のようでした。
円形波自体は



y = A sin2\pi(\frac{t}{T}-\frac{r}{\lambda})
で表されます。ここでT=周期\lambda=波長r=\sqrt{x^2+z^2}です。

上記のプログラムでは

pos.y = -1.5 + sin(time * 5.0f - glm::length(pos - glm::vec3(0, pos.y, 0)) * 8.0f) / 16.0f;

としています。

Caustics描画

イデアは次のNVIDIAのblogに書かれているものと同じです。
Chapter 2. Rendering Water Caustics | NVIDIA Developer

f:id:Aqoole_Hateena:20210617231213p:plain
出典 https://developer.nvidia.com/gpugems/gpugems/part-i-natural-effects/chapter-2-rendering-water-caustics figure 2-8 Accessing the Environment Map

ただし今回の例だと光源が一つだけなのでlight mapは作成せずに、shaderの中で直接計算しています。

//caustics
uint causticFlags = gl_RayFlagsOpaqueEXT | gl_RayFlagsTerminateOnFirstHitEXT;
traceRayEXT(topLevelAS, causticFlags, 0xFF, 1, 0, 1, refractPos, 0.001, vec3(0.0, -1.0, 0.0), tMax, 2);
vec3 waterSurface = prd.pos;
vec3 waterNormal = prd.normal;
vec3 waterV = refract(vec3(0.0, -1.0, 0.0), -waterNormal, nWater/nAir);
float t = (pushC.lightPosition.y - waterSurface.y) / waterV.y;
vec3 sunMapPoint = waterSurface + t * waterV;
float caustics = 1.0 / (1.0 + length(sunMapPoint - pushC.lightPosition));
color += caustics;

図でいうところの



v1 = t * waterV
ということになります。


本来ならCausticsは光源から波に当たった光の内、屈折した結果ある点に集まるすべての光をトレースする必要がありますが、それだと難易度が高すぎるので、一番強い光ということで水面に垂直にあたる光のみをトレースしています。
これだけでも、実施例のようにそれなりの結果が得られますね。