isectHexPillar
#交差判定関数 #シェーダー
https://gyazo.com/ac33b63ef428aec54a822aa768487bf1
半直線と六角柱の交差判定関数
やりかた
基本アプローチはisectBoxと同じ
3D Convex Hullなので、ジオメトリを構成する無限平面のうち、 一番近い裏面 < 一番遠い表面 かつ 0 < 一番遠い表面 であれば、その表面が交差点となる
rd を踏まえ、3枚の側面・1枚の終端との交差判定を行う
60度ずつぐるぐるする dir が面の向きを表す
rdd = dot(rd, dir) が負であればその方向の面との交差判定をするし、正であれば逆の方向の面との交差判定をする
dst で SIZE に SQRT3 / 2.0 を乗じている
これは中心から辺までの距離が$ \frac{\sqrt3}{2}単位のため
($ 1なのは中心から頂点までの距離)
これ忘れやすそうなので注意
割とiqのisectBoxの手法にインスパイアされた手法です
Hexagonal Grid Traversalでも同様のことをする
code:glsl
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 our ray origin
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));
}
https://www.shadertoy.com/view/mt23Dt
ちなみに、iqの答え
iqさんの答えです
ぼくのコードでいうところの rdd ・ src ・ dst あたりが、iqさんのコードだとコンパクトにまとまっていて良いと思いました
でもぼくはよみやすいほうがすきです
さすがにmat4をつかうとかはやってない
https://www.shadertoy.com/view/tljBWy