조명 효과
0
0194
coreafive
2011.06.30 10:43:53
URL
EMBED
Page 0:
Page 1: 셰이더 프로그래밍
렌더링 파이프라인 & 조명효과 박형준
Page 2: 화면에 폴리곤이 표시될때까지…
정점 정보 입력 삼각형 데이터를 GPU에 보낸다. 정점 처리 화면에서의 위치 결정 주사선 변환 정점 위치로부터 출력 픽셀을 결정한다. 픽셀 처리 각 픽셀의 색을 결정한다. 프레임 버퍼에 출력 폴리곤이 그려진다.
Page 3: 여기서 중요한 게 바로!
정점 처리와 픽셀 처리이다. 즉, 정점 처리에서 정점 셰이더, 픽셀 처리에서 픽셀 셰이더를 사용하여 프로그래머가 직접 조작을 할 수 있다.
Page 4: 이러한 순서를 통해 렌더링이 이루어진다.
Page 5: 조명 효과
그림은 평면으로 그려져 있지만 입체적인 3차원 물체로 인식한다. 원근법과 음영이 바로 이러한 효과를 연출하는 요소이다. 음영은 물체에 조명을 비추었을 때 조명이 정면으로 비치는 부분은 밝고, 비치지 않는 부분은 어둡게 하는 기법이다.
Page 6: 램버트 확산 조명
확산 반사광 환경광
Page 7: 확산 반영광 ( DIFFUSE )
구에 빛을 비출 때 빛이 정면으로 비추는 부분이 제일 밝게 빛나고 빛이 비치지 않는 구의 뒷면은 어둡게 나타남. N ● L을 통해 나타내므로 두 벡터 사이의 각이 90도를 넘는다 는 것은 L이 뒷면을 비추는 것을 의미하므로 어둡게 나오는 것이다. 즉, 입사광의 강도는 N과 L의 코사인으로 결졍되는 것이다.
Page 8: 확산 반영광 ( DIFFUSE )
즉 최종적인 식은 다음과 같이 정의할 수 있을 것이다. I = I_d × K_d × cosΘ = I_d × K_d ×max( (N ● L), 0 ) 확산 반영광은 음영 기법을 표현하는 데 적합하며, 특히 종이처 럼 표면이 거친 물체를 표현하는 데 유용하다.
Page 9: 환경광 ( AMBIENT )
확산 반영광의 경우 광원이 뒷면을 비출 경우 어둡게 되므로 뒷면 이 보이지 않는 문제가 있다. 그래서 최소한의 색을 두어 보이게 하기 위해 등장하게 되었다. 일정한 밝기로 물체를 전부 칠하는 빛으로서 물체 면의 방향이나 빛의 방향이 전혀 영향을 주지 않기 때문에 추가할 경우 어느 정 도의 물체 윤곽은 알 수 있지만 입체적인 효과는 전혀 없다.
Page 10: 환경광 ( AMBIENT
환경광은 즉 간접광이다. 다양한 반사를 반복해 다른 물체를 비추어야 하지만 이를 정확 하게 계산하는 것은 매우 어려운 일이다. 즉 물리적인 모델이라기 보다는 대충 적당히 때려 맞춘 조명색이 라는 것을 알아야 한다. 물체의 밝기를 조정하는 데 있어서 유용하다. 조명이 없음에도 불구하고 밝은 경우 그냥 환경광을 사용한다고 봐도 무방할 듯 싶다. I = I_a × K_a
Page 11: 쉐이더 코드로 알아보는 시간! DIFFUSE + AMBIENT ( 월드좌표계공간)
float3 L = -vLightDir; float3 N = normalize( mul( Normal, (float3)mWIT ); vLightDir은 정점으로부터 광원으로 향하는 방향이므로 즉, L_pos – V_pos로 구할 수 있다.
N을 월드 좌표계로 변환하는 데 mWIT는 단순히 월드 행렬로 변환하는 행렬이 아니라 월드 좌표계로 변환하는 행렬의 역전치 행렬이다. 이렇게 하는 이유에 대해서는 다음 장에서 좀 더 자세히 설명하 겠다.
Page 12: 쉐이더 코드로 알아보는 시간! DIFFUSE + AMBIENT ( 월드좌표계공간)
노멀벡터는 특별한 벡터로서 물체를 평행 이동하면 변하지 않으 며, 물체를 회전할 경우에는 회전한 방향으로 바꾼다. 또한 물체의 크기를 변환할 때도 방향을 바꾼다. 즉 요약해서 설명하자면..
이동 -> 노멀벡터 방향 바뀌지 않음. 회전 -> 노멀벡터 방향 바뀜. 크기 -> 노멀벡터 방향 바뀜. 즉 단순히 월드행렬로 변환할 경우 이러한 변환(회전,크기)이 이 루어졌다면 잘못된 노멀벡터를 사용하게 되어 이상한 조명으로 계산되어 질 것이다.
Page 13: 쉐이더 코드로 알아보는 시간! DIFFUSE + AMBIENT ( 월드좌표계공간)
즉 비균등 크기 변환을 할 경우 그림과 같이 노멀 벡터의 방향이 망가지게 되므로 이를 해결하기 위해서 M = Tranpose( Reverse( W ) ); 와 같이 변환행렬을 구하여 노멀벡터에 곱해주면 정확한 월드 좌표계 공간의 노멀벡터를 얻을 수 있다.
Page 14: 좀 더 노멀벡터에 대해 자세히 알아보자.
여기서 알아두어야 할 것은 회전 행렬의 역행렬은 전치행렬과 일맥상통하다는 것이다. Tranose(M) == Reverse(M) 라는 뜻이다! 그렇다면 왜 노멀벡터에 월드행렬을 곱하여 월드좌표계공간으로 옮기지 않고 전치, 역행렬과 같은 것들을 하는 걸까?
앞에서도 설명했듯이 크기변환을 할 경우에 월드행렬만을 곱해 줄 경우 이상한 노멀벡터를 얻게 되므로 조명이 망가진다. 하지만 이동,회전의 경우는 월드 행렬만 곱해주어도 상관없다.
Page 15: 좀 더 노멀벡터에 대해 자세히 알아보자.
왜냐하면 Transpose( Reverse( M ) ) 에서 회전행렬은 T == R이라고 했으므로,
Transpose( Transpose(M) ) 즉 하나의 명령이 중복되고 있으므로 할 필요가 없는 것이다. M == Transpose( Transpose(M) )이므로 그냥 M이라는 뜻이다. 또 한가지는 크기 변환은 노멀라이즈를 해줘야 한다. 왜냐하면 크기가 1이 아니기 때문에 방향벡터가 필요한 우리로서는 해주 지 않으면 역시 이상한 벡터를 가지고 있게 되는 것이다.
Page 16: 결론지어서 종결시켜보자.
월드 좌표계 공간으로 변환되는 노멀벡터는…
(이동,회전)일 경우에는 월드변환만 곱해주어도 상관없다!
하지만 크기일 경우에는 Transpose( Reverse(M) )와 함께 이 변환된 결과 벡 터를 노멀라이즈화해야만 정확한 노멀 벡터를 얻을 수 있다.
Page 17: 쉐이더 코드로 알아보는 시간! DIFFUSE + AMBIENT ( 로컬좌표계공간)
하지만! 월드좌표계 공간보다는 로컬좌표계 공간에서 계산을 하 는 게 더 편안하다! 여기에 대해서 알아보자! float3 L float3 N = -vLightDir; = Normal;
Out.Color = I_a * K_a // 환경광 + I_d * K_d * ( max( 0, dot( N, L ) ); // 확산광
Page 18: 쉐이더 코드로 알아보는 시간! DIFFUSE + AMBIENT ( 로컬좌표계공간)
차이점은….
더 이상 노멀 벡터에다 이상한 짓(?)을 하지 않아도 된다. 즉 계산이 무척 단순화되었다.
광원 벡터 역시 외부에서 던져주는 로컬 좌표계 공간의 값을 월드 좌표계로 옮길 필요없이 그대로 사용하면 된다!
즉! 필요없는 연산이 줄었다는 것을 볼 수 있다!
Page 19: 좀 더 최적화를 하고 싶다!!!!
I_d * K_d를 매번 계산? 정점마다 바뀔 일이 없기 때문에 미리 계산해서 쉐이더에 보내 도록 한다! 3차원벡터를 사용하지만 쉐이더의 입력레지스터는 4차원벡터 로 인식해 사용된다는 것을 이용한다! 광원벡터의 4번째 인수에 환경광의 색 강도를 설정하여 더해주 도록 한다! 즉, ( Nx, Ny, Nz, 1 ) * ( Lx, Ly, Lz, Lw ) 는 Nx * Lx + Ny * Ly + Nz * Lz + Lw 를 하여 Lw를 환경 광의 강도라 생각하는 것이다!
Page 20: 쉐이더 코드로 알아보는 시간! DIFFUSE + AMBIENT ( 최적화 버전)
즉, I = Color * ( 0.3 + 1.0 * ( N ● L ) ) 이를 쉐이더 코드로 표현하면…
float4 L = -vLightDir; float amb = L.w; Out.Color = vColor * max( amb, dot( Normal, L ) );
Page 21: 퐁 반영 반사
퐁 반영 반사광 블린 – 퐁 반영 반사광
Page 22: 퐁 반영 반사광
표면에 반짝이는 현상을 표현하기 위해 사용한다. 특히 이러한 반사된 부분을 하이라이트라 명명한다. 퐁은 시선이 물체에 반사되었을 때의 반사 벡터 R을 구해서 반 사 벡터가 광원 벡터( 정점에서 광원을 향하는 ) L과 가까우 면 강한 빛, R과 L이 멀면 약한 빛이 되도록 수식화하였다.
Page 23: 퐁 반영 반사광
즉 수식을 살펴보면… I = I_s * K_s * cosnθ I = I_s * K_s * ( L ● R )n
따라서 L과 R의 사이각도가 작을 수록 강한 빛이 반사될 것이 고, 커질 수록 약한 빛이 반사된다는 것을 수식을 통해 증명할 수 있다. 그렇다면 반사벡터 R을 어떻게 구할까?
Page 24: 퐁 반영 반사광
반사벡터 R을 구하라!!!!
Page 25: 퐁 반영 반사광
반사 벡터 R을 구할려면
N R L
다음과 같은 간단한 그림을 그릴 수 있다. 우리는 우선 저 노란선을 먼저 구해보도록 해보자.
Page 26: 퐁 반영 반사광
우선 우리는 이 그림으로부터 시작할 필요가 있다.
L
N 여기서 N과 L는 방향벡터라 가정하므로 크기는 1일 것이다. 우선 고교시절 배운 삼각함수의 간단한 식 하나를 가져와서 적용해보자. cosθ = |N| / |L| |N| = |L|cosθ 을 구할 수 있다.
Page 27: 퐁 반영 반사광
L
N
여기서 L을 N에 투영하면 우리는 노란선의 크기를 알 수 있다. 투영을 위해서는 벡터의 내적을 사용하면 된다. 즉N●L = |N||L|cosθ |L|cosθ = N ● L / |N| 을 구할 수 있다.
Page 28: 퐁 반영 반사광
|N|= |L|cosθ |L|cosθ = N ● L / |N|
이 두 식을 이용해서 |N| = N ● L / |N| 을 구할 수 있다. 하지만 완전히 끝난 것이 아니다. 방향을 설정해주어야 하므로 N/|N|을 곱해주어야 한다. 즉 최종적으로 |N| = ((N ● L)N)/|N|2 가 나오게 된다.
Page 29: 퐁 반영 반사광
우리는 앞에서 앞에서 N과 L을 방향벡터라 가정했으므로 둘 의 크기는 1이므로 식을 더 단순화 시키자면… |N| = ((N ● L)N)/|N|2 결론적으로 |N| = ((N ● L)N)으로 구할 수 있는 것이다.
Page 30: 퐁 반영 반사광
노란선의 크기를 알므로 좀 더 확장시키 면 다음과 같은 그림을 볼 수 있게 된다. N R 2X -L 거울(?)효과에 의해 X의 2배의 길이를 쉽게 구할 수 있다. 즉 R벡터는 2X + (-L)을 통해 구할 수 있다. R = 2(N●L)N - L L X
Page 31: 퐁 반영 반사광
결론적으로 반사 벡터와 광원 벡터 방향이 같은 부분에만 하이 라이트 효과가 나타나며, 반사 지수에 의해 하이라이트 효과가 바뀌어진다. 반사 지수가 작을수록 하이라이트의 범위는 넓어지게 된다.
흠.. 이상한게 셰이더 프로그래밍책과 다른 기술서에서 설명한 공식이 약간 차이가 있는 데 날 당황하게 만드네요.
Page 32: 쉐이더 코드로 알아보는 시간! 퐁 반영 반사
float3 eye float3 L float3 N float3 R
= normalize( vEyePos – Pos.xyz ); = -vLightDir ; = Normal.xyz; = -eye + 2.0f * dot( N, eye ) * N;
그렇다… 내가 구한 식은 광원 벡터 L을 이용했는 데 이것은 시 선 벡터를 이용해서 반사 벡터를 계산하고 있다. 어찌되었던 효과를 나타내는 데는 크게 지장이 없으므로 넘어간 다.
Page 33: 쉐이더 코드로 알아보는 시간! 퐁 반영 반사
Out.Color = vColor * max( amb, dot( Normal, vLightDir ) ) + pow( max( 0, dot( L, R ), 10 );
공식이 복잡해보이는가? Ambient + Diffuse + Specular이 모두 합쳐져서 그렇게 보일뿐 이다. 로컬 좌표계에서 계산하고 있으므로 m = mWorld * mView; D3DXMatrixInverse( &m, NULL, &m ); v = D3DXVECTOR4( 0, 0, 0, 1 ); D3DXVec4Transform( &v, &v, &m ); 을 통해 로컬 좌표계 공간으로 위치를 변환시킬 수 있다. 즉 뷰좌표계 공간에서는 카메라의 위치가 (0,0,0,1) 원점에 위치해있 다는 것을 알면 해결된다.
Page 34: 블린 – 퐁 반영 반사광
퐁 반영 반사 모델에서는 반사 벡터를 계산해야만 하는데 특정 환경에서 반사 벡터를 계산하는 것이 어렵기 때문에 등장하게 되었다. 하프벡터는 광원 벡터 L과 시선 벡터 E의 중간의 각도에 있는 벡터로 아래와 같이 나타낸다.
Page 35: 쉐이더 코드로 알아보는 시간! 블린 – 퐁 반영 반사광
float3 N = Normal; float3 L = -vLightDir; float3 eye = normalize( vEyePos – Pos.xyz ); float3 H = normalize(L + eye);
Out.Color = vColor * max( amb, dot( Normal, vLightDir ) ) + pow( max( 0, dot( N, H ) ), 10 ); 하프벡터를 구하여 N,H을 내적하는 것만 차이가 날뿐 나머진 모두 같다.
Page 36: 블린 – 퐁 반영 반사광
어째서 이 두식이 비슷한 결과를 나타내는 것일까?
그것은 시선 벡터와 노멀 벡터가 이루는 각도와 광원 벡터와 노멀 벡터가 이루는 각도가 동일한 곳으로 시선벡터로부터 반사 벡터를 만들었을 때, 반사 벡터가 광원 벡터와 일치하는 상황이 나 타나게 되는 것이다. 즉, 두 모델로 만들었을 경우 하이라이트의 중심이 같은 위치가 생성되는 것이다.
Page 37: 픽셀 단위 반영 반사 계산
모델의 품질에 상관없이 동일한 결과를 낼 수 있다는 장점이 있 다. 단, 픽셀 별로 계산을 하기 때문에 정점 셰이더가 아닌 픽셀 셰 이더를 사용해야 하고, 부하가 이전에 비해 크다.
Page 38: 쉐이더 코드로 알아보는 시간! 픽셀 단위 반영 반사광
float3 L = -vLightDir.xyz; float3 H = normalize( L + In.Eye ); float3 N = normalize( In.N ); return Out.Color + pow( max( 0, dot( N,H ) ), 10 );
이와 같이 보간되어진 노멀 벡터를 가져와서 픽셀별로 계산하는 것을 볼 수 있다.
Page 39: 금속 반사 모델
쿡-토런스 반사 모델 컬러 시프트
Page 40: 쿡 토런스 반사 모델
표면의 거친 정도를 표현할 수 있는 반사 모델 금속 표면을 표현하는 데 적절하다. 빛의 입사 방향이 뒤쪽으로 갈수록 반영 반사 부분이 윤곽 방향으 로 가늘고 길게 나타난다. 즉 실제 금속 반사의 모습과 유사하다.
Page 41: 쿡 토런스 반사 모델
토런스와 스페로우는 표면의 미세한 거침을 반영 반사하는 미세 면이라고 생각하여 “이들 미세면이 여러 방향으로 향하고 있기 때문에 하라라이트 가 희미해진다”는 이론으로 표면 반사를 설명한다. 이러한 이론을 더욱더 발전시켜서 반영반사색을 다음과 같이 표 현한다.
I = Kct ( D * G * F ) / N ● V
Page 42: 쿡 토런스 반사 모델
I = Kct ( D * G * F ) / N ● V 인수에 대해 자세히 알아보자. Kct : 색의 스펙트럼에 의존하는 반사율( 즉, 물체의 색 ) D : 미세면 분포 함수 G : 기하감쇠 계수 F : 프레넬 공식에 의한 반사 계수 N : 물체 표면의 법선 벡터 V : 시선 벡터 D는 퐁 조명 모델의 반영 반사 지수에 해당하는 희석효과. G는 미세 그림자 효과 F는 거울 그 자체의 특성에 의해 입사각에 따라 반상광의 강도 가 변하는 항입니다.
Page 43: 뭐야!!!!이거…. 일단 진정하고…
Page 44: 쿡 토런스 반사 모델 미세면 분포 함수 D
미세면이 통계적으로 면의 법선에 대해서 어떻게 어긋나 분포하 고 있는지를 기술합니다. 각각의 각도에 관해서 그 반사 벡터가 광원 벡터로 향하는 비 율을 나타내는 함수이다.
즉 미세면 대부분이 노멀벡터들과 엇갈리고 있으면 입사광 대부 분이 다른 방향으로 산란되어 산란광이 퍼지는데, 더욱 희미한 반사가 된다. 반대로, 대부분의 미세면이 정면으로 향하고 있으면 거울처럼 그 대로 반사한다.
Page 45: 쿡 토런스 반사 모델 미세면 분포 함수 D
Beckmann분포 함수를 사용하여 면의 법선의 분포도를 정의한 다. α는 N(노멀벡터)과 H(반각벡터)의 사잇각으로 즉 밑에 그림과 같이 구할 수 있다. 인수 m은 면의 거친 정보를 결정하는 상수로서, m이 작을수록 반 사광의 넓이가 날카로운 분포가 된다. m이 0에 가까우면 입사광이 반사 벅터 방향으로만 반사된다.
D = (1/4m^2*(N dot H)^4) * e ^ (-1/m^2 * [1-(N dot H)^2)/(N dot H)^2]
Page 46: 쿡 토런스 조명 모델 기하 감쇠율 G
미세면에 입사한 빛이 다른 미세면에 그림자를 만드는 효과를 나타낸다. 시선이 노멀 벡터에 직교하거나 빛의 방향이 노멀 벡터에 직교 하면 이 효과가 더욱 커지게 된다.
Page 47: 쿡 토런스 조명 모델 기하 감쇠율 G
즉 빛이 물체의 정면에서 비칠 때는 빛이 가려지지 않으므로 일반 적인 반사가 일어나지만 빛이 물체의 뒤에서 비추면 입사각과 반사각이 작아지므로 가 림현상이 일어난다.(2번째 식) 또 하나는, 본래 시선이 들어가야 할 입사광이 다른 미세면에 차단되어 닿지 않는 경우에 일어난다.(3번째식) 단 이 현상이 빛이 차단되면 일어나기 때문에 그렇지 않을 경우 그대로 반사해야 되므로 최대값 1로 설정한다. 반사 될 경우는 기하 감쇠율항에서 구해진 최소값을 활용한다.
Page 48: 쿡 토런스 조명 모델 프레넬 항 F
반질반질한 받침을 정면에서 보았을 때 받침 자체의 색이 보이 는데 반해, 옆에서 보면 주위의 경치가 보여지는 현상을 표현한 다. 프레넬 반사는 강약이 있으며, 반사가 발생하는 모든 물질에서 관측되는 현상으로 그 효과는 굴절률에 의해 결정된다.
왼쪽 그림에서는 물에 주변 경치가 비쳐지지 않다가 오른쪽 그림에서 주변의 경치가 비쳐지 는 것을 볼 수 있다. 이것이 바로 프네넬 반사이다!
Page 49: 쿡 토런스 조명 모델 프레넬 항 F
프레넬 항은 프레넬 공식을 사용해서 입사한 광원 벡터와 반각 벡터를 사용해서 계산한다. 프레넬 항은 약간 틀리더라도 크게 차이가 나지 않으므로, 근사 값이 자주 이용된다. 유명한 것은 Schlick에 의한 근사방법이 있다.
Page 50: 쿡 토런스 조명 모델 1/(N●V)
관측자 측에서 본 단위 면적 당 미세면의 농도를 나타낸다. 시선이 판을 수직으로 보이는 판보다 비스듬하게 기울여 있는 판이 시선에 들어가는 표면이 넓게 보인다. 즉 이 판의 넓이라는 것이 바로 미세면의 개수와 같다는 것을 의 미한다. 결론적으로 물체를 바라보았을 때, 단위 면적 당 미세면의 개수는 비스듬하게 보는 쪽이 정면으로 보는 때보다 많다는 것을 알 수 있 다. 이 항은 가장 자리 부분에서 반영 반사가 일어나기 쉽게 하는 역할을 한다.
Page 51: 쉐이더 코드로 알아보는 시간! 쿡 토런스 조명 모델
픽셀 셰이더에서 반사 모델을 구현하게 된다. float3 L = -vLightDir.xyz; float3 N = normalize( In.N ); float3 V = normalize( vEyePos – In.x ); float3 H = normalize( L + V ); L은 광원벡터, N은 노멀벡터, V는 시선벡터, H는 반각벡터이 다. 또한 선형 보간의 영향으로 크기가 1이 아닐 수 있으므로 정규화를 꼭 해줘야 한다.
Page 52: 쉐이더 코드로 알아보는 시간! 쿡 토런스 조명 모델
계산에 사용되는 각도를 구한다. float NV = dot( N, V ); float NH = dot( N, H ); float VH = dot( V, H ); float NL = dot( N, L ); float LH = dot( L, H );
Page 53: 쉐이더 코드로 알아보는 시간! 쿡 토런스 조명 모델
Beckmann분포 함수 계산하기. const float m = 0.35f; float NH2 = NH * NH; float D = exp( -( 1 – NH2 ) / ( NH2 * m * m ) ) / ( 4 * m * m * NH2 * NH2 ) ); 식이 복잡해 보이지만 D = (1/4m^2*(N dot H)^4) * e ^ (-1/m^2 * [1-(N dot H)^2)/(N dot H)^2]을 코드로 구현 한 것에 지나지 않는 것을 볼 수 있다.
Page 54: 쉐이더 코드로 알아보는 시간! 쿡 토런스 조명 모델
기하 감쇠율 계산하기. float G = min( 1, min( 2 * NH * NV / VH ), 2 * NH * NL / VH ) ); 역시 공식을 그대로 대입한 것에 불과하다. 이렇게 공식이 있으면 그것을 응용하는 게 우리 프로그래머가 가 장 추구해야 될 일이 아닐까 싶다.
Page 55: 쉐이더 코드로 알아보는 시간! 쿡 토런스 조명 모델
프레넬 계산하기. float n = 20.0f; // 복소 굴절률의 실수부 float g = sqrt( n * n + LH * LH – 1 ); float gpc = g + LH; float gnc = g - LH; float cgpc = LH * gpc - 1; float cgnc = LH * gnc - 1; float F = 0.5f * gnc * gnc * ( 1 + cgpc * cgpc / * ( cgnc * cgnc ) ) / ( gpc * gpc );
Page 56: 쉐이더 코드로 알아보는 시간! 쿡 토런스 조명 모델
최종적으로 구한 값들을 모두 합친다! return In.Color // 확산광 + 환경광 + ks * max( 0, F * D * G/NV ); I = Kct ( D * G * F ) / N ● V 이 식을 그저 코드로 구현한 것을 볼 수 있다.
// 반영 반사광
Page 57: 컬러 시프트
빛의 입사 방향이 수평에 가까울 때에는 반사광의 색이 입사광 색 자체가 되는 거이므로, 입사 벡터와 시선 벡터가 거의 같을 때 반사광에 금속의 색이 첨가되는 현상을 말한다.
Page 58: 컬러 시프트
컬러 시프트가 발생하는 원인에는 프레넬 반사에 있다. 빛이 정면에 있을 때 프레넬 반사가 굴절률에 의존하므로 물질의 색이 나타나지만, 빛이 뒤에 있을 때는 반사율이 굴절률을 따르 지 않기 때문에 입사광의 색이 그대로 반사되는 것이 컬러 시프 트 현상이다! 쿡과 토런스는 프네넬 계수를 주파수마다 사용하는 것이 귀찮아 서 바로 빛이 정면에 있을 때와 뒤에 있을 때의 색을 알고 있으므로 그 중간값을 선형 보간하자!라는 것이었다.
color = (l-t) * color0 + t * light_color
Page 59: 컬러 시프트
인수 t를 컬러 시프트 효과가 프레넬 계수에 의해 정해진다는 사 실로부터 대표적인 굴절률 n을 적당하게 결정한 프레넬 계수에 의하여 보간을 사용하도록 한다. t = F – F(0) / F(π/2) – F(0)
정리하면 컬러 시프트 계수를 계산하는 식은 다음과 같다. color = color0 + (light_color-color0) max(0,FF(0))/1-F(0)
Page 60: 쉐이더 코드로 알아보는 시간! 컬러 시프트
float F = 0.5f * gnc * gnc ( 1 + cgqc * cgqc / ( cgnc * cgnc ) ) / (qpc * qpc ); float F0 = ((n-1)*(n-1))/((n+1)*(n+1)); float4 color = c0 + (light_color-c0) * max( 0, F-F0 ) / ( 1-F0); t = F – F(0) / F(π/2) – F(0) color = color0 + (light_color-color0) max(0,FF(0))/1-F(0) 위와 같은 식을 코드로 옮긴 것에 불과하다. 아무튼 복잡한 건 맞다.. 빛이 뒤쪽에 있을 때 컬러시프트는 잘 알 수 있다.. 내가 보기엔…솔직히.. 차이점을 잘 모르겠다.
Page 61: 범프 맵핑에 대해.
범프 맵핑 범프맵 정점 좌표계( 표면 좌표계 )
Page 62: 범프 맵핑
이전까지 조명효과를 다루어 오면서 노멀 벡터의 중요성을 알았 다. 즉, 조명은 노멀 벡터에 의해서 정해진다고 해도 과언이 아니다. 그러므로 조명의 강도를 조절하기 위해서 노멀 벡터를 변화시키는 방법이 널리 이용되고 있다. 일반적으로 노멀 벡터는 정점마다 설정되어 있어서 픽셀 단위 조 명에서도 그러한 정점에 정의된 노멀 벡터를 보간해서 사용한다. 즉! 노멀 벡터를 잘 이용하면 저폴리곤의 모델도 고폴리곤의 모델 처럼 보이게 할 수 있다!
Page 63: 범프 맵핑
특수한 텍스처( 범프맵핑에 사용될 텍스쳐 )를 통해 폴리곤 수를 늘 리지 않고 텍스쳐에 광원반응 값을 넣어서 마치 굴곡이 있는 것처 럼 나타내는 기술이다. 왼쪽 그림은 범프 맵핑을 사용하지 않은 것이고, 오른쪽 그림은 범프 맵핑을 사용한 것이다. 즉, 높이차가 있는 것을 볼 수 있다.
Page 64: 범프 맵핑…하지만 속임수일뿐…
하지만 실제로 폴리곤 조작을 하는 것이 아니므로 진짜로 폴리곤 에 기복이 생긴 게 아니다. 그러므로 범프 맵핑의 경우 옆면을 바라본다면 높이차가 없이 평 평한 면을 볼 수 있을 것이다.
Page 65: 서비스로… 노멀 맵핑에 대해서도 맛만…
근본적으로 범프 맵핑과 같은 원리지만 더 발전된 기술로서, 범프 맵핑이 텍스쳐의 광원값을 이용해 실제로 없는 굴곡을 있는 것처 럼 만들듯이 노멀 맵핑은 하이폴리곤 모델에서 얻은 값을 노멀맵 에 적용해 저폴리곤 모델을 마치 고폴리곤 모델처럼 보이게 하는 기술이다. 요즘 대부분의 게임에서 사용될 만큼 대중화된 기술이 다.
Page 66: 범프맵
법프 맵핑에 사용되는 특수한 텍스처를 지칭한다. 높이맵, 노멀맵의 두 가지 종류가 존재한다. 높이맵은 노멀 방향의 기복 크기를 기록한 텍스쳐로서 장소가 높은 곳이면 하얀색, 낮은 곳이면 검은색으로 표시한다. 간단히 생각해서 지형에서 사용되는 높이맵을 생각해도 될 듯 싶다.. 다만 이건 실제로 정점이 이동한게 차이일뿐… 노멀맵은 노멀 벡터의 방향을 나타낸다. 높이맵에서 기복을 계산하고 그 높이의 기복으로부터 노멀 벡터를 계산해서 텍스쳐에 기록한다.
Page 67: 높이 맵에서 노멀맵 구하기!
텍스처 좌표가 (i,j)인 높이 맵의 값을 H(i,j)라 하자. 가로 방향과 세로 방향의 높이맵의 변화는 좌우 텍셀에서의 높이 차이의 평균을 취해서 구할 수 있다. 다음 장에서 공식을 살펴보자. H(i,j-1)
H(i-1,j)
H(i,j)
H(i+1,j)
H(i,j+1)
Page 68: 높이 맵에서 노멀맵 구하기!
가로 방향( U ) δH/δU = ½ { [H(i+1,j)-H(i,j)] + [H(i,j)-H(i-1,j)} = ½ { [H(i+1,j) - H(i-1,j)]} 세로 방향( V ) δH/δV = ½ { [H(i,j+1)-H(i,j)] + [H(i,j)-H(i,j-1)} = ½ { [H(i,j+1) - H(i,j-1)]} H(i,j-1)
H(i-1,j)
H(i,j)
H(i+1,j)
H(i,j+1)
Page 69: 높이 맵에서 노멀맵 구하기!
텍셀의 간격을 1로 하면 기울기 벡터는 U축을 du, V축을 dv로 다음과 같이 표현할 수 있다. du = (1.0, δH/δU) = ( 1.0, ½ { [H(i+1,j) - H(i-1,j)]} ) dv = (1.0, δH/δV) = ( 1.0, ½ { [H(i,j+1) - H(i,j-1)]} ) 노멀 벡터는 기울기 벡터와 직교하기 때문에 외적의 계산을 통해 얻을 수 있다. N = du × dv / |du × dv| = normalize( -½ { [H(i+1,j) - H(i-1,j)]}, ½ { [H(i, j+1) - H(i,j-1)]}, 1 ) = normalize( -[H(i+1,j) - H(i-1,j)], [H(i, j+1) - H(i,j-1)], 2 )
Page 70: 높이 맵에서 노멀맵 구하기!
DX에서는 이와 같은 연산을 자동적으로 해주는 함수를 제공해주 고 있기 때문에 신경쓸 필요가 없다. 산의 높이 H(i,j)를 크기변환을 할 경우, 노멀값을 얼마나 기울이 는 지에 대한 조정하기 위한 값이 된다. 즉 만약 높이를 10배 크게 했을 경우 산의 기복은 더욱 커지게 될 것이다. 또 한가지의 노멀 맵의 섭동 크기를 조정하는 방법으로는 원래의 높이 맵을 흐릿하는 것으로 이 흐릿한 화상으로 인해 폭넓게 노멀 맵의 변화가 일어나므로 완만한 기복이 생성될 것이다.
Page 71: 높이맵은 실시간이 아닌 미리 준비를 하는 게 좋다.
실제로 게임에서 적용할 때에는 이렇게 실시간으로 계산해서 높 이맵을 만드는 것이 아니라 미리 그래픽툴을 통해 만들어 놓는 것 이 초기화의 부하를 줄이고 좀 더 편안하게 작업을 할 수 있다. 기본적으로 NVIDIA의 노멀맵 플러그인이 공개되어있으므로 이 를 사용해서 쉽게 높이 맵을 노멀 맵으로 변환할 수 있으므로 사용 하는 게 좋을 거 같다.
Page 72: 정점 좌표계( 표면 좌표계 )
지금까지는 조명계산을 월드좌표 혹은 로컬좌표에서 하였지만 범 프 맵핑을 할려면 새로운 좌표계를 고려해야 한다. 그것이 바로 정점별로 계산한 접선 벡터를 기저 벡터로 한 좌표 계이다. 즉 모든 정점마다 노멀 벡터가 Z축의 하늘 방향으로 향하고, 접 선 벡터가 X축을, 바이노멀(종법선) 벡터가 Y축을 향한다. 정점 좌표계는 노멀 벡터가 Z축을 향해 있다는 것으로부터 정확 히 오브젝트의 메시를 XY평면에서 펼친 것 같은 이미지가 된다.
Page 73: 정점 좌표계에 대해서 좀더…
정점 좌표계는 즉 지구본을 2D화면으로 펼친 것으로 접선벡터 (Tangent)가 가로 x축, 종법선벡터(binormal)이 세로 y축을 가리키고 법선벡터(normal)이 z축이 높이를 가리키게 된다. 즉, 노멀맵 그대로 노멀이 된다는 것이다. 정점 좌표계에서의 z축은 정점별 노멀벡터지만 조명 계산을 할 경우에는 노멀맵에 의한 노멀벡터를 사용해서 픽셀 단위로 기복 이 있는 조명을 처리하게 된다.
Page 74: 정점 좌표계에 대해서 좀더…
노멀맵에서 저장하는 노멀방향을 로컬 좌표계나 월드좌표계에 서의 노멀벡터로 하면 각각의 좌표계에서 범프 맵핑에 의한 조 명이 가능하겠지만 노멀 맵을 생성하는 것은 상당히 피곤한 일 이다. 실행 시 노멀맵의 노멀벡터를 로컬 좌표계로 변환하는 경우도 있 지만 이 방법은 픽셀 셰이더의 계산 부하가 많아져 추천할 만 한 방법이 아니다. 즉, 차후 픽셀 셰이더에서 이 부하가 적어진다면 정점 좌표계가 필요 없어질 지도 모름…
Page 75: 정점 좌표계 즉 탄젠트 공간의 개념
만약 우리가 어떠한 3차원 물체를 2차원 평면으로 만들었다면, 우리는 이 2차원 평면을 바탕으로 3차원의 물체를 절대로 만들 수 없다. 하지만 3차원 물체를 2차원 평면으로 변환할 때, 2차 원 평면의 각 점에 그것이 3차원에서 가졌던 어떠한 정보를 저 장해 두었다면 어떠할까? 이 경우 우리는 어떻게든 2차원 평면을 다시 3차원 물체로 복원 시킬 수 있다. 여기서 말하는 그 정보가 바로 탄젠트 공간이다!
Page 76: 정점 좌표계 즉 탄젠트 공간의 개념
3차원 공간의 탄젠트 공간은 다음의 세 벡터를 축으로 하는 공간 이다.
한 점의 법선 벡터( 노멀 벡터 ) 한 점의 접선 벡터( 탄젠트 벡터 ) 종법선 벡터 ( 바이노멀 벡터 ) : 법선과 접선 벡터를 외적하면 구해지는 벡터
일반적으로 탄젠트 공간은 원래의 공간과 같 은 차원을 가지며, 한 점에서 생성 가능한 모 든 접선 벡터를 포함한다. 가령 옆 그림의 경 우, 접선 벡터와 바이노멀 벡터가 생성하는 평면은 가능한 모든 접선 벡터를 포함한다. 여기서 주의할 점은 탄젠트 공간의 개념은 비 단 3차원 공간만이 아닌 임의의 차원의 공간 에 대해서도 적용될 수 있다는 점이다.
Page 77: 정점 좌표계 즉 탄젠트 공간의 개념
또한 모든 탄젠트 공간들은 2배의 dimension을 갖는 새로운 공 간을 생성하기 위해 서로 붙여질 수 있는데 이를 가능하게 해주는 것이 바로 탄젠트 번들의 개념이다. 이에 대한 개념은 형태학 및 manifold등의 개념을 이해하여 야 하므로 자세한 설명은 생략하기로 한다. 왜 사인,코사인이 아닌 탄젠트를 이용하는 것일까? 한 점의 기울기를 가장 직관적으로 구할 수 있는 것이 다름아닌 탄젠트이기 때문이다. 가령 우리가 한 점의 기울기를 구해야 한다고 하자면 한 점의 접 선을 빗변으로 하는 직각 삼각형을 생성하여야 하고 즉 그 직각 삼각형의 높이/밑변으로 기울기를 구해주게 된다. 바로 이 기울기가 직각 삼각형의 빗변에 대응하는 각도의 탄젠 트값이다. 즉 기울기가 탄젠트이고 탄젠트가 기울기라는 것이다.
Page 78: 로컬 좌표계 공간에서 탄젠트 공간으로 변환하자!
접선 벡터 T는 텍스처의 가로 방향 u가 변화했을 때 위치 좌표 변화이므로 정규화를 고려하지 않는다면 다음과 같다. T = dP / du 종접선 벡터 B는 텍스처 좌표의 세로 방향 v가 변화했을 때의 위치 좌표 변화이므로 다음과 같다. B = dP / dv
Page 79: 로컬 좌표계 공간에서 탄젠트 공간으로 변환하자!
여기서 폴리곤의 정점 좌표를 x0, x1, x2 텍스쳐 좌표를 (u0,v0),(u1,v1),(u2,v2)라 하면 정점 좌표는 접선 벡터와 종법선 벡터를 사용해서 다음과 같이 쓸 수 있다.
Q1 = P1 – P0 Q2 = P2 – P0 T = dP / du, B = dP / dv 이 식을 이용해서 Q1 = (u1-u0)T + (v1-v0)B Q2 = (u2-u0)T + (v2-v0)B P1(u1,v1)
P2( u2,v2)
Q2
P(u0,v0) Q1
Page 80: 로컬 좌표계 공간에서 탄젠트 공간으로 변환하자!
Q1 = (u1-u0)T + (v1-v0)B Q2 = (u2-u0)T + (v2-v0)B 이렇게 구한 식에서 u1-u0 = s1 v1-v0 = t1 u2-u0 = s2 v2-v0 = t2 즉 다시 한번 정리하면… Q1 = [ s1 t1 ] [ T ] Q2 = [ s2 t2 ] [ B ] 로 표시할 수 있다.
Page 81: 로컬 좌표계 공간에서 탄젠트 공간으로 변환하자!
마지막으로 우리는 T,B의 값을 구해야 되기 때문에.. 앞의 행렬을 역행렬을 통해 다음과 같이 표현할 수 있다. [ t2 -t1 ] [ Q1] [T] 1/(s1t2-s2-t2) * [ -s2 s1 ] [ Q2] = [B] 1/(s1t2-s2-t2)는 정규화할 때 사라지므로 신경쓸 필요가 없다.
즉 T는 ( t2(Q1)x – t1(Q2)x, t2(Q1)y – t1(Q2)y, t2(Q1)z - t1(Q2)z ) B는 ( -S2(Q1)x – S1(Q2)x, -S2(Q1)y – S1(Q2)y, -S2(Q1)z – S1(Q2)Z )
Page 82: 로컬 좌표계 공간에서 탄젠트 공간으로 변환하자!
[ Tx Bx Nx ] [ Ty By Ny ] [ Tz Bz Nz ] 즉 이 행렬은 탄젠트 공간에서 로컬 공간으로 변환할 수 있는 것 이기 때문에 우리는 지금 로컬 공간에서 탄젠트 공간으로 변환해 야 되기 때문에 이를 역행렬 시키면 된다.
하지만 직교 행렬의 성질(역행렬==전치행렬)을 이용해 전치시 키면 [ Tx Ty Tz ] [ Bx By Bz ] [ Nx Ny Nz] 을 구할 수가 있다.
Page 83: 로컬 좌표계 공간에서 탄젠트 공간으로 변환하자!
위에서 구한 T와 B 그리고 N이 직교하는 지 알수 없기에 재 정 규화를 통해 교정을 해줘야 한다. 그람-슈미트 알고리즘을 통해 T를 정규화 시켜주면 된다. 그리고 나서 B는 T와 N의 외적을 통해 구해주면 된다. T’ = T – ( N ● T )N B’ = N × T 다시 이 구한 값들을 이용해 행렬로 변환하면… [ T’x T’y T’z ] [ B’x B’y B’z ] [ Nx Ny Nz ]
Page 84: 쉐이더 코드로 알아보는 시간! 범프 맵핑
struct VS_OUTPUT { float4 Pos float4 Color float2 Tex float3 L float3 E };
: POSITION; : COLOR0; : TEXCOORD0; : TEXCOORD1; : TEXCOORD2;
// 정점색 // 디컬텍스처좌표 // 광원벡터 // 노멀벡터
정점 셰이더에서 픽셀 셰이더로 넘길 구조체를 정의한다.
Page 85: 쉐이더 코드로 알아보는 시간! 범프 맵핑
// 위치 변환 Out.Pos = mul( Pos, mWVP ); // 메시 색 Out.Color = vColor; // 광원*메시 색을 설정 // 텍스처 좌표 Out.Tex = Texcoord; // 디컬용 텍스쳐 좌표 설정 좌표계 변환 기저 float3 N = Normal; float3 T = Tangent; float3 B = cross( N,T );
Page 86: 쉐이더 코드로 알아보는 시간! 범프 맵핑
// 반영 반사용 벡터 float3 E = vEyePos – Pos.xyz; Out.E.x = dot( E,T ); Out.E.y = dot( E,B ); Out.E.z = dot( E,N ); float3 L = -vLightDir.xyz; Out.L.x = dot( L,T ); Out.L.y = dot( L,B ); Out.L.z = dot( L,N );
// 시선벡터를 구한다.
// 광원 벡터
정점 공간(탄젠트 공간)의 기저 벡터를 사용해서 시선 벡터와 광원 벡터를 로컬 좌표 계에서 정점 공간 좌표계(탄젠트 공간)로 변환합니다. 기저 벡터 변환은 기저 벡터를 세로로 나열한 종행렬을 사용하여 변환하지만, 시선 벡터등의 보통 벡터(의 계수)는 기저 벡터를 가로로 나열한 횡행렬로 사용해서 변환합 니다. 횡행렬로 변환하면 각 각의 기저 벡터와 내적 계산한 것이 새로운 계수의 성분 이 된다.
Page 87: 쉐이더 코드로 알아보는 시간! 범프 맵핑
픽셀 셰이더에서의 처리 부분 // 노멀맵으로부터 노멀값을 가져온다. // 노멀 벡터는 [-1,1]의 범위를 가지는데 색상값은 [0,1]의 범위를 가지므로, // 2를 곱한 후 -1을 빼주어 노멀 벡터의 범위로 변환하도록 한다. float3 N = 2.0f*tex2D( NormalSamp, In.Tex ).xyz-1.0; float3 L = normalize(In.L); float3 R = reflect(-normalize(In.E), N); float amb = -vLightDir.w; // 광원벡터 // 반사벡터 // 환경광의강도
return In.Color * tex2D( DecaleSamp, In.Tex )/ / 확산광과 환경광에 * (max(0, dot(N, L))+amb) // 정점색과텍스처색을합성한다 + 0.3f * pow(max(0,dot(R, L)), 8); // Phong반영반사광
Page 88: 정리하면서 참고하였던 자료들…
http://x66vx.egloos.com/m/3716315 노멀벡터를 월드좌표계 공간으로 변환할 때 왜 이래야 되는지에 생각한 자료…
Page 89: