본문 바로가기
Unity

[Unity] Lambert와 Blinn-Phong (쉐이더 스타트업)

by 소리쿤 2023. 2. 12.

둘의 상관 관계를 아는 것이 중요해보여서 정리

ChatGPT에 의하면 위와 같음

 

Lambert는 표면에서 모든 방향으로 반사광이 나간다는 것을 가정함,

입사광을 뒤집은 벡터와 표면 법선 벡터의 각도가 작을 수록 밝음을 이용한 쉐이더 방식

 

코드로 표현하면 saturate(dot(viewVec, normalVec))이다.

 

Blinn-Phong은 여기서 좀 발전했는데, Lambert에 Blinn-Phong 공식을 더해 스페큘러를 추가하는 방식이다.

내가 보는 뷰 벡터로부터 반사된 방향에 조명이 있으면, 그 부분 하이라이트가 가장 높음을 가정함.

 

즉, 뷰의 반사 벡터(R)와 조명 벡터(L)를 내적하여 하이라이트 정도를 얻어낼 수 있음

( R ㅇ L )  = 하이라이트 레벨

 

조명 벡터는 Lambert의 LightingXX 함수에 lightDir에서 구할 수 있고,

뷰의 반사 벡터는 구할 수 없지만, 노말 벡터(N)는 알 수 있으며,

R = 2N ( L ㅇ N ) - L 공식을 써서 R을 구한 후, L을 내적하면 하이라이트 레벨이 나온다.

 

하이라이트 레벨 = ( 2N ( L ㅇ N ) - L ) ㅇ L 

 

위 공식을 Phong 공식이라고 하며, 이것을 뷰 벡터(V)를 통해 간략화한 공식

( normalize(L + V) ㅇ N ) = 하이라이트 레벨

 

이 Blinn-Pong 공식이며, 이를 Lambert 라이팅에 합친 것을 Blinn-Pong 쉐이더라고 한다.

 

예시)

 void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 d = tex2D(_MainTex, IN.uv_MainTex);
            o.Albedo = d.rgb;
            o.Normal = UnpackNormal(tex2D(_BumpTex, IN.uv_BumpTex));
        }
 
 float4 LightingTest(SurfaceOutput s, float3 lightDir, float viewDir, float atten)
        {
            float lambertVal = dot(s.Normal, lightDir);
            // float halfLambertVal = saturate(lambertVal) * 0.5f + 0.5f;
  
            float4 albedoLambertVec;
            albedoLambertVec.rgb = lambertVal * s.Albedo * _LightColor0.rgb * atten;

            float blinnPongVal = dot((lightDir + viewDir), s.Normal);
            blinnPongVal = pow(saturate(blinnPongVal), 100);
            // pow할 수록 밝은 면적이 좁아짐, x값이 클 수록 더 밝아지나, 기울기가 낮음
            // 100은 프로퍼티로 하자.

            float4 finalVec = albedoLambertVec + blinnPongVal;
            finalVec.a = s.Alpha;

            return finalVec;
        }

pow 2번 인자를 높이면...
낮추면...

 

Gloss를 위한 텍스처의 알파값만 써서 최종적으로 만든 finalVec에 곱해 

스페큘러를 원하는 곳에만 넣을 수도 있음

 

근데... 여기에 Lim Light를 추가하려고 코드를 넣었는데

대체 왜 이런 걸까 ㅅㅂ

float4 LightingTest(SurfaceOutput s, float3 lightDir, float viewDir, float atten)
        {
            float lambertVal = dot(s.Normal, lightDir);
            float halfLambertVal = saturate(lambertVal) * 0.5f + 0.5f;
  
            float4 albedoLambertVec;
            albedoLambertVec.rgb = lambertVal * s.Albedo * _LightColor0.rgb * atten;

            float blinnPongVal = dot(normalize(lightDir + viewDir), s.Normal);
            blinnPongVal = pow(saturate(blinnPongVal), _GlossPower);

	/* 추가한 Lim 코드 
            float limLight = abs(dot(s.Normal, viewDir)); 
            float inLimLight = 1 - limLight;

            float3 limDir = pow(inLimLight, _LimPower) * 0.5f;
	*/
            
            float4 finalVec;
            finalVec.rgb = albedoLambertVec.rgb + blinnPongVal // + limDir;
            finalVec.a = s.Alpha;

            return finalVec;
        }

나는 테두리 하얀 선을 추가하고 싶었다...

 

나중에 깨달음)

viewDir 파라미터를 float으로 받았었다... 아이고 인간아

float4 LightingTest(SurfaceOutput s, float3 lightDir, float3 viewDir, float atten)
        {
            float lambertVal = dot(s.Normal, lightDir);
            float halfLambertVal = saturate(lambertVal) * 0.5f + 0.5f;
  
            float4 albedoLambertVec;
            albedoLambertVec.rgb = lambertVal * s.Albedo * _LightColor0.rgb * atten;

            float blinnPongVal = dot(normalize(lightDir + viewDir), s.Normal);
            blinnPongVal = pow(saturate(blinnPongVal), _GlossPower);

            float limLight = abs(dot(s.Normal, viewDir));
            float inLimLight = 1 - limLight;

            float3 limDir = pow(inLimLight, _LimPower) * float3(1, 0, 0) * 0.5f;

            float4 finalVec;
            finalVec.rgb = albedoLambertVec.rgb + blinnPongVal + limDir;
            finalVec.a = s.Alpha;

            return finalVec;
        }

완성