top of page

(UnityShader) 05 Shader Programming

  • 작성자 사진: Nobody
    Nobody
  • 2020년 5월 16일
  • 4분 분량

최종 수정일: 2020년 6월 3일

Digital Light


성경에서 창세기의 가장 유명한 구절이 있습니다. "빛이 있으라." 그렇게 모든 것이 시작됩니다. 온 세상으로 부터 빛이 내게 옵니다. 망막으로 빛을 받아들이고 시신경이 정보를 뇌로 전달하여 시각정보 처리를 담당하는 대뇌피질이 이것을 분석해서 자기 나름대로 해석하여 비로소 한 인간이 세상을 인식하는 과정이 완성됩니다.


인간이 어떤 대상을 관측하기 위해서는 빛이 필요합니다. 유니티든 언리얼이든 게임 엔진에서 사용되는 디지털 라이트가 어떻게 현실의 광원을 모방하는지에 대해 알아보겠습니다.


(포인트 라이트 / 스팟 라이트 / 디렉셔널 라이트)


현실에서 빛이란 단일 광자가 특정한 방향으로 진행하는 파동이자 입자이지만, 게임 엔진에서 이것을 구현하는 것은 너무나도 어려운 일이기에 크게 세가지 종류의 라이트로 분류하고 간략화 합니다. 그리고 빛은 광자가 아니라 하나의 방향. 즉 '라이트 벡터'로 정의됩니다.


포인트 라이트(Point Light)는 3차원 좌표를 가진 하나의 점에서 모든 방향으로 빛이 발산하는 라이트 벡터 입니다.


스팟 라이트(Spot Light)는 포인트 라이트에서 지정된 특정 각도(범위)의 라이트 벡터만 남기고 나머지는 다 제거한 것입니다.


디렉셔널 라이트(Directional Light)는 3차원 좌표도 없이 라이트 벡터만 존재하는 라이트 입니다.


여기 생각해볼만한 문제입니다. 게임 엔진 속에서 포인트 라이트 주변에 위와 같이 평평한 빨강색 물체 A와 파란색 물체 B가 있습니다. A와 B중에 무엇이 더 밝을까요?



정답은 B입니다. 그러나! 현실에서는 A가 광원과 가까워서 더 밝습니다.


빛이 물체에 도달하기 전, 대기 중의 입자와 먼지와 충돌해서 산란하여 감쇄(Attenuation)됩니다. 광자가 온전하게 전달 되지 못하는 것이지요. 따라서 가까운 것이 더 많은 광자를 반사하기 때문에 더 밝습니다.


그런데 위 자료와 같이 게임 엔진에서는 B가 더 밝습니다. 왜냐하면 게임 엔진은 빛을 오직 벡터로만 계산하기 때문입니다.



같은 이유로 여기 두번째 문제입니다. 게임 엔진 속에서 똑같은 크기의 광원에 수직인 빨강색 물체와 기울어진 파랑색 물체 중에 무엇이 더 밝을까요? (기울어져서 거리에 따른 감쇄는 무시 합시다)



정답은 빨강색 물체입니다. 심지어 현실에서도 결과가 똑같습니다. 다음과 같이 쉽게 증명할 수 있지요.


두 물체가 광원을 반사한 면적을 비교해봅시다. 같은 양의 광자가 파랑색 물체 표면에는 비교적 넓게 반사 되었으니 어두울 수 밖에 없습니다.


이전 문제의 원리도 위와 같이 설명할 수 있습니다. A와 B의 라이트 벡터들의 평균 각도를 생각해봅시다. A보다 B에 수직(90도)에 가까운 벡터가 많습니다. 그래서 B가 밝은 것이지요.


그리고 디렉셔널 라이트의 경우를 봅시다. 광원이 시작하는 3차원의 점도 없고, 감쇄도 없는 오로지 라이트 벡터만이 존재합니다. 그래서 아무리 오브젝트의 거리가 멀어도 밝기 차이가 없습니다.


또한 앞서 말했듯이 오브젝트가 기울어지면 반사되는 각도가 커져서 표면이 어두워지는 것을 볼 수 있습니다. 그렇다면...


