しゅみぷろスマートフォン用の表示で見るしゅみぷろプログラミングとかこの広告は、90日以上更新していないブログに表示しています。UnityTexturePaintはじめにesprog.hatenablog.com以前書いた記事で、テクスチャペイントするブラシに凹凸の表現を施したいと思い、法線マップについて勉強してみましたので纏めておきます。以下のサイトを参考にさせていただきました。0から学ぶ法線マップシェーディングについて必要になる知識は以下の記事で纏めてあります。しゅみぷろ本記事で作成したサンプルはgithub.comで公開しています。法線マップとは物体の表面の凹凸を表現する手法のことです。3Dモデルにおいて、細かな凹凸を表現するにはメッシュを細分化し、微調整することで表現します。この方法では、凹凸の多いオブジェクトの場合に頂点数がすごいことになります。そこで、面の細かな凹凸を近似するための方法として考えられたのが法線マップです。この凹凸を近似するためにバンプマップを用いた方法があります。法線マップでは物体表面の向いている方向(法線)をテクスチャに書き込み、シェーディングの法線計算処理でこの法線マップの情報を用いることで凹凸を表現します。こちらの記事でマイクロファセットについて言及しましたが、考え方は似ています。マイクロファセットはある一点における極小の面の法線分布を考えていたのに対し、法線マップではある面(主に三角形平面)のある点における法線を定義します(通常のシェーディングではこの「ある点の法線」はモデルの頂点の持つ法線などから計算されます)。法線マップは実際に凹凸をつけた3Dモデルから専用のアプリケーション等を用いて生成します。グレースケール画像から法線マップを作るサービスもあります。実際にグレースケール画像から法線マップを作ってみると、以下の様な感じになります。これをUnityのデフォルトのマテリアルに適用してみるとこんな感じになります。法線マップが表す法線は接空間上のベクトルです。接空間とは、3Dモデル表面のある1点に乗っかっている座標空間のことです。画像の青軸は3Dモデル表面の法線を表しています。通常のシェーディングではこの法線を用いて陰影を付けていくのですが、これは面ごとに固定された値を持っていますので、面の持つ細かな凹凸を表現できないわけです。法線マップを使ってこの接空間上で面内のある点がどの方向を向いているのかを定義しているのですが、この法線マップの存在する空間は接空間です。シェーディングを行う場合はこのオブジェクトの法線マップの値とライトの方向ベクトル(スペキュラーでも用いる場合は視点の方向ベクトルも)の存在する空間を同じものにする必要があります。空間を統一する手法としては法線マップの法線をライトやカメラの座標空間に変換するライトやカメラの方向ベクトルを接空間に変換するといった2通りの方法が考えられます。1の方法の場合法線マップの法線はピクセルシェーダーで取得することになりため、座標変換処理がピクセル処理時に毎回行われることになります。2の方法の場合頂点シェーダーでライトやカメラの方向ベクトルを変換してやるだけで良いため、1の方法に比べて計算量が少なく済みます。今回は2の方法について考えていきます。接空間への理解を深める目的で、こんなものを作ってみました。マウスでクリックしたオブジェクト表面上の接空間を表示しています。リポジトリは以下で公開しています。github.comオブジェクトの法線さえわかってしまえば、接空間を求めることは簡単です。頂点シェーダーにおいて画像の青軸(Normal)は既知です。0)方向を表すベクトルです。この2つのベクトルの外積から赤軸(Tangent)を求めることができます(後述しますが、外積の性質上この方法でTangentを求めるのはBADです)。青軸と赤軸方向のベクトルの外積から緑軸(Binormal)を求めることができます。normal);みたいになります。先ほど頂点シェーダーにおいて画像の青軸(Normal)は既知です。0)方向を表すベクトルです。この2つのベクトルの外積から赤軸(Tangent)を求めることができます(後述しますが、外積の性質上この方法でTangentを求めるのはBADです)。0)を用いてることが問題になります。法線がこのベクトルと同じだった場合(つまり法線が真上を向いていた場合)、外積はゼロベクトルを返します。これでは都合が悪いので回避する必要があります。回避する方法は適当で良ければどうにでもなるのですが、シェーダープログラムの場合はTangentをモデルからの入力として取得することができます。Tangentセマンティクスをつけたfloat4型の変数をvertexシェーダーで受け取る構造体に定義するだけです。ちなみにBinormalもTangent同様にセマンティクスをつけたfloat4型の変数を定義すればモデルデータから受け取れるはずなのですが、Unityではシェーダーコンパイル時に怒られました。面倒だったので、これについてはNormalとTangentの外積から算出しました。しゅみぷろ同様、ライトはカスタムライト(といってもただの位置情報ですが...)を用いています。シェーダーにパラメータを渡すC#スクリプトです。}}特に説明は不要かと思います。次に実際に法線マップから法線を取得し、シェーディングに凹凸を反映させるシェーダーです。}}これもコードを追っていけばそこまで難しいことはしていません。座標変換も、元の座標(単位ベクトル)に接空間の方向ベクトルをそれぞれの軸方向について掛けてやるだけです。変換行列は直交行列なのでInvTangentMatrixでは行列を転置することで逆行列を得ています。実装していて詰まった部分は、法線マップから法線情報を抽出する際に、法線マップのテクスチャデータフォーマットに気をつけなければならないことです。1.0)といった方法で法線を取得できるのですが、UnityではDXT5nmというフォーマットに変換される場合があるそうです。どちらにせよ法線情報を正確に取得するには、UnityCG.cgincで定義されているUnpackNormalを使います。このNormalを用いてディフューズやスペキュラーを計算してやれば、凹凸をうまく表現できるわけです。チリペヂィア今回のシェーダーではディフューズのみ計算していますが、結構いい感じに凸凹っぽく見えます。液垂れ表現についてです。UnityでMatcapシェーダー書いたのでメモ。)はてなブログをはじめよう!es_programさんは、はてなブログを使っています。ブログを報告するスターをつけました引用をストックしましたストック一覧を見る閉じる引用するにはまずログインしてくださいログイン閉じる引用をストックできませんでした。再度お試しください閉じる限定公開記事のため引用できません。