Custom FullScreen Pass シェーダーで、ノーマルバッファから法線とラフネスのデータを読み込んでみました。
カスタムパス
Projectウィンドウで「Custom FullScreen Pass シェーダー」を新規作成し、マテリアルに設定します。
Custom Pass VolumeコンポーネントにFullScrreenCustomPassを追加し、マテリアルをアタッチします。
Custom FullScreen Pass シェーダー
シェーダーでは、includeディレクティブでNormalBuffer.hlslを読み込みます。
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/Material/NormalBuffer.hlsl"
DecodeFromNormalBuffer関数で法線とラフネスのデータを読み込みます。
float4 FullScreenPass(Varyings varyings) : SV_Target
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(varyings);
// 深度
float depth = LoadCameraDepth(varyings.positionCS.xy);
NormalData normalData;
// 法線とラフネスのデータを読み込む
DecodeFromNormalBuffer(varyings.positionCS.xy, normalData);
NormalDataに法線とラフネスの値が含まれています。
// ファークリッププレーンを除外
float isNotFarClip = depth != UNITY_RAW_FAR_CLIP_VALUE;
// float roug = normalData.perceptualRoughness;
// return float4(roug, roug, roug, 1) * isNotFarClip;
return float4(normalData.normalWS * isNotFarClip, 1);
}
ファークリッププレーンを除外しないと、エフェクトが残ります。
法線エッジ抽出
ノーマルデータを使って法線エッジ抽出をしてみました。
float4 FullScreenPass(Varyings varyings) : SV_Target
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(varyings);
float depth = LoadCameraDepth(varyings.positionCS.xy);
PositionInputs posInput = GetPositionInput(varyings.positionCS.xy, _ScreenSize.zw, depth, UNITY_MATRIX_I_VP, UNITY_MATRIX_V);
float3 viewDirection = GetWorldSpaceNormalizeViewDir(posInput.positionWS);
float4 color = float4(0.0, 0.0, 0.0, 0.0);
// Load the camera color buffer at the mip 0 if we're not at the before rendering injection point
if (_CustomPassInjectionPoint != CUSTOMPASSINJECTIONPOINT_BEFORE_RENDERING)
color = float4(CustomPassLoadCameraColor(varyings.positionCS.xy, 0), 1);
// 4点のUV座標を作成
float2 rightTopUV = posInput.positionNDC.xy + _ScreenSize.zw * _EdgeRadius;
float2 leftButtomUV = posInput.positionNDC.xy - _ScreenSize.zw * _EdgeRadius;
float2 leftTopUV = posInput.positionNDC.xy + _ScreenSize.zw * float2(-1,1) * _EdgeRadius;
float2 rightButtomUV = posInput.positionNDC.xy - _ScreenSize.zw * float2(-1, 1) * _EdgeRadius;
// ノーマルデータを読み込み
NormalData normalData0, normalData1, normalData2, normalData3;
DecodeFromNormalBuffer(_ScreenSize.xy * rightTopUV, normalData0);
DecodeFromNormalBuffer(_ScreenSize.xy * leftButtomUV, normalData1);
DecodeFromNormalBuffer(_ScreenSize.xy * leftTopUV, normalData2);
DecodeFromNormalBuffer(_ScreenSize.xy * rightButtomUV, normalData3);
// 対角線の差
float3 normalDiff0 = normalData0.normalWS - normalData1.normalWS;
float3 normalDiff1 = normalData2.normalWS - normalData3.normalWS;
// 長さを計算
float edgeStrength = sqrt(dot(normalDiff0, normalDiff0) + dot(normalDiff1, normalDiff1));
// ファークリッププレーンを除外
float isNotFarClip = depth != UNITY_RAW_FAR_CLIP_VALUE;
// 閾値を適用
edgeStrength = step(_EdgeTreshold, edgeStrength)* isNotFarClip;
// カスタムカラーバッファを読み込み
float customColor = CustomPassLoadCustomColor(varyings.positionCS.xy);
// 輪郭は指定の色、それ以外はカメラカラーにする
return lerp(color,_EdgeColor, edgeStrength * customColor);
}
深度やカメラカラーを読み込みます。
float4 FullScreenPass(Varyings varyings) : SV_Target
{
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(varyings);
float depth = LoadCameraDepth(varyings.positionCS.xy);
PositionInputs posInput = GetPositionInput(varyings.positionCS.xy, _ScreenSize.zw, depth, UNITY_MATRIX_I_VP, UNITY_MATRIX_V);
float3 viewDirection = GetWorldSpaceNormalizeViewDir(posInput.positionWS);
float4 color = float4(0.0, 0.0, 0.0, 0.0);
// Load the camera color buffer at the mip 0 if we're not at the before rendering injection point
if (_CustomPassInjectionPoint != CUSTOMPASSINJECTIONPOINT_BEFORE_RENDERING)
color = float4(CustomPassLoadCameraColor(varyings.positionCS.xy, 0), 1);
一辺がエッジ半径*2の正方形の4つの角にあるピクセルの座標を計算します。
// 4点のUV座標を作成
float2 rightTopUV = posInput.positionNDC.xy + _ScreenSize.zw * _EdgeRadius;
float2 leftButtomUV = posInput.positionNDC.xy - _ScreenSize.zw * _EdgeRadius;
float2 leftTopUV = posInput.positionNDC.xy + _ScreenSize.zw * float2(-1,1) * _EdgeRadius;
float2 rightButtomUV = posInput.positionNDC.xy - _ScreenSize.zw * float2(-1, 1) * _EdgeRadius;
ノーマルデータを読み込みます。
// ノーマルデータを読み込み
NormalData normalData0, normalData1, normalData2, normalData3;
DecodeFromNormalBuffer(_ScreenSize.xy * rightTopUV, normalData0);
DecodeFromNormalBuffer(_ScreenSize.xy * leftButtomUV, normalData1);
DecodeFromNormalBuffer(_ScreenSize.xy * leftTopUV, normalData2);
DecodeFromNormalBuffer(_ScreenSize.xy * rightButtomUV, normalData3);
対角線同士の法線の差のベクトルを計算します。
// 対角線の差
float3 normalDiff0 = normalData0.normalWS - normalData1.normalWS;
float3 normalDiff1 = normalData2.normalWS - normalData3.normalWS;
ドット積を足して平方根にします。差が大きいと大きな値になります。
// 長さを計算
float edgeStrength = sqrt(dot(normalDiff0, normalDiff0) + dot(normalDiff1, normalDiff1));
step関数を使って、閾値で0と1に分けます。ファークリッププレーンは0にしています。
// ファークリッププレーンを除外
float isNotFarClip = depth != UNITY_RAW_FAR_CLIP_VALUE;
// 閾値を適用
edgeStrength = step(_EdgeTreshold, edgeStrength)* isNotFarClip;
カスタムカラーバッファに白く描画した部分だけエフェクトを適用します。lerp関数で、エッジはインスペクタで指定する色、それ以外はカメラカラーバッファの色にします。
// カスタムカラーバッファを読み込み
float customColor = CustomPassLoadCustomColor(varyings.positionCS.xy);
// 輪郭は指定の色、それ以外はカメラカラーにする
return lerp(color,_EdgeColor, edgeStrength * customColor);
}
Draw Renderersカスタムパス
フルスクリーンカスタムパスの前にDraw Renderersカスタムパスを使って、対象のレイヤーのみ白でカスタムカラーバッファに描画しています。
void GetSurfaceAndBuiltinData(FragInputs fragInputs, float3 viewDirection, inout PositionInputs posInput, out SurfaceData surfaceData, out BuiltinData builtinData)
{
// Write back the data to the output structures
ZERO_BUILTIN_INITIALIZE(builtinData); // No call to InitBuiltinData as we don't have any lighting
ZERO_INITIALIZE(SurfaceData, surfaceData);
builtinData.opacity = 1;
builtinData.emissiveColor = float3(0, 0, 0);
surfaceData.color = float3(1,1,1);
}