본문 바로가기
유니티/Shader Study

URP Shader Study #6 - Transparent Shader + Culling

by witn331ss 2023. 12. 2.

Transpaent 부터는 유니티가 자동으로 정렬해주지 않아서

사용자가 직접 정렬을 해줘야만 한다.

 

겹치는 픽셀을 픽셀을 어떻게 그려야하는지에 대한 선언을 해줘야한다.

 

프로퍼티스에 제어해줄 것을 꺼내준다

       _MainTex("Main Texture", 2D) = "white" {}
       _AlphaCut("Alphacut", Range(0,1)) = .5

 

그 다음 Tags 에서 이것은 Transpaent 이라고 알려주자

	   "RenderPipeline"="UniversalPipeline"
                "RenderType"="Transparent"          
                "Queue"="Transparent"

 

인풋 어셈블리 레벨에서 UV 쓸거니까 선언해주고

               float2 uv : TEXCOORD0;

 

지금 필요한 변수들을 꺼내어보자.

            float _AlphaCut;
            float4 _MainTex_ST;
            Texture2D _MainTex;

            SamplerState sampler_MainTex;

 

버텍스셰이더 내에서 UV를 지정해주고

            o.uv = v.uv.xy * _MainTex_ST.xy + _MainTex_ST.zw;

 

이제 픽셀셰이더에서 텍스쳐를 넣어주고 

텍스쳐의 알파 값에 텍스쳐알파 *알파컷(float) 을 넣어주자

                half4 tex01 = _MainTex.Sample(sampler_MainTex, i.uv);
                tex01.a = tex01.a * _AlphaCut;
                

                return tex01;

 

이 코드들로는 당장 Range를 움직여도 반투명으로 움직이진 않는다.

이제 이것을 선언해주어야 한다.

 

##

 

잠깐 이론을 보고 넘어가자.

 

Transpaent 는 모든 셰이더가 실행되고, 모든 텍스쳐가 적용된 후 픽셀이 화면에 작성되게 되며,

이런 픽셀이 기존 픽셀에 결합되는 방법은 Blend command(operation)으로 제어하게 된다.

블렌딩 과정은 아래와 같이 알파테스트가 수행한 이후 진행되게 된다.

 

 

Blend Operation을 선언해줄건데

Blend operation은 해당 픽셀을 그릴때 앞에 그려진 픽셀과 현재 픽셀을 어떻게 계산할 것인지를 선언하는 과정이다. 

 

패스에 이런식으로 선언할 것이다.

 

Src(Source)는 계산된 컬러를 말하고

Dst(Destination)은 이미 화면에 표시된 컬러를 말한다. 

 

화면에 그려진 앞매쉬랑 뒷매쉬를 어떻게 블렌드 해줄 것이냐? 정도로 생각하면 된다.

