22/0722 断面にハッチングができるようになりました
データのみ先に更新します。記事は後程更新予定です。
自分のレベルは
・シェーダー言語さっぱり分からない
・Unity歴は1年
・今回初めてShaderGraphを触った
です。このくらいの人向けの記事です(なのでもっといい方法があるかも)
データはこちら
22/0722 リンク切れ修正、データ更新しました
───────
目的
・切断部に面がある(ように見える)断面表示
・サブメッシュのあるオブジェクトに対応するため、1シェーダーで断面表示をする
・ぴったり重なった面や多少のめり込みに対応する
環境
Unity2019.4.2(おそらくこれ以降も可)
20220722 データのほうは 2020.3.22f1に移行しました
今回はライトの情報を取得したいので、URPです
前知識
ShaderGraphの概要と使い方は↓の動画を見てください
方針決め
「Unity 断面表示」などで検索すると、↓がヒットします
これらは深度バッファなどを利用してクリッピングし、その後断面部分を塗っている…ようです。
切断部に面を張っていないのが参考になりました。
しかしこれらは「マスク用」「断面用」のように、複数のシェーダーで組みになっていて、
今回は1つのシェーダーにしたいので、使えませんでした。
また、切断部に色をつけないのであれば、ディソルブシェーダーというジャンルが
断面表示に近い感じです。クリッピングにはアルファを使っているみたいです。
この方法は1つのマテリアルでも使えます。
以上から、クリッピングにはアルファを使い、
切断部を単色塗りつぶしをすることにしました。
試作(Unlit)
はじめPBRのライティングを無効にする方法がわからず、
Unlitにライティングを追加することにしました。
試作の過程
(参考1)https://qiita.com/o_s_t/items/c546962bbd9f064c9908
UnlitからPBRを再現している方がいました(すごい)。
これを全部見ていくと、PBRの処理がよく理解できそうです。
(参考2)https://blogs.unity3d.com/jp/2019/07/31/custom-lighting-in-shader-graph-expanding-your-graphs-in-2019/
こちらは公式で、ライトの情報を取得する方法が書いてあります。
これは後で断面のライティングをするために使います。
(参考3)https://noahbannister.blog/2019/09/23/unity-lwrp-pbr-shader-node/
こちらは通常のPBR処理を呼び出しています。
今回はこちらを使用。
このままのコードではエラーが出たため、修正
SHADERGRAPH_PREVIEWを使って、プレビュー用の値を設定してやらないといけないみたいです
void LightweightPBR_float
(float3 Albedo,
float Metallic,
float3 Specular,
float Smoothness,
float Occlusion,
float3 Emission,
float Alpha,
float3 PositionWS,
float3 NormalWS,
float3 ViewDirectionWS,
float FogCoord,
float3 VertexLighting,
float3 BakedGI,
out float4 fragOut)
{
#if SHADERGRAPH_PREVIEW
fragOut = float4 (1, 1, 1, 1);
#else
InputData inputData;
inputData.positionWS = PositionWS;
inputData.normalWS = NormalWS;
inputData.viewDirectionWS = ViewDirectionWS;
inputData.shadowCoord = TransformWorldToShadowCoord(PositionWS);// GetShadowCoord(GetVertexPositionInputs(PositionWS));
inputData.fogCoord = FogCoord;
inputData.vertexLighting = VertexLighting;
inputData.bakedGI = BakedGI;
fragOut = UniversalFragmentPBR(inputData, Albedo, Metallic, Specular, Smoothness, Occlusion, Emission, Alpha);
#endif
}
おおよそ最終版と同じ見た目ですが、他のオブジェクトからの影を受けられていません。
↑こちらの記事を参考すれば実現できそうですが、
この辺りでPBRもmetallic、normal、smoothneesを0にし、emissionを指定すればUnlitのようになることがわかり、PBRにまき直しました。
最終版(PBR)
やっていることは
1.スクリプトから断面用プレーンのtransformをマテリアルに送る
2.アルファで切断、
3.断面部から見える裏面をmetallic、normal、smoothneesは0にして真っ黒に
4.断面色+偽のライティングをemissionに設定
です。
まず、スクリプトです。
[ExecuteInEditMode]と書くとplay時以外にも実行してくれます。
スクリプトからレンダラー経由でマテリアルの情報を書き換えると
インスタンス化(マテリアルがオブジェクトごとに独立)されるので注意。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
[ExecuteInEditMode]
public class 断面指示 : MonoBehaviour
{
public Renderer[] レンダラー;
void Update()
{
for (int i=0;i< レンダラー.Length; i++)
{
Material[] M = レンダラー[i].materials;
for(int j = 0; j < M.Length; j++)
{
M[j].SetVector("_DANMEN_Pos", transform.position);
M[j].SetVector("_DANMEN_Vec", transform.up);
}
レンダラー[i].materials = M;
}
}
}
スクリプトからパラメーターを変更したい場合は、Referenceの名前を"_パラメーター"などに変更しておきます。
次にグラフの説明です
####①重なったメッシュが汚くなるため、法線内側方向に目立たない範囲で縮める
####②通常のアルベドのテクスチャ処理
####③断面部分にライティングをするための処理
いろいろ厳密には違うんでしょうが、自分にはこれが限界でした。
通常だとオブジェクトのノーマルを入れるところに、スクリプトから渡した断面用プレーンのtransformを使ってライティングを行います。
使い勝手をよくするため、断面色をベースカラーにするか、別の色にするか、偽ライティングを有効にするか切り替えできるようにしています。
ライトの情報は↓の記事よりカスタムファンクションを作成しました。
ライティングの結果、オブジェクトの影が落ちています。
GIも使っているので、照り返しがあるとリアルな面になります。
(positionは裏面のものなので、なんとなくそれっぽいだけですが)
ただし、他のオブジェクトからの影は落とせないです。
実際にはここに面がないためです(なにか方法があれば教えてください)。
####④通常のノーマル処理
テクスチャがある時とない時でBranchで切り替え。
(もっとスマートな方法があるかも)
####⑤通常のオクルージョン処理
あっているはず…
####⑥断面位置でカットする処理
スクリプトから渡した断面用プレーンのtransformを使い、プレーンのどちら側にあるかをベクトルの内積を使って求め、アルファを0or1に切り替えます。
#####⑦表、裏面(断面部)で切り替える
↑で作ったノードをつなぎます
これで完成です
#まとめ
今回初めてShaderGraphを触りましたが、使いやすかったです。
そもそものシェーダーができる範囲がとても広く、見た目だけでなくもはや機能のような
物を作れると分かり勉強になりました。
また今回Qiitaで記事を書くのが初めてなんですが、そこそこ時間がかかり、
丁寧な記事を書く先人に一層感謝しました。