카테고리 없음

Unity_URPShader_HLSL_코드구조

zelkova 2021. 2. 4. 10:30

 

<목차로 돌아가기>

 

Shader Lab코드구조
// 쉐이더 이름이라고 생각하면 된다.
// 특수문자 / 으로 그룹을 구분한다.
Shader "Example/URPUnlitSimpleVertex" {
    Tags { 
        // Tag는 실행되는 시기와 조건을 정의한다.
    }
    Properties {
        // 속성 값으로 다양한 종류의 피라미터를 넣을 수 있습니다.
        // 참조를 위한 이름과 표시될 이름을 작성하고 값을 입력합니다.
    	// 텍스쳐, 색, 백터 등 다른 값들을 여기에 배치함
        // 유니티 Inspector 창에서 입력값을 조절할 수 있는 값 지정
    }
    SubShader {
        // 여기에 3가지 유형의 세이더를 작성합니다.
        // 표면세이더, 프로그먼트 쉐이더, 고정쉐이더
    }
    SubShader {
    	// SubShader를 추가할 수 있다.
        // 아래에 추가할수록 하위 버전의 디바이스를 위한 쉐이더이다.
        // 오래된 그래픽 카드에서 돌아갈 수 있도록 하기위해서 작성
        Pass {
           // 물체를 그릴 때 GPU에게 넘겨줄(pass) 작업을 기술하는 곳.
           // pass가 두개이상이면 멀티패스라고 부르며 두 번에 걸쳐 그린다.
        }
    }
    Fallback "Diffuse" // 하위버전마저 힘들다면, 가장 낮은 품질로 처리.
}

 

쉐이더 이름 바꾸기

Shader "Example/URPUnlitSimpleVertex" 이 부분이 유니티에서 아래와 같이 나온다.

Tag

 

Tags {
   //투명도가 없음(Opaque)
   "RenderType" = "Opaque" 
   //파이프라인 대상
   "RenderPipeline" = "UniversalRenderPipeline" 
   //랜더링 대기순서
   "Queue" = "Background" 
}

 

Properties

Properties는 변수를 지정하는 곳이다.

변수명 ("인스펙터에 보여지는 이름", 데이터타입) = 데이터 타입에 초기 값

Shader "Example/URPUnlitSimpleVertex" {
    Properties {
        _MyColor ("Green Color", Color) = (0, 1, 0, 1)
        _VertexCount ("display name", Int) = number
        _MaxValue ("display name", Vector) = (number,number,number,number)
        _MyTexture ("My Texture", 2D) = "white" { }
    }
}

 

SubShader

 

Shader "Example/URPUnlitSimpleVertex" {
    SubShader{
        Pass {
            // vertex를 정의 (점의 위치를 결정)
            #pragma vertex vert
            // fragment를 정의 (픽셀의 색상을 결정)
            #pragma fragment frag
            
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            
            // 정점 셰이더 입력 구조
            struct Attributes {
                float4 positionOS : POSITION;
                float3 normalOS   : NORMAL;
            };
        }
    }
}

▷ SubShader

여러개를 지정할 수 있습니다.

이를 통하여 다른 빌드 데스크톱(Direct3D), MAC(OpenGL), 모바일용(OpenGL ES), 안드로이드(vulkan), 아이폰(Metal)등의 플랫폼에 대응할 수 있습니다.

또한 Direct 9, DirectX 10과 같이 다른 버전이라도 SubShader을 추가하여 대응할 수 있습니다.

Unity에서 컴파일할 타겟을 설정하거나 볼 수 있습니다.

▷ Pass

실질적으로 랜더링되는 영역입니다.

여러개의 Pass를 가질 수 있지만 여러번 랜더링 되므로 더 많은 자원을 소모합니다.

 

▷ #paragma

paragma는 그리스어에서 유래했으며  유니티에서 스니핏(snippet)이라고도 불립니다.

조치 또는 수행해야 할 일을 나타냅니다.

셰이더의 조명 계산 설정, 기타 세부적인 분기를 정해주며 전처리(Preprocessing)라고 할 수 있습니다.

 

▷ #include

자주 사용되는 HLSL 매크로와 함수를 현재 작성하는 스크립트에 넣는 기능입니다.
다른 HLSL파일에 대한 참조도 포함되어 있습니다.

- UnityShaderVariables.cginc

변환, 카메라 및 조명 데이터와 같이 렌더링에 필요한 전체 셰이더 변수를 정의합니다.필요한 경우 모두 Unity에서 설정합니다. 

 

- HLSLSupport.cginc

대상 플랫폼에 관계없이 동일한 코드를 사용할 수 있도록 설정합니다. 따라서 플랫폼 별 데이터 유형 등을 사용하는 것에 대해 걱정할 필요가 없습니다.

 

- UnityInstancing.cginc

