予約フラグメントシェーダでは、前段のシェーダから出力されたフラグメントに対して、ライティングなどの処理を行うことができます。
「5. シェーダプログラム」でも述べたように、予約フラグメントシェーダはバイナリからロードする必要はなく、頂点シェーダとジオメトリシェーダと同じプログラムオブジェクトに、特別な名前(GL_DMP_FRAGMENT_SHADER_DMP
)をアタッチすることで使用できるようになります。予約フラグメントシェーダはフラグメント処理機能の集合です。その機能には以下のものがあります。
- フラグメントライティング
- シャドウ
- フォグ
- ガス
- その他(アルファテスト、w バッファ)
各フラグメント処理には予約ユニフォームが存在します。これらの予約ユニフォームには初期値が設定されており、アプリケーションは必要に応じて値を設定しなければなりません。
12.1. フラグメントオペレーションモード
予約フラグメント処理は、OpenGL 標準のフラグメント処理のパイプライン(アルファテスト以降)を独自の処理に切り替えることで、シャドウやガスといった特殊なレンダリングパスが必要な処理を行うことができます。この切り替えは、フラグメントオペレーションモードの予約ユニフォーム(dmp_FragOperation.mode
)に、glUniform1i()
で切り替えるモードを設定することで行われます。
フラグメントオペレーションモード |
切り替わるパイプライン |
---|---|
|
OpenGL 標準のフラグメント処理パイプライン(標準モード) |
|
シャドウ累積パス用のフラグメント処理パイプライン(シャドウモード) |
|
密度情報描画用のフラグメント処理パイプライン(ガスモード) |
12.2. フラグメントライティング
3DS のライティングは頂点単位ではなく、フラグメント単位にプライマリカラーとセカンダリカラーを算出するフラグメントライティングです。また、テクスチャを参照して法線ベクトルを摂動する(ずらす)バンプ機能やシャドウテクスチャの生成とカラーの算出を行うシャドウ機能、スポットライトおよびライトとの距離による減衰を算出する機能を有しています。
プライマリカラーとセカンダリカラーの計算は、2 つのベクトルの内積から参照テーブルの値を出力する関数を複数組み合わせ、それらの出力値を積と和によって統合する(コンバイン)方法で決定される計算式に従って行われます。3DS では、これらの参照テーブルと内積値を計算するベクトルの組み合わせやコンバイン方法を任意に決定することはできませんが、プリセットされた設定から選択することができます。
ライティング計算式は視点座標系を基準に考えられているため、いくつかのベクトルには視点座標系での値を設定することが想定されています。必ずしも視点座標系でなければならないわけではありませんが、使用されるベクトルの座標系はすべて一致していなければなりません。
フラグメントライティングを使用するには、予約ユニフォーム dmp_FragmentLighting.enabled
に glUniform1i()
で GL_TRUE
を設定し、少なくとも 1 つ以上のライトを有効にしなければなりません。また頂点シェーダで法線、視線ベクトル、接線ベクトル(ライティングで考慮しなければならない場合)をクォータニオンに変換し、頂点属性の 1 つとして出力しなければなりません。
予約ユニフォーム |
種別 |
設定値 |
---|---|---|
|
|
|
12.2.1. クォータニオン変換
ライティングの演算で使用されるベクトルはすべて同じ座標系でなければなりません。つまり、後述するバンプマッピングなどでは、テクスチャから参照された摂動法線を surface-local 座標系(頂点を原点に法線が z 軸の正方向と一致する座標系)から視点座標系へと変換する必要があります。
下記の surface-local 座標系から視点座標系への変換を行う回転行列は、法線、接線、従法線で構成される 3x3 行列で、クォータニオン(Qx, Qy, Qz, Qw) に変換可能です。
(E は視点座標系での座標値、T は接線、N は法線、B は従法線、S は surface-local 座標系での座標値)
フラグメントライティングの実装では、頂点単位で入力される法線、接線、従法線(法線と接線から計算できる)の 3 ベクトルからフラグメント単位のベクトルを生成せず、頂点単位のクォータニオンからフラグメント単位のクォータニオンを生成します。クォータニオンはフラグメントライティングのプロセス中に元の回転行列に変換されて使用されます。そのため、フラグメントライティングを使用するには、頂点シェーダで各ベクトルを有効なクォータニオンに変換して出力する必要があります。
法線のみのクォータ二オン生成について
CTR-SDK のサンプルデモには、法線のみの頂点情報からクォータニオンを生成する頂点シェーダ(l_position_view_quaternion)が収録されています。この頂点シェーダでは、surface-local 座標系での法線を視点座標系での法線に変換するクォータニオンとして、回転軸に単位法線ベクトル (0, 0, 1) と視点座標系に変換された法線 (Nx, Ny, Nz) の半角ベクトルを用いて 180 度回転させるクォータニオンを生成しています。
回転させる軸を (α, β, γ)、回転角を θ とした場合、求められるクォータニオン Q は以下のようになります。
頂点シェーダのコンポーネントでは、クォータニオンの実部を w 成分に設定します。
θ = 180°のため、
Q = ( 0; α, β, γ )
であり、また回転させる軸は半角ベクトルなので、
となります。
なお、(Nx, Ny, Nz) が (0, 0, -1) の場合のみ、半角ベクトルの向きが定まらないために (α, β, γ) = (1, 0, 0) として定めています。
12.2.2. ライティングの概要
3DS のフラグメントライティングでは、必ずプライマリカラーとセカンダリカラーの 2 つのカラーが算出されます。
プライマリカラーは、ライトがフラグメントの環境光(Ambient)と拡散光(Diffuse)に与える影響にシャドウ、スポットライト減衰および距離減衰を掛け合わせたものをライトごとに計算して累積し、シーンから受ける環境光の影響およびフラグメントの放射光(Emission)を加算したものです。これはフラグメントの基本となる色となります。
セカンダリカラーは、ライトがフラグメントの持つ 2 項の鏡面光(Specular)に与える影響にシャドウ、スポットライト減衰および距離減衰を掛け合わせたものをライトごとに計算して累積したものです。これは主に、フラグメントのハイライト部分の色となります。
環境光、拡散光、放射光、鏡面光をもとにオブジェクトの色を決定するのは OpenGL と同様ですが、ライティングの計算がフラグメント単位で行われる点と、2 つの鏡面光を算出する点、鏡面光の算出方法の多様性に違いがあります。特に 2 つある鏡面光は、角度によって色調が変化する物質の表現などに利用することができます。
12.2.3. シーン設定
フラグメントライティングで扱うことのできるシーンの大きさは -216 ~ 215 の範囲です。シーンを構成する各フラグメントと視点との距離やライトと視点との距離が 216 以上にならないように注意してください。
シーンがフラグメントに与える影響はシーンの環境光(Global Ambient)です。シーンの環境光の指定は、予約ユニフォーム(dmp_FragmentLighting.ambient
)に glUniform4fv()
で RGBA カラーを設定することで行います。
予約ユニフォーム |
種別 |
設定値 |
---|---|---|
|
|
シーンの環境光 (R, G, B, A) を指定する。 各成分とも 0.0 ~ 1.0 (デフォルト)(0.2, 0.2, 0.2, 1.0) |
12.2.4. マテリアル設定
マテリアル設定は簡単に言えば、フラグメントを物質(マテリアル)として見たときの、その素材や質感を環境光や鏡面光などの色情報で表現したものです。マテリアルに関係する設定は予約ユニフォーム(dmp_FragmentMaterial.*
)で指定します。
設定がどのようにライティング計算に使用されるのかは、「プライマリカラーの計算式」および「セカンダリカラーの計算式」で説明します。
予約ユニフォーム |
種別 |
設定値 |
---|---|---|
|
|
環境光(R, G, B, A)を指定する。各成分とも 0.0 ~ 1.0 (デフォルト)(0.2, 0.2, 0.2, 1.0) |
|
|
拡散光(R, G, B, A)を指定する。各成分とも 0.0 ~ 1.0 (デフォルト)(0.8, 0.8, 0.8, 1.0) |
|
|
放射光(R, G, B, A)を指定する。各成分とも 0.0 ~ 1.0 (デフォルト)(0.0, 0.0, 0.0, 1.0) |
|
|
鏡面光 0(R, G, B, A)を指定する。各成分とも 0.0 ~ 1.0 (デフォルト)(0.0, 0.0, 0.0, 1.0) |
|
|
鏡面光 1(R, G, B, A)を指定する。各成分とも 0.0 ~ 1.0 (デフォルト)(0.0, 0.0, 0.0, 1.0) |
|
|
ライティング計算で使用される参照テーブル番号を指定する。 各ファクタとも 0 ~ 31 |
12.2.5. ライト設定
ライト設定には、ライトがマテリアルに与える影響の設定とライト自身の設定の 2 種類が存在します。フラグメントライティングで扱うことのできるライトの数は 8 個です。ライトに関係する設定は予約ユニフォーム(dmp_FragmentLightSource[i].*
、i
はライト番号 0 ~ 7)で指定します。
設定がどのようにライティング計算に使用されるのかは、「プライマリカラーの計算式」および「セカンダリカラーの計算式」で説明します。
予約ユニフォーム |
種別 |
設定値 |
---|---|---|
|
|
ライトの有効・無効を指定する。
|
|
|
環境光 (R, G, B, A) を指定する。各成分とも 0.0 ~ 1.0 (デフォルト)(0.0, 0.0, 0.0, 0.0) |
|
|
拡散光 (R, G, B, A) を指定する。各成分とも 0.0 ~ 1.0 (デフォルト)ライト番号 0 のみ (1.0, 1.0, 1.0, 1.0) |
|
|
鏡面光 0(R, G, B, A)を指定する。各成分とも 0.0 ~ 1.0 (デフォルト)ライト番号 0 のみ (1.0, 1.0, 1.0, 1.0) |
|
|
鏡面光 1(R, G, B, A)を指定する。各成分とも 0.0 ~ 1.0 (デフォルト)(0.0, 0.0, 0.0, 0.0) |
|
|
ライトの光源位置 (x, y, z, w) を指定する。 ベクトルは正規化されていなくてもかまいません。w 成分で平行光源(0.0)と点光源の判別が行われます。 (デフォルト)(0.0, 0.0, 1.0, 0.0) |
|
|
スポットライトの向き (x, y, z) を指定する。 ベクトルは正規化されていなくてもかまいません。 (デフォルト)(0.0, 0.0, -1.0) |
|
|
ライトがシャドウの影響を受けるかどうかを指定する。
|
|
|
ジオメトリファクタ 0 をライティング計算に使用するかどうかを指定する。
|
|
|
ジオメトリファクタ 1 をライティング計算に使用するかどうかを指定する。
|
|
|
両面ライティングを行うかどうかを指定する。
|
|
|
スポットライトの光量分布による減衰を行うかどうかを指定する。
|
|
|
ライトとの距離による減衰を行うかどうかを指定する。
|
|
|
距離減衰のバイアス値を指定する。 (デフォルト)0.0 |
|
|
距離減衰のスケール値を指定する。 (デフォルト)1.0 |
|
|
スポットライト減衰および距離減衰の計算で使用される参照テーブル番号を指定する。 各ファクタとも 0 ~ 31 |
表中の "*
" は "dmp_FragmentLightSource[i]
" の略です。i
はライト番号 0 ~ 7 です。
12.2.6. ライティング環境
シャドウテクスチャの選択やバンプ設定、参照テーブルの入力値など、ライティング全般に関係する設定は予約ユニフォーム(dmp_LightEnv.*
)で指定します。
設定がどのようにライティング計算に使用されるのかは、「プライマリカラーの計算式」および「セカンダリカラーの計算式」で説明します。
予約ユニフォーム |
種別 |
設定値 |
---|---|---|
|
|
各ファクタの参照テーブルへの入力値を絶対値化するかどうかを指定する。
|
|
|
各ファクタの参照テーブルへの入力値に使用する、2 ベクトル間の角度のコサイン値を指定する。
|
|
|
各ファクタの参照テーブルの出力値に対するスケール値を指定する。参照テーブルから出力される値は、スケール値を適用したあとに -2.0 ~ +2.0 の範囲にクランプされます。 0.25 |
|
|
シャドウとして使用するテクスチャユニットを指定する。
|
|
|
バンプマップとして使用するテクスチャユニットを指定する。
|
|
|
法線または接線の摂動モードを指定する。
|
|
|
法線の第3成分を再生成するかどうかを指定する。
|
|
|
各ファクタのコンフィグレーションを指定する。
|
|
|
シャドウの項を反転(1.0 - shadow)させるかどうかを指定する。
|
|
|
プライマリカラーにシャドウを影響させるかどうかを指定する。
|
|
|
セカンダリカラーにシャドウを影響させるかどうかを指定する。
|
|
|
アルファ成分にシャドウを影響させるかどうかを指定する。
|
|
|
フレネルファクタの出力モードを指定する。
|
|
|
鏡面光の出力値をクランプするかどうかを指定する。
|
|
|
分布 0(D0)に対する参照テーブルからの出力値を適用するかどうかを指定する。
|
|
|
分布 1(D1)に対する参照テーブルからの出力値を適用するかどうかを指定する。
|
|
|
反射(RR、RG、RB)に対する参照テーブルからの出力値を適用するかどうかを指定する。
|
表中の "*
" は "dmp_LightEnv
" の略です。
12.2.7. プライマリカラーの計算式
プライマリカラーの計算式の概略は以下の数式で表すことができます。
Colorprimary = ∑((Diffuse × DPLN × Shadow + Ambient) × Spot × DistAtt + Ambientglobal + Emission
各項の計算を詳細に解説します。
拡散光と環境光にはマテリアルとライトのカラー成分を掛け合わせた値が適用されます。
Diffuse = Diffusematerial × Diffuselight
Ambient = Ambientmaterial × Ambientlight
拡散光はライトの当たる角度とシャドウによる影響を受けます。
DPLN = max { 0, L∙N } or abs ( L∙N )
ライトの当たる角度による影響は式中の DPLN で表され、その値は正規化されたライトベクトルと法線の内積値ですが、両面ライティングの設定(dmp_FragmentLightSource[i].twoSideDiffuse
)で単面ライティング(GL_FALSE
)を指定している場合は 0 と内積値との最大値が、両面ライティング(GL_TRUE
)を指定している場合は内積値の絶対値が適用されます。
シャドウ減衰の影響は式中の Shadow で表されます。dmp_LightEnv.shadowPrimary
または dmp_FragmentLightSource[i].shadowed
のどちらかに GL_FALSE
が指定されている場合はシャドウの影響を受けないため 1.0 が適用されます。どちらの予約ユニフォームにも GL_TRUE
が指定されている場合は、dmp_LightEnv.shadowSelector
で指定されたテクスチャユニットからサンプルされた値が適用されます。このとき、dmp_LightEnv.invertShadow
に GL_TRUE
が指定されている場合は、"1.0 -(サンプルされた値)" が適用されます。シャドウテクスチャ以外がバインドされたテクスチャユニットを指定した場合、サンプルされた値にはカラー成分がそのまま適用されます。
スポットライトの影響は式中の Spot で表されます。ライトごとに参照テーブルの設定および使用・不使用の設定が可能です。スポットライトの使用・不使用は dmp_FragmentLightSource[i].spotEnabled
で、スポットライトで使用する参照テーブルは dmp_FragmentLightSource[i].samplerSP
で設定します。スポットライトの方向ベクトルは dmp_FragmentLightSource[i].spotDirection
で設定します。通常は、スポットライトの参照テーブルへの入力値(dmp_LightEnv.lutInputSP
)には GL_LIGHT_ENV_SP_DMP
を指定することになります。
距離減衰の影響は式中の DistAtt で表されます。距離減衰の影響は平行光源では考慮されません。
距離減衰の適用は dmp_FragmentLightSource[i].distanceAttenuationEnabled
で設定し、ライトごとに制御することができます。ただし、dmp_LightEnv.config
に GL_LIGHT_ENV_LAYER_CONFIG7_DMP
を設定している場合は距離減衰の影響が無効(1.0 を適用)となります。
使用する参照テーブルは dmp_FragmentLightSource[i].samplerDA
で設定し、参照テーブルへの入力値にはスケール値(dmp_FragmentLightSource[i].distanceAttenuationScale
)とバイアス(dmp_FragmentLightSource[i].distanceAttenuationBias
)の設定値が考慮されます。
ここまでがフラグメントのプライマリカラーのライトごとの影響分です。これを有効なライトの数だけ算出し、ライトの影響を受けないグローバル環境光とマテリアルの放出光を加算して、フラグメントの最終的なプライマリカラーを算出します。
グローバル環境光には、マテリアルの環境光とシーン設定の環境光を掛け合わせた値が適用されます。
Ambientglobal = Ambientmaterial × Ambientscene
ただし、ライト番号 0 の光源が無効となっている(dmp_FragmentLightSource[0].enabled
に GL_FALSE
が設定されている)場合は、グローバル環境光とマテリアルの放出光には 0.0 が適用されます。
12.2.8. セカンダリカラーの計算式
セカンダリカラーの計算式の概略は以下の数式で表すことができます。
Colorsecondary = ∑((Specular0 + Specular1) × f × Shadow × Spot × DistAtt)
各項の計算を詳細に解説します。
dmp_LightEnv.shadowPrimary
の代わりに dmp_LightEnv.shadowSecondary
の設定が考慮されること以外は、Shadow、Spot、DistAtt の各項はプライマリカラーと同じ計算式で算出されます。
Specular0 と Specular1 はそれぞれ、以下のように計算されます。
Specular0 = Specular0material × Specular0light × Distribution0 × Geometry0
Specular1 = ReflectionRGB × Specular1light × Distribution1 × Geometry1
基本的に、マテリアルとライトの鏡面光に分布関数とジオメトリファクタを掛け合わせた値が鏡面光として算出されます。設定によってはマテリアルの鏡面光 1 にあたる項には、カラーごとに異なる反射の参照テーブルの出力値を適用することもできます。反射と分布の参照テーブルを調整することで、様々な質感を持ったフラグメントの表現が可能になります。
分布関数(ディストリビューションファクタ)は式中の Distribution0 と Distribution1 で表されます。分布関数は分布 0(D0)と分布 1(D1)の参照テーブルで設定することになります。
使用・不使用の制御は dmp_LightEnv.lutEnabledD0
(lutEnabledD1
)で行います。使用する参照テーブルのテーブル番号は dmp_FragmentLightSource[i].samplerD0
(samplerD1
)で指定します。参照テーブルへの入力値は dmp_LightEnv.lutInputD0
(lutInputD1
)で指定し、入力値の絶対値化を dmp_LightEnv.absLutInputD0
(absLutInputD1
)で指定します。参照テーブルからの出力値には dmp_LightEnv.lutScaleD0
(lutScaleD1
)で指定されたスケール値が考慮されます。
反射は式中の ReflectionRGB で表され、マテリアルの鏡面光 1 の代わりに RGB の各成分の反射を算出する関数を参照テーブル(RR、RG、RB)で設定します。使用・不使用の制御は dmp_LightEnv.lutEnabledRefl
で行い、GL_FALSE
を指定した場合はマテリアルの鏡面光 1 が適用されます。各成分で使用する参照テーブルのテーブル番号は dmp_FragmentLightSource[i].samplerRR
(samplerRG
、samplerRB
)で指定します。参照テーブルへの入力値は dmp_LightEnv.lutInputRR
(lutInputRG
、lutInputRB
)で指定し、入力値の絶対値化を dmp_LightEnv.absLutInputRR
(absLutInputRG
、absLutInputRB
)で指定します。
ジオメトリファクタは式中の Geometry0 と Geometry1 で表され、これらは Cook-Torrance の照明モデルで使用される項です。使用・不使用の制御は dmp_FragmentLightSource[i].geomFactor0
(geomFactor1
)で行います。GL_FALSE
を指定した場合は 1.0 が適用され、GL_TRUE
を指定した場合は Cook-Torrance の照明モデルで使用するジオメトリファクタに近似された値が適用されます。
f は正規化されたライトベクトルと法線の内積値を用いて、フラグメントへのライティングが有効か無効かを判定する関数です。dmp_LightEnv.clampHighlights
に GL_FALSE
を指定した場合は必ず 1.0 が適用され、通常は光の通過しない領域まで光を通過させて材質が半透明であるオブジェクトを表現するときに使用します。GL_TRUE
を指定した場合は内積値が 0.0 以下ならば 0.0、0.0 以上ならば 1.0 が適用されます。この場合、ライティングされない箇所には鏡面光が存在しない状態となります。
フラグメントの最終的なセカンダリカラーは、フラグメントのセカンダリカラーのライトごとの影響分を有効なライトの数だけ算出したものになります。
12.2.9. アルファ成分のライティング
ここまでで説明したプライマリカラーとセカンダリカラーの計算式では、アルファ成分が 1.0 に固定されて算出されていました。フラグメントライティングでは、フレネルファクタとシャドウを使ったアルファ成分のライティングを行うことができます。
フレネルファクタの本来の目的は、半透明物体のフレネル反射の参照テーブル(FR)として使用されることなのですが、アルファ成分を参照テーブルの出力値に置き換える機能として、ほかの目的に利用することもできます。
フレネルファクタで使用する参照テーブルのテーブル番号は dmp_FragmentLightSource[i].samplerFR
で指定します。参照テーブルへの入力値は dmp_LightEnv.lutInputFR
で指定し、入力値の絶対値化を dmp_LightEnv.absLutInputFR
で指定します。複数のライトが有効になっている場合は、その中で最も大きなライト番号のライトのライトベクトルとの内積値が参照テーブルへの入力値に使用されます。フレネルファクタの適用範囲の制御は dmp_LightEnv.fresnelSelector
で行い、以下の設定値から選択します。
設定値 |
適用の範囲 |
---|---|
|
適用しない(アルファ成分は 1.0 固定) |
|
プライマリカラーのアルファ成分のみ |
|
セカンダリカラーのアルファ成分のみ |
|
プライマリカラーとセカンダリカラーのアルファ成分 |
フレネルファクタの適用範囲はシャドウのアルファ成分への影響にも適用されます。シャドウのアルファ成分への影響を制御する dmp_LightEnv.shadowAlpha
に GL_TRUE
を指定した場合は、適用されるアルファ成分にシャドウのアルファ成分が乗算されます。カラーと同じように、dmp_LightEnv.invertShadow
に GL_TRUE
が指定されている場合は、乗算される値に "1.0 -(シャドウのアルファ値)" が適用されます。
12.2.10. 参照テーブルの作成と指定
ライティングの計算式で使用される参照テーブルには、以下の 8 種類の参照テーブルが存在します。
- 反射(RR、RG、RB の 3 種類)
- ディストリビューションファクタ(D0、D1 の 2 種類)
- フレネルファクタ(FR)
- スポットライト(SP)
- ライトの距離減衰(DA)
反射(RR、RG、RB)、ディストリビューションファクタ(D0、D1)、フレネルファクタ(FR)の参照テーブルはマテリアル設定のため、すべてのライトで共通のテーブルが参照されることになります。スポットライト(SP)とライトの距離減衰(DA)については、ライト単位で異なる参照テーブルを設定することができます。
ライトの距離減衰を除いて、セカンダリカラーのライティング計算式の各項に、どの参照テーブルが使用されるかはレイヤコンフィグレーション(dmp_LightEnv.config
)の設定によって制御することができます。
レイヤコンフィグレーションの設定値 |
Rr |
Rg |
Rb |
D0 |
D1 |
Fr |
Sp |
サイクル数 |
---|---|---|---|---|---|---|---|---|
|
RR |
RR |
RR |
D0 |
- |
- |
SP |
1 |
|
RR |
RR |
RR |
- |
- |
FR |
SP |
1 |
|
RR |
RR |
RR |
D0 |
D1 |
- |
- |
1 |
|
- |
- |
- |
D0 |
D1 |
FR |
- |
1 |
|
RR |
RG |
RB |
D0 |
D1 |
- |
SP |
2 |
|
RR |
RG |
RB |
D0 |
- |
FR |
SP |
2 |
|
RR |
RR |
RR |
D0 |
D1 |
FR |
SP |
2 |
|
RR |
RG |
RB |
D0 |
D1 |
FR |
SP |
4 |
上表は、反射の RGB 成分、ディストリビューションファクタ、フレネルファクタ、スポットライトの各項が、どの参照テーブルから値を取得するかを示しています。対応表で「-」となっている項は、ライティングの計算式では 1.0 が適用されます。つまり、計算式から該当の項が消えたことになります。サイクル数はライティング計算に必要なハードウェアのサイクル数を示しています。ライティング計算を高速に行いたい場合は、この数値がなるべく小さなレイヤコンフィグレーションを選択することをお勧めします。
カラーバッファへの書き込みアクセスのみが行われる設定(glColorMask()
でカラーバッファの全成分に GL_TRUE
を設定し、GL_BLEND
および GL_COLOR_LOGIC_OP
を glDisable()
で無効にしている状態)では、GL_LIGHT_ENV_LAYER_CONFIG4_DMP
~ GL_LIGHT_ENV_LAYER_CONFIG6_DMP
のレイヤコンフィグレーションで 1 ピクセルを処理するためには 2 サイクルではなく、3 サイクル必要です。
GL_LIGHT_ENV_LAYER_CONFIG7_DMP
を設定した場合、プライマリ・セカンダリカラーの計算で距離減衰の影響が無効となります。
例えば GL_LIGHT_ENV_LAYER_CONFIG0_DMP
を設定した場合、反射の RGB 成分はすべて参照テーブル RR から、分布 0 は D0 から、スポットライトは SP から、それぞれ値を取得します。そして、分布 1 とフレネルファクタには固定値の 1.0 が適用されます。
参照テーブルは「7.7. 参照テーブルのロード」で説明したように、glTexImage1D()
で用意します。フラグメントライティングで使用する参照テーブルの width
は 512 固定です。512 要素のうち、前半の 256 要素には参照テーブルのサンプリング値を格納し、後半の 256 要素には個々のサンプリング値の差分を格納します。
サンプリング値の格納順は参照テーブルへの入力値の範囲が「0.0 ~ 1.0」か「-1.0 ~ 1.0」かで異なります。入力値の範囲は、予約ユニフォーム dmp_LightEnv.absLutInputXX
(XX
=D0
,D1
,RR
,RG
,RB
,FR
,SP
) の指定で変化し、GL_TRUE
を指定した場合は「0.0 ~ 1.0」、GL_FALSE
を指定した場合は「-1.0 ~ 1.0」となります。
ここではサンプリング値の取得手順を先に説明してから、それぞれの場合の格納手順を説明します。
入力値の範囲が「0.0 ~ 1.0」の場合、入力値に 256 を乗算して 255 にクランプした値の整数部分が値を取得する際のインデックスとなります。そのインデックスで参照テーブルからサンプリング値を取得し、小数部とインデックス + 256 の位置の差分値を乗算したものを加算して最終的なサンプリング値を求めます。
index=min(floor(input * 256), 255); samplingValue=LUT[index] + LUT[index + 256] * (input * 256 - index);
入力値の範囲が「-1.0 ~ 1.0」の場合、入力に 128 を乗算して整数部分を 2 の補数に変換した値がインデックスとなります。そのインデックスで参照テーブルからサンプリング値を取得し、小数部とインデックス + 256 の位置の差分値を乗算したものを加算して最終的なサンプリング値を求めます。
if (input < 0.0) { flooredInput=floor(input * 128); index = 255 + flooredInput; samplingValue=LUT[index] + LUT[index + 256] * (input * 128 - flooredInput); } else { index=min(floor(input * 128), 127); samplingValue=LUT[index] + LUT[index + 256] * (input * 128 - index); }
サンプリング値の格納順を図 12-3 に示します。
基本的に参照テーブルの作成は、インデックスを 256 または 128 で除算した値で計算したサンプリング値を格納し、次のサンプリング値との差分をインデックス + 256 の位置に格納することで行います。範囲が -1.0 ~ 1.0 のときは、参照テーブルが不連続になることに注意してください。
入力値に対するサンプリング値を計算する関数を func
としたときのコード例をそれぞれの場合で示します。
for (i = 0; i < 256; i++) LUT[i] = func((float) i / 256.0f); for (i = 0; i < 255; i++) LUT[i + 256] = LUT[i + 1] - LUT[i]; LUT[511] = func(1.0f) - LUT[255] * 16.0f / 15.0f;
差分の最終要素で入力値が 1.0 の値との差分を格納する際に(16.0/15.0)を乗算しているのは、GPU の実装で小数部の精度が 4 ビットであるため、そのままの差分を格納すると入力の最大値で 1.0 でのサンプリング値にならないからです。
for (i = 0; i < 128; i++) { LUT[i] = func((float) i / 128.0f); LUT[255 - i] = func((float) (i + 1) * -1.0f / 128.0f); } for (i= 0; i < 127; i++) { LUT[i + 256] = LUT[i + 1] - LUT[i]; LUT[i + 384] = LUT[i + 129] - LUT[i + 128]; } LUT[383] = func(1.0f) - LUT[127] * 16.0f / 15.0f; LUT[511] = LUT[0] - LUT[255];
こちらの場合も、そのまま差分を格納すると入力の最大値で 1.0 でのサンプリング値にならないために(16.0/15.0)を乗算しています。
作成した参照テーブルを glTexImage1D()
でロードします。ロード先の参照テーブルは glBindTexture()
の target
に GL_LUT_TEXTUREi_DMP
で指定します。ライティング計算時に参照テーブルを参照させるには、glUniform1i()
で参照テーブル番号を予約ユニフォームに指定します。ここで指定する参照テーブル番号は GL_LUT_TEXTUREi_DMP
の i
(0 ~ 31) が表す数値で、テクスチャの名前(ID)や GL_LUT_TEXTUREi_DMP
を直接指定するわけではないことに注意してください。参照テーブル番号を指定する予約ユニフォームは、反射、ディストリビューションファクタ、フレネルファクタならばマテリアル設定 dmp_FragmentMaterial.samplerXX
(XX=DO,D1,RR,RG,RB,FR
) 、スポットライトとライトの距離減衰ならばライト設定 dmp_FragmentLightSource[i].samplerXX
(XX=SP,DA
) になります。
glBindTexture(GL_LUT_TEXTURE2_DMP, lutTextureID); glTexImage1D(GL_LUT_TEXTURE2_DMP, 0, GL_LUMINANCEF_DMP, 512, 0, GL_LUMINANCEF_DMP, GL_FLOAT, LUT); glUniform1i(glGetUniformLocation(progID, "dmp_FragmentMaterial.samplerRR"), 2);
12.2.11. 参照テーブルへの入力値
ライトの距離減衰(DA)を除く項(D0、D1、RR、RG、RB、FR、SP)はすべて、2 つのベクトルがなす角のコサイン値(正規化されたベクトル同士の内積値)が入力値となります。
対象となるベクトルは法線(N)、ライトベクトル(L)、視線ベクトル(V)、ハーフベクトル(H)、接線(T)、従法線(B)、スポットライトの方向ベクトル、ハーフベクトルの接平面への投影です。
各ファクタの参照テーブルへの入力値に使用する値の指定は、ライティング環境の予約ユニフォーム dmp_LightEnv.lutInputXX
(XX
=D0
,D1
,RR
,RG
,RB
,FR
,SP
) に以下の値を glUniform1i()
で設定することで行います。各ファクタの参照テーブルへの入力値には、指定の 2 ベクトル間の角度のコサイン値が適用されます。
設定値 |
対象となる 2 つのベクトル |
---|---|
|
法線とハーフベクトル(デフォルト) |
|
ビューベクトルとハーフベクトル |
|
法線とビューベクトル |
|
ライトベクトルと法線 |
|
ライトベクトルの逆ベクトルとスポットライトの方向ベクトル(RR, RG, RB, FR は不可) |
|
ハーフベクトルの接平面への投影と接線(RR, RG, RB, FR は不可) |
12.2.12. ライトの距離減衰項
ライトの距離減衰項への入力値は以下の式で計算されます。
fposition はフラグメントの位置、lposition はライトの位置で、どちらも視点座標系での値を想定しています。ライトの位置は点光源でのみ意味のある値をとり、平行光源では意味をなしません。
この数式から、ライトの距離減衰項への入力値はフラグメントとライト間の距離にスケール値を乗算し、さらにバイアス値を加算したものであるとわかります。
参照テーブルは入力値の範囲が 0.0 ~ 1.0(絶対値)であるものとして作成してください。スケール値は予約ユニフォーム dmp_FragmentLightSource[i].distanceAttenuationScale
に、バイアス値は予約ユニフォーム dmp_FragmentLightSource[i].distanceAttenuationBias
に、それぞれ glUniform1f()
で値を設定してください。
レイヤコンフィグレーションの設定値に GL_LIGHT_ENV_LAYER_CONFIG7_DMP
を指定した場合は距離減衰を無効にしなければなりません。dmp_FragmentLightSource[i].distanceAttenuationEnabled
に GL_FALSE
を設定してください。
12.2.13. テクスチャコンバイナの設定
フラグメントライティングで算出したプライマリカラーとセカンダリカラーは、テクスチャコンバイナの入力ソースの 1 つとして使用することができます。
入力ソースの予約ユニフォーム(dmp_TexEnv[i].srcRgb
と dmp_TexEnv[i].srcAlpha
)に、プライマリカラーは GL_FRAGMENT_PRIMARY_COLOR_DMP
、セカンダリカラーは GL_FRAGMENT_SECONDARY_COLOR_DMP
を設定することで使用可能です。
ライティングが無効(dmp_FragmentLighting.enabled
に GL_FALSE
を設定)になっている場合の出力は (0.0, 0.0, 0.0, 1.0) が出力されます。
12.3. バンプマッピング
バンプマッピングはフラグメントライティングの機能の 1 つで、テクスチャで入力される法線マップによってフラグメントの法線や接線を摂動させる(ずらす)機能です。バンプマッピングはオブジェクトの表面に凹凸による陰影が付いたような表現を施すことができ、少ないポリゴンで構成された簡単なモデルから、見かけの複雑なモデルを描画させることができます。
12.3.1. 予約ユニフォーム
バンプマッピングの予約ユニフォームには、以下のものが存在します。
法線マップ
法線マップ(dmp_LightEnv.bumpSelector
)には、バンプマッピングで使用する法線マップのテクスチャがバインドされたテクスチャユニットを指定してください。法線マップのテクスチャは、摂動ベクトルの x、y、z 成分をそれぞれ R、G、B 成分にエンコードして作成します。エンコード方法とは、ベクトルの -1.0 を最小輝度(8 ビットフォーマットならば 0)に、1.0 を最大輝度(8 ビットフォーマットならば 255 )に変換することです。
摂動方法
摂動方法(dmp_LightEnv.bumpMode
)に GL_LIGHT_ENV_BUMP_NOT_USED_BUMP
以外を指定することで、バンプマッピングが有効になります。摂動方法には以下のものがあります。
摂動方法 |
摂動されるベクトル |
---|---|
|
なし |
|
法線ベクトル(バンプマッピング) |
|
接線ベクトル(タンジェントマッピング) |
法線の再計算機能
法線の再計算機能(dmp_LightEnv.bumpRenorm
)に GL_TRUE
を指定すると、テクスチャからサンプルされた B 成分を摂動ベクトルの z 成分として使用せずに、z 成分が x、y 成分から再計算されます。
※ ルート内が負値となる場合、計算結果は 0 となります。
多くの場合において、テクスチャからサンプルされた値を使用するよりも再計算を行う方が良好な結果となります。また、R と G 成分のみのフォーマット(GL_HILO8_DMP
)のテクスチャを使用して法線のバンプマッピングを行う場合は、再計算機能を有効にしなければなりません。ただし、異方性反射等で利用するために摂動方法でタンジェントマッピングを選択している場合は、この機能を使用しないことを推奨しています。これはフラグメントライティングのタンジェントマッピングが z 成分の存在しない摂動接線の入力を想定しているためで、再計算機能が有効になっていると z 成分に 0 以外が生成される可能性があるからです。
再計算機能を無効にすると、テクスチャからサンプルされた摂動法線が正規化されずに使用されます。テクスチャには、あらかじめ正規化した値を格納してください。また、テクスチャのフィルタモードがポイントサンプリング(GL_NEAREST
)でない場合は、フィルタリングの影響で正規化されていない値が摂動法線として使用される可能性がありますので、法線の再計算機能を有効にしてください。
予約ユニフォーム |
種別 |
設定値 |
---|---|---|
|
|
法線マップとして使用するテクスチャユニットを指定する。
|
|
|
法線または接線の摂動モードを指定する。
|
|
|
法線の第 3 成分を再生成するかどうかを指定する。
|
12.4. シャドウ
3DS のシャドウは、シャドウバッファ(光源を起点としたシーンのデプス情報)を作成するシャドウ累積パス(第 1 パス)と、作成したシャドウバッファを参照してシャドウを落とす参照パス(第 2 パス)の 2 パスでシャドウを描画する機能です。また、第 1 パスでデプス情報とともに収集されるシャドウ強度の情報により、ソフトシャドウの表現を施すことができます。
12.4.1. シャドウ累積パス
シャドウ累積パスのために、フラグメントオペレーションモード(dmp_FragOperation.mode
)をシャドウモード(GL_FRAGOP_MODE_SHADOW_DMP
)に切り替え、シャドウ情報(デプス値とシャドウ強度)がシャドウテクスチャ(フォーマットとタイプの組み合わせが GL_SHADOW_DMP
と GL_UNSIGNED_INT
のテクスチャ)に格納されるようにしなければなりません。シャドウテクスチャにシャドウ情報を書き込むことができるのはテクスチャユニット 0(GL_TEXTURE0
)だけであることに注意してください。また、シャドウテクスチャにはミップマップを適用することができません。
フラグメントのパイプラインがシャドウモードに切り替わると、デプスやステンシルではなく、カラーバッファのアタッチメントポイントにシャドウ情報が出力されます。そのため、シャドウテクスチャはカラーバッファのアタッチメントポイント(GL_COLOR_ATTACHMENT0
)にアタッチされていなければなりません。シャドウモードでは、デプスとステンシルのアタッチメントポイントにアタッチされたレンダーターゲットは無視されます。アルファテストとステンシルテストは行われません。
以下の手順で、シャドウテクスチャの作成とレンダーターゲットへの指定を行うことができます。
glActiveTexture(GL_TEXTURE0); glGenTextures(1, &shadowTexID); glBindTexture(GL_TEXTURE_2D, shadowTexID); glTexImage2D(GL_TEXTURE_2D, 0, GL_SHADOW_DMP, shadowWidth, shadowHeight, 0, GL_SHADOW_DMP, GL_UNSIGNED_INT, 0); glGenFramebuffers(1, &shadowFboID); glBindFramebuffer(GL_FRAMEBUFFER, shadowFboID); glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, shadowTexID, 0);
シャドウ情報の累積は光源座標系で行われます。シャドウ情報は、光源からの深度(デプス情報)とシャドウ強度からなり、シャドウを描画する際のカラー情報の G 成分(R, B, A 成分は影響しない)が 1.0 であればシャドウのない状態、0.0 であれば不透明なハードシャドウのある状態、それ以外であれば不透明ではないシャドウ(ソフトシャドウ)のある状態です。
不透明なハードシャドウが描画されるときはシャドウのデプス情報のみが更新され、シャドウの強度は更新されません。フラグメントはシャドウバッファ内の対応するピクセルと深度の比較(GL_LESS
に相当)が行われ、フラグメント側の値が小さければシャドウバッファのデプス情報が更新されます。
不透明ではないシャドウ(ソフトシャドウ)が描画されるときはシャドウの強度情報のみが更新され、シャドウのデプス情報は更新されません。フラグメントはシャドウバッファ内の対応するピクセルと深度の比較(GL_LESS
に相当)が行われ、フラグメント側が小さければ、さらに強度情報に関しても比較(GL_LESS
に相当)が行われ、これもフラグメント側が小さければシャドウバッファの強度情報が更新されます。
シャドウ累積パスでカラーバッファを初期化する際には、glClearColor()
でクリアカラーに (1.0, 1.0, 1.0, 1.0) を指定し、glClear()
には GL_COLOR_BUFFER_BIT
を指定しなければなりません。クリアカラーには、G 成分のみではなく、すべてのカラー成分(R, G, B, A)を 1.0 にしなければならないことに注意してください。
次のシャドウ参照パスでは視点座標系で処理が行われるため、デプス情報は視点空間上の線形補間(多くの場合、非線形の関係である OpenGL とは異なります)によって生成されなければなりません。そのため、予約ユニフォームで w バッファのスケール因子(dmp_FragOperation.wScale
)に任意のスケール因子を glUniform1f()
で設定しなければなりません。初期値は 0.0 ですが、このままでは OpenGL と同じ非線形の関係となり、near クリップ面が視点に近い場合に far クリップ面付近のデプス情報の有効精度が小さくなってしまいます。線形補間の関係にあるようにするには、f を far クリップ面のクリップ値とすれば、射影変換が透視投影である場合は 1.0/f を、平行投影である場合は 1.0 をスケール因子に設定してください。
ハードシャドウのレンダリングは影を落とすオブジェクトをカラーの G 成分を 0.0 で描画することで行います。テクスチャを無効にした状態で、頂点カラーの G 成分が 0.0 で出力されるように頂点シェーダを実装するのが一般的な方法です。これ以外のカラーで描画された場合はソフトシャドウとして扱われます。
必要なシャドウ情報を累積するためには、何回かのレンダリングパスが必要になる場合があります。不透明でないシャドウに関する情報を累積するときは、先に不透明なハードシャドウに関する情報を累積した後に行わなければなりません。順序が逆であったり、交互に累積したりした場合の結果は保証されません。光源が動かない場合は、あらかじめシャドウテクスチャに動かない物体のみ描画しておくことでシャドウテクスチャの生成処理を軽減させることができます。また、オブジェクトの形状を単純化させてポリゴン数を減らすこともパフォーマンス向上には効果的です。
頂点シェーダで頂点カラーを出力するように実装した場合の、シャドウレンダリング時のテクスチャユニット 0 およびテクスチャコンバイナは以下のように設定します。
glUniform1i(glGetUniformLocation(progID, "dmp_Texture[0].samplerType"), GL_FALSE); glUniform1i(glGetUniformLocation(progID, "dmp_TexEnv[0].combineRgb"), GL_REPLACE); glUniform1i(glGetUniformLocation(progID, "dmp_TexEnv[0].combineAlpha"), GL_REPLACE); glUniform3i(glGetUniformLocation(progID, "dmp_TexEnv[0].operandRgb"), GL_SRC_COLOR, GL_SRC_COLOR, GL_SRC_COLOR); glUniform3i(glGetUniformLocation(progID, "dmp_TexEnv[0].operandAlpha"), GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA); glUniform3i(glGetUniformLocation(progID, "dmp_TexEnv[0].srcRgb"), GL_PRIMARY_COLOR, GL_PRIMARY_COLOR, GL_PRIMARY_COLOR); glUniform3i(glGetUniformLocation(progID, "dmp_TexEnv[0].srcAlpha"), GL_PRIMARY_COLOR, GL_PRIMARY_COLOR, GL_PRIMARY_COLOR);
12.4.2. シャドウ参照パス
シャドウ参照パスのために、フラグメントオペレーションモードを通常モード(GL_FRAGOP_MODE_GL_DMP
)に切り替え、テクスチャユニット 0(GL_TEXTURE0
)がシャドウ情報を累積したシャドウテクスチャを参照するように設定しなければなりません。シャドウテクスチャを参照させるには、予約ユニフォーム(dmp_TexEnv[0].samplerType
)にシャドウテクスチャ(GL_TEXTURE_SHADOW_2D_DMP
)を指定し、シャドウ情報を累積したテクスチャを GL_TEXTURE_2D
にバインドさせなければなりません。テクスチャコンバイナには、フラグメントライティングが有効な場合はフラグメントライティングのプライマリとセカンダリカラーを入力し、無効な場合は頂点カラーとテクスチャユニット 0 の出力を入力します。
シャドウ累積パスを行った際に、射影変換に透視投影を適用したか平行投影を適用したかを、予約ユニフォーム(dmp_Texture[0].perspectiveShadow
)で設定します。透視投影ならば GL_TRUE
を、平行投影ならば GL_FALSE
を指定してください。
glUniform1i(glGetUniformLocation(progID, "dmp_Texture[0].samplerType"), GL_TEXTURE_SHADOW_2D_DMP); glUniform1i(glGetUniformLocation(progID, "dmp_Texture[0].perspectiveShadow"), GL_TRUE); glUniform1i(glGetUniformLocation(progID, "dmp_TexEnv[0].combineRgb"), GL_MODULATE); glUniform1i(glGetUniformLocation(progID, "dmp_TexEnv[0].combineAlpha"), GL_MODULATE); glUniform3i(glGetUniformLocation(progID, "dmp_TexEnv[0].operandRgb"), GL_SRC_COLOR, GL_SRC_COLOR, GL_SRC_COLOR); glUniform3i(glGetUniformLocation(progID, "dmp_TexEnv[0].operandAlpha"), GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA); glUniform3i(glGetUniformLocation(progID, "dmp_TexEnv[0].srcRgb"), GL_TEXTURE0, GL_PRIMARY_COLOR, GL_PRIMARY_COLOR); glUniform3i(glGetUniformLocation(progID, "dmp_TexEnv[0].srcAlpha"), GL_TEXTURE0, GL_PRIMARY_COLOR, GL_PRIMARY_COLOR); glUniform1i(glGetUniformLocation(progID, "dmp_FragOperation.mode"), GL_FRAGOP_MODE_GL_DMP); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, shadowTexID);
シャドウ情報をテクスチャとして参照するのは 3DS でも OpenGL でも同じです。テクスチャユニットではテクセルから得るシャドウ情報とテクスチャ座標とを比較しますが、このときに適切なテクスチャ座標が指定されていないと正しく比較処理が行われません。シャドウテクスチャを参照するときのテクスチャ座標が、OpenGL では (s/q, t/q, r/q) であるのに対し、3DS では (s/r, t/r, r - bias) であることに注意しなければなりません。(s, t, r, q) はテクスチャ変換行列適用後のテクスチャ座標、bias は予約ユニフォーム(dmp_Texture[0].shadowZBias
)に設定されたバイアス値です。平行投影によりシャドウ情報を累積している場合は、テクスチャ座標 s, t はそのまま参照されますが、透視投影の場合はテクスチャ変換行列とバイアス値を調整しなければなりません。
累積したシャドウ情報とテクスチャ座標の比較処理が正しく行われるようにする設定としては、以下のテクスチャ変換行列とバイアス値を使用することが考えられます。
透視投影の場合
平行投影の場合
n は near クリップ面のクリップ値、f は far クリップ面のクリップ値、r と t は Frustum の右辺値と上辺値です。
ただし、テクスチャ座標 r が 0.0 ~ 1.0 の範囲外にある場合、シャドウバッファの深度情報と比較される値 (r – bias) を計算するときには、r を 0.0 ~ 1.0 の範囲にクランプしたあと、bias による減算とクランプが行われます。正しい比較が行われるためには、シャドウ累積パスの光源座標系で、far 平面の奥に配置されるようなオブジェクトに関する bias には 0 を指定してください。
OpenGL のコードであれば以下のように記述することでテクスチャ変換行列を生成することができます。
glMatrixMode(GL_TEXTURE); glLoadIdentity(); // 透視投影(glFrustum(-r, r, -t, t, n, f))で累積していた場合 glFrustumf(r/n, -3r/n, t/n, -3t/n, 1.0f, 0.0f); glScalef(-1.0f/(f-n), -1.0f/(f-n), -1.0f/(f-n)); // 平行投影(glOrtho(-r, r, -t, t, n, f))で累積していた場合 glOrthof(-3r, r, -3t, t, 2n-f, f);
ボーダーカラーとテクスチャラッピングモードの設定によって、0.0 ~ 1.0 の範囲外に算出されたテクスチャ座標によるサンプリング結果を制御することができます。テクスチャラッピングモードにより、テクスチャ座標の s, t 成分に対して GL_CLAMP_TO_BORDER
が設定されている場合、範囲外のサンプリング値はボーダーカラー(すべての成分値が 0.0 または 1.0 に統一されていなければならない)の設定になることが保証されています。現在の実装では、ラッピングモードに GL_CLAMP_TO_BORDER
以外を設定した場合や、ボーダーカラーに (0.0, 0.0, 0.0, 0.0) または (1.0, 1.0, 1.0, 1.0) 以外を設定した場合のサンプリング結果は不定です。
glBindTexture(GL_TEXTURE_2D, shadowTexID); GLfloat bcolor[] = {1.0f, 1.0f, 1.0f, 1.0f}; glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, bcolor); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_LOD, 0);
光源座標系でのデプス値がシャドウテクスチャのテクセルに含まれるデプス情報よりも大きい場合、比較後のシャドウ強度は 0.0 となります。それ以外の場合はシャドウテクスチャのテクセルに含まれるシャドウ強度そのものです。シャドウ強度はテクスチャユニットから RGBA の各要素の値として出力されます。
12.4.3. 全方位シャドウマッピング
キューブマッピングとシャドウテクスチャの組み合わせで、全方位シャドウマッピングを実現することができます。
実現するためには、シャドウ累積パスでテクスチャユニットにバインドするテクスチャをキューブマップテクスチャ(GL_TEXTURE_CUBE_MAP_POSITIVE_{X,Y,Z}
または GL_TEXTURE_CUBE_MAP_NEGATIVE_{X,Y,Z}
の 6 つ)を指定して 6 回レンダリングを行い、シャドウ参照パスで参照するテクスチャ(dmp_Texture[0]. samplerType
)に GL_TEXTURE_SHADOW_CUBE_DMP
を指定しなければなりません。また、テクスチャ座標のラッピングモードには、S, T 方向ともに GL_CLAMP_TO_EDGE
を指定しなければなりません。
12.4.4. シルエットシェーダを利用したソフトシャドウ
シャドウテクスチャはデプス情報とシャドウ強度を含んでおり、シャドウテクスチャを参照しているテクスチャユニットからは、シャドウ領域内ならば黒 (0.0, 0.0, 0.0) が、領域外ならばシャドウテクスチャに保持されているシャドウ強度(0.0 ~ 1.0)がサンプリング値として出力されます。このシャドウ強度を適切な値に設定することでソフトシャドウを表現することができます。
3DS のシルエットシェーダは、シルエットのプリミティブ(シルエットエッジ)をシャドウが完全に消える部分に向かってシャドウ強度が段階的に変わるようなソフトシャドウ領域として描画することができます。シルエットシェーダを使用したレンダリングは、不透明ハードシャドウのレンダリングを行った後で行います。シルエットエッジのカラーの G 成分には 1.0 を指定し、頂点シェーダで頂点カラーの G 成分に 0.0 が出力されるように設定します。そのほかの設定は不透明ハードシャドウのレンダリング時と同じにします。これにより、シルエットエッジの矩形はオブジェクト側の G 成分が 0.0 に、外側に向かって段階的に G 成分が 1.0 となるように描画されます。
このソフトシャドウ領域の累積パスでは、シャドウ強度に対してオブジェクトまでの相対距離による付加的モジュレーションを適用することができます。これを減衰因子と呼び、予約ユニフォームのソフトシャドウのバイアス値(dmp_FragOperation.penumbraBias
)とスケール値(dmp_FragOperation.penumbraScale
)により、ソフトシャドウ領域の幅を調整することができます。これらの値を調整することで、オブジェクトに近い位置のソフトシャドウは幅が狭くなり、より自然なソフトシャドウを表現することができるようになります。
減衰因子は以下の数式で計算されます。式中の Zfrag はフラグメントのデプス値、Zrec はシャドウテクスチャに格納されているデプス値です。前もってオブジェクトを描画し、シャドウテクスチャにデプス値を格納していなければシャドウ強度の減衰の効果は正しく反映されません。
12.4.5. シャドウアーティファクトへの対処
12.4.5.1. セルフシャドウエイリアシング
マルチパスによるシャドウレンダリングでは、誤ってフラグメントが自分自身に影を落とすセルフシャドウエイリアシングにより、レンダリング結果にモアレが発生するなどの問題が発生することがあります。これは、累積時のデプス情報が参照時に光源から見たデプス値よりもわずかに小さな場合に起こります。このとき発生する不自然な影をシャドウアーティファクトと呼びます。
シャドウアーティファクトの抑制方法として、参照パスのデプス値に負のオフセット値(バイアス)を適用する方法が存在します。バイアス値は、予約ユニフォームの dmp_Texture[0].shadowZBias
で設定することができます。
glUniform1f(glGetUniformLocation(progID, "dmp_Texture[0].shadowZBias"), 1.2f*n/(f-n));
12.4.5.2. シルエットのシャドウアーティファクト
参照パスでシャドウ領域ではないと判断された場合、シャドウテクスチャのシャドウ強度がサンプリング値として出力されることは以前にも説明しました。通常はシャドウ領域外のシャドウ強度は 1.0 で輝度の減衰は起こりませんが、シルエットシェーダで描画されたソフトシャドウ領域で出力されるシャドウ強度が 1.0 以外になることで輝度の減衰が起こり、オブジェクトによってはシャドウアーティファクトが発生することになります。
シルエットのシャドウが生成するシャドウアーティファクトに対しては、セルフシャドウエイリアシングの抑制方法は有効ではありませんが、フラグメントライティングのシャドウに関する設定とテクスチャコンバイナの設定を調整することで抑制することができます。
主に光源に対して平行な面にソフトシャドウによる輝度減衰が起こることが原因ですので、シャドウテクスチャの出力値(シャドウ減衰項)を以下の計算式で算出する方法が存在します。
ShadowAttenuation = 1.0 - f (1.0 - ShadowIntensity)
f はフラグメントの法線が光源に垂直なときは 0.0 近傍、平行なときは 1.0 近傍の値を返す関数です。
この計算式によりシャドウ減衰項は、フラグメントの法線と光源が垂直に近い場合は 1.0(シャドウの影響なし)、平行に近い場合はシャドウ強度そのものとなります。
このシャドウ減衰項は、関数 f の部分をフレネルファクタ(参照テーブル FR)、シャドウ強度の反転部分をライティング環境の予約ユニフォーム(dmp_LightEnv.shadowAlpha
、dmp_LightEnv.invertShadow
)、最終的な値の反転部分をテクスチャコンバイナの設定により実装することができます。
ライティング関連は以下の設定で行います。フレネルファクタがアルファのみに影響を与えることに注意してください。アルファ成分はテクスチャコンバイナで乗算します。関数 f は入力値の 2 乗を返す設定です。
glUniform1i(glGetUniformLocation(progID, "dmp_FragmentLighting.enabled"), GL_TRUE); // ..other code.. glUniform1i(glGetUniformLocation(progID, "dmp_LightEnv.lutInputFR"), GL_LIGHT_ENV_LN_DMP); glUniform1i(glGetUniformLocation(progID, "dmp_LightEnv.config"), GL_LIGHT_ENV_LAYER_CONFIG1_DMP); glUniform1i(glGetUniformLocation(progID, "dmp_LightEnv.fresnelSelector"), GL_LIGHT_ENV_PRI_SEC_ALPHA_FRESNEL_DMP); glUniform1i(glGetUniformLocation(progID, "dmp_LightEnv.shadowAlpha"), GL_TRUE); glUniform1i(glGetUniformLocation(progID, "dmp_LightEnv.invertShadow"), GL_TRUE); GLfloat lut[512]; int j; memset(lut, 0, sizeof(lut)); for (j = 1; j < 128; j++) { lut[j] = powf((float)j/127.0f, 2.0f); lut[j+255] = lut[j] - lut[j-1]; } glTexImage1D(GL_LUT_TEXTURE0_DMP, 0, GL_LUMINANCEF_DMP, 512, 0, GL_LUMINANCEF_DMP, GL_FLOAT, lut); glUniform1i(glGetUniformLocation(progID, "dmp_FragmentMaterial.samplerFR"), 0);
以上のコードで、f (1.0 - ShadowIntensity) の部分がフラグメントのプライマリとセカンダリのアルファ成分として出力されるようになります。
次に、テクスチャコンバイナの設定により最終的なシャドウ減衰項を算出し、フラグメントのプライマリカラーと乗算します。このとき、フラグメントのプライマリアルファを反転(GL_ONE_MINUS_SRC_ALPHA
)していることに注意してください。
glUniform1i(glGetUniformLocation(progID, ("dmp_TexEnv[0].combineRgb"), GL_MODULATE); glUniform1i(glGetUniformLocation(progID, ("dmp_TexEnv[0].combineAlpha"), GL_REPLACE); glUniform3i(glGetUniformLocation(progID, ("dmp_TexEnv[0].operandRgb"), GL_SRC_COLOR, GL_ONE_MINUS_SRC_ALPHA, GL_SRC_COLOR); glUniform3i(glGetUniformLocation(progID, ("dmp_TexEnv[0].operandAlpha"), GL_SRC_ALPHA, GL_SRC_ALPHA, GL_SRC_ALPHA); glUniform3i(glGetUniformLocation(progID, ("dmp_TexEnv[0].srcRgb"), GL_FRAGMENT_PRIMARY_COLOR_DMP, GL_FRAGMENT_PRIMARY_COLOR_DMP, GL_PRIMARY_COLOR); glUniform3i(glGetUniformLocation(progID, ("dmp_TexEnv[0].srcAlpha"), GL_PRIMARY_COLOR, GL_PRIMARY_COLOR, GL_PRIMARY_COLOR);
12.4.6. 予約ユニフォーム
以下の表は、シャドウで使用する予約ユニフォームの一覧です。
予約ユニフォーム |
種別 |
設定値 |
---|---|---|
|
|
参照するテクスチャの種別を指定する。シャドウで使用可能なのは以下の 2 つです。
|
|
|
射影変換に透視投影を適用しているかどうかを指定する。
|
|
|
参照パスのデプス値に適用される負のオフセットのバイアス値を指定する。 0.0(デフォルト) |
|
|
フラグメントオペレーションモードを指定する。累積パスではシャドウモード、参照パスでは標準モードを指定する。
|
|
|
w バッファのスケール因子を指定する。 0.0(デフォルト) |
|
|
ソフトシャドウのスケール値を指定する。 1.0(デフォルト) |
|
|
ソフトシャドウのバイアス値を指定する。 0.0(デフォルト) |
12.4.7. シャドウテクスチャの内容を確認する方法
シャドウテクスチャにレンダリングしたイメージを確認するには、シャドウテクスチャをカレントのカラーバッファにアタッチした状態で、format
に GL_RGBA
を、type
に GL_UNSIGNED_BYTE
を指定して glReadPixels()
を呼び出し、テクセルデータを読み出してください。テクセルデータに u32
型のポインタでアクセスした場合、下位 8 bit にシャドウ強度が、上位 24 bit にデプス値が格納されています。デプス値だけを取得するには右に 8 bit シフトしてください。
シャドウテクスチャに格納されているシャドウ強度とデプス値は、8 bit 値と 24 bit 値で範囲 0.0 ~ 1.0 の値を表しているため、それぞれ精度が異なります。
シャドウ強度は 0x00 ~ 0xFF の範囲の値をとり、0xFF ならばソフトシャドウ領域が存在せず、それ以外ならばソフトシャドウ領域が存在することを示しています。つまり、0x00 ならばシャドウ強度は 0.0、0xFF ならば 1.0 です。
デプス値は near 値を 0x000000、far 値を 0xFFFFFF とする範囲にスケーリングされた値です。つまり、0x000000 ならばデプス値は 0.0、0xFFFFFF ならば 1.0 です。シャドウ累積パスで w バッファ(「10.3.3. w バッファ」)を有効にしている場合は、このスケーリングが一様であることに注意してください。
シャドウテクスチャに書き込まれるシャドウ強度とデプス値は、シャドウが存在しない領域ではシャドウ強度が 0xFF、デプス値が 0xFFFFFF となり、ハードシャドウだけが存在する領域ではシャドウ強度が 0xFF、デプス値が 0xFFFFFF 以外の値となります。また、ハードシャドウとソフトシャドウの両方が存在する領域ではシャドウ強度が 0xFF 以外、デプス値が 0xFFFFFF 以外の値となります。
シャドウの種類 |
シャドウ強度 |
デプス値 |
---|---|---|
シャドウなし |
0xFF |
0xFFFFFF |
ハードシャドウのみ |
0xFF |
0xFFFFFF 以外 |
ソフトシャドウあり |
0xFF 以外 |
0xFFFFFF 以外 |
12.5. フォグ
3DS のフォグは OpenGL ES 1.1 に定義されているフォグとほぼ同等の機能を持ちますが、OpenGL が視点からの距離でフォグの効果が決定されるのに対し、3DS は投影で補正されたデプス値により決定されます。また、フォグ係数の指定を参照テーブルで行う点も異なり、フォグの開始・終了・密度などの設定は存在しません。
OpenGL と同様に、フォグ適用後のフラグメントのカラーは以下の計算式で決定されます。
Color = f × Cfragment + (1 - f) × Cfog
f はフォグ係数(0.0 ~ 1.0)です。
12.5.1. 予約ユニフォーム
フォグの予約ユニフォームには、以下のものが存在します。
フォグモード
フォグ機能を有効にするには、フォグモード(dmp_Fog.mode
)に glUniform1i()
で GL_FOG
を設定してください。無効にするには GL_FALSE
を設定します。
フォグカラー
フォグカラー(dmp_Fog.color
)には glUniform3f()
でカラー値を設定します。RGB 成分のみでアルファ成分は設定しません。
フォグ係数
フォグ係数は入力値としてウィンドウ座標系でのデプス値をとる参照テーブルを設定します。glUniform1i()
で使用する参照テーブル番号を予約ユニフォーム(dmp_Fog.sampler
)に指定してください。ここで指定する参照テーブル番号は GL_LUT_TEXTUREi_DMP
の i
(0 ~ 31) が表す数値で、テクスチャの名前(ID)や GL_LUT_TEXTUREi_DMP
を直接指定するわけではないことに注意してください。
入力デプス値の反転
フォグ係数の参照テーブルへの入力値を反転(z ではなく 1 - z に)するかどうかを選択することができます。反転させる場合は、予約ユニフォーム(dmp_Fog.zFlip
)に glUniform1i()
で GL_TRUE
を設定してください。
予約ユニフォーム |
種別 |
設定値 |
---|---|---|
|
|
フォグパイプラインの処理モード(フォグモード)を指定する。
|
|
|
フォグカラーを指定する。アルファ成分はありません。 各成分とも 0.0 ~ 1.0 (デフォルト)(0.0, 0.0, 0.0) |
|
|
フォグ係数の参照テーブルに入力するデプス値を反転するかどうかを指定する。
|
|
|
フォグ係数に使用する参照テーブルを指定する。 0 ~ 31 |
12.5.2. 参照テーブルの作成
フォグ係数の参照テーブルは入力値の範囲が 0.0 ~ 1.0 で width
は 256 固定です。ほかの参照テーブルと同様に前半の 128 要素には出力値を、後半の 128 要素には出力値間の差分を設定します。
OpenGL のフォグ係数を 3DS で実装する際に、参照テーブルの作成で注意しなければならないのは入力値の変換方法です。入力値はウィンドウ座標系のデプス値であり、ニアクリップ平面で最小値(0.0)、ファークリップ平面で最大値(1.0)となるのですが、透視投影では視点座標系のデプス値との対応が線形的ではありません。そのため、参照テーブルの出力値を設定するときには、ウィンドウ座標系のデプス値から視点座標系のデプス値に変換した値で計算された出力値でなければなりません。
ウィンドウ座標系である入力値(0.0 ~ 1.0)はクリップ座標系の(0.0 ~ -1.0)にマッピングされることを考慮して、視点座標系への変換には以下の計算式を使用します。入力値の符号を逆にする点に注意してください。
(Xe Ye Ze We) = (0.0 0.0 -Zw 1.0) × Mprojection-1
フォグ係数は視点座標系の原点からフラグメントへの距離の関数ですので、その関数への入力値は視点座標系でのフラグメントと xy 平面との距離 -Ze / We で近似することができます。
以下にそのコード例を示します。FogCoef()
はフォグ係数の関数です。
float Fog_LUT[256], Fog_c[128 + 1]; int i; Matrix44 invPM; Vector4 v_eye, v_clip(0.0f, 0.0f, 0.0f, 1.0f); MTX44Inverse(&invPM, &projMatrix); Vector4 v0(invPM.m[0]); Vector4 v1(invPM.m[1]); Vector4 v2(invPM.m[2]); Vector4 v3(invPM.m[3]); for (i = 0; i <= 128; i++) { v_clip.z = -(static_cast<f32>(i)) / 128; v_eye.x = VEC4Dot(&v0, &v_clip); v_eye.y = VEC4Dot(&v1, &v_clip); v_eye.z = VEC4Dot(&v2, &v_clip); v_eye.w = VEC4Dot(&v3, &v_clip); Fog_c[i] = -(v_eye.z / v_eye.w); } for (i = 0; i < 128; i++) { Fog_LUT[i] = FogCoef(Fog_c[i]); Fog_LUT[128 + i]= FogCoef(Fog_c[i + 1]) - FogCoef(Fog_c[i]); }
OpenGL のフォグ機能は視点座標系の z 座標値の影響を受けていましたが、3DS のフォグ機能は透視投影補正されたデプス値の影響を受けます。そのため、ニアクリップ面、ファークリップ面が変更されるとフォグの効果が変化し、w バッファ(「10.3.3. w バッファ」参照)を使用するか、通常のデプスバッファを使用するかで、同じ参照テーブルが異なった効果を与えることになります。
12.6. ガスレンダリング
ガスレンダリング機能は、ポリゴンオブジェクトのデプス情報を考慮しながら作成される密度情報を使って、ガスモードに設定されたフォグ機能がガス状物体をレンダリングする機能です。ガス状物体をレンダリングするには、ポリゴンオブジェクトのデプス情報を生成するポリゴンオブジェクト描画パス、ガステクスチャに密度情報を累積する密度情報描画パス、ガステクスチャを参照してガス状物体をレンダリングするシェーディングパスの 3 パスが必要となります。
12.6.1. ポリゴンオブジェクト描画パス
このパスでは通常通りにポリゴンオブジェクトをレンダリングし、ポリゴンオブジェクトとガス状物体との交差判定をするためのデプス情報を作成します。レンダリング結果のうち、デプスバッファの内容が次のパスで使用されます。
12.6.2. 密度情報描画パス
このパスではガス状物体を構成する最小単位であるガスパーティクル(密度パターンテクスチャ)をポイントスプライトなどでレンダリングし、ガスの密度情報をカラーバッファに累積します。カラーバッファの内容はガステクスチャにコピーして次のパスで使用されます。
カラーバッファとガステクスチャの準備
内部フォーマットが GL_GAS_DMP
のカラーバッファと、レンダリング結果をコピーするテクスチャ(ガステクスチャ)を用意します。カラーバッファはデプスバッファと同じサイズで作成しますが、ガステクスチャは幅と高さのテクセル数が 2 のべき乗でなければならず、type
には GL_UNSIGNED_SHORT
を指定しなければなりません。また、ガステクスチャは常にポイントサンプリングされるため、拡大時と縮小時のフィルタ設定は無効となります。フィルタ設定には GL_NEAREST
を指定してください。ガステクスチャにはミップマップを適用することができません。
// Generating Object glGenFramebuffers(1, &gasAccFboID); glGenTextures(1, &gasTexID); // Renderbuffer & Framebuffer glGenRenderbuffers(1, &gasAccColorID); glBindRenderbuffer(GL_RENDERBUFFER, gasAccColorID); glRenderbufferStorage(GL_RENDERBUFFER, GL_GAS_DMP, GAS_ACC_WIDTH, GAS_ACC_HEIGHT); glBindFramebuffer(GL_FRAMEBUFFER, gasAccFboID); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, gasAccColorID); // Gaseous Texture glBindTexture(GL_TEXTURE_2D, gasTexID); glTexImage2D(GL_TEXTURE_2D, 0, GL_GAS_DMP, GAS_TEX_WIDTH, GAS_TEX_HEIGHT, 0, GL_GAS_DMP, GL_UNSIGNED_SHORT, 0); glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
ガスパーティクルのレンダリング
カラーバッファに密度情報を描画するには、フラグメントオペレーションモード(dmp_FragOperation.mode
)をガスモード(GL_FRAGOP_MODE_GAS_ACC_DMP
)に切り替えます。カラーバッファには単純に累積したもの(D1)とポリゴンオブジェクトとの交差を考慮したもの(D2)の 2 種類の密度情報が蓄積されます。
デプスバッファの内容が更新されないようにデプステストとデプスマスクはオフにし、ブレンディングとフォグもオフにします。ただし、ポリゴンオブジェクトをレンダリングしたときのデプステストの比較関数はそのままにしておいてください。
バッファをクリアするのは密度情報が描画されるカラーバッファのみです。デプスバッファをクリアしてはいけません。
レンダリングされるガスパーティクルの R 成分(Df)が密度情報としてカラーバッファに蓄積されます。D1 と D2 は以下の計算式によって D1’ と D2’ に更新されます。D2’ の式はデプステストの比較関数によって変化します。
Zb はデプスバッファに格納されているデプス値、Zf はフラグメントのデプス値です。
D1 にはフラグメント(ガスパーティクル)の密度情報がそのまま累積されます。D2 にはデプスバッファのデプス値とフラグメントのデプス値との差分にデプス方向の減衰係数 EZ を掛け合わせた値が、さらにフラグメントの密度情報と掛け合わされて累積されます。係数 EZ は予約ユニフォーム(dmp_Gas.deltaZ
)に glUniform1f()
で設定された浮動小数点数です。
// Change to Gas Accumulation Mode glBindFramebuffer(GL_FRAMEBUFFER, gasAccFboID); glUniform1i(glGetUniformLocation(progID, "dmp_FragOperation.mode"), GL_FRAGOP_MODE_GAS_ACC_DMP); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); glDisable(GL_BLEND); glUniform1i(glGetUniformLocation(progID, "dmp_Fog.mode"), GL_FALSE); // Colorbuffer Clear glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClear(GL_COLOR_BUFFER_BIT); // Set dmp_Gas.deltaZ glDepthFunc(GL_LESS); glUniform1f(glGetUniformLocation(progID, "dmp_Gas.deltaZ"), 50.0f);
ガステクスチャへのコピー
すべてのガスパーティクルをレンダリングしたのち、カラーバッファに蓄積した密度情報をガステクスチャにコピーします。
通常、ガステクスチャはカラーバッファと同じ大きさで確保されない(幅と高さが 2 のべき乗でなければならない)ため、カラーバッファから glCopyTexSubImage2D()
で部分コピーすることになります。ガステクスチャは次のパスで必要となります。
// Bind and CopyTo Gaseous Texture glBindTexture(GL_TEXTURE_2D, gasTexID); glCopyTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, 0, 0, GAS_ACC_WIDTH, GAS_ACC_HEIGHT);
12.6.3. シェーディングパス
このパスでは、ガステクスチャに蓄積されている密度情報を参照してガス状物体のシェーディングを行い、その結果をポリゴンオブジェクト描画パスのカラーバッファとブレンディングします。
ガス状物体のシェーディングにはガスモードに設定されたフォグ機能を使用します。また、テクスチャコンバイナからフォグへの入力に関しては特殊な設定が必要となります。
フォグへの入力(テクスチャコンバイナの設定)
フォグは入力として、最終の 1 つ前のテクスチャコンバイナから RGBA 成分(実際に使用するのは R 成分のみ)と、最終のテクスチャコンバイナの入力ソース 2(srcRgb
, srcAlpha
の第 3 要素)に指定された入力ソース(ガステクスチャをサンプリングするテクスチャユニットを指定すること)から密度情報を受け取ります。最終のテクスチャコンバイナからの出力は無視されますので、結果的に使用可能なテクスチャコンバイナが 1 段減ることになります。
最終的に、フォグからの出力とカラーバッファとのブレンディングを行います。フォグから出力されるアルファ値はポリゴンオブジェクトとの交差を考慮したものですので、フォグのアルファ値をもとに両出力をブレンディングすることで正しい前後関係でガス状物体を描画することができます。
ガスモード時のフォグの動作
ガスモード時のフォグは、入力された情報をもとにガス状物体のシェーディングを行います。フォグ機能のガスモードを有効にするには、フォグモード(dmp_Fog.mode
)に GL_GAS_DMP
を設定してください。
シェーディング結果の RGB 成分はシェーディング参照テーブルによって決定されます。アルファ成分は、交差を考慮した密度情報(D2)に対してガスの減衰(dmp_Gas.attenuation
)を掛け合わせたものを入力値としたときの、フォグ係数(dmp_Fog.sampler
)で指定された参照テーブルからの出力値で決定されます。
シェーディング参照テーブル
シェーディング参照テーブルは密度情報もしくはシェーディング強度を入力値として受け取り、シェーディング結果の RGB 成分値を出力します。
シェーディング参照テーブルには、width
に 16 を指定して作成したテーブルを成分別で指定します。入力される値は 0.0 ~ 1.0 の範囲で、出力する値の範囲も 0.0 ~ 1.0 です。前半の 8 要素に出力値を、後半の 8 要素に出力値間の差分を設定することや、出力値が差分値で補間されて算出されることはほかの参照テーブルと同じです。
使用するシェーディング参照テーブルは、glUniform1i()
で参照テーブル番号を以下の予約ユニフォームに指定します。ここで指定する参照テーブル番号は GL_LUT_TEXTUREi_DMP
の i
(0 ~ 31) が表す数値で、テクスチャの名前(ID)や GL_LUT_TEXTUREi_DMP
を直接指定するわけではないことに注意してください。
予約ユニフォーム |
種別 |
設定値 |
---|---|---|
|
|
シェーディング参照テーブル(R、G、B 成分)に使用する参照テーブル番号を指定する。 0 ~ 31 |
シェーディング参照テーブルの例と、その実装例を以下に示します。
// Define GLfloat shading_color[3 * 9] = { 0.00f, 0.00f, 0.00f, 0.20f, 0.15f, 0.05f, 0.60f, 0.25f, 0.15f, 0.90f, 0.35f, 0.20f, 0.92f, 0.60f, 0.15f, 0.95f, 0.85f, 0.05f, 1.00f, 0.95f, 0.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f, 1.00f }; GLfloat samplerTR[16], samplerTG[16], samplerTB[16]; // Table for(int i = 0; i < 8; i++) { // shading color value samplerTR[i] = shading_color[3*i + 0]; samplerTG[i] = shading_color[3*i + 1]; samplerTB[i] = shading_color[3*i + 2]; // difference of shading color value samplerTR[8 + i] = shading_color[3*(i + 1) + 0] - shading_color[3*i + 0]; samplerTG[8 + i] = shading_color[3*(i + 1) + 1] - shading_color[3*i + 1]; samplerTB[8 + i] = shading_color[3*(i + 1) + 2] - shading_color[3*i + 2]; } // Texture glBindTexture(GL_LUT_TEXTURE1_DMP, samplerTR_ID); glTexImage1D(GL_LUT_TEXTURE1_DMP, 0, GL_LUMINANCEF_DMP, 16, 0, GL_LUMINANCEF_DMP, GL_FLOAT, samplerTR); glBindTexture(GL_LUT_TEXTURE2_DMP, samplerTG_ID); glTexImage1D(GL_LUT_TEXTURE2_DMP, 0, GL_LUMINANCEF_DMP, 16, 0, GL_LUMINANCEF_DMP, GL_FLOAT, samplerTG); glBindTexture(GL_LUT_TEXTURE3_DMP, samplerTB_ID); glTexImage1D(GL_LUT_TEXTURE3_DMP, 0, GL_LUMINANCEF_DMP, 16, 0, GL_LUMINANCEF_DMP, GL_FLOAT, samplerTB); // Set Uniform glUniform1i(glGetUniformLocation(progID, "dmp_Gas.samplerTR"), 1); glUniform1i(glGetUniformLocation(progID, "dmp_Gas.samplerTG"), 2); glUniform1i(glGetUniformLocation(progID, "dmp_Gas.samplerTB"), 3);
密度情報によるシェーディング
ライトによる陰影の影響を考慮せず、密度情報だけでシェーディングを行う場合は、glUniform1i()
で予約ユニフォーム(dmp_Gas.colorLutInput
)に GL_GAS_DENSITY_DMP
を設定し、シェーディング参照テーブルへの入力値に密度情報を選択してください。
密度情報描画パスでも説明したように、ガステクスチャに格納される密度情報は 2 種類存在します。1 つはポリゴンオブジェクトとの交差を考慮しない密度情報(D1)で、もう 1 つは交差を考慮する密度情報(D2)です。どちらの密度情報を使用するのかは予約ユニフォーム(dmp_Gas.shadingDensitySrc
)に設定する値で選択することができます。
D1 ならば GL_GAS_PLAIN_DENSITY_DMP
を、D2 ならば GL_GAS_DEPTH_DENSITY_DMP
を glUniform1i()
で設定してください。ガスモード時のフォグから出力されるアルファ成分は交差を考慮した密度情報(D2)から算出されていますので、シェーディング参照テーブルへの入力値に D1 を選択したとしても、アルファ値を利用したブレンディングを行うことでポリゴンオブジェクトとガス状物体を正しい前後関係で描画することができます。
参照テーブルへの入力値は 0.0 ~ 1.0 です。密度情報に密度の最大値を逆数にして掛け合わせることで、この範囲に収まる値にします。最大値の逆数は自動的に計算させることができます。予約ユニフォーム(dmp_Gas.autoAcc
)に glUniform1i()
で GL_TRUE
を設定した場合は、密度情報描画パスでの D1 の最大値から逆数を計算します。
GL_FALSE
を設定した場合は、予約ユニフォーム(dmp_Gas.accMax
)に glUniform1f()
で設定した値を最大値の逆数として使用します。
シェーディング強度によるシェーディング
ライトによる陰影の影響を考慮したシェーディング強度を算出してシェーディングを行う場合は、glUniform1i()
で予約ユニフォーム(dmp_Gas.colorLutInput
)に GL_GAS_LIGHT_FACTOR_DMP
を設定し、シェーディング参照テーブルへの入力値にシェーディング強度を選択してください。
シェーディング強度は、平面シェーディング強度(IG)と視線シェーディング強度(IS)と呼ばれる 2 つの算出値の合計です。IG と IS の計算式は以下のように定義されています。
ig = r × (1.0 - lightAtt × d1)
IG = (1.0 - ig) × lightMin + ig × lightMax
is = LZ × (1.0 - scattAtt × d1)
IS = (1.0 - is) × scattMin + is × scattMax
d1 はポリゴンオブジェクトとの交差を考慮していない密度情報(D1)に、密度情報によるシェーディングと同様に密度の最大値の逆数が掛け合わされた値です。r はフォグに入力されたテクスチャコンバイナの出力値の R 成分、lightAtt、lightMin、lightMax は平面シェーディング強度の係数です。LZ は視点座標系の z 軸に対するライトの方向、scattAtt、scattMin、scattMax は視線シェーディング強度の係数です。これらの値がとる範囲は 0.0 ~ 1.0 です。
計算式から、平面シェーディング強度は r と (1.0 - lightAtt × d1) に比例し、r が大きくなると lightMax に近付き、d1 が大きくなると lightMin に近付くことがわかります。また、視線シェーディング強度は LZ と (1.0 - scattAtt × d1) に比例し、LZ が大きくなると scattMax に近付き、d1 が大きくなると scattMin に近付くことがわかります。
シェーディング参照テーブルに入力されるシェーディング強度が、ガス状物体の密度が低いほど大きくなる点に注意しなければなりません。これはガス状物体の密度の低い部分では光が透過し、密度の高い部分では光が吸収されることを示しています。つまり、lightMin および scattMin はライトの影響が小さいときのシェーディング強度に、lightMax および scattMax はライトの影響が大きいときのシェーディング強度になるように設定します。lightAtt および scattAtt は密度による光の減衰の割合を設定します。シェーディング参照テーブルの設定によっては、必ずしも lightMin < lightMax(scattMin < scattMax)の関係になるとは限らないことに注意してください。そして、アルファ値を決定するフォグ係数への入力値が密度に比例することにも注意が必要です。
平面シェーディング強度の係数(lightMin、lightMax、lightAtt)は予約ユニフォーム(dmp_Gas.lightXY
)に、視線シェーディング強度の係数(scattMin、scattMax、scattAtt)と視点座標系の z 軸に対するライトの方向(LZ)は予約ユニフォーム(dmp_Gas.lightZ
)に、それぞれまとめて設定します。設定順は、最小値、最大値、減衰値の順で行い、視線シェーディング強度にはその後に LZ を追加して設定します。
以下に、シェーディング強度の係数を設定するコードを示します。
GLfloat lightXY[3], lightZ[4]; GLfloat lightMin, lightMax, lightAtt; GLfloat scattMin, scattMax, scattAtt, LZ; //... // lightXY lightXY[0] = lightMin; lightXY[1] = lightMax; lightXY[2] = lightAtt; // lightZ lightZ[0] = scattMin; lightZ[1] = scattMax; lightZ[2] = scattAtt; lightZ[3] = LZ; // Set Uniform glUniform3fv(glGetUniformLocation(progID, "dmp_Gas.lightXY"), 1, lightXY); glUniform4fv(glGetUniformLocation(progID, "dmp_Gas.lightZ"), 1, lightZ);
アルファ値のシェーディング
アルファ成分のシェーディング結果は、交差を考慮した密度情報(D2)とガスの減衰(dmp_Gas.attenuation
)とを掛け合わせ、それを入力値とするフォグ係数(dmp_Fog.sampler
)の参照テーブルからの出力値で決定されます。
通常、ガスの密度が低いときは 0.0 に、高いときは 1.0 に近付く関数をフォグ係数に指定することになります。
// Fog Factor for(int i = 0; i < 128; i++) { fogTable[i]= 1.0f - exp(-8.0f * i / 128.0f); } for(int i = 0; i < 128; i++) { fogTable[128 + i] = fogTable[i + 1] - fogTable[i]; } fogTable[255] = 0; // Set LUT glGenTextures(1, &fogLUT_ID); glBindTexture(GL_LUT_TEXTURE0_DMP, fogLUT_ID); glTexImage1D(GL_LUT_TEXTURE0_DMP, 0, GL_LUMINANCEF_DMP, 256, 0, GL_LUMINANCEF_DMP, GL_FLOAT, fogTable);
以下のコードは、シェーディングパスで必要となるユニフォーム設定と、ガステクスチャを貼り付けた Quad ポリゴンの描画の実装例です。
// Bind Framebuffer(Colorbuffer) glBindFramebuffer(GL_FRAMEBUFFER, renderFboID); // Bind Gas Texture glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, gasTexID); glUniform1i(glGetUniformLocation(progID, "dmp_Texture[0].samplerType"), GL_TEXTURE_2D); // Set TextureCombiner #5 glUniform3i(glGetUniformLocation(progID, "dmp_TexEnv[5].srcRgb"), GL_PREVIOUS, GL_PREVIOUS, GL_TEXTURE0); glUniform3i(glGetUniformLocation(progID, "dmp_TexEnv[5].srcAlpha"), GL_PREVIOUS, GL_PREVIOUS, GL_TEXTURE0); // Set Uniform for Gas Shading Mode glUniform1i(glGetUniformLocation(progID, "dmp_Fog.sampler"), 0); glUniform1i(glGetUniformLocation(progID, "dmp_Gas.autoAcc"), GL_FALSE); glUniform1i(glGetUniformLocation(progID, "dmp_Gas.samplerTR"), 1); glUniform1i(glGetUniformLocation(progID, "dmp_Gas.samplerTG"), 2); glUniform1i(glGetUniformLocation(progID, "dmp_Gas.samplerTB"), 3); glUniform1i(glGetUniformLocation(progID, "dmp_Gas.shadingDensitySrc"), GL_GAS_DEPTH_DENSITY_DMP); glUniform1i(glGetUniformLocation(progID, "dmp_Gas.colorLutInput"), GL_GAS_DENSITY_DMP); glUniform1f(glGetUniformLocation(progID, "dmp_Gas.accMax"), 1.0f/6.0f); glUniform4fv(glGetUniformLocation(progID, "dmp_Gas.lightZ"), 1, gasLightZ); glUniform3fv(glGetUniformLocation(progID, "dmp_Gas.lightXY"), 1, gasLightXY); // Change to Gas Shading Mode glUniform1i(glGetUniformLocation(progID, "dmp_FragOperation.mode"), GL_FRAGOP_MODE_GL_DMP); glUniform1i(glGetUniformLocation(progID, "dmp_Fog.mode"), GL_GAS_DMP); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE);
// Gaseous Shading // Texture Coord float u0 = 0.0f; float v0 = 0.0f; float u1 = (GAS_ACC_WIDTH * 1.0f) / (GAS_TEX_WIDTH * 1.0f); float v1 = (GAS_ACC_HEIGHT * 1.0f) / (GAS_TEX_HEIGHT * 1.0f); GLfloat texCoord[8]= {u0, v0, u0, v1, u1, v1, u1, v0}; // Vertex GLushort quadIndex[6] = {0, 1, 2, 0, 2, 3}; GLfloat vertex[16] = { -1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 1.0f, 0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 0.0f, 1.0f }; // Set Array GLfloat quadIndexID, quadVertexID, quadTexCoordID; glGenBuffers(1, &quadIndexID); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadIndexID); glBufferData(GL_ELEMENT_ARRAY_BUFFER, 6 * sizeof(GLushort), &quadIndex, GL_STATIC_DRAW); glGenBuffers(1, &quadVertexID); glBindBuffer(GL_ARRAY_BUFFER, quadVertexID); glBufferData(GL_ARRAY_BUFFER, 16 * sizeof(GLfloat), &vertex, GL_STATIC_DRAW); glGenBuffers(1, &quadTexCoordID); glBindBuffer(GL_ARRAY_BUFFER, quadTexCoordID); glBufferData(GL_ARRAY_BUFFER, 8 * sizeof(GLfloat), &texCoord, GL_STATIC_DRAW); // Draw Quad glEnableVertexAttribArray(0); glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, quadVertexID); glVertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ARRAY_BUFFER, quadTexCoordID); glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, quadIndexID); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_SHORT, 0);
12.6.4. 予約ユニフォーム
以下の表は、ガスで使用する予約ユニフォームの一覧です。
予約ユニフォーム |
種別 |
設定値 |
---|---|---|
|
|
フラグメントオペレーションモードを指定する。密度情報描画パスではガスモード、その他のパスでは標準モードを指定する。
|
|
|
フォグパイプラインの処理モードを指定する。シェーディングパスではガスモード、密度情報描画パスでは無効を指定する。
|
|
|
フォグ係数に使用する参照テーブルを指定する。フォグ係数はガス状物体のアルファ値の算出で使用する。 0 ~ 31 |
|
|
デプス方向の減衰係数 EZ を指定する。 10.0(デフォルト) |
|
|
密度情報の最大値の逆数を自動的に計算するかどうかを指定する。
|
|
|
密度情報の最大値の逆数を指定する。 0.0 以上 1.0(デフォルト) |
|
|
RGB 成分のシェーディング参照テーブルをそれぞれ指定する。 0 ~ 31 |
|
|
シェーディングで使用する密度情報を指定する。
|
|
|
シェーディング参照テーブルへの入力値を密度またはシェーディング強度から指定する。
|
|
|
平面シェーディングの制御に使用する、最小強度、最大強度、密度の減衰を指定する。 (lightMin, lightMax, lightAtt) 各制御値とも 0.0 ~ 1.0 (0.0, 0.0, 0.0)(デフォルト) |
|
|
視線シェーディングの制御に使用する、最小強度、最大強度、密度の減衰、視線方向の影響を指定する。 (scattMin, scattMax, scattAtt, LZ) 各制御値とも 0.0 ~ 1.0 (0.0, 0.0, 0.0, 0.0)(デフォルト) |
|
|
シェーディングのアルファ値の計算で使用する密度減衰の係数を指定する。 0.0 以上 1.0(デフォルト) |