7
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.

【Ray Tracing】Voxelのためだけのアンビエントオクルージョン

Last updated at Posted at 2019-11-27

#概要
【Ray tracing】Voxelを作ってみるの続き。
Ambient Occlusionを普通にかけると元の形が反映されるため特別なAOが必要。
#Ambient Occlusion
理想的なAO。これをやっていきたい。
Voxel Edgesをいじったやつ)
Voxel Edges - Google Chrome 2019_11_26 18_20_54 (2).png
コード見てもさっぱりわからないので作者のiqさんの記事を参考にする。
http://www.iquilezles.org/www/articles/voxellines/voxellines.htm
記事を見るとAOするにはいくつか手順があるらしい。
記事の内容とコードを見比べて整理してみる。

step1: 周りに他のvoxelがあるか調べる。

具体的に考えてみる。たとえば現在のvoxelの面がy軸方向にあるとする(図中の斜線部分)。
そしてその周りのvoxelの面をそれぞれva、vb、vc、vdに分類してみる、けどAOを加えるかどうかを判断するのは上のvc、vdだけしか使わないらしい。
(Edgeもやりたい場合はva、vbも必要になるけど今回は使わない。)
ao1.jpg

イメージしやすく各面との差分を書いてみる。
va、vbも書いちゃったけど(0, 1, 0)を加えるだけでvc、vdになるので、
まずは簡単なvaとvbを考えてみる。
ao2.jpg
まず(0, 1, 0)はnormal方向なので普通にnormalだけ使って表せるように見えるけど、
もしこれが(0, -1, 0)方向の時は結果が反転してしまう。
なのでnormalに絶対値をとって面がx、y、z軸方向どっちに向いているかを考える。
normalはすでにこの記事で出してるのでこれをもとに周りのvoxelたちを得る。

vec3 dir = abs(normal);
// voxelは現在のvoxel
// 現在のvoxelから見て右の面
vec3 va_x = voxel + dir.yzx;
// 左の面
vec3 va_y = voxel - dir.yzx;
// 前の面
vec3 va_z = voxel + dir.zxy;
// 後の面
vec3 va_w = voxel - dir.zxy;

// 右前の面
// ちょっと難しいけどzにも(0, 1, 0)の値が入ってるのを考える & xyzとかぶらない並びにしてあげる。
vec3 vb_x = voxel + dir.yzx + dir.zxy;
// 左前の面
vec3 vb_y = voxel - dir.yzx + dir.zxy;
// 左後ろの面
vec3 vb_z = voxel - dir.yzx - dir.zxy;
// 右後ろの面
vec3 vb_w = voxel + dir.yzx - dir.zxy;

// vc、vdはそれぞれva、vbにnormalを足せばいい(例えばnormalが(0, -1, 0)の場合、vc、vdは一個下のvoxelであるため)
vec3 vc_x = va_x + normal;
vec3 vc_y = va_y + normal;
vec3 vc_z = va_z + normal;
vec3 vc_w = va_w + normal;

vec3 vd_x = vb_x + normal;
vec3 vd_y = vb_y + normal;
vec3 vd_z = vb_z + normal;
vec3 vd_w = vb_w + normal;

// 各voxelでオブジェクトにhitしているか判定する
// hitしてたら1.0を、してなかったら0.0を返す
vec4 vc = vec4(
  step(mapTheWorld(vc_x), 0.0),
  step(mapTheWorld(vc_y), 0.0),
  step(mapTheWorld(vc_z), 0.0),
  step(mapTheWorld(vc_w), 0.0)
);

vec4 vd = vec4(
  step(mapTheWorld(vd_x), 0.0),
  step(mapTheWorld(vd_y), 0.0),
  step(mapTheWorld(vd_z), 0.0),
  step(mapTheWorld(vd_w), 0.0)
);

これで周りのvoxelがあるか把握できた。

step2: voxelのuvを作る。

uvを作る前にuvwを作る。
普通に実際のpositionからvoxelの位置を引くことによって0から1の数値が得られるはず。

// hitT、hitVoxelなどは
// https://qiita.com/misaki_mofu/items/026cd353054856921995参照
vec3 realPos = ro + hitT * rd;
vec3 uvw = realPos - hitVoxel;

gl_FragColorにuvwを渡すとこんな感じになる。
07_voxel2 - Google Chrome 2019_11_27 0_39_24 (2).png

dirで場合分けをしてuvwからuvを得る

// uvの向きは基本的にdirの向きから見て横軸縦軸の順番にすると良さそう。
vec2 uv = dir.x > 0.0 ? uvw.zy : dir.y > 0.0 ? uvw.xz : uvw.yx;
// iqさんのコードでは下の方法で出してる。こっちのほうがすっきり見えるけど個人的にわかりずらい。
vec2 uv = vec2( dot(dir.yzx, uvw), dot(dir.zxy, uvw) );

uvをgl_FragColorに入れるとこんな感じでuvが取れたことが分かる。
07_voxel2 - Google Chrome 2019_11_27 1_24_28 (2).png

step3: Ambient Occlusionを計算する

材料はそろったのでいよいよAmbient Occlusionを実装する。
分かりやすくするために最初に右側のオクルージョンだけ考えてみる。
ao3.jpg
ao4.jpg
式で表すと

// さっきやったようにvc.xyzwには1.0か0.0が入っている。
float rightOcclusion = uv.x * vc.x;

ao5.jpg
式で表すと

float frontRightOcclusion = uv.x * uv.y * vd.x * (1.0 - vc.x) * (1.0 - vc.z);

他のvoxelでも同じようにやる。

float calcOcc( in vec2 uv, vec4 vc, vec4 vd )
{
    vec2 st = 1.0 - uv;

    // vc.xyzwがあるとき
    vec4 wa = vec4( uv.x, st.x, uv.y, st.y ) * vc;

   // vd.xyzwがあるとき(そして両脇のvcにvoxelがないとき)
    vec4 wb = vec4(uv.x*uv.y,
                   st.x*uv.y,
                   st.x*st.y,
                   uv.x*st.y) * vd * (1.0-vc.xzyw) * (1.0-vc.zywx);
    
    // 最後に足し合わせる。voxelがあるほどaoが濃くなる。
    return wa.x + wa.y + wa.z + wa.w +
           wb.x + wb.y + wb.z + wb.w;
}

あとはcolorにあてる。

float occ = calcOcc(uv, vc, vd);
// 必ず反転させる。
// お好みで滑らかにする。
occ = 1.0 - occ * occ / 12.0;
occ = occ*occ;
occ = occ*occ;
// colorにかける。
color *= occ;

#実践してみる!
デモ
ソースコード

前回やったtorusのvoxelでやってみる。
07_voxel2 - Google Chrome 2019_11_28 0_22_14 (2).png
かかったけど、かかりすぎかな?
ちょっと自重させる。
07_voxel2 - Google Chrome 2019_11_28 0_30_37 (2).png
ちょっとだけ自然になった気がする。

最後に

カメラを回転させると変になる。uvがずれる感じ。
なので、tMaxの初期値を変更させる。これでuvのずれが無くなってどの向きでもAOが正しくかかるようになる!

// vec3 tMax = (voxel - ro) / rd;
vec3 tMax = (voxel - ro + rayStep * 0.5 + 0.5) / rd;

以上がvoxelのためだけのアンビエントオクルージョンでした。おわり。

7
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
7
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?