LoginSignup
21
21

More than 1 year has passed since last update.

【Unity/ShaderGraph】CADライクな断面表示

Last updated at Posted at 2021-05-01

↓のような表示をするため紆余曲折あったので、共有します
ezgif.com-gif-maker (1).gif

22/0722 断面にハッチングができるようになりました
データのみ先に更新します。記事は後程更新予定です。
image.png

自分のレベルは
・シェーダー言語さっぱり分からない
・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を使って、プレビュー用の値を設定してやらないといけないみたいです

test.hlsl
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
}
結果がこちら

image.png
おおよそ最終版と同じ見た目ですが、他のオブジェクトからの影を受けられていません。

↑こちらの記事を参考すれば実現できそうですが、
この辺りでPBRもmetallic、normal、smoothneesを0にし、emissionを指定すればUnlitのようになることがわかり、PBRにまき直しました。

最終版(PBR)

やっていることは
1.スクリプトから断面用プレーンのtransformをマテリアルに送る
2.アルファで切断、
3.断面部から見える裏面をmetallic、normal、smoothneesは0にして真っ黒に
4.断面色+偽のライティングをemissionに設定
です。

まず、スクリプトです。
[ExecuteInEditMode]と書くとplay時以外にも実行してくれます。
スクリプトからレンダラー経由でマテリアルの情報を書き換えると
インスタンス化(マテリアルがオブジェクトごとに独立)されるので注意。

断面指示.cs
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;

        }
    }
}

10.png

スクリプトからパラメーターを変更したい場合は、Referenceの名前を"_パラメーター"などに変更しておきます。

image.png

次にグラフの説明です

1_1.png

####①重なったメッシュが汚くなるため、法線内側方向に目立たない範囲で縮める
2.png
ezgif.com-gif-maker (2).gif

####②通常のアルベドのテクスチャ処理
3.png
####③断面部分にライティングをするための処理
4.png
いろいろ厳密には違うんでしょうが、自分にはこれが限界でした。
通常だとオブジェクトのノーマルを入れるところに、スクリプトから渡した断面用プレーンのtransformを使ってライティングを行います。
使い勝手をよくするため、断面色をベースカラーにするか、別の色にするか、偽ライティングを有効にするか切り替えできるようにしています。
ライトの情報は↓の記事よりカスタムファンクションを作成しました。

9.png
ライティングの結果、オブジェクトの影が落ちています。
GIも使っているので、照り返しがあるとリアルな面になります。
(positionは裏面のものなので、なんとなくそれっぽいだけですが)
image.png
ただし、他のオブジェクトからの影は落とせないです。
実際にはここに面がないためです(なにか方法があれば教えてください)。
image.png

####④通常のノーマル処理
テクスチャがある時とない時でBranchで切り替え。
(もっとスマートな方法があるかも)
5.png
####⑤通常のオクルージョン処理
あっているはず…
6.png
####⑥断面位置でカットする処理
7.png
スクリプトから渡した断面用プレーンのtransformを使い、プレーンのどちら側にあるかをベクトルの内積を使って求め、アルファを0or1に切り替えます。

#####⑦表、裏面(断面部)で切り替える
↑で作ったノードをつなぎます
8.png

これで完成です

#まとめ
今回初めてShaderGraphを触りましたが、使いやすかったです。
そもそものシェーダーができる範囲がとても広く、見た目だけでなくもはや機能のような
物を作れると分かり勉強になりました。

また今回Qiitaで記事を書くのが初めてなんですが、そこそこ時間がかかり、
丁寧な記事を書く先人に一層感謝しました。

以上です。
ありがとうございました。
image.png

21
21
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
21
21