GLSLでレイトレーシングするときにレイを作成する方法を忘れるのでメモ。
透視投影カメラでレイを作成するため関数は次のようになる。
引数に渡されたorigin
とray
にそれぞれレイの原点と方向がセットされる。
origin
はカメラの位置position
と同じなのでout
を用いずray
を返り値にするだけでもよいが、後述する平行投影カメラのレイを作成する関数orthographic
と形をできるだけ似せるためにこのようにした。
// perspective camera
// @param st - sceen position in [0, 1]
// @param position - position of camera
// @param target - target of camera
// @param vup - up direction of camera
// @param vfov - vertical field of view
// @param aspect - aspect rage (x / y)
// @param origin - ray origin
// @param ray - ray direction
void perspective(in vec2 st, in vec3 position, in vec3 target, in vec3 vup, in float vfov, in float aspect, out vec3 origin, out vec3 ray) {
vec2 uv = st * 2.0 - 1.0;
float radian = vfov * PI / 180.0;
float h = tan(radian * 0.5);
float w = h * aspect;
vec3 front = normalize(target - position);
vec3 right = normalize(cross(front, normalize(vup)));
vec3 up = normalize(cross(right, front));
ray = normalize(right * w * uv.x + up * h * uv.y + front);
origin = position;
}

平行投影カメラでレイを作成するための関数は次のようになる。
先に述べたように引数はperspective
とほぼ同じ。
// orthographic camera
// @param st - sceen position in [0, 1]
// @param position - position of camera
// @param target - target of camera
// @param vup - up direction of camera
// @param height - height of camera view
// @param aspect - aspect rage (x / y)
// @param origin - ray origin
// @param ray - ray direction
void orthographic(in vec2 st, in vec3 position, in vec3 target, in vec3 vup, in float height, in float aspect, out vec3 origin, out vec3 ray) {
vec2 uv = st * 2.0 - 1.0;
float width = height * aspect;
vec3 front = normalize(target - position);
vec3 right = normalize(cross(front, normalize(vup)));
vec3 up = normalize(cross(right, front));
ray = front;
origin = position + right * width * 0.5 * uv.x + up * height * 0.5 * uv.y;
}

以下は、この関数を使ったGLSLプログラムの例である。
main関数内でperspective
とorthographic
のどちらかを選択する。
glslsandbox.comを使って作成したので、uniform変数などはそれに従ってる。
# ifdef GL_ES
precision mediump float;
# endif
# extension GL_OES_standard_derivatives : enable
# define PI 3.14159265359
uniform float time;
uniform vec2 mouse;
uniform vec2 resolution;
float random(vec2 st) {
return fract(sin(dot(st.xy, vec2(12.9898, 78.233))) * 43758.5453123);
}
vec3 repete(vec3 p, vec3 interval) {
return mod(p, interval) - interval / 2.0;
}
vec3 translate(vec3 p, vec3 offset) {
return p - offset;
}
float sphere(vec3 p, float radius) {
return length(p) - radius;
}
float box(vec3 p, vec3 size) {
return length(max(abs(p) - size / 2.0, 0.0));
}
float scene(vec3 p) {
vec3 q = repete(p, vec3(2.0, 0.0, 2.0));
vec3 sp = translate(q, vec3(-0.5, 0.0, 0.0));
vec3 bp = translate(q, vec3(0.5, 0.0, 0.0));
return min(sphere(sp, 0.5), box(bp, vec3(0.5)));
}
vec3 normal(vec3 p) {
float d = 0.00001;
return normalize(vec3(
scene(p + vec3(d, 0.0, 0.0)) - scene(p + vec3(- d, 0.0, 0.0)),
scene(p + vec3(0.0, d, 0.0)) - scene(p + vec3(0.0, - d, 0.0)),
scene(p + vec3(0.0, 0.0, d)) - scene(p + vec3(0.0, 0.0, - d))
));
}
// perspective camera
// @param st - sceen position in [0, 1]
// @param position - position of camera
// @param target - target of camera
// @param vup - up direction of camera
// @param vfov - vertical field of view
// @param aspect - aspect rage (x / y)
// @param origin - ray origin
// @param ray - ray direction
void perspective(in vec2 st, in vec3 position, in vec3 target, in vec3 vup, in float vfov, in float aspect, out vec3 origin, out vec3 ray) {
vec2 uv = st * 2.0 - 1.0;
float radian = vfov * PI / 180.0;
float h = tan(radian * 0.5);
float w = h * aspect;
vec3 front = normalize(target - position);
vec3 right = normalize(cross(front, normalize(vup)));
vec3 up = normalize(cross(right, front));
ray = normalize(right * w * uv.x + up * h * uv.y + front);
origin = position;
}
// orthographic camera
// @param st - sceen position in [0, 1]
// @param position - position of camera
// @param target - target of camera
// @param vup - up direction of camera
// @param height - height of camera view
// @param aspect - aspect rage (x / y)
// @param origin - ray origin
// @param ray - ray direction
void orthographic(in vec2 st, in vec3 position, in vec3 target, in vec3 vup, in float height, in float aspect, out vec3 origin, out vec3 ray) {
vec2 uv = st * 2.0 - 1.0;
float width = height * aspect;
vec3 front = normalize(target - position);
vec3 right = normalize(cross(front, normalize(vup)));
vec3 up = normalize(cross(right, front));
ray = front;
origin = position + right * width * 0.5 * uv.x + up * height * 0.5 * uv.y;
}
void main( void ) {
vec2 st = gl_FragCoord.xy / resolution;
vec3 position = vec3(mouse.x * 10.0 - 5.0, mouse.y * 10.0 - 5.0, 5.0);
vec3 target = vec3(0.0, 0.0, 0.0);
vec3 light = normalize(vec3(1.0, 2.0, 3.0));
vec3 origin, ray;
perspective(st, position, target, vec3(0.0, 1.0, 0.0), 60.0, resolution.x / resolution.y, origin, ray);
//orthographic(st, position, target, vec3(0.0, 1.0, 0.0), 10.0, resolution.x / resolution.y, origin, ray);
float d = 0.0;
vec3 p = origin;
for(int i = 0; i < 64; i++) {
d = scene(p);
p += ray * d;
}
vec3 color = abs(d) < 0.001 ? clamp(dot(normal(p), light), 0.0, 1.0) * vec3(1.0, 1.0, 1.0) + vec3(0.2) : vec3(0.0);
gl_FragColor = vec4(color, 1.0);
}