Search
Duplicate

Unity/ Surface Shader 작성법

Surface Shader

Vertex-Fragment Shader보다 좀 더 높은 수준에서 작성되는 셰이더. Vertex-Fragment 에서 번거롭고 복잡하게 작성해야 하는 것 –라이팅 계산 등– 들을 프로그램 내부에서 자동으로 처리하고, 프로그래머는 직관적인 속성들만 제어하면 되도록 정의되어 있다. –물론 그 자동으로 처리되는 부분을 커스터마이징 하고자 한다면 할 수 있다.
Surface Shader는 유니티에서 주로 사용하는 셰이더이다. 처음 셰이더 파일을 만들면 Default 로 들어가 있는 코드가 서피스 셰이더로 작성되어 있음.

파이프라인

Surface Shader에서는 주로 Surface Function을 이용하여 재질을 제어하는 코드를 작성하지만, 필요하다면 Vertex를 제어하거나 Lighting을 제어하는 코드도 만들 수 있다. 위 이미지는 그 3가지 프로세스의 순서를 나타내고 있다.

Unity에서의 작성법

SubShader { CGPROGRAM #pragma surface surf Lambert struct Input { float4 color : COLOR; }; void surf (Input IN, inout SurfaceOutput o) { o.Albedo = 1; } ENDCG }
GLSL
복사
가장 기본적인 형태의 Surface Shader 코드 형태. 재질에 대한 속성만 정의해주면 효과가 적용되기 때문에 Vertex-Fragment Shader에 비해 매우 간결한 구성을 갖고 있다.
Vertex-Fragment와 달리 pass가 없는게 특징인데, 만일 여러개의 pass를 그리고 싶다면 CGPROGRAM-ENDCG 자체를 여러번 작성하면 된다.
#pragma 다음에 surface가 나오면 Surface Shader를 사용한다는 의미가 된다. surface 다음에 나오는 surf는 Surface Function의 이름이 된다. 자신의 마음대로 써도 무방하지만 일반적으로는 surf 를 사용한다. 마치 vertex function의 이름은 vert, fragment function의 이름은 frag라고 쓰는 것과 비슷함.
surface 함수에서 받을 인자는 구조체 형식으로 선언한다. 위 코드의 Input 구조체에서 필요한 값들을 정의해 두면 surface 함수에서 해당 값을 받아 사용할 수 있다.
vertex-fragment 함수가 최종 결과값을 return 하는 것과 달리 surface 함수는 inout을 이용하여 값을 참조 형식으로 사용한다.
#pragma 맨 마지막에 써 있는 Lambert는 현재 Surface Shader에서 사용할 라이팅 방식을 의미한다. 미리 정의되어 있는 라이팅을 사용한다면 라이팅과 관련한 별다른 코드를 작성하지 않고도 라이팅 효과를 얻을 수 있다. 커스텀 라이팅을 사용할 경우 Labert 대신 커스텀 라이팅을 처리할 함수의 이름을 쓰면 된다.
SubShader { Tags { "RenderType" = "Opaque" } CGPROGRAM #pragma surface surf Lambert vertex:vert sampler2D _MainTex; float _Amount; struct Input { float2 uv_MainTex; }; void vert (inout appdata_full v) { v.vertex.xyz += v.normal * _Amount; } void surf (Input IN, inout SurfaceOutput o) { o.Albedo = tex2D (_MainTex, IN.uv_MainTex).rgb; } ENDCG }
GLSL
복사
Surface Shader에서 Vertex를 제어해야 하는 경우 #pragma 부분에 vertex를 제어하는 함수를 선언하면 된다. vertex는 버텍스 제어를 한다는 의미고 : 뒤에 사용하는 이름이 vertex function의 이름이 된다.
Vertex Shader와 달리 Suraface Shader의 vertex 함수는 최종 결과값을 inout을 이용하여 참조 형식으로 사용한다.
vertex 함수에서 받는 appdata_full은 유니티 내부에 정의되어 있는 형식이다.
SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Phong sampler2D _MainTex; float4 _MainTint; struct Input { float2 uv_MainTex; }; void surf (Input IN, inout SurfaceOutput o) { half4 c = tex2D (_MainTex, IN.uv_MainTex) * _MainTint; o.Albedo = c.rgb; o.Alpha = c.a; } inline fixed4 LightingPhong (SurfaceOutput s, fixed3 lightDir, half3 viewDir, fixed atten) { // 라이팅 처리 return c; } ENDCG }
GLSL
복사
커스텀 라이팅을 사용하려면 #pragma 영역에 사용하고자 하는 커스텀 라이팅 함수의 이름을 쓰면 된다. 그런데 여기에 쓴 커스텀 라이팅 함수의 이름이 바로 라이팅 함수의 이름이 되지는 않고 ‘Lighting+(라이팅함수이름)’ 처럼 앞에 Lighting이 붙는 형식으로 함수 이름이 정해진다.
Surface Shader 파이프라인 상 라이팅은 가장 마지막에 그려지기 때문에 surface 함수에서 사용한 SurfaceOutput을 받아 처리하게 된다. surface 함수와 vertex 함수와 달리 float4, half4, fixed4 등의 반환값을 갖는 형태로 작성된다.

참고 자료