프로그래밍 2017.07.12 22:59

스크린 스페이스 데칼 구현 원리

 

 

이전부터 구현해 보고 싶었던 데칼!

쉐이더 작업 이전에는, 실시간으로 면을 구성해서(버텍스)

UV 를 만들고, 데칼이미지를 붙이는 방식으로,

면 구성에 있어서도 복잡성이 높고,

문제점도 많은 방식으로 알고 있었다

 

지금에는 실시간 면 구성이라는 부분이 필요없고,

씬의 장면이 렌더링된 깊이 버퍼를 이용해서,

기존의 오브젝트 렌더링 쉐이더에 추가적인 작업만 해주면,

비교적 간단하게 구현이 가능하다

 

포스트 이펙트용 2D 이미지에 덧그린다던지 하는 방식이 아니다!

기존의 오브젝트 렌더링 쉐이더 코드에 추가적인 작업이 필요할 뿐이다

렌더링 스테이트가 바뀐다던지 하는 부분도 없다

 

 

//참고

 

https://www.slideshare.net/blindrendererkr/40000

 

http://ttmayrin.tistory.com/37

 

http://ldh9451.tistory.com/entry/Screen-Space-Decal

 

 

구현에 대한 프리텐테이션과, 소스가 있음에도 불구하고,

원리에 대해서는 설명들이 좀 부족한거 같다

소스 자체는 라인도 적고, 간단한 편인데,

일단 이해가 잘가지 않으니...

 

이것저것 뒤져보고, 연구해 보면서,

이해한 내용을 정리해 놓으려 한다

관심있으신 분들은 참고하시길 ^^

 

 

픽셀의 깊이값으로 뷰상에서의 위치값을 만들어 내는 방법

참고

 

http://m.blog.daum.net/rockeracer/38

 

디퍼드 렌더링에서는 기본적으로 사용하는 부분인것 같다,

하지만, 나는 포워드 렌더링을 사용하니,

처음보았을때,

어디서 도출된 코드인지를 몰라서, 이해가 가지 않았었다

 

 

데칼 구현의 원리에 대해서 간단하게 정리해 보자면,

 

 

 

 

 

위 이미지에서 보듯이

땅과 박스의 접점 픽셀을 골라내고, (주황색 영역)

이 픽셀에 데칼 이미지를 입히는 것이라 할수 있다

 

일단, 데칼의 이미지를 입히는 부분은 빼고,

접점 픽셀을 골라내는 부분에 집중해보자

(우리가 좀더 궁금한 부분은 이부분일 것이다)

 

접점 픽셀이란 곧,

씬의 깊이버퍼의 픽셀중

박스의 min, max 값 사이에 위치하는 픽셀이라 할수 있다,

개념적으로는 매우 간단하다,

실제 이것만 구현이 가능하다면, 땅이 얼마다 울퉁불퉁하던지 간에

땅에 착 달라붙는 데칼을 만들수가 있다

 

월드영역이라면, 우리는 꽤 간단하게 구할수 있을 것이다

하나의 점이 AABB( 또는 OBB) 의 박스 영역에 들어오는지를 체크하는 것과 동일하다

 

쉽지 않은가?

 

ㅎㅎㅎ

 

그런데, 사실, 쉐이더의 PS 내부가 아니라면, (cpp 소스 구현 상에서는)

하나하나의 픽셀의 위치를 알수는 없다,

그러므로, 픽셀 하나하나가 박스 경계속에 포함되는지 체크해 볼수가 없다

 

그럼, 쉐이더 연산에서는 구할수 있을까?

PS() 에서는,

하나 하나의 픽셀을 처리하므로, 가능하다

 

만약,

땅의 픽셀의 월드값을 알수 있다면,

픽셀 쉐이더 함수에서,

데칼박스의 min, max 값( 월드상에서의 값, 박스를 회전시키지 않는다는 가정하에)

사이에 위치하는 지를 체크하고,

매핑을 시키던지, 색상을 바꾸던지해서 표시해볼수 있을 것이다,

 

 

실제 구현을 한다고 생각해 보자

 

1. 데칼 박스를 그릴때

PS() 에서, 장면이 그려진( 위의 이미지에서는 땅 ) depth 버퍼를 가져와,

데칼 박스와 겹쳐지는 땅픽셀의 깊이값을 가져올수 있다

 

픽셀의 깊이값이 있으면, 알려진 몇 개의 방법으로,

스크린좌표상( 프로젝션이 적용된 )의 위치값을 알아올수 있다

 

픽셀의 스크린좌표( 프로젝션이 적용된) 값을 Projection Inverse 매트릭스와 곱하면,

View 상에서의 위치값이 나올것이고,

 

다시 이값을 View Inverse 매트릭스와 곱하면,

월드좌표상의 값을 구할수 있을 것이다

 

이 픽셀의 월드값이,

