🎡Shadertoyで動作する3Dの床を表示するためのレイマーチングのコード
Shadertoyで動作する3Dの床を表示するためのレイマーチングのコードを作成する際、床を描画するための基本的なアプローチは、レイがy=0の平面(床)に到達する点を見つけることです。以下は、そのようなシンプルなレイマーチング法を用いた床を描画するためのGLSLコードの例です。このコードはShadertoyで直接動作します。
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
// 正規化デバイス座標(NDC)へ変換
vec2 uv = (fragCoord - 0.5 * iResolution.xy) / iResolution.y;
// カメラ設定
vec3 ro = vec3(0.0, 1.0, 3.0); // カメラの位置
vec3 rd = normalize(vec3(uv, -1.0)); // カメラからのレイの方向
// 床との交差点を計算
float t = -ro.y / rd.y;
vec3 p = ro + t * rd;
// 床の描画
if(t > 0.0) {
// 床の色を計算(シンプルなチェック模様)
float checks = mod(floor(p.x) + floor(p.z), 2.0);
vec3 col = mix(vec3(0.8), vec3(0.4), checks);
fragColor = vec4(col, 1.0);
} else {
// 背景色
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
}
}
このコードでは、以下の手順で床を描画しています:
ビューポート座標の変換: fragCoord(フラグメントの座標)を使用して、画面の中心を原点とする正規化デバイス座標(NDC)に変換します。これにより、画面の中心が(0,0)になり、y軸が画面の縦方向の長さに正規化されます。
カメラの設定: カメラの位置(ro)とレイの方向(rd)を設定します。この例では、カメラは原点の上に位置し、向きは画面に向かっています。
床との交差点の計算: レイがy=0の平面(床)と交差する点を計算します。この交差点は、レイの方向のy成分でカメラの位置のy成分を割ることによって得られます。
床の描画: 交差点が正の場合(カメラの前にある場合)、床を描画します。この例では、床にシンプルなチェック模様を適用しています。それ以外の場合(レイが上向きまたは交差しない場合)は、背景色を黒に設定します。
このコードをShadertoyに貼り付けて実行すると、y=0の平面にチェック模様の床が描画されるはずです。カメラの位置や向き、床の模様など、さらに詳細な設定を調整することで、さまざまな3Dシーンを作成できます
ループをさせなくてもレイマーチングというのですか?
レイマーチングについてのご質問にお答えします。レイマーチングという用語は、一般に、シーン内のオブジェクトに対する視線(レイ)の交差を検出するために使用される技術を指します。特に、3Dグラフィックスにおいて、レイマーチングはシーンの形状や表面をサンプリングするためにレイを進行させる方法を表します。このプロセスは通常、反復的な手順によって行われ、各ステップでレイを少しずつ前進させ、シーンの形状との交差点を検出します。
しかし、私が以前に提供したコードの例では、床とレイの交差を計算するために直接的な数学的解法を使用しています。この場合、レイが平面と交差する点は、レイの方向ベクトルと平面の方程式を用いて直接計算されます。このようなアプローチでは、実際には反復的なプロセス(ループ)を使用せずに交差点を見つけることができるため、「レイマーチング」という用語は技術的には正確ではないかもしれません。
この直接的な交差計算法は、特定の単純な形状(例えば、平面や球)に対しては非常に効率的ですが、より複雑なシーンや形状に対しては適用できないことがあります。複雑な形状やシーンに対しては、実際にレイマーチング(すなわち、レイを進行させるための反復的なプロセス)を使用することが一般的です。
要するに、レイと平面の交差点を直接計算する方法は、伝統的なレイマーチングのアプローチとは異なります。伝統的なレイマーチングは、シーンの探索にループ(反復的なステップ)を使用し、複雑な形状やマテリアルとの交差を検出するために用いられます。
void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
// 正規化デバイス座標(NDC)へ変換
vec2 uv = (fragCoord - iResolution.xy * 0.5) / iResolution.y;
// カメラ設定
vec3 ro = vec3(0.0, 1.0, 3.0); // カメラの位置
vec3 rd = normalize(vec3(uv, -1.0)); // カメラからのレイの方向
// レイマーチングのパラメータ
float minDist = 0.01; // 最小距離
float maxDist = 100.0; // 最大距離
float surfaceDist = maxDist; // 表面までの距離
bool hitSurface = false; // 表面に到達したかどうか
// レイマーチング
float t = 0.0;
for(int i = 0; i < 100; i++) {
vec3 p = ro + t * rd; // 現在の点
float d = p.y; // y=0の平面との距離
if(d < minDist) {
// 表面に到達
hitSurface = true;
surfaceDist = t;
break;
}
t += d; // レイを進める
if(t > maxDist) break; // 最大距離を超えたら終了
}
// 描画
if(hitSurface) {
// 床の色
fragColor = vec4(vec3(0.5 + 0.5 * sin(surfaceDist * 0.1)), 1.0);
} else {
// 背景色
fragColor = vec4(0.0, 0.0, 0.0, 1.0);
}
}
このコードでは、レイが床(この場合はy=0の平面)と交差するかどうかを確認するために、レイマーチングのループを使用しています。レイはカメラの位置から開始し、指定された方向に徐々に進みます。各ステップで、レイが平面に十分に近づいたかどうか(minDistで指定された距離よりも小さいかどうか)をチェックします。もしレイが平面に到達した場合は、その位置でループを終了し、床の色を計算しています。レイが最大距離(maxDistで指定)を超えても平面に到達しない場合は、背景色を描画します。
この例は、レイマーチングの基本的な実装を示しており、床の表面の描画に限定されています。さらに複雑なシーンや形状を扱うには、距離関数を調整し、光の計算やマテリアルの詳細を追加する必要があります