Eevee でリアルタイムセルルック
EEVEEでのNPR表現の開発は私がやります。というか、ほぼ完成です。#b3d #開発 #blender #blender28 #npr pic.twitter.com/cINEeA5UBs
— 神崎航 @卒制〆切後2週間 (@kanzwataru) 2018年4月28日
Eevee に Shader to RGB ノードが追加された(実装は神崎航氏)。このノードはディフューズ・キューブマップの色を取得でき、透明オブジェクトにも対応している。ただしスクリーンエフェクト(Screen Space Reflections やScreen Space Subsurface Scatteringなど)の情報は取得できない。
Shader to RGB で取得した色を強さ(Strength)1の放射(Emission)ノードにつなぐと指定した色をそのままレンダリングできる。
古い内容
そのほかの NPR Shader の実装案
Eevee の実装について
source/blender/source/blender/gpu/shaders/gpu_shader_material.glsl を見てみると、Eevee では BSDF で関数を出力しておらず、ライティングした結果を次のノードへ渡している。具体的には以下のデータを渡している。
- radiance(色)
- opacity(不透明度)
- SSR(スクリーン空間反射エフェクト)に必要な情報(法線・色・ラフネス・ID)
- SSS(皮下拡散)に必要な情報
実際にライティングする関数は source/blender/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl の eevee_closure_diffuse だ。
NPR Uber Shader
NPR Uber Shader は Blender レンダーをシミュレートする、プリンシプル BSDF のようなノードだ。扱いやすいが出力が BSDF なので加工しづらい。
BSDF を分離する方法
この方法は Blender レンダーのマテリアルのような色・アルファ・法線・残りの BSDF データを出力するノードとそれらを結合して BSDF へ変換するノードとを追加する方法だ。こうすると Blender レンダーのノードを移植できるというメリットがある。Blender レンダーとまったく同じレンダリング結果になるわけではないが。
Eevee Toon Shaders ノード を使えば Eevee で Blinn や Phong をシミュレートできる。しかしそのノードは最終的に放射ノードとディフューズBSDFノードとで色を付けている。この方法では正確な色を指定できない。
加えて単光源にしか対応していない。複数光源のノードを組むこともできるがとても複雑になる。
Eevee にトーン(Toon)BSDF が実装されれば複数光源の問題は解決するが、正確な色を指定できない問題はどうにもならない。
Blender レンダーと PBR の違い
Blender レンダーのマテリアルノードの最終的な出力は表示される色だ。それに対して PBR(Cycles や Eevee)では双方向拡散率分布関数(BSDF)が最終的なノード出力だ。
Blender レンダーでは一度レンダリングされた結果をさらにマテリアルノードで編集していた。なので影のできる場所が特定できた。それに対して PBR ではそれらの情報は取得できない。PBR では入射光の情報とオブジェクトに関する情報(法線や UV など)から BSDF を作る必要がある。というのも影が落ちている場所は単に光が入ってこないだけなので BSDF を作るうえで考慮する必要がない。
色の問題
PBR で正確な色を指定できないのは、レンダリングに必要なすべてのパラメータにアクセスできないからだ。現状では方程式の赤で囲った部分のみコントロールできる。Leは放射ノードのような発光を表現する項で、frはそれ以外の BSDF で設定する項だ。
放射ノードのみを使えばほぼ正確に色を指定できる。しかし放射ノードは陰を作らず、影の影響を受けない。
Eevee でリアルタイムセルルックを作るには
ライティングノードを作る
Eevee のレンダリングエンジン本体に手を入れて、光源のシャドウマップを参照するノードを追加する必要がある。光源のシャドウマップを参照するノードは実装するとするなら、ライトを指定してライティングされた色を出力するノードになるだろう。表面の材質を指定する方法はないので、グレーのディフューズだと仮定する。
// シェーダの疑似コード p // 頂点位置 n // 法線 light_pos // 光源の位置 l // ライトの方向 c // ライトの色 s // 光の強さ env // 環境光 tex_depth // シャドウマップテクスチャ DepthMapMatrix // 頂点位置をシャドウマップの UV 座標へ変換する行列 float real_depth = distance(p, light_pos); float2 uv = (p * DepthMapMatrix).xy; float depth_map = texture2D(tex_depth, uv); if( depth_map < real_depth ){ return env; } else{ return dot(n, l) * s * c + env; }
BSDFノードを作る
Specular ノードが参考になる。Specular ノードは source/blender/source/blender/gpu/shaders/gpu_shader_material.glsl の node_eevee_specular 関数だ。
Eevee シェーダの基本的なライティングは source/blender/source/blender/draw/engines/eevee/shaders/lit_surface_frag.glsl で行われていて、光源からの可視性は light_visibility 関数で取得できそう。
ポストエフェクト
リアルタイムポストエフェクトにセルルックの処理を実装することもできる。しかし半透明オブジェクト(深度バッファの問題)や鏡(法線・深度・UV の問題)の処理が困難だ。
リアルタイムでないセルルック
Cycles でやっていたようにマテリアルオーバーライドを使う。2018年 4 月の時点ではマテリアルオーバーライドはまだ未実装だ。
輪郭抽出は反転ポリゴン法、フリースタイル、コンポジットがそのまま使える。