1 / 73

Practical Implementation of High Dynamic Range Rendering

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 表現について

ezra
Download Presentation

Practical Implementation of High Dynamic Range Rendering

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. 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/

  2. 本日の内容 • HDRで何が変わるのか • HDRレンダリングのトピック • DX8ハードウェアでのHDR実装 • DX9ハードウェアでのHDR実装 • グレア生成 • 露光調整 • ゲームでのHDR表現について • 参照

  3. HDRで何が変わるのか • 眩しい光の表現 • 眩しい反射光の表現 • フレネル反射 • 正面方向への明るい光源の反射 • 露光調整の演出 • リアルな被写界深度表現 • リアルなモーションブラー HDRのコストパフォーマンス

  4. 眩しい光

  5. 眩しい反射

  6. HDRフレネル 低い反射率での 明るい映り込み

  7. 露光調整の演出

  8. HDR被写界深度処理 将来の実装に向けて

  9. HDRモーションブラー 将来の実装に向けて

  10. HDRレンダリングのトピック • ダイナミックレンジ • HDRバッファレンダリング • グレア生成 • 露光調整 • トーンマッピング

  11. ダイナミックレンジ • 最も大きい値と最も小さい値の比 • ディスプレイアブルイメージ • 28 ローダイナミックレンジ(LDR) • 絶対輝度のフレームバッファ • シーンを絶対的な輝度でレンダリング • 232以上 全ての輝度を絶対的に表現 • 相対輝度のフレームバッファ • シーンを露光でスケーリングしてレンダリング • 215~16以上 輝度の低い部分は重要ではない

  12. HDRバッファレンダリング • フレームバッファ • グレア生成のため • 環境マップ • 反射率が低くても輝度をクランプさせないため • 眩しい反射表現のため • 自己発光テクスチャ • レンダリング後グレア生成のため • デカールテクスチャはHDR不要

  13. フレームバッファのHDR • グレア生成のため • 相対輝度でのレンダリングなら • 理想的には215~16以上 • ゲーム映像では • 212~13(4000~10000)程度で許容できる

  14. 環境マップのHDR • 非常に重要 • リアルな鏡面反射表現のため • 眩しい鏡面反射表現のため • 非金属の鏡面反射率 • 正面への反射率は4%以下がほとんど • 明るい光源は4%以下の反射後も充分に眩しい • 1~4%程度の反射後も眩しさを維持したい • ダイナミックレンジで10000~20000以上

  15. DX8ハードウェアでのHDR実装 • 選択の余地は無い • ピクセルシェーダ1.x • 整数演算のみ • HDR用バッファフォーマット • 低精度バッファのみ • αチャネルを輝度情報として利用 • フェイクでそれらしく表現 • 正確な演算は不可能

  16. 光沢反射マテリアル フェイクHDRピクセルシェーダ ps_1_1 text0 text1 text2 madr0.rgb, v0, t2, v1 // プライマリ拡散反射カラーを // シャドウ/ライトマップでスケール // その他のライティング結果を加算 +mult0.a, v1.a, t0.a // 鏡面反射率をグロスマップでスケール mulr0.rgb, t0, r0 // 拡散反射カラーをデカールテクスチャで変調 +mulr0.a, t0.a, t1.a // r0.a = 鏡面反射率 * 環境マップ輝度 mult1.rgb, t1, c0 // 環境マップカラーを鏡面反射カラーで変調 +mulr1.a, r0.a, t1.a // 環境マップ輝度がどの程度高いかを示す値 // r1.a = 鏡面反射率 * 環境マップ輝度 * グロスマップ lrpr0.rgb, t0.a, t1, r0 // 鏡面反射率で環境マップを反射 mulr1.a, r1.a, c0.a // 環境マップ輝度がどの程度高いかを示す値 // r1.a = 鏡面反射率 * 環境マップ輝度 * グロスマップ * Clamp(光沢 * 2 , 0, 1) lrpr0.rgb, r1.a, t1, r0 // 出力カラー // 環境マップ輝度がどの程度高いか(r1.a)によって // 環境マップカラーとLDRの計算結果を補間 +lrpr0.a, r1.a, t1.a, r0.a // 出力輝度 // v0.rgb : プライマリライト拡散反射カラー // v1.rgb : その他のライト/環境光カラー // ライティング結果は露光 * 0.5 で // スケーリング済み // // v1.a : 鏡面反射率(フレネル考慮済み) // // t0.rgb : デカールテクスチャ(拡散反射スケール) // t0.a : グロスマップ(鏡面反射率スケール) // t1.rgb : 環境マップカラー // t1.a : 環境マップ輝度 // t2.rgb : シャドウ/ライトマップ // // c0.rgb : 鏡面反射カラー // c0.a : Clamp(光沢 * 2, 0, 1)

  17. 自己発光マテリアル フェイクHDRピクセルシェーダ ps_1_1 text0 text1 text2 madr0.rgb, v0, t2, v1 // プライマリ拡散反射カラーを // シャドウ/ライトマップでスケール // その他のライティング結果を加算 +mult0.a, t0.a, c1.a // 自己発光の強さをスケール mulr0.rgb, t0, r0 // 拡散反射カラーをデカールテクスチャで変調 mulr1.rgb, t0, c1 // 自己発光カラーはデカールテクスチャ * 自己発光カラー lrpr0.rgb, t0.a, r1, r0 // 出力カラー // 自己発光を拡散カラーに追加 // 輝度情報が高い程拡散カラーから // 自己発光カラーに近づける +movr0.a, t0.a // 自己発光が高い程輝度情報も高く // v0.rgb : プライマリライト拡散反射カラー // v1.rgb : その他のライト/環境光カラー // ライティング結果は露光 * 0.5 で // スケーリング済み // // t0.rgb : デカールテクスチャ(拡散反射スケール) // t0.a : 自己発光輝度(どの程度強く発光しているか) // t2.rgb : シャドウ/ライトマップ // // c1.rgb : 自己発光カラー(t0.rgb の変調) // c1.a : 発光の強さ(t0.a のスケール) // 露光 * 0.5 でスケーリング済み

  18. ディスプレイイメージの生成 • グレア生成用の高輝度成分抽出 Threshold 0.4~0.5程度のしきい値 • グレア生成 • ディプレイアブルイメージ生成 • フレームバッファから輝度を計算 • 輝度にグレア生成結果を加算

  19. 高輝度成分抽出シェーダ // 高輝度成分抽出ピクセルシェーダ // 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 text0 // フレームバッファ addr0.a, t0.a, c0.a // r0.a = (1 - Threshold) / 16 + Buffer.a mulr0.rgb, t0, r0.a // r0.rgb = Buffer.rgb * ( (1 - threshold) / 16 + Buffer.a )

  20. グレア生成 • 参照: • Kawase, Masaki. “Frame Buffer Postprocessing Effects in DOUBLE-S.T.E.A.L (Wreckless)”

  21. 輝度計算/グレア合成 // トーンマップ/グレア合成ピクセルシェーダ // Out.rgb = (FrameBuffer.rgb + FrameBuffer.rgb * FrameBuffer.a^2 * 16) * 2 + Glare.rgb * Glare.rgb // t0.rgb : フレームバッファカラー // t0.a : フレームバッファ追加輝度 // t1.rgb : グレア生成結果 ps_1_1 text0 // フレームバッファ text1 // グレア mul_x4r0.a, t0.a, t0.a // r0.a = FrameBuffer.a^2 * 4 mul_x4r1.rgb, t0, r0.a // r1.rgb = FrameBuffer.rgb * FrameBuffer.a^2 * 16 add_x2r0.rgb, t0, r1 // (FrameBuffer.rgb + FrameBuffer.rgb * FrameBuffer.a^2 * 16) * 2 madr0.rgb, t1, t1, r0 // グレアを加算

  22. DX8のHDR実装について • 正確な計算は不可能 • フェイクで如何にそれらしく表現するか • 理論よりも見た目で実装

  23. DX9ハードウェアでのHDR実装 • 現状ではさまざまな制限 • 状況に応じて実装を使い分ける • ピクセルシェーダ • ピクセルシェーダ2.0以降 • ピクセルシェーダ1.x • HDR用バッファフォーマット • 高精度整数/浮動小数点バッファ • 低精度整数バッファ

  24. 高精度バッファの問題点 • メモリ使用量 • A16B16G16R16 / A16B16G16R16F • 64bpp(Bits Per Pixel) • A8R8G8B8 の2倍 • A32B32G32R32 / A32B32G32R32F • 128bpp • A8R8G8B8 の4倍 • 少なくとも従来のフルカラーのバッファの2倍以上

  25. 高精度バッファの問題点 • 機能の制限 • αブレンディングを利用できない • シーンのレンダリングに影響 • 浮動小数点フォーマットではフィルタリングを利用できない • 環境マップ/自己発光テクスチャのクォリティに影響 • サポート状況 • 高精度バッファを利用できない環境もある

  26. 高精度バッファの実用性 • 現在のハードウェアでは問題点が多い • 現状での高精度バッファの利用 • A16B16G16R16 / A16B16G16R16F • ハードウェアがサポートしている • メモリに余裕がある • αブレンドを利用できなくても良い • 条件は厳しい…

  27. 低精度バッファの利用 • 低精度バッファの利用 • A8R8G8B8 / A2R10G10B10 etc. • メモリ使用量の軽減 • αブレンディング可能 • 演算は不正確だが大きな問題にはならない

  28. トーンマップによる圧縮 • ディスプレイアブルフォーマットでレンダリング • 最終出力映像への劣化が無い • フィルム/網膜の非線形感度を近似 • 充分高いダイナミックレンジを保持できる • 参照: • Reinhard, Erik, Mike Stark, Peter Shirley, and Jim Ferwerda. “Photographic Tone Reproduction for Digital Images” • αチャネルを利用しない • 別の用途に利用可能

  29. 低精度バッファフォーマット • A8R8G8B8(8 bits per color channel) • αチャネルを別の用途に利用可能 • 全ての環境で動作可能 • RGB精度に多少の不安あり • A2B10G10R10 A2R10G10B10(10 bits per color channel) • より高いカラーチャネル精度 • αビットが少ない • 他の情報には不向き • サポートされない環境も多い

  30. 環境マップフォーマット • 理想的なフォーマット • 10000~20000以上のダイナミックレンジ • サンプリングフィルタを利用可能

  31. 環境マップフォーマット • 環境マップの解像度は比較的低い • αチャネルやαブレンドはあまり重要ではない • メモリに余裕があるならfx16(16bit 整数)を利用 • A16B16G16R16 * (256~512)として使用 • エンコード/デコードが高速 • サンプリングフィルタを利用可能 • 将来的には • A16B16G16R16F(fp16)で全てを処理

  32. 低精度環境マップのHDR • 低精度環境マップを利用するケース • 高精度整数バッファがサポートされていない • メモリに余裕が無い • フィル性能に余裕が無い場合 • DX8用フェイクHDRと同じフォーマット • フィル性能に充分な余裕がある場合 • トーンマップと同様のカラー圧縮 • αチャネルに指数を格納 • より正確な演算が可能 • αチャネルをスケーラに使うだけでは不充分 • DX8用のフェイクの方がインパクトがある

  33. トーンマップ同様のカラー圧縮 • 環境マップレンダリング時にエンコード Offset輝度カーブを制御する調整値(2~4程度) • Offsetを大きくすると • 高輝度領域の精度が上がる • 低輝度領域の精度が下がる • フレームバッファレンダリング時にデコード • フェッチした環境マップからデコード δ ゼロ除算を避ける小さな調整値

  34. トーンマップ同様のカラー圧縮 • 高輝度領域の解像度が犠牲になる • 広く明るい領域の反射表現の劣化 • 面積の小さい光源ではあまり問題ない

  35. E8R8G8B8 • αチャネルにRGB共通の指数を格納 • 底を1.04~1.08程度に • 底=1.04のダイナミックレンジは約23000(1.04256) offset 64~128程度 • 底をより大きくすると • ダイナミックレンジは高くなる • 分解能は低くなる • マッハバンドが現れる • 環境マップレンダリング時にエンコード • フレームバッファレンダリング時にデコード • フェッチした環境マップからデコード

  36. E8R8G8B8エンコード(HLSL) // a^n = b #define LOG(a, b) ( log((b)) / log((a)) ) #define EXP_BASE (1.06) #define EXP_OFFSET (128.0) // ピクセルシェーダ6命令 // rgb は露光によるスケーリング済み float4 EncodeHDR_RGB_RGBE8(in float3 rgb) { // 共通の指数計算 float fLen = dot(rgb.rgb, 1.0) ; float fExp = LOG(EXP_BASE, fLen) ; float4 ret ; ret.a = (fExp + EXP_OFFSET) / 256 ; ret.rgb = rgb / fLen ; return ret ; } // より正確なエンコード #define EXP_BASE (1.04) #define EXP_OFFSET (64.0) // ピクセルシェーダ13命令 float4 EncodeHDR_RGB_RGBE8(infloat3 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) ; return ret ; }

  37. // ピクセルシェーダ5命令 float3 DecodeHDR_RGBE8_RGB(infloat4 rgbe) { float fExp = rgbe.a * 256 - EXP_OFFSET ; float fScaler = pow(EXP_BASE, fExp) ; return (rgbe.rgb * fScaler) ; } エンコード/デコードは部分精度で充分 テクスチャフォーマットの誤差の方が遥かに大きい E8R8G8B8デコード // R16F テクスチャフォーマットを利用可能な場合 // αチャネルからスケールにテクスチャで変換しても良い float3 DecodeHDR_RGBE8_RGB(infloat4 rgbe) { // samp1D_Exp: 256x1 の一次元浮動小数点テクスチャ // pow(EXP_BASE, uCoord * 256 - EXP_OFFSET) float fScaler = tex1D(samp1D_Exp, rgbe.a).r ; return (rgbe.rgb * fScaler) ; }

  38. 自己発光テクスチャ • 自ら発光するオブジェクトのテクスチャ • αチャネルをRGB共通のスケールに scaler 16~128程度 • グレア生成に反応する明るさにすると魅力的 • オフラインでエンコード • デコードは高速

  39. 光沢反射マテリアル トーンマップ付きレンダリング float4 PS_GlossReflect(PS_INPUT_GlossReflect vIn) : COLOR0 { float4 vDecalMap = tex2D(samp2D_Decal, vIn.tcDecal) ; float3 vLightMap = tex2D(samp2D_LightMap, vIn.tcLightMap) ; 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 ; 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_GlossReflect { float2 tcDecal : TEXCOORD0 ; float3 tcReflect : TEXCOORD1 ; float2 tcLightMap : TEXCOORD2 ; float2 tcFresnel : TEXCOORD3 ; // 以下は露光スケール済みのライティング結果 // クランプを避けるために TEXCOORD を利用 float3 cPrimaryDiffuse : TEXCOORD6 ; float3 cOtherDiffuse : TEXCOORD7 ; } ;

  40. 自己発光マテリアル トーンマップ付きレンダリング float4PS_SelfIllum(PS_INPUT_SelfIllum vIn) : COLOR0 { float4 vDecalMap = tex2D(samp2D_Decal, vIn.tcDecal) ; float3 vLightMap = tex2D(samp2D_LightMap, vIn.tcLightMap) ; float3 vDiffuse = vIn.cPrimaryDiffuse * vLightMap + vIn.cOtherDiffuse ; vDiffuse *= vDecalMap ; // 自己発光テクスチャの HDR デコード // fEmissiveScale : 自己発光輝度 * 露光 float3 vEmissive = vDecalMap.rgb * vDecalMap.a * fEmissiveScale ; // 自己発光を加算 float3 vLum = vDiffuse + vEmissive ; // HDR トーンマップエンコード float4 vOut ; vOut.rgb = vLum / (vLum + 1.0) ; vOut.a = 0.0 ; return vOut ; } struct PS_INPUT_SelfIllum { float2 tcDecal : TEXCOORD0 ; float2 tcLightMap : TEXCOORD2 ; // 以下は露光スケール済みのライティング結果 // クランプを避けるために TEXCOORD を利用 float3 cPrimaryDiffuse : TEXCOORD6 ; float3 cOtherDiffuse : TEXCOORD7 ; } ;

  41. ディスプレイイメージの生成 • グレア生成用の高輝度成分抽出 Threshold 0.5~0.8程度のしきい値 • 正規化のため1- Threshold で除算 • グレア生成 • テクスチャフィルタのため整数バッファで • 理想は浮動小数点バッファ • ディプレイアブルイメージ生成 • フレームバッファにグレアを加算

  42. struct PS_INPUT_Display { float2 tcFrameBuffer : TEXCOORD0 ; float2 tcGlare : TEXCOORD1 ; } ; float4PS_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 ; } グレア合成

  43. DX9のHDR実装について • 高精度バッファを利用 • メモリ使用量が多い • ブレンディングを行えない • 近い将来には実用的に… • 低精度バッファを利用 • ピクセルシェーダ負荷が高い • DX8同様のフェイク処理も検討すること • パフォーマンスが高い • メモリ使用量が少ない • 効果が大きい

  44. グレア生成 • グレア生成の注意点 • 複数のガウスフィルタの合成によるブルーム • イメージプロセスとスプライトによるグレア表現

  45. グレア生成の注意点 • かなりのマルチパスフィルタ • 整数バッファの精度に注意 • 値のクランプ • 丸め誤差 • fx16でも充分では無い • 常に値の分解能と範囲に注意すること

  46. 複数のガウスフィルタの合成 • ブルーム生成 • 単独のガウスフィルタではあまり良い結果を得られない • フィルタ半径が小さい • にも関わらず光源中央付近の鋭さは足らない • 複数のガウスフィルタの合成 • 半径の異なる複数のガウスフィルタを合成 • より広く、なおかつ鋭いグレアを表現できる

  47. 複数のガウスフィルタの合成

  48. 複数のガウスフィルタの合成 元のイメージ

  49. 複数のガウスフィルタの合成 • フィルタ半径が大きいと負荷が高い • 縮小バッファの活用 • 半径の大きなフィルタほどローパス • 低い解像度でガウスブラーを適用してからバイリニアフィルタで拡大しても誤差は少ない(目立たない) • フィルタ半径ではなく画像解像度を変える • 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) • … • 数百ピクセル四方の巨大なフィルタカーネルも高速に適用可能

  50. 縮小バッファのガウスフィルタ 1/4 x 1/4 (256x192 pixels) 1/8 x 1/8 (128x96 pixels) 1/16 x 1/16 (64x48 pixels) 1/32 x 1/32 (32x48 pixels) 1/64 x 1/64 (16x12 pixels)

More Related