라이트 벡터와, 라이트 벡터가 오브젝트 표면에 반사되는 벡터만 알고 있다면 저희는 두 벡터사이의 각도 차이를 연산해서 물체의 표면의 밝기를 정의할 수 있는겁니다. 쉐이더 코드로 각도 차이를 연산하는 방법은 다음 포스트에서 다룰 것 입니다.


Vector & Scalar



앞에서 벡터를 간단하게 방향이라고 정의했는데, 제대로 설명하자면 물리량의 단위중 하나입니다. 모든 물리량은 질량, 면적, 길이와 같이 크기만을 가지는 스칼라(Scalar)와 힘, 가속도, 속도와 같은 크기와 방향을 동시에 갖는 벡터(Vector)로 분류 됩니다.



또한 벡터를 위와 같이 세 개의 스칼라 값인 float3(n,n,n)으로 표기할 수 있습니다. 2차원 그래프의 원점(0,0) 로부터 모든 벡터가 다음과 같이 표기 될 수 있습니다. 이때 벡터A와 벡터B의 크기는 달라도(A=4, B=2) 방향은 같습니다.


(유니티 엔진 기즈모)


즉 게임 엔진이나 3D툴 상의 기즈모(Gizmo)는 크기가 1이며 x,y,z 각 축에 대응되는 vector3 세 개를 모아둔 것 입니다. 또한 크기가 1인 벡터를 '단위 벡터' 라고 부르는데 쉐이더 프로그래밍을 할 때 모든 벡터가 단위 벡터면 벡터 연산들이 매우 쉬워지는 장점이 있습니다. 이 부분은 나중에 다루도록 하겠습니다.


이렇듯 부동소수점 float으로 숫자, 색, 벡터까지 모두 표현 할 수 있습니다. 이말은 상호간에 자유로운 변환이 가능 하다는걸 의미합니다. 예를 들어 단위 벡터 float(1,0,0)은 빨강색으로 치환 될 수 있습니다.


Digital Light Reflection


위에서 다룬 디지털 라이트와 같은 맥락에서, 현실에서도 물체 표면에 반사되는 빛은 오로지 광자 뿐 입니다. 하지만 상술 했듯이 게임 엔진으로 구현하기 위해 반사광은 크게 세가지의 라이트 모델로 분류됩니다.


(Diffuse Light / Ambient Light / Specular Light)


디퓨즈 라이트(Diffuse Light)는 난반사 계열의 '직접광' 입니다. 광원에서 발산 된 광자가 어떠한방해도 받지않고 그대로 물체 표면에 반사되어 나오는 빛을 직접광이라 합니다.


앰비언트 라이트(Ambient Light)는 같은 난반사 계열의 '간접광' 입니다. 광원에서 발산 된 광자가 주변 환경에 한 번 이상 부딪치고 물체 표면에 반사 되었을 때 환경의 색이 물체 표면에 반사 되어 섞여 보이게 됩니다. 이것을 환경광 혹은 간접광이라 합니다.


스펙큘러 라이트(Specular Light)는 정반사 계열의 빛입니다. 자료 화면의 금속 표면에서 반사되는 주변 환경이 대표적으로 관측 할 수 있는 스펙큘러 입니다. 물체 표면의 결에 따라 스페큘러의 모양이 변합니다.


라이트에 대한 자세한 정보는 따로 포스트로 정리 해두었으니 링크에서 참고 하시면 됩니다.


Physically Based Shader


물리 기반 쉐이더(PBS)를 사용하는 게임 엔진에서 중요한 원칙은 에너지 보존 법칙(Energy Conservation)입니다. 물체 표면에 들어온 빛보다 더 많은 양의 빛을 반사 할 수 없다는 룰입니다. 이는 현실의 물리 법칙을 그대로 모방한 것이지요.


