1
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

posted at

updated at

Organization

レイマーチングで使われるSDFを解釈する~Part3~

はじめに

こんにちは、アドベントカレンダー11日目担当の避雷です。今回は前回に引き続き、レイマーチングのSDFを読み解いていこうと思います。今回は多角形について扱います。

解説

サンプルはこちら
https://www.shadertoy.com/view/WdKXzd

三角柱

image.png

iq氏のサイトに書かれているSDFはこちら。

float sdTriPrism( vec3 p, vec2 h )
{
  vec3 q = abs(p);
  return max(q.z-h.y,max(q.x*0.866025+p.y*0.5,-p.y)-h.x*0.5);
}

なんだか良くわからない数字がハードコーディングされています。
良く観察するとh.yが三角柱の高さ、h.xが三角柱の底辺のサイズを制御しているみたいです。
また、0.866025について、生成物が正三角形であること、かなりの精度で記述されていることなどを考えれば、これは$\frac{\sqrt{3}}{2}$であると予測できます。実際に$\sqrt{3} = 1.73...$なのでこの予測は正しそうです。
数式を観察してみます。実装の内容は、

  • 底辺の生成($-y < 0$)
  • 斜辺の生成($0.886x + p.y * 0.5 < 0$)
  • 絶対値で折り返す($q = abs(p)$)(この折り返しによって斜辺が左右両方になる)
  • $(x,y)$を$h.x * 0.5$(h.xは三角形の一辺の長さとして定義されている)だけ右下にずらす

image.png

TIPS

これは当然のことではありますが、三角柱2つの和を取ると六芒星柱になります。
image.png

float sdHexStar(vec3 p, vec2 h){
    float result = sdTriPrism(p,h);
    p.y = -p.y;
    result = min(result,sdTriPrism(p,h));
    return result;
}

このぐらいになると情報量が増えてだいぶ魅せられる画になってくるような気がしますね。
さらに奥行き方向の座標による捻りや太さにブレを持たせるとだいぶ良くなってくるような気がします。

六角柱

image.png
iq氏のサイトに書かれているSDFはこちら。


float sdHexPrism( vec3 p, vec2 h )
{
  const vec3 k = vec3(-0.8660254, 0.5, 0.57735);
  p = abs(p);
  p.xy -= 2.0*min(dot(k.xy, p.xy), 0.0)*k.xy;//A
  vec2 d = vec2(
       length(p.xy-vec2(clamp(p.x,-k.z*h.x,k.z*h.x), h.x))*sign(p.y-h.x),//B
       p.z-h.y );//C
  return min(max(d.x,d.y),0.0) + length(max(d,0.0));//D
}

これもなんだか良くわからないベクトルがハードコーディングされています。
h.x…1辺の長さ h.y…柱の高さ
$k = (\frac{\sqrt{3}}{2},\frac{1}{2},\frac{1}{\sqrt{3}})$

行われている処理を分解してみましょう。

  • 座標の絶対値を取る $p = abs(p)$ これによって第一象限($x > 0,y > 0$)にのみ注目すればよくなる
  • 内積を利用して線Aを折り線としてAの左側の点を右側に移す($A$)
  • xy平面での六角形までの距離を取得して、符号をつける($B$)
  • 六角柱の高さの部分を切り取る($C$)
  • xy平面とz軸での長さを取得する($D$)

Picture004.png

TIPS

上記がExactな六角柱のSDFですが、実際にライブコーディング等でこんな式を暗記するのはあまり現実的ではありません。
代わりに上下反転させた三角柱を二つ重ねて共通部分を取ることでも似たような図形を作ることは可能です。

float sdHexPrismAlter(vec3 p, vec2 h){
    float result = sdTriPrism(p,h);
    p.y = -p.y;
    result = max(result,sdTriPrism(p,h));
    return result;
}

image.png

正八面体

image.png

正八面体です。実はPart1の距離についての解説でも同等な図形を紹介してはいるのですが、iq氏による実装をみてみましょう

float sdOctahedron( vec3 p, float s)
{
  p = abs(p);
  float m = p.x+p.y+p.z-s;
  vec3 q;
       if( 3.0*p.x < m ) q = p.xyz;
  else if( 3.0*p.y < m ) q = p.yzx;
  else if( 3.0*p.z < m ) q = p.zxy;
  else return m*0.57735027;

  float k = clamp(0.5*(q.z-q.y+s),0.0,s); 
  return length(vec3(q.x,q.y-s+k,q.z-k)); 
}

8面のうち、いずれかの面の真上に存在するときは$m * \frac{1}{\sqrt{3}}$(mはL1ノルムによる距離を測ったもの)を、それ以外の時はちゃんと計測して返しています。

おわりに

今回は角柱系の実装を紹介しました。ここら辺は一見するとわかりづらいマジックナンバーが大量に出現しがちなので読解に根気が必要になってしまいます。
次回はレイマーチング解釈シリーズのラスト、SDFの加工処理についてやって行こうと思います。

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Sign upLogin
1
Help us understand the problem. What are the problem?