はじめに
WebGLのレイマーチングの中でマウスとの当たり判定を作ってみました。
動機は、綺麗なデモは素晴らしいんだけど、ゲーム屋の俺としては当たり判定入れてゲームにしたいとなと思ったからです。
ソース
レイマーチングのソースは @doxas さんのコードをコピペしています。
https://qiita.com/doxas/items/477fda867da467116f8d
このリンクの記事のレイマーチングにマウスとの当たり判定を、ちょびっと書き足しました。
fragment.txt
precision mediump float;
uniform vec2 m;//mouse -1.0 〜 1.0
uniform float t;//time
uniform vec2 r;//resolution
struct Ray{
vec3 origin;
vec3 direction;
};
struct Sphere{
float radius;
vec3 position;
vec3 color;
};
bool intersectSphere(Ray R, Sphere S){
vec3 a = R.origin - S.position;
float b = dot(a, R.direction);
float c = dot(a, a) - (S.radius * S.radius);
float d = b * b - c;
if(d > 0.0){
float t = -b - sqrt(d);
return (t > 0.0);
}
return false;
}
void main(void){
// fragment position
vec2 p = (gl_FragCoord.xy * 2.0 - r) / min(r.x, r.y);
// ray init
Ray ray;
float z = 10.0;//書き足した
ray.origin = vec3(0.0, 0.0, z);
ray.direction = normalize(vec3(p.x, p.y, -1.0));
// sphere init
Sphere sphere;
sphere.radius = 4.0;
sphere.position = vec3(0.0);
sphere.color = vec3(1.0, 1.0, 1.0);
// ray hit check
vec3 destColor = vec3(0.0);
float flag = 0.0;
if(intersectSphere(ray, sphere)){
destColor = sphere.color;
flag += 1.0;
}
// mouse hit check ここも書き足した
vec3 a = sphere.position - vec3(m.x * z, m.y * z, 0.0);
//何故か半径を1.1倍にしないと上手くいかなかった。1.0倍でもそこそこいける。
float radius = sphere.radius * 1.1;
if (dot(a, a) < radius * radius && destColor.x == 1.0) {
destColor = vec3(1.0, 0.0, 0.0);
}
gl_FragColor = vec4(destColor, 1.0);
}
おわりに
フラグメントシェーダからシェーダ呼び出し元に値を返す事が出来れば完璧なのですが、この記事に書かれている https://qiita.com/emadurandal/items/4c7bd2a26ef2d732d734 WebGL2.0からのTransform Feedbackの機能を待つしかないようです。