외곽선 강조 Shader 코드
OutlineShader/Objects
- 객체 단위로 외곽선을 그리는 Shader이다.
Shader "OutlineShader/Objects"
{
Properties
{
_ContourColor("Contour Color", Color) = (1,1,1,1) // 외곽선 색상
_MaxDistance("Max Distance", float) = 15 // 카메라와의 최대 거리
}
HLSLINCLUDE
#pragma target 4.5
#pragma only_renderers d3d11 ps4 xboxone vulkan metal switch // 셰이더가 작동할 플랫폼 제한
#pragma multi_compile_instancing // GPU 인스턴싱을 활성화하여 성능 최적화
#pragma instancing_options renderinglayer // GPU 인스턴싱 시 렌더링 레이어도 적용
ENDHLSL
SubShader
{
Pass
{
Name "FirstPass"
Tags { "LightMode" = "FirstPass" }
Blend Off // 알파 블렌딩 비활성화
ZWrite On // 깊이 버퍼 쓰기 활성화
ZTest LEqual // 깊이 테스트가 현재 픽셀의 깊이 값이 같거나 작은 경우에만 통과
Cull Back // 후면 컬링 활성화
HLSLPROGRAM
// Vertex Shader에 전달할 데이터
#define ATTRIBUTES_NEED_TEXCOORD0
#define ATTRIBUTES_NEED_NORMAL
#define ATTRIBUTES_NEED_TANGENT
// Fragment Shader에 전달할 데이터
#define VARYINGS_NEED_TEXCOORD0
#define VARYINGS_NEED_TANGENT_TO_WORLD
#define VARYINGS_NEED_POSITION_WS
// Unity HDRP의 Custom Pass용 헤더 파일 포함
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassRenderers.hlsl"
// 셰이더에 사용할 변수들 정의
float _MaxDistance;
float4 _ContourColor;
float _AlphaThreshold;
// 커스텀 패스에서 표면과 기본 데이터 처리 함수 정의
void GetSurfaceAndBuiltinData(FragInputs fragInputs, float3 viewDirection, inout PositionInputs posInput, out SurfaceData surfaceData, out BuiltinData builtinData)
{
// 카메라와 객체의 거리가 _MaxDistance보다 크면 해당 픽셀을 그리지 않음
if( length(fragInputs.positionRWS) > _MaxDistance ) discard;
// 표면과 기본 데이터 초기화 (조명 계산 없음)
ZERO_INITIALIZE(BuiltinData, builtinData); // No call to InitBuiltinData as we don't have any lighting
ZERO_INITIALIZE(SurfaceData, surfaceData); // No call to InitBuiltinData as we don't have any lighting
builtinData.opacity = 1; // 객체 불투명하게 설정
builtinData.emissiveColor = float3(0, 0, 0); // 발광 색상 설정 (발광 없음)
surfaceData.color = float3(1,1,1) * _ContourColor.rgb; // 표면 색상을 외곽선 색상으로 설정
}
// HDRP의 비조명(Unlit) 패스 포함
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/ShaderPass/ShaderPassForwardUnlit.hlsl"
#pragma vertex Vert // Vertex Shader 선언
#pragma fragment Frag // Fragment Shader 선언
ENDHLSL
}
}
}
OutlineShader/Fullscreen
- 화면 전체에 외곽선 효과를 적용하는 후처리 셰이더이다.
Shader "OutlineShader/Fullscreen"
{
properties
{
_Iterations ("Iterations", Range(1,3) ) = 1 // 외곽선 효과의 반복 횟수
_OutlineWidth ("Outline Width", Float ) = 5 // 외곽선 너비
_InsideColor ("Inside Color", Color) = (1, 1, 0, 0.5) // 내부 색상
}
HLSLINCLUDE
#pragma vertex Vert // Vertex Shader 선언
#pragma target 4.5
#pragma only_renderers d3d11 ps4 xboxone vulkan metal switch
#include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl"
#define v2 1.41421 // √2
#define c45 0.707107 // cos(45°)
#define c225 0.9238795 // cos(22.5°)
#define s225 0.3826834 // sin(22.5°)
// 외곽선 탐지를 위한 Offset (화면 상의 다양한 방향으로 샘플링)
#define MAXSAMPLES 16
static float2 offsets[MAXSAMPLES] = {
float2( 1, 0 ),
float2( -1, 0 ),
float2( 0, 1 ),
float2( 0, -1 ),
float2( c45, c45 ),
float2( c45, -c45 ),
float2( -c45, c45 ),
float2( -c45, -c45 ),
float2( c225, s225 ),
float2( c225, -s225 ),
float2( -c225, s225 ),
float2( -c225, -s225 ),
float2( s225, c225 ),
float2( s225, -c225 ),
float2( -s225, c225 ),
float2( -s225, -c225 )
};
int _Iterations;
float _OutlineWidth;
float4 _InsideColor;
// Fragment Shader: 화면 전체 패스를 처리
float4 FullScreenPass(Varyings varyings) : SV_Target
{
// 스테레오 렌더링 설정
UNITY_SETUP_STEREO_EYE_INDEX_POST_VERTEX(varyings);
// 깊이 정보를 로드
float depth = LoadCameraDepth(varyings.positionCS.xy);
// 화면 좌표와 카메라 정보를 기반으로 PositionInputs 계산
PositionInputs posInput = GetPositionInput(varyings.positionCS.xy, _ScreenSize.zw, depth, UNITY_MATRIX_I_VP, UNITY_MATRIX_V);
// 뷰 방향 벡터 계산 (카메라 방향)
float3 viewDirection = GetWorldSpaceNormalizeViewDir(posInput.positionWS);
// 현재 픽셀의 컬러를 로드
float4 c = LoadCustomColor(posInput.positionSS);
// 각 픽셀당 UV 오프셋 계산
float2 uvOffsetPerPixel = 1.0/_ScreenSize.xy;
// 샘플링할 픽셀 수 계산 (최대 MAXSAMPLES까지)
uint sampleCount = min( 2 * pow(2, _Iterations ), MAXSAMPLES );
// 외곽선 정보를 저장할 변수 초기화
float4 outline = float4(0,0,0,0);
// 각 오프셋 방향으로 샘플링하여 외곽선을 탐색
for (uint i=0 ; i<sampleCount ; ++i )
{
outline = max( SampleCustomColor( posInput.positionNDC + uvOffsetPerPixel * _OutlineWidth * offsets[i] ), outline );
}
// 결과 색상 초기화
float4 o = float4(0,0,0,0);
// 샘플링된 외곽선 색상을 적용
o = lerp(o, float4(outline.rgb, 1), outline.a);
// 외곽선이 없는 부분에 내부 색상 적용
o = lerp(o, _InsideColor, c.a);
return o;
}
ENDHLSL
SubShader
{
Pass
{
Name "Custom Pass 0"
ZWrite Off // 깊이 쓰기 비활성화
ZTest Always // 항상 렌더링 (깊이 테스트 없음)
Blend SrcAlpha OneMinusSrcAlpha// 알파 블렌딩 설정
Cull Off // 컬링 비활성화 (양면 렌더링)
HLSLPROGRAM
#pragma fragment FullScreenPass // Fragment Shader 선언
ENDHLSL
}
}
Fallback Off
}