드로 콜을 줄이기위한 특정 렌더링 기술인 인스턴스 지원을위한 것입니다. 파일을 직접 포함하지는 않지만 UnityShaderVariables에 따라 다릅니다 .

 

▷ struct

하나이상의 변수를 묶어서 새로운 자료형을 정의합니다.

 

예제1

기본 셰이더 값은 아래와 같습니다.

메쉬를 흰색으로 랜더링 합니다.

Render Queue는 Geometry의 기본값인 2000

양면 전역 조명을 활성화하는 토글이 있습니다.

 

더보기

 

Shader "StudySRP/Catlike1"
{
	Properties{}

	SubShader{
		Pass {}
	}
}

 

예제2

 

SV_TARGET과 SV_POSITION은 랜더 대상에 대한 시스템 기본값을 제공합니다.

현재 모든 정점이 0으로 설정되면 메시가 한 지점으로 축소되고 아무것도 렌더링되지 않습니다.

 

SV는 시스템 값을 나타내고 Position은 최종 정점위치를 나타냅니다.

어디서 가져오는지 궁금하다면 아래쪽에서 확인가능합니다.

 {unity 설치 경로} /Data/CGIncludes/HLSLSupport.cginc 

 

더보기
Shader "StudySRP/Catlike1"
{
	Properties{}

	SubShader{
		Pass {
			HLSLPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#include "Catlike1Pass.hlsl"

				#ifndef CUSTOM_UNLIT_PASS_INCLUDED
					#define CUSTOM_UNLIT_PASS_INCLUDED	
				#endif

				float4 vert() : SV_POSITION {
					return 0.0;
				}
	
				float4 frag() : SV_TARGET{
					return 0.0;
				} 
			ENDHLSL
		}
	}
}

 

예제3

float3 positionOS : POSITION를 통하여 공간위치를 나타내면 위와 같습니다.

출력한 위치가 잘못된 공간에 있기때문에 이렇게 출력됩니다.

더보기
Shader "StudySRP/Catlike1"
{
	Properties{}

	SubShader{
		Pass {
			HLSLPROGRAM
				#pragma vertex vert
				#pragma fragment frag
				#include "Catlike1Pass.hlsl"

				#ifndef CUSTOM_UNLIT_PASS_INCLUDED
					#define CUSTOM_UNLIT_PASS_INCLUDED	
				#endif

				float4 vert(float3 positionOS : POSITION) : SV_POSITION {
					return float4(positionOS, 1.0);
				}
	
				float4 frag() : SV_TARGET{
					return 0.0;
				} 
			ENDHLSL
		}
	}
}

 

예제4

 

정확하게 그리려면 무언가가 그려질 때 GPU로 전송되는 행렬이필요합니다.

 

// 객체 행렬을 가져오고

float4x4 unity_ObjectToWorld; 

 

//객체를 월드 공간으로 변환시킵니다.

float3 TransformObjectToWorld (float3 positionOS) {
    return mul(unity_ObjectToWorld, float4(positionOS, 1.0)).xyz;
}

 

// 월드공간에 투영 행렬을 곱하여 보여지는 정확한 좌표를 구합니다.

float4 TransformWorldToHClip (float3 positionWS) {
    return mul(unity_MatrixVP, float4(positionWS, 1.0));
}

더보기
Shader "StudySRP/Catlike1"
{
  Properties{}

  SubShader{
    Pass {
      HLSLPROGRAM
        #ifndef CUSTOM_UNITY_INPUT_INCLUDED
        #define CUSTOM_UNITY_INPUT_INCLUDED
        float4x4 unity_ObjectToWorld;

        float4x4 unity_MatrixVP;
        #endif

        #ifndef CUSTOM_COMMON_INCLUDED
        #define CUSTOM_COMMON_INCLUDED
        #include "UnityInput.hlsl"

        float3 TransformObjectToWorld(float3 positionOS) {
          return mul(unity_ObjectToWorld, float4(positionOS, 1.0)).xyz;
        }

        float4 TransformWorldToHClip(float3 positionWS) {
          return mul(unity_MatrixVP, float4(positionWS, 1.0));
        }
        #endif

        #pragma vertex vert
        #pragma fragment frag
        #include "Catlike1Pass.hlsl"

        #ifndef CUSTOM_UNLIT_PASS_INCLUDED
          #define CUSTOM_UNLIT_PASS_INCLUDED	
        #endif

        #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"

        float4 vert(float3 positionOS : POSITION) : SV_POSITION {
          float3 positionWS = TransformObjectToWorld(positionOS.xyz);
          return TransformWorldToHClip(positionWS);
        }
	
        float4 frag() : SV_TARGET{
          return 0.0;
        } 
      ENDHLSL
    }
  }
}

 

예제5

이제까지 정의한 기능은 이미 유니티에서 구현되어 있습니다 ^-^. (나만 삽질할 수없지)

float3 TransformObjectToWorld(float3 positionOS) {
     return mul(unity_ObjectToWorld, float4(positionOS, 1.0)).xyz;
}

