11 – 01 ~ 02 커스텀 라이트 기본형 / Lambert 라이트 연산 (Unity Shader)
텍스처 한 장만 받는 기본형 쉐이더를 만듭니다.
직접 확인해보기 위해서 아래 에셋을 사용했습니다. (무료)
https://assetstore.unity.com/packages/3d/characters/humanoids/barbarian-warrior-75519 <- TestAsset
Shader "Custom/CustomLight" { Properties { _MainTex ("Albedo (RGB)", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Lambert noambient // noambient - 주변광 또는 라이트 프로브를 적용하지 않습니다. #pragma target 3.0 sampler2D _MainTex; struct Input { float2 uv_MainTex; }; void surf (Input IN, inout SurfaceOutput o) { fixed4 c = tex2D (_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }
Lambert 라이트를 삭제하고 자신만의 라이트 이름을 지어서 커스텀 라이트를 구동시켜 보겠습니다.
바뀌거나 추가된 내용을 하나씩 보겠습니다.
Lambert 대신에 _MyLight로 바뀌었습니다.
여기는 이름을 마음대로 지어도 상관없습니다.
우리가 만든 커스텀 함수의 모습입니다.
이전까지는 void surf 사용했습니다.
이 함수는 float4로 시작합니다. 함수가 float4 를 반환 하므로 return float4 출력값을 가지고 있습니다.
이어서 나오는 이름도 Lighting + “커스텀 라이트 이름” 을 추가해 주어야 합니다.
SurfaceOutput 구조체를 통째로 받아옵니다.
float3 lightDir 은 이전 파트에서 배웠던 조명 방향의 벡터 입니다.
단, 조명 계산을 편하게 사용하기 위해서 뒤집혀지고 길이가 1인 단위 벡터의 상태입니다.
float atten
atten ( attenuation ) 감쇠
그림자를 받거나 거리가 멀어지면서 점점 조명이 흐려지는 라이트의 거리별 감쇠 현상을 나타냅니다.
내가 빛을 받는 것을 없앨 때 사용하는 부분입니다.
Lambert 라이트 연산
우리가 만든 커스텀 라이트에서 변경합니다.
Shader "Custom/CustomLight" { Properties { _MainTex ("Albedo (RGB)", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf _MyLight noambient // noambient - 주변광 또는 라이트 프로브를 적용하지 않습니다. #pragma target 3.0 sampler2D _MainTex; struct Input { float2 uv_MainTex; }; void surf (Input IN, inout SurfaceOutput o) { fixed4 c = tex2D (_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; o.Alpha = c.a; } float4 Lighting_MyLight(SurfaceOutput s, float3 lightDir, float atten) { float ndotl = dot(s.Normal, lightDir); // dot 노멀 백터와 라이트 벡터를 내적 연산 // surf 에서 Normal 값을 받지 않아도 버텍스에서 노말값을 평범하게 받음 return ndotl; } ENDCG } FallBack "Diffuse" }
lightDir은 버텍스에서 바라보는 조명의 방향인 뒤집힌 방향 이기 때문에 두 벤터를 단순하게 내적해주면 cos 값이 나오게 됩니다.
위와 같은 코드는 가장 밝은 곳이 1 / 어두워 지는 경계선은 0 / 가장 어두운 곳은 -1 값으로 되어서
나중에 추가 라이트를 비추거나 할 때 문제가 됩니다.
이 현상을 방지하기 위해서 saturate (값) 함수를 사용합니다.
max 함수도 사용하지만 최대 값이 없습니다.
max(0, dot(s.Normal, lightDir));
float ndotl = max(0, dot(s.Normal, lightDir));
Shader "Custom/CustomLight" { Properties { _MainTex ("Albedo (RGB)", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf _MyLight noambient // noambient - 주변광 또는 라이트 프로브를 적용하지 않습니다. #pragma target 3.0 sampler2D _MainTex; struct Input { float2 uv_MainTex; }; void surf (Input IN, inout SurfaceOutput o) { fixed4 c = tex2D (_MainTex, IN.uv_MainTex); o.Albedo = c.rgb; o.Alpha = c.a; } float4 Lighting_MyLight(SurfaceOutput s, float3 lightDir, float atten) { float ndotl = saturate(dot(s.Normal, lightDir)); // dot 노멀 백터와 라이트 벡터를 내적 연산 // surf 에서 Normal 값을 받지 않아도 버텍스에서 노말값을 평범하게 받음 // saturate 0 이하 값을 0으로 만들어줌 // float ndotl = max(0, dot(s.Normal, lightDir)); // max 함수도 있지만 최대 값이 없습니다. return ndotl; } ENDCG } FallBack "Diffuse" }
NormalMap 적용
Shader "Custom/CustomLight" { Properties { _MainTex ("Albedo (RGB)", 2D) = "white" {} _BumpMap("Normal Map", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf _MyLight noambient // noambient - 주변광 또는 라이트 프로브를 적용하지 않습니다. #pragma target 3.0 sampler2D _MainTex; sampler2D _BumpMap; struct Input { float2 uv_MainTex; float2 uv_BumpMap; }; void surf (Input IN, inout SurfaceOutput o) { fixed4 c = tex2D (_MainTex, IN.uv_MainTex); float3 d = UnpackNormal(tex2D(_BumpMap, IN.uv_BumpMap)); o.Normal = d; o.Albedo = c.rgb; o.Alpha = c.a; } float4 Lighting_MyLight(SurfaceOutput s, float3 lightDir, float atten) { float ndotl = saturate(dot(s.Normal, lightDir)); // dot 노멀 백터와 라이트 벡터를 내적 연산 // surf 에서 Normal 값을 받지 않아도 버텍스에서 노말값을 평범하게 받음 // saturate 0 이하 값을 0으로 만들어줌 // float ndotl = max(0, dot(s.Normal, lightDir)); // max 함수도 있지만 최대 값이 없습니다. return ndotl; } ENDCG } FallBack "Diffuse" }
환경광 제거