レイマーチングだと簡単にオブジェクトを繰り返しできるのですが、遠くの方は繰り返しが途中で終わったり、エイリアスが発生したりして絵が汚くなりがちです。
例えば、以下のシーンでは球が途中が途切れていたり、地面のチェッカー模様が奥のほうでごちゃごちゃしたりしています。

このようなときは、カメラからの距離に応じてフォッグをかけてごまかすのがよさそうです。
透過率(フォッグのかからない具合)を$f$として$[0, 1]$の範囲で表すと、元の色${\bf c_i}$とフォッグの色${\bf c_f}$を$f$で補間することでフォッグをかけることができます。
{\bf c} = (1 - f){\bf c_f} + f{\bf c_i}
GLSLで書くと単純にmix
関数で実装できます。
vec3 color = mix(fogColor, sceneColor, fogIntensity);
問題はどのように$f$を求めるかですが、いくつか方法があります。
線形フォッグ
以下の式で$f$を求めると、$d_{start}$より手前では$f=1$、$d_{end}$より奥では$f=0$、$d_{start}$から$d_{end}$の間では$f$が$1$から$0$に線形に変化するようになります。
f = clamp(\frac{d_{end} - d}{d_{end} - d_{start}}, 0, 1)
GLSLで実装すると以下のようになります。
float linearFog(float d, float start, float end) {
return clamp((end - d) / (end - start), 0.0, 1.0);
}
上述したシーンに線形フォッグを適用すると次のようになります。線形フォッグはパラメータが直感的でわかりやすいですが、フォッグのかかっている箇所とかかかっていない箇所がくっきりしており、リアルではないかなという印象です。

指数フォッグ 1
指数を用いて$f$を求める方法です。$density$には$0$から$1$の間の値を指定します。
f = e^{-d \times density}
グラフにすると以下のようになり、距離に応じてなだらかに$f$の値が変化することがわかります。

GLSLで実装すると、このようになります。
float expFog(float d, float density) {
return exp(-d * density);
}
このフォッグを最初のシーンに適用すると、線形フォッグのときよりもリアリティは増しますが、手前のオブジェクトにもフォッグがかかりモヤっとした印象になりました。

指数フォッグ 2
先に紹介した方法と式が似ていますが、指数部分を2乗しています。
f = e^{-(d \times density)^2}
グラフにすると以下のようになり、手前部分はゆっくりと変化し、奥に行くにつれて急激に$f$が0に近づいていきます。

GLSLで実装すると以下のようになります。
float exp2Fog(float d, float density) {
float dd = d * density;
return exp(-dd * dd);
}
この方法だと手前部分がくっきり見えるようになりつつ、フォッグのリアリティも維持しているような感じがします。

終わりに
今回はフォッグについてまとめてみました。フォッグ自体は3Dで定番のエフェクトなので目新しいことはないですが、繰り返し表現が簡単に実現できるレイマーチングでも有用かなと考えています。
今回のサンプルとして作成したシーンはglslsandboxに置いておきました。#define FOG_TYPE
を変更することで使用するフォッグを変更することができます。
http://glslsandbox.com/e#50866.0