Surface Shader

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

Surface Shader는 유니티에서 주로 사용하는 셰이더이다. 처음 셰이더 파일을 만들면 Default 로 들어가 있는 코드가 서피스 셰이더로 작성되어 있음.

파이프라인

https://i1.wp.com/www.alanzucconi.com/wp-content/uploads/2015/06/Surface-shader.png?resize=640%2C297

http://www.alanzucconi.com/2015/06/17/surface-shaders-in-unity3d/

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
}

가장 기본적인 형태의 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
}

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
}