기본적인 반사 벡터
반사벡터에 대한 이론과 언리얼 에디터에서 반사벡터 만들기는 요 링크를 참조하면 된다.
http://mgun.tistory.com/1306
반사벡터 공식은 R = -E + 2*dot(N,E)*N 라고 했다.
언리얼 머티리얼 세이더에서는 이미 반사벡터가 노드로 제공되어 지는데
shader 함수로는 이미 reflect라는 함수가 제공되어 진다.
Output.Reflect = reflect(lightDir, worldNormal);
이렇게 간단하게 reflection 값을 구할 수 있다.
reflect함수가 없더라도 큰 걱정은 없다. 이미 reflect를 구할 수 있는 공싟을 알고 있으니까.
Output.Reflect = lightColor * dot(-lightDir, worldNormal);
내적시 밑동을 맞춰준다라고 생각한다면 부호도 금방 생각난다.
이제 반사벡터도 생겼겠다, 반사모델계의 Hello World인 Phong를 만들어 보자.
http://mgun.tistory.com/1099
상당히 오래전에 쓴 글이긴 하지만 대략적인 phong model에 대한 설명은 이곳에 있으니 패스.
공식은 diffuse + ambient + specular인데 diffuse는 dot(L, N)을, specular에는 dot(l,R)에 승수해 준것을 곱해준다.
즉, diffuse는 float3 lambertResult = dot(-lightDir, worldNormal);
ambient는 그냥 적당히 float3 ambient = float3(0.1f, 0.1f, 0.1f);
specular은 specular = saturate(dot(reflection, -viewDir));
specular = pow(specular, 20.0f); 이 된다.
이 세개의 조명값들을 다 더하면 원하는 결과값이 나온다.
specular의 값을 구하기 위해 dot 연산시 viewDIr의 방향이 눈에서 정점으로 향하는 벡터라 반사벡터와
밑동을 맞춰주기 위해 -을 곱해준 것을 눈여겨 보자.
그리고 아직 좀 의문스로운 부분 중 하나가 specular을 계산할 때 light vector를 사용하지 않고 view vector을 사용한다는 점이다.
내가 아는 한도 내에서는 light vector을 사용했었는데 말이다.
내가 사랑하는 타카시 아저씨 책에는 분명 specular은 dot(L,R) 인데....L을 쓰는 것과 V를 쓰는것의 차이는 뭘까?
언리얼에서도 dot(V,R)을 쓰고 있던데 말야...
이에 대한 대답은 아래 두 링크에 나와 있다.
http://kblog.popekim.com/2012/01/04-part-3.html
http://www.renderman.or.kr/RenderMan/shader_05.html
그리고 추가적으로 per pixel specular 에서 부하를 먹는 주 요인인 pow를 좀 더 빠르게 처리하기 위해 언렬에서 쓰는 방법.
영어라 항상 큰맘먹고 읽어봐야 하는 GAMASUTRA, 하지만 좋은글이 정말 많다. 그리고 제목보고 낚여서 읽다가 원하는
내용이 아닌경우 대략난감..읽은부분이 아까워서라도 다 읽어버리게 한다.ㅜㅜ.
http://www.gamasutra.com/view/feature/2972/a_noninteger_power_function_on_.php
늘 좋은 정보로 가득한 GDF : http://gamedevforever.tistory.com/36
일반적인 specular reflection 함수에 대한 error들에 대한 글.
(목록 ui가 좀 불편하긴 하지만 재미난 글들이 많은 블로그.)
http://chihiroblog.blogspot.kr/2010/02/specular-reflection-function.html
blin phong은 그냥 H vector 구해서 쓰면 된다. R vector 구하기 귀찮을 때 쓰기도 하지만 더 빠른 속도를 위해서
쓰기도 한다. 결과물 차이는 좀 나기는 하는데 그다지 신경쓸 정도는 아닌듯...
cook은 공식이 좀 복잡한데 타카시 아저씨 책 공식을 그대로 옮겨주면 위와 같은 결과물이 나온다.
결과 값은 return diffuse + ks*max(0,F*D*G/NV);
반사값은 D x G x F / dot(N,V)로 구해주고 이 값을 diffuse에 더해준다.
추가적으로 "Programming Vertex Geometry and Pixel Shaders" 에 있는 "The Cook Torrance Equation"도 좀 보자.
우선 geometric term을 구하는 방법은 똑같다.
float G = min(1, min(2*NH*NV/VH, 2*NH*NL/VH) );
그런데 기하감쇠계수인 D 값을 구할 때 분모에 있는 4배수값이 없다. 타카시 책을 보고 만든거랑 비교해 보자.
float D = exp(-(1-NH2) / (NH2*roughness)) / (4*roughness*NH2*NH2);
공식이 약간 틀리다. 하지만 이 D라는 항은 그냥 면의 거친정도를 나타내 주는 항이다.
저 4를 나눠주던 곱해주던 그냥 매직넘버 같은 것일 뿐, 큰 의미는 없다.
가장 중요한 프레넬 F는 어떻게 되어 있는가? 책에서는 dotV,H)가 아니라 dot(L,H)라고 쓴다.
이것도 그닥 문제가 될 부분은 없다.
이제 specular을 보자.
타카시 책에서는 대략적인 값을 넣어줬었는데 대략 이렇게 되어 있다.
이 공식에서 양쪽에 dot(N,L)을 곱해주자.
Rs * dot(N,L) = (F * D * G) / dot(N,V)
많이 보던 모양이다.
타카시 책의 cook torrance 의 반영반사광 값 I = kct * (D*G*F)/dot(N,V) 였다.
kct는 물체의 색인데 요것도 추가로 곱해줘서 모양을 맞춰보자.
Rs * dot(N,L) * kct = kct * (F*D*G) / dot(N,V) 이 된다.
float4 kct = float4(2.0*0.486, 2.0*0.433, 2.0*0.185, 1.0);
kct 값은 대충 요렇게 임의의 값을 넣어주기로 하고 계속 테스트를 해보자.
결과값에서 타카시 책에는 이렇게 결론을 낸다.
return diffuse + ks*max(0,(F*D*G)/(NV));
지금 비교하고 있는 문서 파일에는 이렇게 결론을 낸다.
float3 final = max(0.0f, NdotL) * (cSpecular * Rs + Input.mColor);
우선 diffuse 값을 Input.mColor로 생각하자.
float3 final = max(0.0f, NdotL) * (cSpecular * Rs + diffuse );
그리고 NdotL이나 NdotV나 같으니 보기 편하게 하기 위해 NdotV로 고치자.
float3 final = max(0.0f, NV) * (cSpecular * Rs + diffuse);
그리고 specular 값인 cSpecular은 우선 ks와 동일하다고 취급해 주자.
float3 final = max(0.0f, NV) * (ks* Rs + diffuse);
순서좀 바꿔주고.
float3 final = (ks* Rs + diffuse) * max(0.0f, NV);
Rs 값도 위에서 구했던 것 처럼 대입해 주자.
Rs = (F*D*G) / dot(N,V)*dot(N,L)
float3 final = (ks* Rs + diffuse) * max(0.0f, NV);
float3 final = (ks* (F*D*G) / dot(N,V)*dot(N,L) + diffuse) * max(0.0f, NV);
= (diffuse + ks*(F*D*G) / (dot(N,V)*dot(N,L) ) * max(0.0f, NV);
타카시 책 공식에서
return diffuse + ks*max(0,(F*D*G)/(NV)); 의 max를 빼 보자.
return diffuse + ks*(F*D*G)/(NV);
여태까지 계산했던 공식에서 max를 빼 보자
float3 final = (diffuse + ks*(F*D*G) / (dot(N,V)*dot(N,L) ) * NV;
dot(N,V) 끼리 없애주면 diffuse + ks * (F*D*G) / (N,L) 이 된다.
결론은 똑같은 공식이다. 다만 결과가 조금 차이가 나는데 그건 계산 순서 때문이다.
즉, dot(N,L)을 구하여 반대편 측(0 이하값)을 먼저 제외해 준 후 계산을 할 건지
반영반사값을 다 계산 하고 그 값이 반대편 값이면 제외시킬 건지의 문제다.
ps - 혹시 잘 못된 부분이 있으면 따끔하게 한마디 해주세요.ㅎㅎ
render monkey 소스가 좀 지저분 할 수 있습니다. 이리저리 테스트 하느라 그런거니
적당히 정리하셔서 사용하시면 됩니다.
'RenderMonkey' 카테고리의 다른 글
Shadow Mapping (0) | 2014.01.11 |
---|---|
fresnel (0) | 2013.10.22 |
normal mapping (법선 매핑) (2) | 2013.02.07 |
lambert (3) | 2013.01.31 |
흐음...렌더몽키라.. (0) | 2013.01.29 |