이번 포스팅부터는 Unity상에서 Surface Shader의 PBR쉐이더가 아닌 구형 라이트 쉐이더를 이용하여

자체 라이팅 쉐이더 제작을 포스팅한다.

 

조명연산을 하기 위해 Vector의 이론, 기본 연산법을 알아보고 Unity상에서 PBR쉐이더가 아닌 구형 라이팅 쉐이더를 

이용하여 Custom Light를 구현한다.

 

Vector

Vector란? : 일반적으로 방향의 정보를 가진 데이터를 Vector라고 표현한다.

 

Vector의 종류

  1. 영벡터(Zero Vector) : 모든 성분이 0인 벡터이며 (0,0,0)으로 표현한다.

  2. 표준기저벡터(Standard basis) : x, y, z축을 따라 진행되는 벡터로 1의 크기를 가진다.

  일반적으로 i = (1, 0, 0), j = (0, 1, 0), k = (0, 0, 1)로 표현한다.

  3. 단위 벡터(Unit Vector) : 벡터의 크기가 1인 벡터라고 하고 기존 벡터의 의미는 방향만 존재하므로 단위 벡터로 변

  경 해도 결과는 같다, 이러한 과정을 정규화(Normalize)라고 한다

 

Vector 연산

 

  1. 벡터 덧셈

https://mrw0119.tistory.com/10

동일한 차원을 가진 두 벡터는 각 값끼리 더하여 결과가 나온다.

 

 2. 벡터 뺼셈

https://mrw0119.tistory.com/10

덧셈과 마찬가지로 동일한 차원을 가진 두벡터는 각 값끼리 뺄셈하여 결과가 나온다.

 

 3. 스칼라 곱

스칼라란 벡터와 달리 크기만 있고 방향이 없는 값이다.

그래서 벡터는 스칼라로 곱하는 것이 가능하다. 양수를 곱하는 경우 벡터의 방향은 변하지 않지만

음수를 곱하는 경우 벡터의 방향이 뒤집힌다.

 

 4. 내적(Dot Product)

두 벡터의 내적을 구한다는 의미는 두 벡터의 내각의 cos값을 구한다는 것이다.

여기서 나온 내적 값에 acos를 계산하면 라디안(Radian)으로 변환된 각도가 나온다. 결과적으로 이 라디안 값을 

디그리(Degree)값으로 변환하면 각도를 구할 수 있게 된다.

 

 5. 외적(Cross Product)

두 벡터에 직교하는 벡터를 구할 수 있고 이 벡터를 법선 벡터라고 한다.

벡터의 외적은 내적과 달리 곱하는 순서에 따라 결과가 달라진다, u와 v 위치에 따른 결과는 다른 값이다.

 

 

구형 라이팅 모델(Legacy lighting model) 

#pragma surface surfaceFunction lightModel [optionalparams]

Unity에서 기본 Standard Surface Shader파일을 만든 후에 위 코드의 lightModel을 조작함으로서 

비-물리기반의 Lambert(diffuse)BlinnPhong(Specular)나 물리기반의 StandardStandardSpecular로 라이팅 모델을 세팅할 수 있다.

 

앞서 설명한 Standard와 StandardSpecular 라이팅 모델은 Unity4 이후로 등장한 PBS 라이팅 모델이며 

이전의 비-물리기반은 구형 라이팅모델로 가볍고 기능이 적어서 현재까지 낮은 사양을 목표로 할때 사용되기도 한다.

이번 장에서는 비-물리기반 라이팅모델에 대해 설명하고 실제 Unity에서 사용해 보겠다.

 

Lambert : 표면이 반사되는 빛의 강도는 보는 방향과 관계없이, 표면과 빛의 방향에 의해 영향 받는 개념으로 램버트 반사 or 램버트 코사인 법칙이라 불린다.

 

BlinnPhong : 최초에 Phong에 의해 제안된 방법으로 반사광과 카메라 벡터가 이루는 각도의 코사인 값을 구하고, 그 결과를 여러번 거듭제곱하여 Specular(정반사)를 구할 수 있는 개념으로 이후 Jim Blinn이라는 사람으로 인해 개량되서 