물체가 어떤 분자들로 구성 되어 있는지에 대한 정보. 즉 '재질'에 따라 정반사와 난반사 간의 비율이 존재하는데 둘은 반비례 관계 입니다. 정반사와 난반사의 총합을 100이라고 한다면 정반사가 1%일 때, 난반사가 99%가 됩니다. 예외로 순수한 금속으로 분류되는 재질들은 모두 100% 비율의 스펙큘러로 디퓨즈가 전혀 없이 우리 눈에 보입니다.



간단하게 증명 할 수 있습니다. 물체 표면의 거친 정도가 높으면 당연히 빛이 반사되는 각도가 넓어집니다. 반대로 표면이 매끄러울수록 빛이 반사되는 각도는 입사각과 반사각이 똑같은 정반사가 더 많아 지는 것 입니다.



유니티의 PBS에서 표면의 거칠기가 바로 정반사와 난반사의 비율을 결정짓는 변수이며 러프니스(Roughness)라고 부릅니다. (유니티 엔진에서는 러프니스를 반전한 값인 Smoothness를 사용합니다)


(현실의 거울)


거울에 라이트를 비추면 빛이 거의 전부 정반사되기 때문에 거울 표면은 밝아지지 않습니다. 또한 같은 이유로 거울 표면에 그림자가 생기지 않습니다. 물론 실제 거울 표면에 묻어 있는 먼지 때문에 완벽한 100% 정반사를 하진 않습니다.


또한 금속 재질의 경우 스펙큘러에 자신 만의 컬러를 묻어 나오게 반사 합니다. 금속 고유의 알베도(Albedo) 컬러가 스펙큘러에 묻어나는 것이지요. 그래서 유니티의 PBS에서는 금속인지 아닌지를 판단하는 메탈릭(Metallic)이라는 변수가 있습니다. 0이면 비금속이고 1이면 금속의 스페큘러를 가지도록 구분합니다.


따라서 PBR 공식에서 메탈릭으로 금속과 비금속을 구분하여 다른 종류의 연산을 합니다.


비금속의 난반사는 Albedo Color * Diffuse Light 이며 정반사는 1 * Specular Light 그대로 출력합니다.


금속의 난반사는 존재하지 않으며 정반사는 Albedo Color * Specular Light인 것입니다.


유니티 엔진에서는 물리 기반 쉐이더(Physically Based Shader)와 비물리 기반 쉐이더(Non-physically Based Shader) 둘다 사용 가능 합니다. 그리고 아티스트의 자유로운 응용과 변칙 사용을 위한 예외 변칙 쉐이더까지 지원합니다.


자료 화면처럼 금속의 스페큘러 컬러는 Albedo Color * Specular Light 지만 알베도 컬러 대신 다른 값을 사용하여 정반사의 색을 바꿔줄 수 있게 한 것입니다.


현실감을 최대한 가져가는 실사풍 그래픽 스타일 게임이 아닌 카툰풍 그래픽 스타일 게임 같은 경우라면, 물리 법칙을 지킬 필요가 없습니다. 물리적으로 옳지 않더라도 아티스트 입장에서 이쁘면 그만입니다.


(언리얼 엔진 5 데모 영상)


물리 법칙을 그대로 영상 매체와 게임 엔진에 재현 하려는 시도를 종합한 PBS의 계보를 거슬러 올라 갈수록 엄밀한 의미로 물리적으로 옳지 않음에도 불구하고, 이 모든 시도의 결과인 쉐이더 모델들을 저희는 PBS라고 부를 수 있습니다. 작금의 최신 기술이 도입된 게임 엔진인 언리얼 엔진 5 조차 물리법칙을 게임 속에 완벽하게 구현하지 못한 근사치 입니다.



유니티 엔진에서 메테리얼을 생성하면 Standard Surface 쉐이더가 자동으로 적용이 되는데 이것이 유니티의 대표 PBS입니다. 프로젝트 창에서 creat - shader - Standard surface shader를 누르면 바로 스탠다드 서페이스 쉐이더 코드를 볼 수 있습니다. 저희도 어느정도 공부를 좀 했으니 소스코드를 보지 않고 어느정도 수준까지는 충분히 따라 작성 할 수 있습니다.





Comments


bottom of page