【URP】Unityでアウトラインのカメラエフェクトを作る【ShaderGraph】
出来たもの
仕組み
カメラに写ってる画像と、その画像をほんのちょっと拡大したものを用意して、2つの画像の差分を取得することで「画像内の、色が変わる境界線」を取得します。
この画像を使って、「黒い部分は元の画像を表示し、黒くない部分はアウトラインとして塗りつぶす」って感じで作ります。
自分でも何言ってるかよくわかんないです。
準備
まず、URPプロジェクトであることを確認してください。
URPじゃないとShaderGraphを使えません。
Create>Shader>Universal RenderPipeline>Unlit Shader Graphで新規ShaderGraphを作ります。
作成したShaderGraphのアイコンの上で、Create>Materialで上記シェーダーを使った新規マテリアルを作成します。
次にCreate>Rendering>Universal Render Pipeline>Renderer Featureで新規RendererFeatureを作ります。
現在使用中のFowardRendererのAdd Renderer Featureを押して、さっき作ったRendererFeatureを貼り付けます。
【FowardRendererの場所】
Edit>Project Settingから
GrahicsのScriptable Render Pipeline Settingの中のアセットをダブルクリックして、
General>Renderer Listの中のアセットをダブルクリックすると出てきます。
フォルダの場所を覚えといたほうが後々便利です。
コードをちょっと書く
Renderer Featureのコードをこんな感じに書き換えます。
using System;
using UnityEngine;
using UnityEngine.Rendering;
using UnityEngine.Rendering.Universal;
using UnityEngine.Serialization;
public class CameraEffectFeature : ScriptableRendererFeature
{
[SerializeField] private Material material;
class CustomRenderPass : ScriptableRenderPass
{
public Material material;
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
if (material == null)
return;
var camera = renderingData.cameraData.camera;
var cmd = CommandBufferPool.Get(string.Empty);
cmd.Blit(Texture2D.whiteTexture, camera.activeTexture, material);
context.ExecuteCommandBuffer(cmd);
context.Submit();
}
}
CustomRenderPass m_ScriptablePass;
public override void Create()
{
m_ScriptablePass = new CustomRenderPass();
m_ScriptablePass.material = material;
m_ScriptablePass.renderPassEvent = RenderPassEvent.AfterRendering + 2;
}
public override void AddRenderPasses(ScriptableRenderer renderer, ref RenderingData renderingData)
{
renderer.EnqueuePass(m_ScriptablePass);
}
}
【注意!!!】
紛らわしいのですが、デフォルトで、Create()内の
m_ScriptablePass.renderPassEvent = RenderPassEvent.AfterRenderingOpaques;
になってるところは
m_ScriptablePass.renderPassEvent = RenderPassEvent.AfterRendering + 2;
に書き換えてください。
コードを書く部分は、class CustomRenderPass内のExcute()と、Create()だけです。
コーディング部分はこれで終わりです。
コードを書いたので、さっき作ったFowardRendererに張り付けたRenderFeatureにマテリアルを参照する箇所ができているはずです。
そこにさっき作ったマテリアルを入れましょう。
SceneビューとGameビューが灰色一色になったら成功です。
ShaderGraph設定
いよいよ本題であるShaderGraphの中身を作っていきます。
作ったShaderGraphをダブルクリックして編集画面を開きます。
①左上の+ボタンを押して変数を追加します。
まずTexture 2Dを追加しましょう。
作成したTexture 2Dをクリックして、編集します。
右上のGraph Inspector>Node Setting>Referenceを「_CameraColorTexture」とします。スペルミスに注意してください。これはカメラの画を取得するものです。
ついでにExposedのチェックを外しておきましょう。チェックがついていると外部からの編集が可能になります。
②何もないところを右クリックして、Createから、Sample Texture 2Dをダブルクリックでノードを作成します。検索欄でTextureと入れるとすぐ出てきます。
出てきたSample Texture 2Dノードの左のTextureをさっきのTexture2Dとつなぎ、RGBAをFragmentにつなぎます。
左上のSave Assetを押してGameビューがカメラ画像に変化したことを確認してください。
③次はその画像を少し拡大する処理を作ります。
同じようにSample Texture 2Dノードを作成し、先ほどのTexture2Dとつなぎます。
次に、Tiling And Offsetノードを作成します。
その後Float変数を作ります。
Graph InspectorでModeをSliderに、Minを0.99にMaxを1.01にします。
次にOne MinusノードとMultiplyノードを作ります。
MultiplyノードのBの値に0.5を入れます。
One Minusノードは「1-x」をする処理のノードで、Multiplyはその名の通り掛け算処理のノードです。
以下の画像のようにノードを繋げます。
現在の全体像はこんな感じです。
今作成したSample Texture 2DノードのRGBAをFragmentにつないでみて動作を確認してみましょう。
まずSaveAssetをしましょう。しないと反映されません。以降も忘れずに。
マテリアルのインスペクタにFloatを入力できる箇所があると思います。
数値をいじってGameビューが拡大縮小してたら成功です。
④次にSubtractノードを作り、A、BにそれぞれSample Texture 2Dをつなぎます。
Subtractは引き算の処理です。
OutをFragmentにつないでみて、こんな感じの絵になっていたら成功です。
Save Assetを忘れずに。
⑤次にLengthノードを作り、InにSubstractのOutをつなぎます。
Lengthはベクトルの長さを取得するノードです。
Fragmentにつないで確認して、白黒画像になっていたら成功です。
⑥これで最後です。
ComparisonノードとBranchノードを作ります。
Comparisonノードは値を比較しbool値を出力するノードです。
Branchのノードはbool値をもとにif-else分岐をするノードです。
さっきのLengthのOutをComparisonのAにつなぎます。
Bは0.01を入力します。
BranchのPredicateにComparisonのOutをつなぎます。
BranchのTrueに、最初に作ったSample Texture 2DのRGBAをつなぎます。
Falseは(0,0,0,1)と入力します。
OutをFragmentにつないで完成です。
最終的なノード構成がこんな感じ。
BranchのTrue値がアウトラインになるので、そこをいじるとアウトラインの色を変えることもできて面白いですね。