BlinnPhong이라는 이름의 라이팅 모델로 완성 되었다.

 

간단 요약으로 Diffuse 라이팅만 사용하는 Lambert가 있고, Diffuse + Specular를 사용하는 BlinnPhong 모델이 있다.

 

Lambert 라이팅모델 쉐이더 

Shader "Custom/Cus_Lambert"
{
    Properties
    {
		_MainTex("Albedo (RGB)", 2D) = "white" {}
		_Color("Color", Color) = (1,1,1,1)
	}
	SubShader
	{
		Tags { "RenderType" = "Opaque" }

		CGPROGRAM

		#pragma surface surf Lambert noambient

		sampler2D _MainTex;
		float4 _Color;
		struct Input
		{
			float2 uv_MainTex;
		};


    void surf (Input IN, inout SurfaceOutput o)
    {
		fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
        o.Albedo = c.rgb;
          
    }
    ENDCG
    }
    FallBack "Diffuse"
}

Unity에서 Standard Surface Shader를 제작 후 기초 코드만 남긴 후 Lambert 라이팅모델 쉐이더를 만들었다.

 

#pragma surface surf Lambert noambient

#pragma 컴파일 지시자에서 위 코드로 수정하여 Lambert 라이팅모델을 사용하고 환경광 영향 받지 않는 코드를

작성한다.

 

void surf (Input IN, inout SurfaceOutput o)

surf함수의 인자값에 SurfaceOutputStandard에서 SurfaceOutput으로 변경한다.

기존 PBS 구조체에서 N-PBS구조체로 변경하는 것이다.

 

struct SurfaceOutput {

    fixed3 Albedo; // 기본색상

    fixed3 Normal; // 반사각을 결정하는 면의 방향

    fixed3 Emission;

    half Specular; // 정반사 강도

    fixed Gloss; // Specular 반사가 퍼지는 정도

    fixed Alpha; // alpha for transparencies

};

 

Blinn Phong 라이팅모델 쉐이더 

Shader "Custom/Cus_BlinnPhong"
{
	Properties
	{
		_MainTex("Albedo (RGB)", 2D) = "white" {}
		_Color("Color", Color) = (1,1,1,1)
		_SpecColor("SpecColor", Color) = (1,1,1,1)
	}
		SubShader
		{
			Tags { "RenderType" = "Opaque" }

			CGPROGRAM

			#pragma surface surf BlinnPhong noambient

			sampler2D _MainTex;
			float4 _Color;
			struct Input
			{
				float2 uv_MainTex;
			};


		void surf(Input IN, inout SurfaceOutput o)
		{
			fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
			o.Albedo = c.rgb;
			o.Specular = 1.0f * _SpecColor;
			o.Gloss = 1.0f;

		}
		ENDCG
		}
			FallBack "Diffuse"
}

Diffuse만 처리하는 Lambert와 다르게 Specular(정반사)까지 표현되는 BlinnPhong 라이트 모델 쉐이더를 완성했다.

 

#pragma surface surf BlinnPhong noambient

Lambert와 마찬가지로 컴파일 지시자에서 BlinnPhong을 세팅한다.

 

Properties
{
  _MainTex("Albedo (RGB)", 2D) = "white" {}
  _Color("Color", Color) = (1,1,1,1)
  _SpecColor("SpecColor", Color) = (1,1,1,1)
}

특이점은 _SpecColor이름으로 예약어가 존재한다. Specular값을 조절하기 위해서는 무조권 해당 이름을 사용을 해야

하고 예약어니 다른 곳에서 중복되서는 안된다. 인터페이스에 선언했으면 내부 코드에서는 따로 변수 선언 하지

않아도 된다.

 

void surf(Input IN, inout SurfaceOutput o)
{
    fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
    o.Albedo = c.rgb;
    o.Specular = 1.0f * _SpecColor;
    o.Gloss = 1.0f;
}

SurfaceOutput 구조체에서 Specular 값을 설정한다. 정반사의 쩅한 정도를 조절한다.

o.Gloss는 Specular의 강도를 조절한다 0~1범위로 사용한다.

 

최종 결과물 비교


WRITTEN BY
CatDarkGame
Technical Artist dhwlgn12@gmail.com

,