Pass
   {  
  Blend SrcFactor DstFactor

  Name "Universal Forward"
  Tags {"LightMode" = "UniversalForward"}

 

 

당장 한번 활용해본다면 Pass 영역에 이걸 추가해보자

 	Pass
    	{  		
              Blend SrcAlpha OneminusSrcAlpha
     	      Name "Universal Forward"
              Tags { "LightMode" = "UniversalForward" }

 

 

아까 지정해줘서 RenderQueue 도 3000으로 Transparent를 그려줄 수 있게 되었고

실제로 반투명해진 머터리얼을 확인할 수 있게 되었다 (헉)

 

 

 

이제 이 텍스쳐를 가지고 몇가지 실험을 해보자.

 

 

Shader에서 Enum으로 api를 불러와 제어할 수 있다. 프로퍼티스에 아래와 같이 추가한다.

[Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend ("Src Blend", Float) = 1
[Enum(UnityEngine.Rendering.BlendMode)] _DstBlend ("Dst Blend", Float) = 0

 

그 다음 pass를 다음과 같이 변경해준다

	Pass
    	{  		
              Blend [_SrcBlend] [_DstBlend]
     	      Name "Universal Forward"
              Tags { "LightMode" = "UniversalForward" }

 

그렇게 되면 이제 안스펙터창에서 조정이 가능해진다

 

색 강조를 하고 싶다면 이런식으로 추가해줄수있겠다.

프로퍼티스
    _Intensity("Intensity", Range(1,20)) = 1
    
    
함수지정
            float _Intensity;


픽셀셰이더 계산식추가

			tex01.rgb = tex01.rgb * _Intensity;

 

하여튼 인자값은 다음과 같다

 

여기서 SrcAlpha 즉 소스알파는 텍스쳐가 가지고있는 알파값을 얘기한다.

나머지는 대상값을 그대로 사용한다.

 

 

자주 사용하는 것들은 이것들이다.

Blend SrcAlpha OneMinusSrcAlpha // Traditional transparency, 알파채널의 있는 알파값을 쓰는것
Blend One OneMinusSrcAlpha		// Premultiplied transparency, 트렌스페어런트의 두배 곱한것
Blend One One 					// Additive, 밝게 타오르는것
Blend OneMinusDstColor One 		// Soft Additive, 재곧내
Blend DstColor Zero 			// Multiplicative, 재곧내
Blend DstColor SrcColor			// 2x Multiplicative, 어둡게곱함

 

 

Culling

컬링은 화면에 그려지는 삼각형의 앞,뒷면을 그릴지 말지를 결정하는 방법이다.

기본값은 면이 보여지는 기본 방향 (Normal Vector)을 그리지만

반대로 뒷면만을 혹은 앞뒤 모두를 그릴 수도 있다.

 

Pass
   {  
    Blend SrcFactor DstFactor
Cull Back //뒷면을 죽인다
	
Name "Universal Forward"

##

Pass
   {  
    Blend SrcFactor DstFactor
Cull Front //앞면을 죽인다
	
Name "Universal Forward"


##

Pass
   {  
    Blend SrcFactor DstFactor
Cull off // 둘다 볼래
	
Name "Universal Forward"

 

물론 이것도 똑같이 프로퍼티스에서 제어해 줄 수 있는 부분이다 ㅋ

[Enum(UnityEngine.Rendering.CullMode)] _Cull ("Cull Mode", Float) = 1

 

Pass 에서는 다음과 같이 서술해준다

 Cull [_Cull]

 

 

##

 

 

 

참고코드

// Shader 시작. 셰이더의 폴더와 이름을 여기서 결정합니다.
Shader "URPTraining/URPBasic"
{


   Properties
             {   
// Properties Block : 셰이더에서 사용할 변수를 선언하고 이를 material inspector에 노출시킵니다
       _MainTex("Main Texture", 2D) = "white" {}
       _AlphaCut("Alphacut", Range(0,1)) = .5
       _Intensity("Intensity", Range(1,20)) = 1

           [Enum(UnityEngine.Rendering.BlendMode)] _SrcBlend("Src Blend", Float) = 1
           [Enum(UnityEngine.Rendering.BlendMode)] _DstBlend("Dst Blend", Float) = 0

           [Enum(UnityEngine.Rendering.CullMode)] _Cull("Cull Mode", Float) = 1

           	}  

	SubShader
	{  

	Tags
            {
//Render type과 Render Queue를 여기서 결정합니다.
	   "RenderPipeline"="UniversalPipeline"
                "RenderType"="Transparent"          
                "Queue"="Transparent"
            }
    	Pass
    	{  		
              Blend [_SrcBlend] [_DstBlend]
              Cull [_Cull]

     	      Name "Universal Forward"
              Tags { "LightMode" = "UniversalForward" }

       	HLSLPROGRAM

        	#pragma prefer_hlslcc gles
        	#pragma exclude_renderers d3d11_9x
        	#pragma vertex vert
        	#pragma fragment frag

//cg shader는 .cginc를 hlsl shader는 .hlsl을 include하게 됩니다.
       	#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"        	
  
//vertex buffer에서 읽어올 정보를 선언합니다. 	
         	struct VertexInput
         	{
            	float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
          	};

//보간기를 통해 버텍스 셰이더에서 픽셀 셰이더로 전달할 정보를 선언합니다.
        	struct VertexOutput
          	{
           	float4 vertex  	: SV_POSITION;
            float2 uv : TEXCOORD0;

      	};

            float _AlphaCut;
            float _Intensity;

            float4 _MainTex_ST;
            Texture2D _MainTex;

            SamplerState sampler_MainTex;

//버텍스 셰이더
      	VertexOutput vert(VertexInput v)
        	{

          	VertexOutput o;      
          	o.vertex = TransformObjectToHClip(v.vertex.xyz);
            o.uv = v.uv.xy * _MainTex_ST.xy + _MainTex_ST.zw;

         	return o;
        	}

//픽셀 셰이더
        half4 frag(VertexOutput i) : SV_Target
        {
                half4 tex01 = _MainTex.Sample(sampler_MainTex, i.uv);
                tex01.rgb = tex01.rgb * _Intensity;
                tex01.a = tex01.a * _AlphaCut;
                

                return tex01;
       	
        	}

        	ENDHLSL  
    	}
     }
}