float4 TransformWorldToHClip(float3 positionWS) {
      return mul(unity_MatrixVP, float4(positionWS, 1.0));
}

위의 함수를 정의를 지우고 SpaceTransform.hlsl를 추가합니다.

#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/SpaceTransforms.hlsl"

 

위의 변수를 지우고 아래의 오브젝트 정의를 추가합니다.

#define UNITY_MATRIX_M unity_ObjectToWorld

 

그리고 역행렬을 대신할 정의를 더 추가해야합니다. 사용하지 않더라도 추가안하면 에러 ㅠㅠ

#define UNITY_MATRIX_I_M unity_WorldToObject 
#define UNITY_MATRIX_V unity_MatrixV
#define UNITY_MATRIX_VP unity_MatrixVP
#define UNITY_MATRIX_P glstate_matrix_projection

 

이제 정의된 값에 대응한 변수를 추가해야합니다.

float4x4 unity_ObjectToWorld;
float4x4 unity_WorldToObject;
real4 unity_WorldTransformParams; 

float4x4 unity_MatrixVP;
float4x4 unity_MatrixV;
float4x4 glstate_matrix_projection;

 

여기서 뜬금없이 real4 라는 정의 벡터가 나오는데 이에 대하여 궁금하다면 아래의 경로를 살펴보면 됩니다.

Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl

 

더보기
Shader "StudySRP/Catlike1"
{
	Properties{}

	SubShader{
		Pass {
			HLSLPROGRAM
				#include "Catlike1Pass.hlsl"
				#include "UnityInput.hlsl"
				#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Common.hlsl"

				#define UNITY_MATRIX_M unity_ObjectToWorld
				#define UNITY_MATRIX_I_M unity_WorldToObject
				#define UNITY_MATRIX_V unity_MatrixV
				#define UNITY_MATRIX_VP unity_MatrixVP
				#define UNITY_MATRIX_P glstate_matrix_projection

				#ifndef CUSTOM_UNITY_INPUT_INCLUDED
					#define CUSTOM_UNITY_INPUT_INCLUDED
					float4x4 unity_ObjectToWorld;
					float4x4 unity_WorldToObject;
					real4 unity_WorldTransformParams;

					float4x4 unity_MatrixVP;
					float4x4 unity_MatrixV;
					float4x4 glstate_matrix_projection;
				#endif

				#include "Packages/com.unity.render-pipelines.core/ShaderLibrary/SpaceTransforms.hlsl"
				
				#pragma vertex vert
				#pragma fragment frag

				#ifndef CUSTOM_UNLIT_PASS_INCLUDED
					#define CUSTOM_UNLIT_PASS_INCLUDED	
				#endif


				float4 vert(float3 positionOS : POSITION) : SV_POSITION {
					float3 positionWS = TransformObjectToWorld(positionOS.xyz);
					return TransformWorldToHClip(positionWS);
				}
	
				float4 frag() : SV_TARGET{
					return 0.0;
				} 
			ENDHLSL
		}
	}
}

 

 

 

HLSL 기본예제 사용해보기.

 

Shader "Example/URPUnlitSimpleVertex"
{
    Properties{ 
    	//인스펙터에 색상지정아이콘 띄어주고 초록색을 설정
        _MyColor("Green Color", Color) = (0, 1, 0, 1)
        //익스펙터에 슬라이드바를 띄어주고 범위는 -0.001부터 0.001까지 기본값은 0
        _Amount("Extrusion", Range(-0.001,0.001)) = 0
    }

    SubShader{
        Tags {
         "RenderType" = "Opaque" 
         "RenderPipeline" = "UniversalRenderPipeline" 
         "Queue" = "Background" 
        }

        Pass{
            //HLSL 시작을 선언하고
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"            

            // Properties에서 선언한 변수들 HLSL에서 사용할 수 있게 할당
            CBUFFER_START(UnityPerMaterial)
                half4 _MyColor;
                half _Amount;
            CBUFFER_END

            // 정점 셰이더 입력 구조
            struct Attributes {
                float4 positionOS : POSITION;
                float3 normalOS   : NORMAL;
            };
            
            // 정점 출력데이터 출력 구조
            struct Varyings{
                float4 positionHCS  : SV_POSITION;
                float3 normalWS   : NORMAL;
            };

            // vertex(점) 위치 정의하기
            Varyings vert(Attributes IN){
                // OUT객체 선언.
                Varyings OUT;
                // 객체 공간에서 균질공간으로 변환
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                // 변환된 값 반환.
                return OUT;
            }

            half4 frag() : SV_Target{
                // 색상을 정의하고 
                half4 customColor;
                // 색상 집어넣기.
                customColor = _MyColor;
                // 색상 반환하여 적용.
                return customColor;
            }
            ENDHLSL
        }
    }
}

 

 

 

반응형