カメラ
3Dの情報を2Dに変換して描画するにはカメラが必要
ro( カメラ(レイの原点))とrd(レイの方向)を定義する
3D空間におけるカメラから特定の点(p)までの距離を計算
ro( カメラ(レイの原点))とrd(レイの方向)を定義について
カメラが原点の場合
スクリーンの位置: スクリーンがカメラから少し離れた位置(例えば z = 1.0 )にあると仮定
レイの方向:レイは常にカメラから z = 1.0 の平面に向かって直線的に進む
vec3 ro = vec3(0.0);
vec3 rd = normalize(vec3(uv, 1.0));
カメラの位置が x や y で 0.0 以外の場合
スクリーンの位置:スクリーンが z = 0.0 の平面上にあると仮定
レイの方向ベクトルの計算:カメラの位置 ro から見たスクリーン上の各点までの実際の方向を求めるために、vec3(uv, 0.0) から ro を引いて計算
// レイの起点: z軸上でカメラを少し後ろに
vec3 ro = vec3(0.5, 0.0, -1.0);
vec3 rd = normalize(vec3(uv, 0.0)-ro);
3D空間におけるカメラから特定の点(p)までの距離を計算
//カメラから無限に伸びる直線と点 p の距離が知りたい場合
//点pとレイroとの最短距離を計算する(両方向)
float DistLine(vec3 ro, vec3 rd, vec3 p) {
return length(cross(p-ro, rd))/length(rd);
}
//特定の方向に向かうレイの距離を知りたい場合
//点pとレイroとの最短距離を計算する(特定の方向)
float DistRay(vec3 ro, vec3 rd, vec3 p) {
vec3 fromOriginToPoint = p - ro; // レイの原点から点pへのベクトル
float projectionLength = dot(fromOriginToPoint, rd); // レイの方向に対する点pの射影の長さ
vec3 closestPoint;
if (projectionLength < 0.0) {
// レイの起点より前方(逆方向)にある場合、最近点はレイの起点
closestPoint = ro;
} else {
// それ以外の場合、最近点はレイ上の計算された位置
closestPoint = ro + projectionLength * rd;
}
return length(p - closestPoint); // 最近点から点pまでの距離を返す
}
特定の点(p)を動かす
void main() {
vec2 uv = (vUv * 2.0 - 1.0) * vec2(uResolution.x / uResolution.y, 1.0);
// レイの起点: z軸上でカメラを少し後ろに
vec3 ro = vec3(0.5, 0.0, -1.0);
// レイの方向: スクリーンがz=0.0の平面上にあると仮定してカメラから見たスクリーン上の各点までの実際の方向
vec3 rd = normalize(vec3(uv, 0.0)-ro);
vec3 p = vec3(0.0, 0.0, 0.2 + sin(uTime));
//点pとレイroとの最短距離
float d = DistLine(ro, rd, p);
d = smoothstep(0.1, 0.09, d);
gl_FragColor = vec4(vec3(d), 1.0);
}
カメラを動かす場合
カメラの方向ベクトル(f、r、u)を計算する必要がある
注視する点(lookat)と、カメラからスクリーンまでの距離(zoom)を定義
f (前方向ベクトル): lookat から ro への方向を正規化
r (右方向ベクトル):上方向ベクトルvec3(0.0, 1.,0 0.0)と f の外積
u (上方向ベクトル): f と r の外積
//struct キーワードを用いて構造体を定義
struct ray {
vec3 o; // レイの原点
vec3 d; // レイの方向
};
ray CameraSetup(vec2 uv, vec3 camPos, vec3 lookAt, vec3 up, float zoom) {
ray cam;
//レイの原点
cam.o = camPos;
//カメラのxyz軸(f、r、u)を計算
vec3 f = normalize(lookAt - cam.o);
vec3 r = normalize(cross(up, f));
vec3 u = normalize(cross(f, r));
//視野平面の中心点
vec3 c = cam.o + f * zoom;
//視野内のスクリーン上の点i
vec3 i = c + uv.x * r + uv.y * u;
//レイの方向
cam.d = normalize(i - cam.o);
return cam;
}
void main() {
vec2 uv = (vUv * 2.0 - 1.0) * vec2(uResolution.x / uResolution.y, 1.0);
float zoom = 1.0;
vec3 camPos = vec3(0.0 + sin(uTime), 0.0, -2.0 + sin(uTime));
vec3 lookAt = vec3(0.0);
/*
カメラのアップベクトル:カメラの「上」方向が常にY軸方向
vec3(0.0, 1.0, 0.0)
例えば、カメラを45度傾ける場合のアップベクトル
vec3(0.707, 0.707, 0.0); // X軸とY軸45度
*/
vec3 up = vec3(0.0, 1.0, 0.0);
ray r = CameraSetup(uv, camPos, lookAt, up, zoom);
float d = DistLine(r.o, r.d, vec3(0.0, 0.0, 0.0));
float d2 = DistLine(r.o, r.d, vec3(0.5, 0.5, 0.0));
float d3 = DistLine(r.o, r.d, vec3(-0.5, -0.5, 0.0));
float d4 = DistLine(r.o, r.d, vec3(0.5, -0.5, 0.0));
float d5 = DistLine(r.o, r.d, vec3(-0.5, 0.5, 0.0));
d = smoothstep(0.1, 0.09, d);
d += smoothstep(0.1, 0.09, d2);
d += smoothstep(0.1, 0.09, d3);
d += smoothstep(0.1, 0.09, d4);
d += smoothstep(0.1, 0.09, d5);
gl_FragColor = vec4(vec3(d), 1.0);
}
球体とレイとの交差
球体の定義
- s:球体の中心
- r:球体の半径
- t :レイのパラメータで、レイ上の位置を決定
t = dot(s – ro, rd) - p:レイ上の点の位置
p = ro + rd * t - y:球体の中心からレイ上の最も近い点までの距離
y = length(s – p)
y < r なら、レイは球体と交差してこのとき t1 と t2 が実数値 - x:球体の中心に最も近い点から球体の表面までの距離
x = sqrt(r * r – y * y) - t1:レイが球体に入る点
t1 = t – x - t2: はレイが球体から出る点
t2 = t + x
float remap01(float a, float b, float t){
return (t-a)/(b-a);
}
void main() {
vec2 uv = (vUv * 2.0 - 1.0) * vec2(uResolution.x / uResolution.y, 1.0);
vec3 col = vec3(0.0);
vec3 ro = vec3(0.0);
vec3 rd = normalize(vec3(uv, 1.0));
vec3 s = vec3(0.0, 0.0, 4.0);
float r = 1.5;
float t = dot(s - ro, rd);
/*
備考:外積を使ってレイに対する垂線の長さを計算
float y = DistLine(ro, rd, s);
*/
vec3 p = ro + rd * t;
float y = length(s - p);
// レイが球と交差しているかを判定
if (y < r) {
// 交差している場合
// 交差点の距離を計算
float x = sqrt(r * r - y * y);
// レイが球に入る点と出る点のパラメータを計算
float t1 = t - x;
float t2 = t + x;
// 球のZ座標に基づいて色をリマップ : c=t1なら白
float c = remap01(s.z, s.z - r, t1);
col = vec3(c);
}
gl_FragColor = vec4(col, 1.0);
}
球体に対してマッピングする
*球体のUVマッピングは、「前面」と「背面」が存在
(U, V) = (0, 0) は球体の「背面」に配置され、「前面」に向かってテクスチャが展開される
mat2 Rot(float a) {
float s=sin(a), c=cos(a);
return mat2(c,-s,s,c);
}
vec3 col = vec3(0.0);
vec3 ro = vec3(0.0, 0.0, 0.0);
vec3 rd = normalize(vec3(uv, 1.0));
vec3 s = vec3(0.0, 0.0, 3.0);
float r = 1.0;
float t = dot(s - ro, rd);
vec3 p = ro + rd * t;
float y = length(s - p);
if (y < r) {
float x = sqrt(r*r - y * y);
float t1 = t - x;
float t2 = t + x;
//t1とt2の球体の中心sに対して相対的な位置
vec3 posF = ro + rd * t1 - s;
vec3 posB = ro + rd * t2 - s;
// 回転
posF.xz *= Rot(uTime);
//球体表面の交差点の座標と、その座標を基にしたUVマッピング座標を計算
vec2 uvF = vec2(atan( posF.x, posF.z), posF.y);
vec2 uvB = vec2(atan( posB.x, posB.z), posB.y);
//前面のマッピング座標
float d = length(uvF);
float m = smoothstep(0.5,0.6, d);
col = vec3(m);
}
gl_FragColor = vec4(col, 1.0);
このページのエフェクトのフラグメントシェーダーのコード
コードを見る
uniform vec2 uResolution;
uniform float uTime;
varying vec2 vUv;
float smax( float a, float b, float k ) {
float h =clamp( 0.5 + 0.5*(b-a)/k, 0.0, 1.0);
return mix( a, b, h ) + k* h *(1.0-h);
}
float Heart(vec2 uv){
uv *= 3.0;
uv.x *= 0.65;
float r = 0.4;
float b = 0.15;
uv.y -= smax(sqrt(abs (uv.x)) * 0.7, b, 0.1);
float d = length(uv);
return smoothstep(r + b, r-b-0.01, d);
}
mat2 Rot(float a) {
float s=sin(a), c=cos(a);
return mat2(c,-s,s,c);
}
vec3 Transform(vec3 p, float a){
p.xz *=Rot(a);
p.xy *=Rot(a*0.3);
return p;
}
float Ball(vec3 ro, vec3 rd, vec3 s, float angle){
float r = 1.0;
float t = dot(s - ro, rd);
vec3 p = ro + rd * t;
float y = length(s - p);
float m = 0.0;
if (y < r) {
float x = sqrt(r*r - y * y);
float t1 = t - x;
float t2 = t + x;
vec3 posF = ro + rd * t1 - s;
vec3 posB = ro + rd * t2 - s;
posF = Transform(posF, angle);
posB = Transform(posB, angle);
vec2 uvF = vec2(atan( posF.x, posF.z), posF.y);
vec2 uvB = vec2(atan( posB.x, posB.z), posB.y);
vec2 scale = vec2(0.6, 0.6);
uvF *= scale;
uvB *= scale;
m = Heart(uvB)+ Heart(uvF);
}
return m;
}
void main() {
vec2 uv = (vUv * 2.0 - 1.0) * vec2(uResolution.x / uResolution.y, 1.0);
vec3 col = vec3(0.0);
vec3 ro = vec3(0.0, 0.0, 0.0);
vec3 rd = normalize(vec3(uv, 1.0));
float t = uTime * 0.6;
float t2 = uTime * 0.2;
float m = 0.0;
float stp = 1.0 / 40.0;
for(float i=0.0; i<1.0; i+=stp) {
float x = mix(-5.0, 5.0, fract(sin(i*564.3)*4570.3));
float y = mix(-5.0, 5.0, fract(i+t*0.1));
float z = mix(4.0, 3.0, i);
float a = t2 + i *564.3;
float d = Ball(ro, rd, vec3(x, y, z), a);
m = max(m, clamp(d, 0.0, 1.0));
}
col = mix(col, vec3( cos(t2)*0.5+0.5, 0.1, sin(t2)*0.5+0.5), m);
vec4 finalcol = vec4(col, 0.3);
finalcol = pow(finalcol, vec4(.4545));
gl_FragColor = finalcol;
}