GDC2004_PIoHDRR_JP(日本語版 5.42MB)

Download Report

Transcript GDC2004_PIoHDRR_JP(日本語版 5.42MB)

Practical Implementation of
High Dynamic Range
Rendering
Masaki Kawase
BUNKASHA GAMES
BUNKASHA PUBLISHING CO.,LTD
http://www.daionet.gr.jp/~masa/
http://www.bunkasha-games.com/
本日の内容
•
•
•
•
•
•
•
•
HDRで何が変わるのか
HDRレンダリングのトピック
DX8ハードウェアでのHDR実装
DX9ハードウェアでのHDR実装
グレア生成
露光調整
ゲームでのHDR表現について
参照
HDRで何が変わるのか
• 眩しい光の表現
• 眩しい反射光の表現
• フレネル反射
– 正面方向への明るい光源の反射
• 露光調整の演出
• リアルな被写界深度表現
• リアルなモーションブラー
HDRのコストパフォーマンス
眩しい光
眩しい反射
HDRフレネル
低い反射率での
明るい映り込み
露光調整の演出
HDR被写界深度処理
将来の実装に向けて
HDRモーションブラー
将来の実装に向けて
HDRレンダリングのトピック
•
•
•
•
•
ダイナミックレンジ
HDRバッファレンダリング
グレア生成
露光調整
トーンマッピング
ダイナミックレンジ
• 最も大きい値と最も小さい値の比
• ディスプレイアブルイメージ
– 28
ローダイナミックレンジ(LDR)
• 絶対輝度のフレームバッファ
– シーンを絶対的な輝度でレンダリング
– 232以上
全ての輝度を絶対的に表現
• 相対輝度のフレームバッファ
– シーンを露光でスケーリングしてレンダリング
– 215~16以上
輝度の低い部分は重要ではない
HDRバッファレンダリング
• フレームバッファ
– グレア生成のため
• 環境マップ
– 反射率が低くても輝度をクランプさせないため
– 眩しい反射表現のため
• 自己発光テクスチャ
– レンダリング後グレア生成のため
• デカールテクスチャはHDR不要
フレームバッファのHDR
• グレア生成のため
• 相対輝度でのレンダリングなら
– 理想的には215~16以上
– ゲーム映像では
• 212~13(4000~10000)程度で許容できる
環境マップのHDR
• 非常に重要
– リアルな鏡面反射表現のため
– 眩しい鏡面反射表現のため
• 非金属の鏡面反射率
– 正面への反射率は4%以下がほとんど
– 明るい光源は4%以下の反射後も充分に眩しい
• 1~4%程度の反射後も眩しさを維持したい
– ダイナミックレンジで10000~20000以上
DX8ハードウェアでのHDR実装
• 選択の余地は無い
• ピクセルシェーダ1.x
– 整数演算のみ
• HDR用バッファフォーマット
– 低精度バッファのみ
– αチャネルを輝度情報として利用
– フェイクでそれらしく表現
• 正確な演算は不可能
フェイクHDRピクセルシェーダ
光沢反射マテリアル
// v0.rgb : プライマリライト拡散反射カラー
// v1.rgb : その他のライト/環境光カラー
//
ライティング結果は露光 * 0.5 で
//
スケーリング済み
ps_1_1
tex t0
tex t1
tex t2
mad r0.rgb,
v0, t2, v1
+mul t0.a, v1.a, t0.a
//
//
//
//
mul r0.rgb, t0, r0
+mul r0.a, t0.a, t1.a
// 拡散反射カラーをデカールテクスチャで変調
// r0.a = 鏡面反射率 * 環境マップ輝度
mul t1.rgb, t1, c0
+mul r1.a, r0.a, t1.a
// 環境マップカラーを鏡面反射カラーで変調
// 環境マップ輝度がどの程度高いかを示す値
// r1.a = 鏡面反射率 * 環境マップ輝度 * グロスマップ
lrp r0.rgb, t0.a, t1, r0
mul r1.a, r1.a, c0.a
// 鏡面反射率で環境マップを反射
// 環境マップ輝度がどの程度高いかを示す値
// r1.a = 鏡面反射率 * 環境マップ輝度 * グロスマップ *
//
// v1.a
: 鏡面反射率(フレネル考慮済み)
//
// t0.rgb : デカールテクスチャ(拡散反射スケール)
// t0.a
: グロスマップ(鏡面反射率スケール)
// t1.rgb : 環境マップカラー
// t1.a
プライマリ拡散反射カラーを
シャドウ/ライトマップでスケール
その他のライティング結果を加算
鏡面反射率をグロスマップでスケール
: 環境マップ輝度
// t2.rgb : シャドウ/ライトマップ
//
// c0.rgb : 鏡面反射カラー
// c0.a
: Clamp(光沢 * 2, 0, 1)
Clamp(光沢 * 2 , 0, 1)
lrp r0.rgb,
r1.a, t1, r0
+lrp r0.a, r1.a,
t1.a, r0.a
//
//
//
//
出力カラー
環境マップ輝度がどの程度高いか(r1.a)によって
環境マップカラーとLDRの計算結果を補間
出力輝度
フェイクHDRピクセルシェーダ
自己発光マテリアル
// v0.rgb : プライマリライト拡散反射カラー
// v1.rgb : その他のライト/環境光カラー
//
ライティング結果は露光 * 0.5 で
//
スケーリング済み
ps_1_1
tex t0
tex t1
tex t2
mad r0.rgb,
//
v0, t2, v1
// プライマリ拡散反射カラーを
// t0.rgb : デカールテクスチャ(拡散反射スケール)
// シャドウ/ライトマップでスケール
// t0.a
か)
// その他のライティング結果を加算
: 自己発光輝度(どの程度強く発光している
+mul t0.a, t0.a, c1.a
// 自己発光の強さをスケール
mul r0.rgb,
t0, r0
// 拡散反射カラーをデカールテクスチャで変調
mul r1.rgb,
t0, c1
// 自己発光カラーはデカールテクスチャ * 自己発光カラー
lrp r0.rgb,
t0.a, r1, r0
// 出力カラー
// t2.rgb : シャドウ/ライトマップ
//
// c1.rgb : 自己発光カラー(t0.rgb の変調)
// c1.a
//
: 発光の強さ(t0.a のスケール)
露光 * 0.5 でスケーリング済み
// 自己発光を拡散カラーに追加
// 輝度情報が高い程拡散カラーから
// 自己発光カラーに近づける
+mov r0.a, t0.a
// 自己発光が高い程輝度情報も高く
ディスプレイイメージの生成
• グレア生成用の高輝度成分抽出
 1  Threshold

Glaresrc  Buffer.rgb 
 Buffer.a 
16


Threshold
0.4~0.5程度のしきい値
• グレア生成
• ディプレイアブルイメージ生成
– フレームバッファから輝度を計算


Lumexposed  Buffer.rgb  Buffer.rgb Buffer.a2 16  2
– 輝度にグレア生成結果を加算
高輝度成分抽出シェーダ
// 高輝度成分抽出ピクセルシェーダ
// Out.rgb = FrameBuffer.rgb * ( (1 - Threshold) / 16 + FrameBuffer.a )
// t0.rgb : フレームバッファカラー
// t0.a
: フレームバッファ追加輝度
//
// c0.a
: 輝度バイアス
//
(1 - Threshold) / 16
//
(0.5~0.6)/16 程度
ps_1_1
tex t0
add r0.a,
// フレームバッファ
mul r0.rgb,
t0.a, c0.a
t0, r0.a
// r0.a = (1 - Threshold) / 16 + Buffer.a
// r0.rgb = Buffer.rgb * ( (1 - threshold) / 16 + Buffer.a )
グレア生成
• 参照:
– Kawase, Masaki. “Frame Buffer Postprocessing Effects in
DOUBLE-S.T.E.A.L (Wreckless)”
輝度計算/グレア合成
// トーンマップ/グレア合成ピクセルシェーダ
// Out.rgb = (FrameBuffer.rgb + FrameBuffer.rgb * FrameBuffer.a^2 * 16) * 2 + Glare.rgb * Glare.rgb
// t0.rgb : フレームバッファカラー
// t0.a
: フレームバッファ追加輝度
// t1.rgb : グレア生成結果
ps_1_1
tex t0
// フレームバッファ
tex t1
// グレア
mul_x4 r0.a,
t0.a, t0.a
// r0.a = FrameBuffer.a^2 * 4
mul_x4 r1.rgb,
t0, r0.a
// r1.rgb = FrameBuffer.rgb * FrameBuffer.a^2 * 16
add_x2 r0.rgb,
t0, r1
// (FrameBuffer.rgb + FrameBuffer.rgb * FrameBuffer.a^2 * 16) * 2
mad r0.rgb,
t1, t1,
r0
// グレアを加算
DX8のHDR実装について
• 正確な計算は不可能
– フェイクで如何にそれらしく表現するか
– 理論よりも見た目で実装
DX9ハードウェアでのHDR実装
• 現状ではさまざまな制限
– 状況に応じて実装を使い分ける
• ピクセルシェーダ
– ピクセルシェーダ2.0以降
– ピクセルシェーダ1.x
• HDR用バッファフォーマット
– 高精度整数/浮動小数点バッファ
– 低精度整数バッファ
高精度バッファの問題点
• メモリ使用量
– A16B16G16R16 / A16B16G16R16F
• 64bpp(Bits Per Pixel)
• A8R8G8B8 の2倍
– A32B32G32R32 / A32B32G32R32F
• 128bpp
• A8R8G8B8 の4倍
– 少なくとも従来のフルカラーのバッファの2倍以上
高精度バッファの問題点
• 機能の制限
– αブレンディングを利用できない
• シーンのレンダリングに影響
– 浮動小数点フォーマットではフィルタリングを
利用できない
• 環境マップ/自己発光テクスチャのクォリティに影
響
• サポート状況
– 高精度バッファを利用できない環境もある
高精度バッファの実用性
• 現在のハードウェアでは問題点が多い
• 現状での高精度バッファの利用
– A16B16G16R16 / A16B16G16R16F
• ハードウェアがサポートしている
• メモリに余裕がある
• αブレンドを利用できなくても良い
– 条件は厳しい…
低精度バッファの利用
• 低精度バッファの利用
– A8R8G8B8 / A2R10G10B10 etc.
– メモリ使用量の軽減
– αブレンディング可能
• 演算は不正確だが大きな問題にはならない
トーンマップによる圧縮
• ディスプレイアブルフォーマットでレンダリング
– 最終出力映像への劣化が無い
• フィルム/網膜の非線形感度を近似
Lumexposed( x, y)
Buffer( x, y) 
Lumexposed( x, y) 1
– 充分高いダイナミックレンジを保持できる
– 参照:
• Reinhard, Erik, Mike Stark, Peter Shirley, and Jim Ferwerda.
“Photographic Tone Reproduction for Digital Images”
• αチャネルを利用しない
– 別の用途に利用可能
低精度バッファフォーマット
• A8R8G8B8(8 bits per color channel)
– αチャネルを別の用途に利用可能
– 全ての環境で動作可能
– RGB精度に多少の不安あり
• A2B10G10R10
A2R10G10B10(10 bits per color channel)
– より高いカラーチャネル精度
– αビットが少ない
• 他の情報には不向き
– サポートされない環境も多い
環境マップフォーマット
• 理想的なフォーマット
– 10000~20000以上のダイナミックレン
ジ
– サンプリングフィルタを利用可能
環境マップフォーマット
• 環境マップの解像度は比較的低い
• αチャネルやαブレンドはあまり重要ではない
• メモリに余裕があるならfx16(16bit 整数)を利用
– A16B16G16R16 * (256~512)として使用
– エンコード/デコードが高速
– サンプリングフィルタを利用可能
• 将来的には
– A16B16G16R16F(fp16)で全てを処理
低精度環境マップのHDR
• 低精度環境マップを利用するケース
– 高精度整数バッファがサポートされていない
– メモリに余裕が無い
• フィル性能に余裕が無い場合
– DX8用フェイクHDRと同じフォーマット
• フィル性能に充分な余裕がある場合
– トーンマップと同様のカラー圧縮
– αチャネルに指数を格納
• より正確な演算が可能
• αチャネルをスケーラに使うだけでは不充分
– DX8用のフェイクの方がインパクトがある
トーンマップ同様のカラー圧縮
• 環境マップレンダリング時にエンコード
Lumexposed( x, y)
Buffer( x, y) 
Lumexposed( x, y)  Offset
Offset
輝度カーブを制御する調整値(2~4程度)
• Offset を大きくすると
– 高輝度領域の精度が上がる
– 低輝度領域の精度が下がる
• フレームバッファレンダリング時にデコード
– フェッチした環境マップからデコード
Buffer( x, y)  Offset
Lumexposed( x, y) 
1δ Buffer( x, y)
δ ゼロ除算を避ける小さな調整値
トーンマップ同様のカラー圧縮
• 高輝度領域の解像度が犠牲になる
– 広く明るい領域の反射表現の劣化
– 面積の小さい光源ではあまり問題ない
E8R8G8B8
• αチャネルにRGB共通の指数を格納
• 底を1.04~1.08程度に
– 底=1.04のダイナミックレンジは約23000(1.04256)
Lumexposed  Buffer.rgb baseBuffer.α256offset
offset
64~128程度
• 底をより大きくすると
– ダイナミックレンジは高くなる
– 分解能は低くなる
• マッハバンドが現れる
• 環境マップレンダリング時にエンコード
• フレームバッファレンダリング時にデコード
– フェッチした環境マップからデコード
E8R8G8B8エンコード
(HLSL)
// a^n = b
#define LOG(a, b)
( log((b)) / log((a)) )
#define EXP_BASE
#define EXP_OFFSET
(1.06)
(128.0)
// ピクセルシェーダ6命令
// rgb は露光によるスケーリング済み
float4 EncodeHDR_RGB_RGBE8(in float3 rgb)
{
// 共通の指数計算
float fLen = dot(rgb.rgb, 1.0) ;
float fExp = LOG(EXP_BASE, fLen) ;
// より正確なエンコード
#define EXP_BASE
#define EXP_OFFSET
// ピクセルシェーダ13命令
float4 EncodeHDR_RGB_RGBE8(in float3 rgb)
{
float4 ret ;
// 共通の指数を最も明るいカラーチャネルから計算
float fLen = max(rgb.r, rgb.g) ;
fLen = max(fLen, rgb.b) ;
float fExp = floor( LOG(EXP_BASE, fLen) ) ;
float4 ret ;
ret.a = clamp( (fExp + EXP_OFFSET) / 256, 0.0, 1.0 ) ;
ret.rgb = rgb / pow(EXP_BASE, ret.a * 256 - EXP_OFFSET) ;
float4 ret ;
ret.a = (fExp + EXP_OFFSET) / 256 ;
ret.rgb = rgb / fLen ;
return ret ;
}
(1.04)
(64.0)
return ret ;
}
E8R8G8B8デコード
// ピクセルシェーダ5命令
float3 DecodeHDR_RGBE8_RGB(in float4 rgbe)
{
float fExp = rgbe.a * 256 - EXP_OFFSET ;
float fScaler = pow(EXP_BASE, fExp) ;
return (rgbe.rgb * fScaler) ;
// R16F テクスチャフォーマットを利用可能な場合
// αチャネルからスケールにテクスチャで変換しても良い
float3 DecodeHDR_RGBE8_RGB(in float4 rgbe)
{
// samp1D_Exp: 256x1 の一次元浮動小数点テクスチャ
//
pow(EXP_BASE, uCoord * 256 - EXP_OFFSET)
float fScaler = tex1D(samp1D_Exp, rgbe.a).r ;
}
return (rgbe.rgb * fScaler) ;
}
• エンコード/デコードは部分精度で充分
– テクスチャフォーマットの誤差の方が遥かに大き
い
自己発光テクスチャ
• 自ら発光するオブジェクトのテクスチャ
• αチャネルをRGB共通のスケールに
Lum  Tex.rgbTex.α  scale
scaler 16~128程度
• グレア生成に反応する明るさにすると魅力的
– オフラインでエンコード
• デコードは高速
トーンマップ付きレンダリング
光沢反射マテリアル
struct PS_INPUT_GlossReflect
float4 PS_GlossReflect(PS_INPUT_GlossReflect vIn) : COLOR0
{
float4 vDecalMap = tex2D(samp2D_Decal, vIn.tcDecal) ;
float3 vLightMap = tex2D(samp2D_LightMap, vIn.tcLightMap) ;
{
float2 tcDecal
: TEXCOORD0 ;
float3 tcReflect
: TEXCOORD1 ;
float2 tcLightMap
: TEXCOORD2 ;
float2 tcFresnel
: TEXCOORD3 ;
float3 vDiffuse = vIn.cPrimaryDiffuse * vLightMap + vIn.cOtherDiffuse ;
vDiffuse *= vDecalMap ;
// 環境マップの HDR デコード
float3 vSpecular = DecodeHDR_RGBE8_RGB( texCUBE(sampCUBE_EnvMap,
vIn.tcReflect) ) ;
float3 vRoughSpecular = texCUBE(sampCUBE_DullEnvMap, vIn.tcReflect) ;
// 以下は露光スケール済みのライティング結果
float fReflectance = tex2D( samp2D_Fresnel, vIn.tcFresnel ).a ;
fReflectance *= vDecalMap.a ;
// クランプを避けるために TEXCOORD を利用
float3 cPrimaryDiffuse : TEXCOORD6 ;
float3 cOtherDiffuse
: TEXCOORD7 ;
vSpecular = lerp(vSpecular, vRoughSpecular, fShininess) ;
float3 vLum = lerp(vDiffuse, vSpecular, fReflectance) ;
} ;
// HDR トーンマップエンコード
float4 vOut ;
vOut.rgb = vLum / (vLum + 1.0) ;
vOut.a = 0.0 ;
return vOut ;
}
トーンマップ付きレンダリング
自己発光マテリアル
struct PS_INPUT_SelfIllum
float4 PS_SelfIllum(PS_INPUT_SelfIllum vIn) : COLOR0
{
float4 vDecalMap = tex2D(samp2D_Decal, vIn.tcDecal) ;
float3 vLightMap = tex2D(samp2D_LightMap, vIn.tcLightMap) ;
{
float2 tcDecal
: TEXCOORD0 ;
float2 tcLightMap
: TEXCOORD2 ;
float3 vDiffuse = vIn.cPrimaryDiffuse * vLightMap + vIn.cOtherDiffuse ;
vDiffuse *= vDecalMap ;
// 自己発光テクスチャの HDR デコード
// fEmissiveScale : 自己発光輝度 * 露光
float3 vEmissive = vDecalMap.rgb * vDecalMap.a * fEmissiveScale ;
// 以下は露光スケール済みのライティング結果
// クランプを避けるために TEXCOORD を利用
float3 cPrimaryDiffuse : TEXCOORD6 ;
float3 cOtherDiffuse
// 自己発光を加算
float3 vLum = vDiffuse + vEmissive ;
: TEXCOORD7 ;
} ;
// HDR トーンマップエンコード
float4 vOut ;
vOut.rgb = vLum / (vLum + 1.0) ;
vOut.a = 0.0 ;
return vOut ;
}
ディスプレイイメージの生成
• グレア生成用の高輝度成分抽出
 Buffer.rgb  Threshold 
Glaresrc  max
, 0
1  Threshold


Threshold
0.5~0.8程度のしきい値
• 正規化のため1- Threshold で除算
• グレア生成
– テクスチャフィルタのため整数バッファで
• 理想は浮動小数点バッファ
• ディプレイアブルイメージ生成
– フレームバッファにグレアを加算
グレア合成
struct PS_INPUT_Display
{
float2 tcFrameBuffer
: TEXCOORD0 ;
float2 tcGlare
: TEXCOORD1 ;
} ;
float4 PS_Display(PS_INPUT_Display vIn) : COLOR0
{
float3 vFrameBuffer = tex2D(samp2D_FrameBuffer, vIn.tcFrameBuffer) ;
float3 vGlare = tex2D(samp2D_Glare, vIn.tcGlare) ;
// グレアを加算
float4 vOut ;
vOut.rgb = vFrameBuffer + vGlare * vGlare ;
vOut.a = 0.0 ;
return vOut ;
}
DX9のHDR実装について
• 高精度バッファを利用
– メモリ使用量が多い
– ブレンディングを行えない
– 近い将来には実用的に…
• 低精度バッファを利用
– ピクセルシェーダ負荷が高い
• DX8同様のフェイク処理も検討すること
– パフォーマンスが高い
– メモリ使用量が少ない
– 効果が大きい
グレア生成
• グレア生成の注意点
• 複数のガウスフィルタの合成によるブルー
ム
• イメージプロセスとスプライトによるグレア
表現
グレア生成の注意点
• かなりのマルチパスフィルタ
– 整数バッファの精度に注意
• 値のクランプ
• 丸め誤差
• fx16でも充分では無い
– 常に値の分解能と範囲に注意すること
複数のガウスフィルタの合成
• ブルーム生成
• 単独のガウスフィルタではあまり良い結果
を得られない
– フィルタ半径が小さい
– にも関わらず光源中央付近の鋭さは足らない
• 複数のガウスフィルタの合成
– 半径の異なる複数のガウスフィルタを合成
– より広く、なおかつ鋭いグレアを表現できる
複数のガウスフィルタの合成
e
r 2
e(0.25r )  2e(0.5r ) 
2
r x y
2
2
中心からの距離
2
4er  8e( 2r )  16e( 4r )
2
2
2
複数のガウスフィルタの合成
元のイメージ
e
r
2
e
( 0.25r ) 2
 2e
( 0.5r )2

4er  8e( 2r )  16e( 4r )
2
2
2
複数のガウスフィルタの合成
• フィルタ半径が大きいと負荷が高い
• 縮小バッファの活用
– 半径の大きなフィルタほどローパス
• 低い解像度でガウスブラーを適用してからバイリニアフィルタ
で拡大しても誤差は少ない(目立たない)
• フィルタ半径ではなく画像解像度を変える
–
–
–
–
–
1/4 x 1/4 (負荷1/16)
1/8 x 1/8 (負荷1/64)
1/16 x 1/16(負荷1/256)
1/32 x 1/32(負荷1/1024)
…
• 数百ピクセル四方の巨大なフィルタカーネルも高速に適用可
能
縮小バッファのガウスフィルタ
1/4 x 1/4 (256x192 pixels)
1/8 x 1/8 (128x96 pixels)
1/32 x 1/32 (32x48 pixels)
1/64 x 1/64 (16x12 pixels)
1/16 x 1/16 (64x48 pixels)
バイリニアフィルタで合成
• バイリニアフィルタで拡大して合成
– 誤差はほとんど認識できない
+
+
+
=
充分な結果を得られる
分離可能なフィルタ
• ガウスフィルタはx,yに変数分離可能
– 2パスで効率良く適用できる
– 参照:
• Mitchell, Jason L. “Real-Time 3D Scene Post-Processing”
• 解像度の低いバッファは高精度整数バッファで
• サンプリング数はあまり多くしない
– 特に解像度の高い画像は負荷が高い
• 解像度の高い画像は1パスのコーンフィルタで
コーンフィルタ
1/16 2/16 1/16
2/16 4/16 2/16
1/16 2/16 1/16
レンダリングされるピクセル
テクスチャのサンプリングポイント
その他のグレア生成
•
•
•
•
残像
スター(光芒)
ゴースト
参照:
– Kawase, Masaki. “Frame Buffer Postprocessing Effects in
DOUBLE-S.T.E.A.L (Wreckless)”
イメージプロセスと
スプライトによるグレア表現
• イメージプロセス
– HDRフレームバッファから高輝度成分を抽出
– イメージベースのポストプロセスで生成
• スプライト表現(伝統的なアルゴリズム)
– 可視光源のチェック
• ジオメトリ情報から可視性をチェック
• 可能ならハードウェア可視性テストを利用
– 描画されたピクセル数から光源の可視性を判断
– 可視光源に対しフレアスプライトを個別に描画
イメージプロセス
スプライト表現
メリット
• イメージプロセス
– 反射光/面光源にも適用可能
– 光源がどれだけ多くとも負荷は固定
– フィルタカーネルによりグレア形状を変更できる
• スプライト表現
– 質の高い表現が可能
– ダイナミックレンジに限界が無い
– 光源が少ない場合は負荷が少ない
デメリット
• イメージプロセス
– 固定の塗り潰し負荷が高い
– エイリアシングが発生する
• 特に面積の小さい光源
– ダイナミックレンジに限界がある
• あまりにも明るく鋭い光源のグレア表現は難しい
• スプライト表現
– 光源が多い程負荷が高くなる
– 形状のある光源への適用が難しい
– 間接光源への適用が難しい
• 眩しい反射(ハイライト部分のグレア)を表現できない
光源の種類による使い分け
• イメージプロセス
– 面積のある光源
• 形をもつ光源
• スクリーン投影面積の大きな光源
– 反射光
– 非常に数の多い光源の集合
• スプライト表現
– 非常に明るく鋭い光源
– 面積の小さな光源
– 光源数が少ない
光源の種類による使い分け
• イメージプロセスで生成すると良い
–
–
–
–
ネオン
夜のビルの窓から漏れる光
電光掲示板
光源の映り込み
• スプライトで表現すると良い
– 太陽
– 街灯
– 車のヘッドライト
• 将来は全てがイメージプロセス?
– 現状ではスプライト表現も充分有効なテクニック
露光調整
• 室内から覗く室外の眩しさの表現
• 室外から覗く室内の暗さの表現
• 暗順応/明順応の演出
– 室内に入るとしばらく真っ暗
– 室内から外へ出るとしばらく眩しくて真っ白
露光調整プロセス
• シーンの平均輝度の取得
• 適正露光値の計算
• 露光値を適正露光値に調整
シーンの平均輝度の取得
• フレームバッファをダウンサンプリング
– 16x16以下に縮小
• HDRバッファ内容から輝度へ変換
– αチャネルを付加的な輝度情報とする場合
Buffer( x, y).rgb 1  Buffer( x, y).a  Scale
Lum( x, y) 
Expcurrent
– RGBカラーをトーンマップで圧縮している場合
Buffer( x, y).rgb
1
Lum( x, y) 

1 δ Buffer( x, y).rgb Expcurrent
シーンの平均輝度の取得
• スクリーンの平均的な輝度を計算
1

Lumavg  exp  logδ
δ  Lum( x, y) 

N
 x, y

δ 適当な調整値
– 参照:
• DirectX 9.0 SDK Summer 2003 Update. “HDRLighting Sample”
• Reinhard, Erik, Mike Stark, Peter Shirley, and Jim Ferwerda.
“Photographic Tone Reproduction for Digital Images”
• いろいろ試行錯誤してみると良い
– スクリーン中央の重みを上げる
適正露光の計算
Scale
Exp 
Lumavg
Exp シーンの適正露光
Scale 適当な調整値
露光の調整
• 徐々に変化させることで順応を表現
Expnew  Expcurrent  Exp  Expcurrent Ratio
Ratio 順応速度(0.02~0.1程度)
露光の調整
• いろいろ試してみる
– 明順応/暗順応で順応速度を変える
– 0.5秒程度前の適正露光に順応させる
– 順応の感度を調整
LogExp  logExp
LogExpcurrent  logExpcurrent
LogExpnew  LogExpcurrent  LogExp LogExpcurrent Ratio
Expnew  expBase  LogExpnew  Base Sensitivity 
Base
Sensitivity
基準となる露光値の対数
順応の強さ(0~1)
ゲームでのHDR表現について
• 正確であるより魅力的であること
– プレイヤーにとって正確さは重要ではない
– 正確であっても魅力的とは限らない
– 不正確であっても魅力的になり得る
• コストパフォーマンス
– HDRによる効果を理解することが重要
– 高速でそれらしく表現できるフェイク処理を考える
• 正確なHDRレンダリングはまだゲームには難しい
– よりインパクトの大きな表現に利用する
• 太陽のグレアだけならスプライトの方が高速で質も高い
ゲームでのHDR表現について
• 整数フォーマットではダイナミックレンジに
制限がある
– 露光による相対的な輝度でレンダリング
• レンダリング時に露光によるスケーリングも行う
• 露光スケーリング後の暗い部分は高精度不要
– 最終ディスプレイアブルイメージでも暗くなるため
• より輝度の高い側にダイナミックレンジを集中でき
る
ゲームでのHDR表現について
• 将来の実装に向けて
– 全てが浮動小数点で演算可能に?
• 環境マップ/フレームバッファ/グレア生成
• 絶対的な輝度でレンダリング可能
• 全てのグレアをイメージベースで生成可能
– HDRの効果の大きい表現
• 絞り羽根形状の現れる被写界深度表現
• 輝度クランプされないモーションブラー
参照
• Reinhard, Erik, Mike Stark, Peter Shirley, and Jim Ferwerda.
“Photographic Tone Reproduction for Digital Images”
• Mitchell, Jason L. “Real-Time 3D Scene Post-Processing”
• DirectX 9.0 SDK Summer 2003 Update. “HDRLighting Sample”
• Debevec, Paul E. “Paul Debevec Home Page”
• Kawase, Masaki. “Frame Buffer Postprocessing Effects in
DOUBLE-S.T.E.A.L (Wreckless)”