멋진 그래픽을 만드려고 노력하다 보면 알파블렌딩만으로는 원하는 걸 만들지 못할 때가 있다.
광선(빛)효과 같은 거는 두 색을 섞으면 안 되고, 원래 색에 색을 더 더해서 밝게 나타나게 해야한다. 혹은 하나를 다른 그림의 마스크로 쓰고 싶을 때도 있을 것이다.
그래서 다양한 그래픽 효과를 여기서 추가해보고자 한다.
D3D에서는 Render State라고 해서 렌더링 시에 여러가지 설정을 줄수 있게 해놓았다.
IDirect3DDevice9::SetRenderState로 RenderState를 설정할 수 있고
IDirect3DDevice9::GetRenderState로 RenderState값을 가져올 수 있다.
여기서는 특히 중요한 몇 개의 설정들을 살펴보겠다.
*D3DRS_ALPHAFUNC
알파채널이 섞인 텍스쳐를 화면에 출력할 때 어떻게 할 것인가?
1. D3DCMP_NEVER : 절대로 출력하지 않는다.
2. D3DCMP_LESS : D3DRS_ALPHAREF 값보다 작으면 출력한다.
3. D3DCMP_EQUAL : D3DRS_ALPHAREF 값과 같으면 출력한다.
4. D3DCMP_LESSEQUAL : D3DRS_ALPHAREF 값과 같거나 작으면 출력한다.
5. D3DCMP_GREATER : D3DRS_ALPHAREF 값보다 크면 출력한다.
6. D3DCMP_NOTEQUAL : D3DRS_ALPHAREF 값과 같지 않으면 출력한다.
7. D3DCMP_GREATEREQUAL : D3DRS_ALPHAREF 값과 같거나 작으면 출력한다.
8. D3DCMP_ALWAYS : 항상 출력한다.
*D3DRS_ALPHAREF
D3DRS_ALPHAFUNC에서 비교대상으로 사용할 값
*D3DRS_BLENDOP
텍스쳐가 그려질 때, dest과 src를 어떻게 섞을까?
(dest는 원래 있는 그림을 말하고, src는 새로 그려질 그림을 말합니다.)
1. D3DBLENDOP_ADD : Result = dest + src
2. D3DBLENDOP_SUBTRACT : Result = src - dest
3. D3DBLENDOP_REVSUBTRACT : Result = dest - src
4. D3DBLENDOP_MIN : Result = dest, src 중 작은 값
5. D3DBLENDOP_MAX : Result = dest, src 중 큰 값
*D3DRS_SRCBLEND, D3DRS_DESTBLEND
dest과 src를 섞을때 어떤 값을 곱할까?
1. D3DBLEND_ZERO : 0을 곱한다.
2. D3DBLEND_ONE : 1을 곱한다.
3. D3DBLEND_SRCCOLOR : src의 R, G, B, A값을 곱한다.
4. D3DBLEND_INVSRCCOLOR : src의 1-R, 1-G, 1-B, 1-A값을 곱한다.
5. D3DBLEND_SRCALPHA : src의 Alpha값을 곱한다.
6. D3DBLEND_INVSRCALPHA : src의 1-Alpha값을 곱한다.
7. D3DBLEND_DESTALPHA : dest의 Alpha값을 곱한다.
8. D3DBLEND_INVDESTALPHA : dest의 1-Alpha값을 곱한다.
9. D3DBLEND_DESTCOLOR : dest의 R, G, B, A값을 곱한다.
10. D3DBLEND_INVDESTCOLOR : dest의 1-R, 1-G, 1-B, 1-A값을 곱한다.
이 얘기로 충분히 이해가 안 될지도 모른다.
그래서 예를 들어가면서 설명하겠다.
*만약 어떤 텍스쳐에서 알파채널이 127이상인 부분만 출력하고 싶다면?
D3DRS_ALPHAREF 를 127로 설정하고
D3DRS_ALPHAFUNC 를 D3DCMP_GREATEREQUAL 로 한다.
*알파블렌딩을 하고 싶어요. 즉 src의 알파값이 255면 src가 그대로 나타나고, 127이면 src와 dest가 반반씩 섞여서 나오고, 0이면 dest가 그대로 나오게 하고 싶으면?
D3DRS_DESTBLEND 를 D3DBLEND_INVSRCALPHA 로 설정하고
D3DRS_SRCBLEND 를 D3DBLEND_SRCALPHA로 설정하고
D3DRS_BLENDOP 을 D3DBLENDOP_ADD로 설정한다.
그러면 src에 (알파값)이 곱해지고 dest에 (1-알파값)이 곱해지고 둘을 더한 것이 결과가 된다.
Result= src * SrcAlpha + dest * (1 - SrcAlpha)
그러면 Src의 알파값이 클수록 Src가 진하게 나타나고, 작을수록 Dest가 진하게 나타나겠죠?
*블렌딩이고 뭐고 없이 src를 그대로 화면에 출력하고 싶으면?
D3DRS_DESTBLEND를 D3DBLEND_ZERO,
D3DRS_SRCBLEND를 D3DBLEND_ONE,
D3DRS_BLENDOP을 D3DBLENDOP_ADD로 설정한다.
Result = src * 1 + dest * 0 이 되어서 결국 Result = Src 가 됩니다.
Src의 알파값이 어떻든 항상 Src가 그대로 출력되는것이죠.
*dest에 src를 더해지게 하고 싶으면?
D3DRS_DESTBLEND를 D3DBLEND_ONE,
D3DRS_SRCBLEND를 D3DBLEND_ONE,
D3DRS_BLENDOP을 D3DBLENDOP_ADD로 설정한다.
Result = src * 1 + dest * 1
src의 알파값이 반영되게 하고 싶으면
D3DRS_SRCBLEND를 D3DBLEND_SRCALPHA로 설정하면
Result = Src * SrcAlpha + Dest * 1 이 되어, 알파값 반영된다.
*dest에 src를 마스킹하고 싶으면?
D3DRS_DESTBLEND를 D3DBLEND_SRCCOLOR,
D3DRS_SRCBLEND를 D3DBLEND_ZERO,
D3DRS_BLENDOP을 D3DBLENDOP_ADD로 설정한다.
Result = Src * 0 + Src * DestColor = Src*Dest가 되어서 마스킹된다.
기타 등등등... 응용방안은 무궁무진하다. 게다가 텍스쳐 출력시에 넣어주는 Color값이 기억나는가? 그것까지 이용한다면 더 강력한 그래픽 효과를 사용할수 있을것이다.
Result = Dest * DestBlend + Src * SrcBlend * Color (BlendOp에 따라서 +가 - 나 min, max로 바뀔 수 있는 것도 기억!)
참고:
*D3DRS_ALPHAREF 를 0으로 설정하고 D3DRS_ALPHAFUNC 를 D3DCMP_GREATER로 설정하면
알파채널이 0보다 큰 부분만 출력됩니다. 근데 D3DRS_ALPHAFUNC 를 D3DCMP_ALWAYS 로 설정해도 어차피 알파채널이 0인 부분은 그리면 출력안되니깐 둘은 같지 않은가?
같지 않다! AlphaFunc에서 걸러진 픽셀들은 SrcBlend, DestBlend에 와보지도 못하지만, AlphaFunc을 통과한 픽셀은 SrcBlend, DestBlend 단계까지 올 수 있다.
예) D3DRS_SRCBLEND 는 D3DBLEND_ONE, D3DRS_DESTBLEND 는 D3DBLEND_ZERO, D3DRS_BLENDOP 은 D3DBLENDOP_ADD 일때
D3DRS_ALPHAREF 가 0, D3DRS_ALPHAFUNC 이 D3DCMP_GREATER 인 경우와
D3DALPHAFUNC 이 D3DRS_ALWAYS 인 경우를 생각해보자.
첫 번째 경우는 알파값이 0인 픽셀들을 걸러져서 그려지지 않으므로, 그 부분은 원래 그림 그대로 남아있을 것이다.
두 번째 경우는 알파값이 0인 픽셀들이 AlphaTest를 통과하고 Result=Dest*0 + Src*1 이므로, 알파값이 0인 부분도 색칠될 것이다.
그리기 모드를 설정하는 함수를 추가해보자. RenderState를 설정하여 무궁무진한 그래픽 효과를 줄수 있지만, 그중에서 주로 쓰이는 몇 가지만 골라봤다.
별로 어려울 거 없다. 그냥 mode에 따라 IDirect3DDevice9::SetRenderState를 적절히 호출하여 설정값들을 바꿔주면 된다. 그리고 설정값을 바꾸기 전에 ID3DXSprite::Flush를 해주자.
ID3DXSprite는 Draw한 텍스쳐들을 곧바로 그리지 않고, 차곡차곡 쌓아두었다가 나중에 한 번에 그린다. 그렇기 때문에 쌓아놓는 도중에 RenderState를 바꾸면, 쌓아놓은 모든 그려할 텍스쳐들이 적용될 설정이 변하는 거다. 따라서 설정을 바꾸기 전에 Flush해줘서 쌓아놓은 텍스쳐들을 그리도록 한다.
한 번 테스트 해볼까? 저번에 불러왔던 스프라이트에 다양한 효과를 줘서 출력해 보자.
광선(빛)효과 같은 거는 두 색을 섞으면 안 되고, 원래 색에 색을 더 더해서 밝게 나타나게 해야한다. 혹은 하나를 다른 그림의 마스크로 쓰고 싶을 때도 있을 것이다.
그래서 다양한 그래픽 효과를 여기서 추가해보고자 한다.
D3D에서는 Render State라고 해서 렌더링 시에 여러가지 설정을 줄수 있게 해놓았다.
IDirect3DDevice9::SetRenderState로 RenderState를 설정할 수 있고
IDirect3DDevice9::GetRenderState로 RenderState값을 가져올 수 있다.
여기서는 특히 중요한 몇 개의 설정들을 살펴보겠다.
*D3DRS_ALPHAFUNC
알파채널이 섞인 텍스쳐를 화면에 출력할 때 어떻게 할 것인가?
1. D3DCMP_NEVER : 절대로 출력하지 않는다.
2. D3DCMP_LESS : D3DRS_ALPHAREF 값보다 작으면 출력한다.
3. D3DCMP_EQUAL : D3DRS_ALPHAREF 값과 같으면 출력한다.
4. D3DCMP_LESSEQUAL : D3DRS_ALPHAREF 값과 같거나 작으면 출력한다.
5. D3DCMP_GREATER : D3DRS_ALPHAREF 값보다 크면 출력한다.
6. D3DCMP_NOTEQUAL : D3DRS_ALPHAREF 값과 같지 않으면 출력한다.
7. D3DCMP_GREATEREQUAL : D3DRS_ALPHAREF 값과 같거나 작으면 출력한다.
8. D3DCMP_ALWAYS : 항상 출력한다.
*D3DRS_ALPHAREF
D3DRS_ALPHAFUNC에서 비교대상으로 사용할 값
*D3DRS_BLENDOP
텍스쳐가 그려질 때, dest과 src를 어떻게 섞을까?
(dest는 원래 있는 그림을 말하고, src는 새로 그려질 그림을 말합니다.)
1. D3DBLENDOP_ADD : Result = dest + src
2. D3DBLENDOP_SUBTRACT : Result = src - dest
3. D3DBLENDOP_REVSUBTRACT : Result = dest - src
4. D3DBLENDOP_MIN : Result = dest, src 중 작은 값
5. D3DBLENDOP_MAX : Result = dest, src 중 큰 값
*D3DRS_SRCBLEND, D3DRS_DESTBLEND
dest과 src를 섞을때 어떤 값을 곱할까?
1. D3DBLEND_ZERO : 0을 곱한다.
2. D3DBLEND_ONE : 1을 곱한다.
3. D3DBLEND_SRCCOLOR : src의 R, G, B, A값을 곱한다.
4. D3DBLEND_INVSRCCOLOR : src의 1-R, 1-G, 1-B, 1-A값을 곱한다.
5. D3DBLEND_SRCALPHA : src의 Alpha값을 곱한다.
6. D3DBLEND_INVSRCALPHA : src의 1-Alpha값을 곱한다.
7. D3DBLEND_DESTALPHA : dest의 Alpha값을 곱한다.
8. D3DBLEND_INVDESTALPHA : dest의 1-Alpha값을 곱한다.
9. D3DBLEND_DESTCOLOR : dest의 R, G, B, A값을 곱한다.
10. D3DBLEND_INVDESTCOLOR : dest의 1-R, 1-G, 1-B, 1-A값을 곱한다.
이 얘기로 충분히 이해가 안 될지도 모른다.
그래서 예를 들어가면서 설명하겠다.
*만약 어떤 텍스쳐에서 알파채널이 127이상인 부분만 출력하고 싶다면?
D3DRS_ALPHAREF 를 127로 설정하고
D3DRS_ALPHAFUNC 를 D3DCMP_GREATEREQUAL 로 한다.
*알파블렌딩을 하고 싶어요. 즉 src의 알파값이 255면 src가 그대로 나타나고, 127이면 src와 dest가 반반씩 섞여서 나오고, 0이면 dest가 그대로 나오게 하고 싶으면?
D3DRS_DESTBLEND 를 D3DBLEND_INVSRCALPHA 로 설정하고
D3DRS_SRCBLEND 를 D3DBLEND_SRCALPHA로 설정하고
D3DRS_BLENDOP 을 D3DBLENDOP_ADD로 설정한다.
그러면 src에 (알파값)이 곱해지고 dest에 (1-알파값)이 곱해지고 둘을 더한 것이 결과가 된다.
Result= src * SrcAlpha + dest * (1 - SrcAlpha)
그러면 Src의 알파값이 클수록 Src가 진하게 나타나고, 작을수록 Dest가 진하게 나타나겠죠?
*블렌딩이고 뭐고 없이 src를 그대로 화면에 출력하고 싶으면?
D3DRS_DESTBLEND를 D3DBLEND_ZERO,
D3DRS_SRCBLEND를 D3DBLEND_ONE,
D3DRS_BLENDOP을 D3DBLENDOP_ADD로 설정한다.
Result = src * 1 + dest * 0 이 되어서 결국 Result = Src 가 됩니다.
Src의 알파값이 어떻든 항상 Src가 그대로 출력되는것이죠.
*dest에 src를 더해지게 하고 싶으면?
D3DRS_DESTBLEND를 D3DBLEND_ONE,
D3DRS_SRCBLEND를 D3DBLEND_ONE,
D3DRS_BLENDOP을 D3DBLENDOP_ADD로 설정한다.
Result = src * 1 + dest * 1
src의 알파값이 반영되게 하고 싶으면
D3DRS_SRCBLEND를 D3DBLEND_SRCALPHA로 설정하면
Result = Src * SrcAlpha + Dest * 1 이 되어, 알파값 반영된다.
*dest에 src를 마스킹하고 싶으면?
D3DRS_DESTBLEND를 D3DBLEND_SRCCOLOR,
D3DRS_SRCBLEND를 D3DBLEND_ZERO,
D3DRS_BLENDOP을 D3DBLENDOP_ADD로 설정한다.
Result = Src * 0 + Src * DestColor = Src*Dest가 되어서 마스킹된다.
기타 등등등... 응용방안은 무궁무진하다. 게다가 텍스쳐 출력시에 넣어주는 Color값이 기억나는가? 그것까지 이용한다면 더 강력한 그래픽 효과를 사용할수 있을것이다.
Result = Dest * DestBlend + Src * SrcBlend * Color (BlendOp에 따라서 +가 - 나 min, max로 바뀔 수 있는 것도 기억!)
참고:
*D3DRS_ALPHAREF 를 0으로 설정하고 D3DRS_ALPHAFUNC 를 D3DCMP_GREATER로 설정하면
알파채널이 0보다 큰 부분만 출력됩니다. 근데 D3DRS_ALPHAFUNC 를 D3DCMP_ALWAYS 로 설정해도 어차피 알파채널이 0인 부분은 그리면 출력안되니깐 둘은 같지 않은가?
같지 않다! AlphaFunc에서 걸러진 픽셀들은 SrcBlend, DestBlend에 와보지도 못하지만, AlphaFunc을 통과한 픽셀은 SrcBlend, DestBlend 단계까지 올 수 있다.
예) D3DRS_SRCBLEND 는 D3DBLEND_ONE, D3DRS_DESTBLEND 는 D3DBLEND_ZERO, D3DRS_BLENDOP 은 D3DBLENDOP_ADD 일때
D3DRS_ALPHAREF 가 0, D3DRS_ALPHAFUNC 이 D3DCMP_GREATER 인 경우와
D3DALPHAFUNC 이 D3DRS_ALWAYS 인 경우를 생각해보자.
첫 번째 경우는 알파값이 0인 픽셀들을 걸러져서 그려지지 않으므로, 그 부분은 원래 그림 그대로 남아있을 것이다.
두 번째 경우는 알파값이 0인 픽셀들이 AlphaTest를 통과하고 Result=Dest*0 + Src*1 이므로, 알파값이 0인 부분도 색칠될 것이다.
그리기 모드를 설정하는 함수를 추가해보자. RenderState를 설정하여 무궁무진한 그래픽 효과를 줄수 있지만, 그중에서 주로 쓰이는 몇 가지만 골라봤다.
별로 어려울 거 없다. 그냥 mode에 따라 IDirect3DDevice9::SetRenderState를 적절히 호출하여 설정값들을 바꿔주면 된다. 그리고 설정값을 바꾸기 전에 ID3DXSprite::Flush를 해주자.
ID3DXSprite는 Draw한 텍스쳐들을 곧바로 그리지 않고, 차곡차곡 쌓아두었다가 나중에 한 번에 그린다. 그렇기 때문에 쌓아놓는 도중에 RenderState를 바꾸면, 쌓아놓은 모든 그려할 텍스쳐들이 적용될 설정이 변하는 거다. 따라서 설정을 바꾸기 전에 Flush해줘서 쌓아놓은 텍스쳐들을 그리도록 한다.
한 번 테스트 해볼까? 저번에 불러왔던 스프라이트에 다양한 효과를 줘서 출력해 보자.
'프로그래밍 > 스페샬' 카테고리의 다른 글
| 리듬게임을 만들어보자 3. 다양한 그래픽 효과 (0) | 2009/07/04 |
|---|---|
| 리듬게임을 만들어보자 2. 텍스쳐와 스프라이트 (0) | 2009/07/03 |
| 리듬게임을 만들어보자 1. 장치 생성과 메인 루프 (2) | 2009/07/03 |
TRACKBACK 0 AND
COMMENT 0
2d그래픽에서는 그림파일을 읽어와 화면에 뿌려주는게 그래픽 처리의 전부이다.
즉, 텍스쳐를 불러와놓고선 필요할때마다 화면에 뿌려주면 그래픽 처리는 끝!
파일에서 텍스쳐를 불러올때는 D3DXCreateTextureFromFile함수나 -Ex함수를 사용한다. 또한 D3DXCreateTextureFromFileInMemory로 메모리상의 파일에서 텍스쳐를 불러올수도 있다. 자세한 사용법은 msdn을 참고하길 바란다.
LPDIRECT3DTEXTURE9를 모아두기 위해서 m_vptxt 멤버변수를 추가했다. vector사용법에 대해서는 다들 잘 알것이라고 생각하므로 자세한 설명은 패쓰!
LoadTexture 함수가 두 개나 선언되었는데, 하나는 파일에서부터, 하나는 메모리상에서 읽어오는 거다.
별 다른건 없고 D3DXCreateTexture-- 함수를 사용해서 성공적으로 텍스쳐를 불러왔으면 m_vptxt에 추가하고, 실패했으면 에러코드를 리턴한다.
그리고 Release3D함수에 텍스쳐를 Release하는 코드도 추가했다. Direct에서는 만들었으면 꼭 Release 해줘야한다. 안그러면 너 메모리 누수 질질.
D3D 텍스쳐는 가로 세로 길이가 2^n꼴만 지원한다. (그래픽장치에 따라서 2^n꼴이 아닌 크기의 텍스쳐를 지원하는 컴퓨터도 있지만, 보편성을 위해서는 2^n꼴로 텍스쳐를 맞춰야한다.)
그리고 256*256의 텍스쳐가 출력시에 가장 최적화된 속도를 낸다. 따라서 우리가 2^n꼴이 아닌 그림 여러 장을 사용하려면 256*256 텍스쳐를 불러오고, 그 텍스쳐 중에서 일부분만을 선택해서 사용해야한다.
앞으로 텍스쳐의 한 조각을 스프라이트(Sprite)라고 부르겠다. (ID3DXSprite와 혼동하지 말자.)
다음과 같이 GSprite구조체를 정의해보자.
itxt는 텍스쳐의 인덱스번호, rect는 텍스쳐에서 사용할 일부분을 나타내는 사각형이다.
이쯤에서 우리가 가지게 될 자원들을 정리해보자면,
여러개의 Texture
여러개의 GSprite.
그리고 출력할때는,
GSprite출력 : itxt번의 텍스쳐 중에서 rect부분만을 잘라내서 출력한다.
이제 vector<GSprite>도 멤버변수로 추가했다.
그러면 스프라이트를 화면에 출력해보자.
ID3DXSprite에서는 텍스쳐를 화면에 출력해주는 함수 Draw를 제공한다.
ID3DXSprite에서는 또한 SetTransform 함수로 화면에 출력할 텍스쳐들이 거칠 행렬변환을 설정해 줄수 있다. 행렬변환을 통해 평행이동, 확대축소, 좌우반전, 회전 등등 각종 효과를 줄수 있으므로, 자유자재로 텍스쳐를 출력할수 있다고 보면 된다.
HRESULT ID3DXSprite::Draw( LPDIRECT3DTEXTURE9 pTexture, CONST RECT * pSrcRect, CONST D3DXVECTOR3 * pCenter, CONST D3DXVECTOR3 * pPosition, D3DCOLOR Color );
pTexture는 출력할 텍스쳐, pSrcRect는 출력할 텍스쳐의 부분, pCenter는 출력시에 중심을 어디로 잡을 것인지, pPosition은 어디에 출력할 것인지, color는 출력색상을 결정한다.
우리가 관심있는건 pTexture와 pSrcRect. 이 둘을 잘 사용하면 우리가 만들고자 했던 GSprite를 화면에 출력하는 함수를 만들수 있다.
index는 출력할 GSprite의 번호, x,y는 화면상의 x,y좌표이고, color는 색깔이다.
아무런 변환도 주지 않을 것이므로 SetTransform함수로 단위행렬을 넘겨줬다.
그리고 Draw함수를 호출했다. Draw함수의 4번째 인수 pPosition에 D3DXVECTOR3(x, y, 0)을 넘겨줘서 x,y에 출력되게 했다. 그리고 실패했을경우에는 에러코드를 리턴하는 꼼꼼함!
ID3DXSprite::Draw의 마지막 인수 color에 대해 궁금해하는 사람이 있을지도 모른다.
색상은 D3DCOLOR_ARGB(a,r,g,b)같은 매크로 함수로 줄수 있는데, 이 색상이 텍스쳐에 곱해져서 출력된다. (색상비율을 결정한다고 보면된다.)
D3DCOLOR_ARGB(255, 255, 255, 255)를 넘겨주면 텍스쳐가 그대로 출력될것이다.
만약 Red를 반 정도로 약하게 출력하고 싶으면 D3DCOLOR_ARGB(255, 127, 255, 255)를 넘겨주면 되고, 반투명하게 하고싶으면 알파값을 줄여서 D3DCOLOR_ARGB(127, 255, 255, 255)를 넘겨주면 된다.
OnCreate에서 Texture하나를 불러오고, Sprite2개를 만들었다.
OnRender에서 Sprite2개를 화면에 찍어주고 있다.
즉, 텍스쳐를 불러와놓고선 필요할때마다 화면에 뿌려주면 그래픽 처리는 끝!
파일에서 텍스쳐를 불러올때는 D3DXCreateTextureFromFile함수나 -Ex함수를 사용한다. 또한 D3DXCreateTextureFromFileInMemory로 메모리상의 파일에서 텍스쳐를 불러올수도 있다. 자세한 사용법은 msdn을 참고하길 바란다.
LPDIRECT3DTEXTURE9를 모아두기 위해서 m_vptxt 멤버변수를 추가했다. vector사용법에 대해서는 다들 잘 알것이라고 생각하므로 자세한 설명은 패쓰!
LoadTexture 함수가 두 개나 선언되었는데, 하나는 파일에서부터, 하나는 메모리상에서 읽어오는 거다.
별 다른건 없고 D3DXCreateTexture-- 함수를 사용해서 성공적으로 텍스쳐를 불러왔으면 m_vptxt에 추가하고, 실패했으면 에러코드를 리턴한다.
그리고 Release3D함수에 텍스쳐를 Release하는 코드도 추가했다. Direct에서는 만들었으면 꼭 Release 해줘야한다. 안그러면 너 메모리 누수 질질.
D3D 텍스쳐는 가로 세로 길이가 2^n꼴만 지원한다. (그래픽장치에 따라서 2^n꼴이 아닌 크기의 텍스쳐를 지원하는 컴퓨터도 있지만, 보편성을 위해서는 2^n꼴로 텍스쳐를 맞춰야한다.)
그리고 256*256의 텍스쳐가 출력시에 가장 최적화된 속도를 낸다. 따라서 우리가 2^n꼴이 아닌 그림 여러 장을 사용하려면 256*256 텍스쳐를 불러오고, 그 텍스쳐 중에서 일부분만을 선택해서 사용해야한다.
앞으로 텍스쳐의 한 조각을 스프라이트(Sprite)라고 부르겠다. (ID3DXSprite와 혼동하지 말자.)
다음과 같이 GSprite구조체를 정의해보자.
itxt는 텍스쳐의 인덱스번호, rect는 텍스쳐에서 사용할 일부분을 나타내는 사각형이다.
이쯤에서 우리가 가지게 될 자원들을 정리해보자면,
여러개의 Texture
여러개의 GSprite.
그리고 출력할때는,
GSprite출력 : itxt번의 텍스쳐 중에서 rect부분만을 잘라내서 출력한다.
이제 vector<GSprite>도 멤버변수로 추가했다.
그러면 스프라이트를 화면에 출력해보자.
ID3DXSprite에서는 텍스쳐를 화면에 출력해주는 함수 Draw를 제공한다.
ID3DXSprite에서는 또한 SetTransform 함수로 화면에 출력할 텍스쳐들이 거칠 행렬변환을 설정해 줄수 있다. 행렬변환을 통해 평행이동, 확대축소, 좌우반전, 회전 등등 각종 효과를 줄수 있으므로, 자유자재로 텍스쳐를 출력할수 있다고 보면 된다.
HRESULT ID3DXSprite::Draw( LPDIRECT3DTEXTURE9 pTexture, CONST RECT * pSrcRect, CONST D3DXVECTOR3 * pCenter, CONST D3DXVECTOR3 * pPosition, D3DCOLOR Color );
pTexture는 출력할 텍스쳐, pSrcRect는 출력할 텍스쳐의 부분, pCenter는 출력시에 중심을 어디로 잡을 것인지, pPosition은 어디에 출력할 것인지, color는 출력색상을 결정한다.
우리가 관심있는건 pTexture와 pSrcRect. 이 둘을 잘 사용하면 우리가 만들고자 했던 GSprite를 화면에 출력하는 함수를 만들수 있다.
index는 출력할 GSprite의 번호, x,y는 화면상의 x,y좌표이고, color는 색깔이다.
아무런 변환도 주지 않을 것이므로 SetTransform함수로 단위행렬을 넘겨줬다.
그리고 Draw함수를 호출했다. Draw함수의 4번째 인수 pPosition에 D3DXVECTOR3(x, y, 0)을 넘겨줘서 x,y에 출력되게 했다. 그리고 실패했을경우에는 에러코드를 리턴하는 꼼꼼함!
ID3DXSprite::Draw의 마지막 인수 color에 대해 궁금해하는 사람이 있을지도 모른다.
색상은 D3DCOLOR_ARGB(a,r,g,b)같은 매크로 함수로 줄수 있는데, 이 색상이 텍스쳐에 곱해져서 출력된다. (색상비율을 결정한다고 보면된다.)
D3DCOLOR_ARGB(255, 255, 255, 255)를 넘겨주면 텍스쳐가 그대로 출력될것이다.
만약 Red를 반 정도로 약하게 출력하고 싶으면 D3DCOLOR_ARGB(255, 127, 255, 255)를 넘겨주면 되고, 반투명하게 하고싶으면 알파값을 줄여서 D3DCOLOR_ARGB(127, 255, 255, 255)를 넘겨주면 된다.
OnCreate에서 Texture하나를 불러오고, Sprite2개를 만들었다.
OnRender에서 Sprite2개를 화면에 찍어주고 있다.
'프로그래밍 > 스페샬' 카테고리의 다른 글
| 리듬게임을 만들어보자 3. 다양한 그래픽 효과 (0) | 2009/07/04 |
|---|---|
| 리듬게임을 만들어보자 2. 텍스쳐와 스프라이트 (0) | 2009/07/03 |
| 리듬게임을 만들어보자 1. 장치 생성과 메인 루프 (2) | 2009/07/03 |
TRACKBACK 0 AND
COMMENT 0
앞으로 만들 리듬게임은
Direct3D와 DirectSound를 이용할거이므로
DirectX9이상이 깔린 윈도우가 필요하다.
아쉽지만 맥빠나 리눅스돌이들은 다음에 봐요.
또한 기본적인 c/c++ 문법이 안되는 사람도 ㅂ2ㅂ2
그리고 최소한 WinApi 조금이라도 해봤다면 좋겠음
준비물: IDE, dx9, 생각할 수 있는 머리(자기것이면 더욱 좋다)
시작한다.
프로젝트에 d3d9.lib, d3dx9d.lib 를 추가해주자.
D3D라이브러리 파일이다.
그리고 헤더 파일은 d3dx9.h이니깐 알아서 잘 추가하자.
우리의 위대한 게임을 관리할 클래스를 하나 만든다.
class GGame!
이 놈의 역할을 설명하자면
D3D 장치를 생성하고 제거해주고
ID3DXSprite도 생성해주고 제거해주고
텍스쳐도 관리해주고
......
GGame에 IDirect3D9, IDirect3DDevice9, ID3DXSprite의 주소를 받을 포인터들을 넣어준다. 안전하게 보호하기 위해서 protected.
또 구조체 m_dev를 만들어준다. 이 놈으로 3D장치의 속성을 결정하게 할것이다.
GRET이라는 타입은 typedef를 이용해서 int를 또다른 이름으로 정의했다.
그리고 #define을 이용해 에러코드들을 정의해놓았다.
D3D 초기화 과정은 다음과 같다.
1. Direct3DCreate9로 IDirect3D9를 얻는다.
2. IDirect3D9::CreateDevice로 IDirect3DDevice9를 얻는다.(이 때 3d 장치의 속성을 결정해 줄수 있다.)
m_dev에서 D3DPRESENT_PARAMETERS 를 얻어 내는 함수 _getdpp를 작성한다.
별거 없다. m_dev안에 값들을 D3DPRESENT_PARAMETERS에 넣어주는것뿐이다.
그럼 이제 Create3DDevice함수, Reset3DDevice, Release3D함수를 작성한다.
#define SAFE_RELEASE(p) if(p){p->Release();p=NULL;}
SAFE_RELEASE는 매크로 함수로 정의해놓았다.
D3DXCreateSprite함수는 ID3DXSprite를 얻는 함수이다.
ID3DXSprite는 2d그래픽을 간편하게 출력할 수 있게 도와준다.
이제 메시지 루프 부분을 짜본다. 이 부분은 일반적인 윈도우 프로그래밍과는 약간 다를 것이다.
먼저 PeekMessage를 통해 메시지가 큐에 있는지 확인한다.
만약에 큐에 메시지가 있다면 그 메시지를 처리한다.
큐에 메시지가 없으면 데이터를 처리하고(OnProc) 화면에 나타낸다.
먼저 islost가 참이면, 즉 장치를 잃었다면(DEVICELOST) 장치를 복구하도록 노력한다.
장치를 잃지 않았다면, IDirect3DDevice9::Clear로 화면을 깨끗이 지우고
BeginScene으로 이제 화면에 그리기 시작할거라고 알려준다.
그리고 ID3DXSprite::Begin을 호출해 Sprite그리기를 시작할 것임을 알려준다.
OnRender에서 그림들을 그리고
ID3DXSprite::End를 호출해 Sprite그리기가 끝났음을 알려주고
IDirect3DDevice9::EndScene을 호출해 화면 그리기가 끝났음을 알려준다.
마지막으로 Present로 백버퍼에 저장된 화면을 프론트버퍼로 전송한다.
만약 장치를 잃었다면 (D3DERR_DEVICELOST) islost를 참으로 한다.
ID3DXSprite는 장치를 잃었을때 OnLostDevice, 장치를 다시 복구했을때 OnResetDevice를 호출해 주어야한다.
메시지 처리부분을 짜본다.
WM_ACTIVATEAPP는 프로그램이 활성화/비활성화 되었을때 호출된다.
전체화면 상태에서 비활성화가 되면 그리기를 중단하고, 다시 활성화되면 그리기를 시작해야하므로
m_activated변수에 활성화상태를 받는다.
이것으로 전체적인 게임 루프부분이 완성되었다.
윈도우 생성하는거부터 잘 살펴볼까
윈도우 생성하는거라고 특별할거 없다.
전역으로 선언된 GGame을 이용해서 다음 함수를 호출한다는게 좀 다르다.
Create3DDevice
MessageLoop
Release3D
WndProc에서는 WM_DESTROY메시지만 처리하고 나머지는 GGame::MsgProc에게 맡긴다.
Direct3D와 DirectSound를 이용할거이므로
DirectX9이상이 깔린 윈도우가 필요하다.
아쉽지만 맥빠나 리눅스돌이들은 다음에 봐요.
또한 기본적인 c/c++ 문법이 안되는 사람도 ㅂ2ㅂ2
그리고 최소한 WinApi 조금이라도 해봤다면 좋겠음
준비물: IDE, dx9, 생각할 수 있는 머리(자기것이면 더욱 좋다)
시작한다.
프로젝트에 d3d9.lib, d3dx9d.lib 를 추가해주자.
D3D라이브러리 파일이다.
그리고 헤더 파일은 d3dx9.h이니깐 알아서 잘 추가하자.
우리의 위대한 게임을 관리할 클래스를 하나 만든다.
class GGame!
이 놈의 역할을 설명하자면
D3D 장치를 생성하고 제거해주고
ID3DXSprite도 생성해주고 제거해주고
텍스쳐도 관리해주고
......
GGame에 IDirect3D9, IDirect3DDevice9, ID3DXSprite의 주소를 받을 포인터들을 넣어준다. 안전하게 보호하기 위해서 protected.
또 구조체 m_dev를 만들어준다. 이 놈으로 3D장치의 속성을 결정하게 할것이다.
GRET이라는 타입은 typedef를 이용해서 int를 또다른 이름으로 정의했다.
그리고 #define을 이용해 에러코드들을 정의해놓았다.
D3D 초기화 과정은 다음과 같다.
1. Direct3DCreate9로 IDirect3D9를 얻는다.
2. IDirect3D9::CreateDevice로 IDirect3DDevice9를 얻는다.(이 때 3d 장치의 속성을 결정해 줄수 있다.)
m_dev에서 D3DPRESENT_PARAMETERS 를 얻어 내는 함수 _getdpp를 작성한다.
별거 없다. m_dev안에 값들을 D3DPRESENT_PARAMETERS에 넣어주는것뿐이다.
그럼 이제 Create3DDevice함수, Reset3DDevice, Release3D함수를 작성한다.
#define SAFE_RELEASE(p) if(p){p->Release();p=NULL;}
SAFE_RELEASE는 매크로 함수로 정의해놓았다.
D3DXCreateSprite함수는 ID3DXSprite를 얻는 함수이다.
ID3DXSprite는 2d그래픽을 간편하게 출력할 수 있게 도와준다.
이제 메시지 루프 부분을 짜본다. 이 부분은 일반적인 윈도우 프로그래밍과는 약간 다를 것이다.
먼저 PeekMessage를 통해 메시지가 큐에 있는지 확인한다.
만약에 큐에 메시지가 있다면 그 메시지를 처리한다.
큐에 메시지가 없으면 데이터를 처리하고(OnProc) 화면에 나타낸다.
먼저 islost가 참이면, 즉 장치를 잃었다면(DEVICELOST) 장치를 복구하도록 노력한다.
장치를 잃지 않았다면, IDirect3DDevice9::Clear로 화면을 깨끗이 지우고
BeginScene으로 이제 화면에 그리기 시작할거라고 알려준다.
그리고 ID3DXSprite::Begin을 호출해 Sprite그리기를 시작할 것임을 알려준다.
OnRender에서 그림들을 그리고
ID3DXSprite::End를 호출해 Sprite그리기가 끝났음을 알려주고
IDirect3DDevice9::EndScene을 호출해 화면 그리기가 끝났음을 알려준다.
마지막으로 Present로 백버퍼에 저장된 화면을 프론트버퍼로 전송한다.
만약 장치를 잃었다면 (D3DERR_DEVICELOST) islost를 참으로 한다.
ID3DXSprite는 장치를 잃었을때 OnLostDevice, 장치를 다시 복구했을때 OnResetDevice를 호출해 주어야한다.
메시지 처리부분을 짜본다.
WM_ACTIVATEAPP는 프로그램이 활성화/비활성화 되었을때 호출된다.
전체화면 상태에서 비활성화가 되면 그리기를 중단하고, 다시 활성화되면 그리기를 시작해야하므로
m_activated변수에 활성화상태를 받는다.
이것으로 전체적인 게임 루프부분이 완성되었다.
윈도우 생성하는거부터 잘 살펴볼까
윈도우 생성하는거라고 특별할거 없다.
전역으로 선언된 GGame을 이용해서 다음 함수를 호출한다는게 좀 다르다.
Create3DDevice
MessageLoop
Release3D
WndProc에서는 WM_DESTROY메시지만 처리하고 나머지는 GGame::MsgProc에게 맡긴다.
'프로그래밍 > 스페샬' 카테고리의 다른 글
| 리듬게임을 만들어보자 3. 다양한 그래픽 효과 (0) | 2009/07/04 |
|---|---|
| 리듬게임을 만들어보자 2. 텍스쳐와 스프라이트 (0) | 2009/07/03 |
| 리듬게임을 만들어보자 1. 장치 생성과 메인 루프 (2) | 2009/07/03 |
TRACKBACK 0 AND
COMMENT 2

PREV