Upgrade to Pro — share decks privately, control downloads, hide ads and more …

距離関数を極める! / SESSIONS 2024

がむ
November 17, 2024

距離関数を極める! / SESSIONS 2024

SESSIONS 2024の「セミナー3: レイマーチングの距離関数を極める! 」の資料です。
https://sessions-party.com/

がむ

November 17, 2024
Tweet

More Decks by がむ

Other Decks in Programming

Transcript

  1. 自己紹介 @gam0022(がむ)/ Sho HOSODA
 • ゲーム会社でグラフィックスエンジニア
 • 昨日のShader Jamsで解説
 Transcendental

    Cube by @gam0022 & @sadakkey 
 2nd place in GLSL Graphics Compo at SESSIONS 2023 
 新千歳空港国際アニメーション映画祭 
 NEW CHITOSE 30 Seconds部門 入選 
 CGWORLD vol.266(2020年10月号)寄稿 
 デモシーンを支えるプロシージャル技術 

  2. 直感とは逆の操作になる例(スケール) ※要注意 s倍にスケールしたい float map(vec3 p) { // s倍にスケールするなら、 //

    pを1/s倍し、さらに距離関数をs倍する float d = primitive(p / s) * s; return d; } 謎の * s🤔 ▪ p/sの空間は、元の空間pよりも スケールが1/s倍に小さくなっている ▪ 元の空間pのスケールに戻すために 距離関数をs倍する必要がある
  3. 符号付き(signed)の距離関数 符号付き(signed)の距離関数 float sdBox(vec3 p, vec3 b) { vec3 q

    = abs(p) - b; return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0); } 負の領域 等高線を可視化
  4. 符号なし(UN signed)の距離関数 負の領域の処理を消すと、unsignedなudBoxになる float udBox(vec3 p, vec3 b) { vec3

    q = abs(p) - b; return length(max(q, 0.0)); } 📝通常のレイマーチングでは 負の領域がなくてもOK

  5. 罠: Subtractionにはsignedが必要 // d1からd2を切り抜く float opSubtraction(float d1, float d2) {

    return max(d1, -d2); } signedなら 符号を反転できる boolean演算の Subtraction(差集合)
  6. 罠: 半透明のレイマーチングにはsignedの距離関数が必要 Refraction & Hue shift by setchi https://www.shadertoy.com/view/MdVfWy vec2

    intersect(vec3 ro, vec3 ray) { float t = 0.0; for (int i = 0; i < 256; i++) { // 距離関数にabsをつけてオブジェクトの内部もレイが進めるようにする float res = abs(map(ro + ray * t)); if (res < 0.005) return vec2(t, res); t += res; } return vec2(-1.0); }
  7. 罠: 衝突点からレイを反射・屈折にはオフセットが必要 Refraction & Hue shift by setchi https://www.shadertoy.com/view/MdVfWy bool

    into = dot(-ray, nor) > 0.0; nor = into ? nor : -nor; eta = into ? 1.0 / eta : eta; ro = pos - nor * OFFSET; ray = refract(ray, nor, eta); kiNaNkomoti さん提供🙏 

  8. exactとNOT exactな距離関数の比較 「exact」なsdBox 「NOT exact」なsdBox float sdBox_NOT_exact(vec3 p, vec3 b)

    { vec3 q = abs(p) - b; return max(max(q.x, q.y), q.z); } float sdBox(vec3 p, vec3 b) { vec3 q = abs(p) - b; return length(max(q, 0.0)) + min(max(q.x, max(q.y, q.z)), 0.0); } 角の距離が不正確👀
  9. テクニック: modによる繰り返し vec3 a = vec3(2); p = mod(p, a)

    - 0.5 * a; Kamoshika Shader Jams SESSIONS 2024

  10. 罠: modによる繰り返しにランダムな変形をすると レイが突き抜けてアーティファクトが起きる グリッドを飛び越えないように制限する実装 by ukeyshima float distance=0.0; float rLen=0.0;

    vec3 rPos=origin; vec3 color=vec3(0.1); for(int i=0;i<100;i++){ distance=distFunc(rPos); if(abs(distance)<0.01){ if(distance==floorDistFunc(rPos)){ color=vec3(0.3,0.3,0.7); } break; } float t1=(1.0-fract(rPos.z))/ray.z; vec3 d1=rPos+t1*ray; float t2=(1.0-fract(rPos.x))/ray.x; vec3 d2=rPos+t2*ray; float t3=(-fract(rPos.x))/ray.x; vec3 d3=rPos+t3*ray; float t4=(-fract(rPos.z))/ray.z; vec3 d4=rPos+t4*ray; if(ray.z>0.0 && d1.x>=floor(rPos.x) && d1.x<=floor(rPos.x)+1.0 && abs(distance)>=length(t1*ray)){ rLen+=length(t1*ray)+0.01; }else if(ray.x>0.0 && d2.z>=floor(rPos.z) && d2.z<=floor(rPos.z)+1.0 && abs(distance)>=length(t2*ray)){ rLen+=length(t2*ray)+0.01; }else if(ray.x<0.0 && d3.z>=floor(rPos.z) && d3.z<=floor(rPos.z)+1.0 && abs(distance)>=length(t3*ray)){ rLen+=length(t3*ray)+0.01; }else if(ray.z<0.0 && d4.x>=floor(rPos.x) && d4.x<=floor(rPos.x)+1.0 && abs(distance)>=length(t4*ray)){ rLen+=length(t4*ray)+0.01; }else{ rLen+=distance; } rPos=origin+rLen*ray; }
  11. vec3 trace( vec3 ro, in vec3 rd, in float tmin,

    in float tmax ) { ro += tmin*rd; vec2 pos = floor(ro.xz); vec3 rdi = 1.0/rd; vec3 rda = abs(rdi); vec2 rds = sign(rd.xz); vec2 dis = (pos-ro.xz+ 0.5 + rds*0.5) * rdi.xz; vec3 res = vec3( -1.0 ); // traverse regular grid (in 2D) vec2 mm = vec2(0.0); for( int i=0; i<28; i++ ) { vec3 cub = mapH( pos ); #if 1 vec2 pr = pos+0.5-ro.xz; vec2 mini = (pr-0.5*rds)*rdi.xz; float s = max( mini.x, mini.y ); if( (tmin+s)>tmax ) break; #endif // intersect box vec3 ce = vec3( pos.x+0.5, 0.5*cub.x, pos.y+0.5 ); vec3 rb = vec3(0.3,cub.x*0.5,0.3); vec3 ra = rb + 0.12; vec3 rc = ro - ce; float tN = maxcomp( -rdi*rc - rda*ra ); float tF = maxcomp( -rdi*rc + rda*ra ); if( tN < tF )//&& tF > 0.0 ) { // raymarch float s = tN; float h = 1.0; for( int j=0; j<24; j++ ) { h = udBox( rc+s*rd, rb, 0.1 ); s += h; if( s>tF ) break; } if( h < (surface*s*2.0) ) { res = vec3( s, cub.yz ); break; } } // step to next cell mm = step( dis.xy, dis.yx ); dis += mm*rda.xz; pos += mm*rds; } res.x += tmin; return res; }
  12. 罠: NOT constantな変形をすると レイが突き抜けてアーティファクトが起きる LiveCoding by gam0022 「WebGL 総本山 +

    normalize.fm あわせて 13周年感謝祭」夜の部 Shader Jams float sdN(vec3 p, float z) { rot(p.xy, -0.07 * TAU); if (p.x < 0.) p.y = -p.y; // X軸方向にミラーリング p.x = abs(p.x); float w = 0.13; float h = 0.07; float s = 4.; float a = w / h / s * p.y; return min( sdBox(p, vec3(0.2, h, z)), // pや箱のサイズを座標に応じて変化することでskew sdBox(p - vec3(0.25 - a, h * (s - 1.), 0), vec3(w - a, h * s, z))); }
  13. テクニック: Log-polar Mapping in 3D / Log-spherical Mapping https://www.osar.fr/notes/logspherical/ Kamoshika

    Shader Jams SESSIONS 2024 Log-polar Mapping in 3Dの例 Kamoshima さん提供🙏 

  14. テクニック: コンパイル高速化の#define ZERO GLSLでは [unroll] と [loop] を指定できない (コンパイラが自動判断する) ▪

    [unroll] ループを展開 ▫ コードが肥大化 ▫ コンパイル時間の増加になる ▪ [loop] ループと展開しない Raymarching - Primitives by iq https://www.shadertoy.com/view/Xds3zN
  15. テクニック: コンパイル高速化の#define ZERO Raymarching - Primitives by iq https://www.shadertoy.com/view/Xds3zN #define

    ZERO (min(iFrame,0)) // https://iquilezles.org/articles/nvscene2008/rwwtt.pdf float calcAO( in vec3 pos, in vec3 nor ) { float occ = 0.0; float sca = 1.0; for( int i=ZERO; i<5; i++ ) { float h = 0.01 + 0.12*float(i)/4.0; float d = map( pos + h*nor ).x; occ += (h-d)*sca; sca *= 0.95; if( occ>0.35 ) break; } return clamp( 1.0 - 3.0*occ, 0.0, 1.0 ) * (0.5+0.5*nor.y); }