PS() 로 넘겨진, 데칼 박스의 min, max 값 사이에 있는지만 체크해서,

사이에 있다면, 이미지를 매핑하는 식으로

 

땅에 달라붙은 데칼을 그릴수 있다

 

-> 사실, 이론상으로만 적어보았고, 테스트를 해보지는 않았다

-> projection inv 와 view inv 를 곱해서, 월드값을 구할수 있는지 잘 모르겠다

-> 내 기억속에서는 안됐던것 같기도 한데

 

-> 어쨋든, 데칼박스를 렌더링할때 PS() 에서

-> 렌더링하는 픽셀 위치에 겹쳐지는

-> 땅의 깊이값을 구하고( z 값만이 있으므로), xy 값도 구해둔다 (위치값)

-> 이 위치값이

-> 데칼박스의 영역안에 있는지를 체크하면,

-> 위 이미지에서의 주황색 영역을, 정확히 구할수 있다라는 것을 이야기하고 싶었다

 

-> 땅이 울퉁불퉁하다던지, 면이 어떤식으로 얽혀있다던지 아무런 문제가 되지 않는다

-> 픽셀 하나하나가 영역안에 있는지를 체크한 것이기 때문데

 

-> 결론적으로, 월드값으로 체크하는 방식은

-> 실제 구현이 가능할지와,

-> 화면안에서 데칼박스가 여러개 있을때라든지,

-> 최종적으로 uv 를 어떤식으로  매핑시킬 것인지

-> 이것저것 문제가 많은 방법이긴 하다

 

 

알려진 방식으로 구현한 스크린 스페이스 데칼

(밑의 이미지들)

 

 

 

 

 

 

위에 링크걸어둔 사이트에서의 데칼 구현 방식에 대해서 설명해 보자면,

 

데칼박스의 렌더링 픽셀의 위치에 해당하는

씬의 깊이값을 구하고,

이 씬의 깊이값을 카메라 TopRight 를 이용해서, View( 카메라뷰) 에서의 위치값을 구한다

 

씬픽셀의 View 위치값 (xyz) 이,

데칼박스의 영역에 들어오는지를 체크하기 위해서

 

카메라뷰와 데칼박스월드의 inverse 매트릭스와 곱하여

 

-> zMatrix zMatInv, zMatW;
-> zMatrixInverse( &zMatInv, NULL, &g_matView );    
-> zMatrixInverse( &zMatW, NULL, &m_LocalToWorld );

-> zMatrixMultiply( &zMatInv, &zMatInv, &zMatW );

-> zMatInv 값을 넘겨준다

 

-> g_matView 는 전역 카메라뷰

-> m_LocalToWorld 는 데칼박스의 회전, 스케일, 이동이 포함된 매트릭스

 

 

View 위치값을 local 값으로 변경

 

 

씬의 픽셀의 local 위치값을

데칼박스의 로컬값 min, max 와 비교해서,

영역안에 들어온다면, 현재 렌더링되는 데칼박스의 pixel 색상을 변경

-> 어차피 겹쳐지는 부분이므로,

-> 현재 렌더링되는 데칼박스의 pixel 의 색상을 조정해주면, 이것이 데칼로 표시된다

-> 이것이 핵심!

 

주의할것은, 데칼박스는 계산의 편의를 위해서,

정점의 xyz 위치가 -05 ~ 0.5 사이의 값들로 이루어진 박스라는 것이다

(맵에서는 필요한만큼 스케일해서 사용)

 

min( -0.5, -0.5, -0.5 ) max( 0.5, 0.5, 0.5 ) 

이렇게 구성해 놓으면, UV 값 역시 1:1 매칭이 가능하다

 

그래서, PS() 에서 이런식으로 구현이 가능해진다

 

//decalLocalpos 는 씬픽셀의 로컬위치값

 

// 위치가 xyz 각각의 축으로 0.5 범위가 넘으면 버려진다

clip( 0.5 - abs( decalLocalpos.xyz ) );

 

// 위치값을 그대로 uv 값으로 사용

TexUV = decalLocalpos.xz + 0.5f;    

 

 

//참고로 View TopRight 구하는 부분은 이렇게
zMatrix matInvProj;
zMatrixInverse( &matInvProj, NULL, &g_matProj );

 

Vector4 vTopRight;
vTopRight.x = 1;
vTopRight.y = 1;
vTopRight.z = 1;
vTopRight.w = 1;

D3DXVec4Transform( &vTopRight, &vTopRight, &matInvProj );
vTopRight.y = -vTopRight.y; //?
//vTopRight /= vTopRight.w;

 

구현 소스는 위에 링크된 블러그 들에서 자세하게 구현되어 있으니

(나역시 거의 그대로 베껴서... ^^ 감사합니다 ~! )

생략해도 될듯

 

 

저작자 표시 비영리 변경 금지
신고
posted by 게임스타 게임스타

댓글을 달아 주세요