* 스카이박스( Skybox )
가상공간의 표현에서 인테리어와 익스테리어 양쪽에 다 사용되는 빠질 수 없는 부분이 하늘에 대한 표현이라고 볼 수 있다.
하늘에 대한 표현방식도 여러 가지가 있겠지만, 이 또한 실시간 처리라는 제약사항 때문에
주로 스카이 박스라는 기법을 가장 많이 사용하고 있다.
이 기법은 가상공간을 감싸고 있는 정육면체의 박스의 각 면에 미리 렌더링된 256x256정도 크기의 하늘 텍스쳐를
6장 준비해서 정육면체의 각 면에 입힌다는 간단한 방식이다.
이 때 고려되는 사항으로는 박스에 텍스쳐가 입혀진 형태이기 때문에 하늘을 보았을 때
박스라는 걸 인지할 수 없도록 해야 한다는 것이다.
이를 위해서 사용되는 방식으로 3ds max와 같은 여러 모델링툴에서 쉽게 볼 수 있는
environment mapping이 사용되고 있다.
그리고, 텍스쳐의 정적인 이미지를 보완하기 위해서 구름을 별도로 표현하는 방식도 많이 쓰이고 있는데,
대체로 스카이 박스를 만들고 다시 구름에 대한 레이어를 만든 후에 구름 텍스쳐를 입힌 상태에서
알파블렌딩을 적용하면서 텍스쳐를 움직이는 방식을 많이 사용하고 있다.
좀 더 자연스럽게 보이기 위해서 여러 레이어를 두는 방식도 많이 사용된다.
또한 스카이 박스의 변형된 형태로 cylinder또는 sphere가 사용되는 게임들도 볼 수가 있다.
* 스카이박스를 생성하기위한 절차를 보면 대략 이렇다.
1. 육면체를 만들수 있는 버텍스 구조를 만든다.
예)struct SkyVertex
{
D3DXVECTOR3 p;
float u, v;
}
texture의 숫자와 position의 숫자가 같은 경우에만 인덱스를 사용 .
텍스쳐좌표값을 일일이 넣어야 되므로 인덱스를 생성하지않고 버텍스단위로 처리
2. 사이즈가 적당히 커야된다.
3. 카메라와 같이 움직여야 한다.
이 부분이 좀 어려웠지만 쉽게 해결됐음.
월드행렬의 _41, _42, _43 을 사용해서 카메라 이동시 스카이박스도 같이 움직이도록 했음.
( _41 = x축이동, _42 = y축이동, _43 = z축이동 )
4. 렌더링옵션
Z-Buffer off -> 스카이 박스 렌더링 -> Z-Buffer on
화면클리어 off + Z-Fighting 을 막을 수 있다. 알파 테스터는 필요할 때만 사용.
( 화면클리어를 on 해도 잘나왔음. 이론상으로 off 라고 하는데 on 해도 되었음 )
5. 모서리 처리
텍스쳐처리후 각 모서리에 선이 간 것을 보완해준다.
* CLAMP 옵션 사용 -> 접합부분이 보이지 않게 한다. ( u, v 값 0.01 - 0.99 ? )
6. 구름
* 스카이박스 렌더링부분, 메인 렌더링 부분
HRESULT CSkybox::_Render()
{
// 카메라가 스카이박스 안에 있으므로 안쪽이 보이게 함
m_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_CCW );
// D3DSAMP_ADDRESSU : u 좌표로 사용하는 텍스처어드레싱모드. 디폴트는 D3DTADDRESS_WRAP 이다.
// 더 자세한 정보는, 「D3DTEXTUREADDRESS 」를 참조할것.
// D3DSAMP_ADDRESSV : v 좌표로 사용하는 텍스처어드레싱모드. 디폴트는 D3DTADDRESS_WRAP 이다.
// 더 자세한 정보는, 「D3DTEXTUREADDRESS 」를 참조할것.
// D3DTADDRESS_CLAMP: 범위 [0.0, 1.0] 의 외측의 텍스처 좌표가, 각각, 0.0 으로 1.0 의 텍스처 컬러로 설정된다.
m_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSU, D3DTADDRESS_CLAMP);
m_pd3dDevice->SetSamplerState(0, D3DSAMP_ADDRESSV, D3DTADDRESS_CLAMP);
m_pd3dDevice->SetFVF( D3DFVF_SKYVERTEX );
m_pd3dDevice->SetStreamSource( 0, m_pVB, 0, sizeof( SKYVERTEX ) );
// 각 면마다 텍스쳐를 입힘
for( int i = 0; i < 6; i++ )
{
m_pd3dDevice->SetTexture( 0, m_pTex[ i ] );
m_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, i*4, 2 );
}
return S_OK;
}
void Render()
{
/// 후면버퍼와 Z버퍼 초기화
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB( 0, 0, 255 ), 1.0f, 0 );
// 애니메이션 행렬설정
// Setup the world, view, and projection matrices
Animate();
/// 렌더링 시작
if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
{
// SKY맵 : 카메라와 같이 움직여야 한다.
// 월드 매트릭스의 _41 = x축이동
// _42 = y축이동
// _43 = z축이동
D3DXMatrixIdentity( &g_matWorld );
g_matWorld._41 = g_pCamera->GetEye()->x;
g_matWorld._42 = g_pCamera->GetEye()->y;
g_matWorld._43 = g_pCamera->GetEye()->z;
g_pd3dDevice->SetTransform( D3DTS_WORLD, &g_matWorld );
// z 버퍼를 끈다. ( z 값을 가지지 않게 설정 )
g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, FALSE );
g_pSkyBox->Draw();
// 지형맵
D3DXMatrixIdentity( &g_matWorld );
g_pd3dDevice->SetTransform( D3DTS_WORLD, &g_matWorld );
g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
g_pTerrain->Draw();
if( !g_bHideFrustum ) g_pFrustum->Draw( g_pd3dDevice );
/// 렌더링 종료
g_pd3dDevice->EndScene();
}
/// 후면버퍼를 보이는 화면으로!
g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
}