isectHexPillar
https://gyazo.com/ac33b63ef428aec54a822aa768487bf1
3D Convex Hullなので、ジオメトリを構成する無限平面のうち、 一番近い裏面 < 一番遠い表面 かつ 0 < 一番遠い表面 であれば、その表面が交差点となる rd を踏まえ、3枚の側面・1枚の終端との衝突判定を行う
60度ずつぐるぐるする dir が面の向きを表す
rdd が正であればその方向の面との衝突判定をするし、負であれば逆の方向の面との衝突判定をする
dst で SIZE に SQRT3 / 2.0 を乗じている
これは中心から辺までの距離が$ \frac{\sqrt3}{2}単位のため
($ 1なのは中心から頂点までの距離)
これ忘れやすそうなので注意
code:glsl
const float FAR = 1E9;
vec4 isectHexPillar( vec3 ro, vec3 rd, float r, float l ) {
float f = -FAR;
float b = FAR;
vec3 n;
// six hexagonal sides
for ( int i = 0; i < 3; i ++ ) {
vec3 dir = vec3( cis( PI * ( 0.5 + float( i ) ) / 3.0 ), 0.0 );
float rdd = dot( rd, dir );
float src = -dot( ro, dir ) / rdd;
float dst = abs( ( SQRT3 / 2.0 ) * r / rdd );
b = min( b, src + dst );
float f2 = src - dst;
if( f < f2 ) {
f = f2;
n = dir * sign( rdd );
}
}
// ends
{
vec3 dir = vec3( 0.0, 0.0, 1.0 );
float rdd = dot( rd, dir );
float src = -dot( ro, dir ) / rdd;
float dst = abs( l / rdd );
b = min( b, src + dst );
float f2 = src - dst;
if ( f < f2 ) {
f = f2;
n = dir * sign( rdd );
}
}
// if back comes faster than front, that's out of the plane
// if front is smaller than zero, that's behind us
if ( f < b && 0.0 < f ) {
return vec4( n, f );
} else {
return vec4( 0.0, 0.0, 0.0, FAR );
}
}
mat4を用いた最適化
上のコードだと、for文回して2枚ずつ交差判定しているけど、mat4使ったらこの8枚の交差判定一気にできるよ
mat4の4x3を使って4枚の平面の向きを持つ
vec4とmat4の掛け算は、ある一つのベクトルと4つのベクトルとの内積の計算を同時にやっていることになる
mat4とvec4の順番気をつけてね
rdd と src のほうは、mat4内で定義したベクトルの通りに内積が欲しい
n のほうは、衝突した面の成分が1となるベクトルで、mat4からお望みの面の向きベクトルを取り出したい
引き続き、iqのisectBoxの手法にインスパイアされた手法です
code:glsl
vec4 isecthex( vec3 ro, vec3 rd, float r, float l ) {
// 4枚の面の向き。wは無視
mat4 dir = mat4(
vec4( SQRT3 / 2.0, 0.5, 0.0, 0.0 ),
vec4( 0.0, 1.0, 0.0, 0.0 ),
vec4( -SQRT3 / 2.0, 0.5, 0.0, 0.0 ),
vec4( 0.0, 0.0, 1.0, 0.0 )
);
// vec4とmat4の掛け算で、4枚分の内積計算を一気に行う
vec4 rdd = vec4( rd, 0.0 ) * dir; // レイの向きと面の向きの近さ。小さいほどレイは伸びる。符号も大事
vec4 src = -( vec4( ro, 0.0 ) * dir ) / rdd; // 面が原点にある場合はこれがそのまま面までの距離
vec4 dst = abs( vec2( ( SQRT3 / 2.0 ) * r, l ).xxxy / rdd ); // 面が原点から離れてる分を計算
// 一番近い裏面を探す
vec4 bv = src + dst;
float b = min( min( bv.x, bv.y ), min( bv.z, bv.w ) );
// 一番遠い表面を探す
vec4 fv = src - dst;
float f = max( max( fv.x, fv.y ), max( fv.z, fv.w ) );
// 交差面の情報を使って、法線を求める
vec4 mask = step( f, fv );
vec3 n = ( dir * mask ).xyz * sign( dot( rdd, mask ) );
// 一番遠い表面が一番近い裏面より近かった場合はおめでとう、そうでなかった場合はざんねん
float isvalid = step( f, b ) * step( 0.0, f );
return vec4( n, mix( FAR, f, isvalid ) );
}
ちなみに、iqの答え
iqさんの答えです
ぼくのコードでいうところの rdd ・ src ・ dst あたりが、iqさんのコードだとコンパクトにまとまっていて良いと思いました
でもぼくはよみやすいほうがすきです
さすがにmat4をつかうとかはやってない