728x90
728x90
728x90
728x90
728x90
728x90
728x90
728x90
728x90

1. Bounding Box

3D World에서 교차 검사는 매우 중요한 이슈이다. 게임을 예로 들자면, 캐릭터끼리 충돌 검출도 해야하고 마우스 Picking도 해야되고 타격 판정도 해야된다. 이외에도 많이 사용되고 있다.

그래서 캐릭터 모델의 특정 범위, 지형 구조물 등에 충돌 판정을 위한 박스나 단순한 모양의 Bounding Volume을 씌운다.

그렇게 복잡한 메쉬끼리 교차 검사를 하는 것이 아니라 단순한 Volume들 끼리 교차 검사를 하는 것이다.

가장 많이 쓰이는 모양이 box이고, 이 box에는 2가지의 종류가 있다.


2. AABB (Axis-Aligned Bounding Box)


기저 축에 정렬된 충돌 박스로 박스를 이루는 면의 노말 벡터들이 곧 X, Y, Z 축과 일치하는 모양이다.

모델을 이루는 다각형의 X, Y, Z 좌표의 최소 최대를 각 박스의 버텍스로 생성한다.

따라서 회전함에 따라 크기가 계속 변하게 된다.

축에 일치된 모양이기 때문에 AABB끼리의 충돌 검출은 매우 간편하다.



void CheckAABB(BoundingBox targetBox)
{
 if (boundingBox.minPos[0] <= targetBox.maxPos[0] && boundingBox.maxPos[0] >= targetBox.minPos[0] &&
 boundingBox.minPos[1] <= targetBox.maxPos[1] && boundingBox.maxPos[1] >= targetBox.minPos[1] &&
 boundingBox.minPos[2] <= targetBox.maxPos[2] && boundingBox.maxPos[2] >= targetBox.minPos[2])
 {
  aabbCollide = true;
  return;
 }
 aabbCollide = false;
}



3. OBB (Oriented Bounding Box)


AABB와 마찬가지로 박스를 이루는 세 면은 서로 수직이지만 해당 면의 노말 벡터가 X, Y, Z와 일치하지 않는 박스이다.

X, Y, Z와 일치하지 않기 때문에 AABB보다 충돌 검출의 시간 복잡도는 높지만 충돌 박스가 XYZ 좌표를 기준으로 최대, 최소로 계속 변하는 AABB보다 항상 좀 더 fit한 바운딩 박스를 만들 수 있다.


void CheckOBB(BoundingBox targetBox)
{
 double c[3][3];
 double absC[3][3];
 double d[3];
 double r0, r1, r;
 int i;
 const double cutoff = 0.999999;
 bool existsParallelPair = false;
 D3DXVECTOR3 diff = boundingBox.centerPos - targetBox.centerPos;
 
 
 for (i = 0; i < 3; ++i)
 {
  c[0][i] = D3DXVec3Dot(&boundingBox.axis[0], &targetBox.axis[i]);
  absC[0][i] = abs(c[0][i]);
  if (absC[0][i] > cutoff)
   existsParallelPair = true;
 }
 
 
 d[0] = D3DXVec3Dot(&diff, &boundingBox.axis[0]);
 r = abs(d[0]);
 r0 = boundingBox.axisLen[0];
 r1 = targetBox.axisLen[0]* absC[0][0] + targetBox.axisLen[1]* absC[0][1] + targetBox.axisLen[2]* absC[0][2];
 if (r > r0 + r1)
 {
  obbCollide = false;
  return;
 }
 for (i = 0; i < 3; ++i)
 {
  c[1][i] = D3DXVec3Dot(&boundingBox.axis[1], &targetBox.axis[i]);
  absC[1][i] = abs(c[1][i]);
  if (absC[1][i] > cutoff)
   existsParallelPair = true;
 }
 d[1] = D3DXVec3Dot(&diff, &boundingBox.axis[1]);
 r = abs(d[1]);
 r0 = boundingBox.axisLen[1];
 r1 = targetBox.axisLen[0] * absC[1][0] + targetBox.axisLen[1] * absC[1][1] + targetBox.axisLen[2] * absC[1][2];
 if (r > r0 + r1)
 {
  obbCollide = false;
  return;
 }
 for (i = 0; i < 3; ++i)
 {
  c[2][i] = D3DXVec3Dot(&boundingBox.axis[2], &targetBox.axis[i]);
  absC[2][i] = abs(c[2][i]);
  if (absC[2][i] > cutoff)
   existsParallelPair = true;
 }
 d[2] = D3DXVec3Dot(&diff, &boundingBox.axis[2]);
 r = abs(d[2]);
 r0 = boundingBox.axisLen[2];
 r1 = targetBox.axisLen[0] * absC[2][0] + targetBox.axisLen[1] * absC[2][1] + targetBox.axisLen[2] * absC[2][2];
 if (r > r0 + r1)
 {
  obbCollide = false;
  return;
 }
 r = abs(D3DXVec3Dot(&diff, &targetBox.axis[0]));
 r0 = boundingBox.axisLen[0] * absC[0][0] + boundingBox.axisLen[1] * absC[1][0] + boundingBox.axisLen[2] * absC[2][0];
 r1 = targetBox.axisLen[0];
 if (r > r0 + r1)
 {
  obbCollide = false;
  return;
 }
 
 
 r = abs(D3DXVec3Dot(&diff, &targetBox.axis[1]));
 r0 = boundingBox.axisLen[0] * absC[0][1] + boundingBox.axisLen[1] * absC[1][1] + boundingBox.axisLen[2] * absC[2][1];
 r1 = targetBox.axisLen[1];
 if (r > r0 + r1)
 {
  obbCollide = false;
  return;
 }
 
 
 r = abs(D3DXVec3Dot(&diff, &targetBox.axis[2]));
 r0 = boundingBox.axisLen[0] * absC[0][2] + boundingBox.axisLen[1] * absC[1][2] + boundingBox.axisLen[2] * absC[2][2];
 r1 = targetBox.axisLen[2];
 if (r > r0 + r1)
 {
  obbCollide = false;
  return;
 }
 
 
 if (existsParallelPair == true)
 {
  obbCollide = true;
  return;
 }
 
 
 r = abs(d[2] * c[1][0] - d[1] * c[2][0]);
 r0 = boundingBox.axisLen[1] * absC[2][0] + boundingBox.axisLen[2] * absC[1][0];
 r1 = targetBox.axisLen[1] * absC[0][2] + targetBox.axisLen[2] * absC[0][1];
 if (r > r0 + r1)
 {
  obbCollide = false;
  return;
 }
 
 
 r = abs(d[2] * c[1][1] - d[1] * c[2][1]);
 r0 = boundingBox.axisLen[1] * absC[2][1] + boundingBox.axisLen[2] * absC[1][1];
 r1 = targetBox.axisLen[0] * absC[0][2] + targetBox.axisLen[2] * absC[0][0];
 if (r > r0 + r1)
 {
  obbCollide = false;
  return;
 }
 
 
 r = abs(d[2] * c[1][2] - d[1] * c[2][2]);
 r0 = boundingBox.axisLen[1] * absC[2][2] + boundingBox.axisLen[2] * absC[1][2];
 r1 = targetBox.axisLen[0] * absC[0][1] + targetBox.axisLen[1] * absC[0][0];
 if (r > r0 + r1)
 {
  obbCollide = false;
  return;
 }
 
 
 r = abs(d[0] * c[2][0] - d[2] * c[0][0]);
 r0 = boundingBox.axisLen[0] * absC[2][0] + boundingBox.axisLen[2] * absC[0][0];
 r1 = targetBox.axisLen[1] * absC[1][2] + targetBox.axisLen[2] * absC[1][1];
 if (r > r0 + r1)
 {
  obbCollide = false;
  return;
 }
 
 
 r = abs(d[0] * c[2][1] - d[2] * c[0][1]);
 r0 = boundingBox.axisLen[0] * absC[2][1] + boundingBox.axisLen[2] * absC[0][1];
 r1 = targetBox.axisLen[0] * absC[1][2] + targetBox.axisLen[2] * absC[1][0];
 if (r > r0 + r1)
 {
  obbCollide = false;
  return;
 }
 
 
 r = abs(d[0] * c[2][2] - d[2] * c[0][2]);
 r0 = boundingBox.axisLen[0] * absC[2][2] + boundingBox.axisLen[2] * absC[0][2];
 r1 = targetBox.axisLen[0] * absC[1][1] + targetBox.axisLen[1] * absC[1][0];
 if (r > r0 + r1)
 {
  obbCollide = false;
  return;
 }
 
 
 r = abs(d[1] * c[0][0] - d[0] * c[1][0]);
 r0 = boundingBox.axisLen[0] * absC[1][0] + boundingBox.axisLen[1] * absC[0][0];
 r1 = targetBox.axisLen[1] * absC[2][2] + targetBox.axisLen[2] * absC[2][1];
 if (r > r0 + r1)
 {
  obbCollide = false;
  return;
 }
 
 
 r = abs(d[1] * c[0][1] - d[0] * c[1][1]);
 r0 = boundingBox.axisLen[0] * absC[1][1] + boundingBox.axisLen[1] * absC[0][1];
 r1 = targetBox.axisLen[0] * absC[2][2] + targetBox.axisLen[2] * absC[2][0];
 if (r > r0 + r1)
 {
  obbCollide = false;
  return;
 }
 
 
 r = abs(d[1] * c[0][2] - d[0] * c[1][2]);
 r0 = boundingBox.axisLen[0] * absC[1][2] + boundingBox.axisLen[1] * absC[0][2];
 r1 = targetBox.axisLen[0] * absC[2][1] + targetBox.axisLen[1] * absC[2][0];
 if (r > r0 + r1)
 {
  obbCollide = false;
  return;
 }
 
 
 obbCollide = true;
 return;
}


출처 : http://hoodymong.tistory.com/8

728x90
728x90

Dithering (디더링)


- 요약

디더링은 해당 픽셀에 요청된 색상이 컴퓨터의 지원 컬러에 없는 색상이면, 다른 색상들을 섞어서 비슷한 색상으로 대체하기 위해 컴퓨터 프로그램에 의해 시도되는 것이다. (오디오 쪽에서는 노이즈를 이용한 방법이 있다....)



- 심화

컴퓨터 그래픽스에 있어서 (흑백 표시 장치나 단색 프린터로) 그레이의 음영변화, 또는 (컬러 표시 장치나 컬러 프린터로) 추가색의 음영 변화의 착각을 생성할 때 사용디는 수법이다. 혼합에서는 어느 이미지의 영역을 여러 가지 패턴으로 착색된 도트의 집합으로서 처리한다. 인쇄 이미지에 가까운 것을 하프톤이라하고, 약간 점묘화법으로 그려진 그림과 비슷하다. 혼합은 각각의 효과를 평균화한다든지 묶어서 하나의 음영 또는 색이라고 인식하도록 인간의 눈이 갖는 성질을 이용하고 있다. 하나의 영역 내의 흰 점과 검은 점과의 비율에 따라서 그 영역은 전체로서 그레이로 보인다. 마찬가지로 붉은 점 중에 흰 점을 섞으면 핑크와 비슷한 색조가 된다. 혼합은 컴퓨터 그래픽스에 현실감을 가한다든지, 해상도가 낮을 때 곡선이나 직선에 나타나는 날카로운 부분을 매끄럽게 하는 데 사용된다. 제한된 수의 색상들을 섞어서 다양한 색을 만들거나 현재 팔래트에 존재하지 않는 컬러를 컬러 패턴으로 대체하여 유사한 컬로로 표현하는 기법으로 포토샵에는 디더링을 조절하는 옵션으로 Diffusion과 Pattern이 있다.


- Diffusion : 색상간의 경계를 자연스럽게 흩어 주는 방법

- Pattern : 이미지를 삼베 같은 패턴으로 거칠게 만드는 방식


        

                                                  (24bit Color)                           (8bit Color)



(Diffusion Dither)

(Pattern Dither)


출처 : http://kbp.kongju.ac.kr/cg_edu/cg1_data/5week_4.htm


728x90
728x90

출처 : http://www.realitymod.com/forum/f189-modding-tutorials/90244-pixel-bleeding-uv-spacing-texture-padding.html


What is Pixel Bleeding?

Pixel Bleeding is to put it as simple as possible, pixel colours on a 2D texture image bleeding into each other due to compression or the size of the texture being decreased, like in a MipMap (which BF2 and many other games use MipMaps so its quite important).

 


Pixel Bleeding itself can not really be avoided. It can be cut down slightly by using a better image format (if available, which in BF2's case the answer most of the time is no) or it can be cut down by using a better image compression tool, like for DDS images, instead of using the Photoshop DDS plugin, use the NVIDIA Texture Tools 2 which will give you a better compression result and will result in less pixel bleeding, but in most cases the difference is marginal.

However saving the texture in a better format or with a better compression tool on its own will not get rid of pixel bleeding, it will only reduce it. The best way to avoid Pixel Bleeding on your models/textures is to simply spacing out your UVs far enough so any bleeding that dose occur on the texture dose not show up on the model.


To demonstrate pixel bleeding I've crated this very simple box with a pyramid on top 


Here are some basic UVs I've quickly done with no spacing between the UVs:


Now I've made this texture up quickly which gives each face of the UV its own, unique colour that also matches the UVs and then compressed the texture as a .jpg, which is like a .dds but you can see the compression effects better.


Looking at the texture from here you will hardly be able to see any problems with it but when its applied to our model, you can see the pixel bleeding very clearly:
 
 

As you can see, each face is meant to have its own colour but for some reason on the edges of the faces a different colour is "bleeding" into the colour that is meant to be there.
Now if we take a look at our UVs a sec and we can see that the face that has green on the edge, is right next to a green part on the texture map.


If we zoom into the UV map a little more we can see that the green bit is "bleeding" over the edge of the UV its meant to be on and as such, is on the wrong part of our UV map and on the wrong face.


This here is the basic definition of Pixel Bleeding and what you want to try and avoid.....

Also as mentioned above, when a texture is made smaller, like in a mipmap, pixel bleeding becomes much worse so to demonstrate, I've resized the same texture above from a 256x256 texture to a 64x64 texture:


And as we can see, the bleeding is much worse now, as well as the other compression effects.
 
 



How to Avoid Pixel Bleeding

To Avoid Pixel Bleeding you just need to space out your UVs. So here is the same object as above, but this time, with its UVs spread out from each other like so:
NOTE: the more you can spread out your UVs the better. Also try to keep your UVs evenly spaced apart as possible, with setting yourself a general minimum distance between each UV that you shouldn't go under. The distance your UVs need to be apart really depends on your end texture size, with a smaller texture needing a bigger distance and a larger texture needing a smaller distance but really, you want to have a space of no less than 3 pixels when it translates to your final texture image. Also keep in mind your texture size may be down sized for performance reasons so you may not be able to have that huge 2048x2048 texture you where hoping for your pistol.


And here is the texture I've made for it, note that the texture colours for each face go outside the UVs:
 

Now if we look at our model with that texture on it, we can see that there is no sign of pixel bleeding on the edges of the UVs (although you can see a few artifacts from the compression on the pyramid and other places).
  

Which if we zoom into the UV with texture, we can see that pixel bleeding is still there on the texture, just that its now outside of our UVs 


Even when using a 64x64 px texture, pixel bleeding is still not a huge issue, although you can now slightly see it on the edges (as well as the artifacts have gotten much worse )
  



Other Things to Note

Even thou in this tutorial I say you must keep all your UVs spread out, this dose not mean you should not stitch up UVs, but this means you should only stitch up UVs that are of the same colour and want to share the basic same texture. So the corner of a wooden or metal box meet, you want to stitch up the both sides on that corner as they will be sharing the same texture, with the metal box also most likley sharing a scratch right down the corner on the stitch itself. But where you have two totally different materials on two faces, like for example, a glass window port hole on a tank surrounded by metal, you don't want to really stitch up the window to the metal as then you would have the colour of the metal bleed into the glass, and you don't want that so be smart when your stitching up or spacing out your UVs 



I hope you have learnt something from this tutorial and any questions don't be afraid to ask.

Cheers!

728x90
728x90

오늘은 오랜만에 재미있는 3D 그래픽에 대해서 이야기 해보겠습니다. 주제는 Bloom Effect, 블룸 효과 입니다.

 

먼저 블룸 효과에 대해서 간단히 이해해 보자면,

 

빛을 받는 물체는 그 주위에 빛을 발산하는 것과 같은 효과를 나타냅니다.

 

뭐 예를 들면, 태양이 쩅쨍 찌는 사막의 한가운데서 먼 사막의 모래 산등성이를 바라보면 마치 모래 산등성이가 태양 빛을 반사하여 희미하게 빛을 발산하는 것처럼 보이는 풍경을 상상할 수 있을 겁니다.

 

상상이 안되면 사진을 직접 보죠. 뭐.  ( 진작에 보여줘야 했었나?  ㅋ )

 


 -블룸 효과 미적용(왼쪽) : 블룸 효과 적용(오른쪽)

이미지 출처: http://cafe.naver.com/raisonblue/379 -

 

 확연히 차이가 나죠. 오른쪽 같은 경우는 빛이 은은하게 퍼지는 것처럼 보입니다. 따라서 블룸 효과를 일종 뽀샤시 효과라고도 합니다만

뭐, 최근 3D 게임 같은 경우는 블룸 효과를 대부분 지원하고 엘더스크롤과 같은 좀 고급 게임은 HDR 효과까지 지원하더군요. HDR 효과는 여기서 논할 거리가 아니기에 생략.

 

자, 이제 블룸 효과가 뭔지는 이해했을 겁니다. 바로 구현 들어가 보죠 -_-

 

그런데 블룸 효과 적용에 앞서 2차원 가우스 분포 함수에 대해서 공부해 볼 필요가 있습니다. 가우스 분포 함수를 이용하면 번짐 효

과를 구현할 수 있기 때문이죠.

 

1차원 가우스 분포 함수의 식이 다음과 같으며

 


 

여기서 б 표준 편차입니다. 표준 편차가 1일 때 1차원 가우스 분포 함수는 다음과 같은 모양을 갖죠

 


 

그래프 모양을 봐선 뭔가 좌우 대칭으로 증폭이 됨을 알 수 있습니다. 즉, 빛을 발산하는 색상이 증폭이 되는 것이며 화면은 x, y 2차원이기 때문에 2차원 가우스 분포 함수를 사용해야 하는 것이죠.

 



-2차원 가우스 분포 함수 모양-

 

식에서 설명하지 않았던 상수 부분  W(x) = 

에서 x가 б 이면 pow( e, 0.5 ) 와 같은 결과므로 대략 0.6065가 됩니다. 이걸로 얻은  W(x)로 가중치 값을 얻어내서 이 값들을 마스크로 사용하면 됩니다.

 

б 는 우리가 임의의 상수로 결정할 수 있으며 그에 따른 마스크 값들을 미리 구해 놓을 수 있습니다.
 
그리고 마스크 적용은 픽셀 셰이더가 수행하면 되겠죠.
 
구현 방안에 있어선, 일단 기존 화면의 렌더링한 결과를 텍스처로 저장한 후에 이 텍스처를 새로 추가한 픽셀 셰이더를 적용하고 화면 사이즈에 맞게 뿌려주면 되는 것입니다. 렌더링 패스도 두 번에 끝나는 매우 저렴한 결과를 보여주죠. ㅋ_ㅋ
 
다음은 블룸 효과 헤더 부분입니다. 다이렉트 x를 사용합니다.

  class BloomResource {


  LPDIRECT3DSURFACE9       lpD3DSurface[ 2];                             //다이렉트 표면 장치
  LPDIRECT3DTEXTURE9       lpD3DTexture[ 2];                             //다이렉트 텍스처 장치

  LPDIRECT3DVERTEXBUFFER9     lpD3DTexVB;                           //텍스처 정점 버텍스

  LPD3DXEFFECT                         lpD3DEffect;                             //다이렉트 이펙트 핸들
  D3DXHANDLE                             hTechBloom;                          //셰이더 테크닉 핸들


  BOOL InitBloomResource( LPDIRECT3DDEVICE9  );
  VOID RenderBloom( LPDIRECT3DDEVICE9 );

 

 };

 
핵심은, 다이렉트 서피스와 텍스처를 각각 2개씩 생성한다는 점이고 그 이유는 다음과 같습니다. 원래 기본 화면을 렌더링 한 후, 그 렌더링 한 결과를 1번 서피스에 저장하고 1번 서피스에 블룸 효과를 적용시켜서 2번에 저장한 후에 1번과 2번의 화면을 합성시키는 것이죠.
따라서, 각 서피스의 결과를 저장할 텍스처 2개를 따로 생성하고 있으며 이 텍스처를 화면에 뿌려주어야 하기 때문에 텍스처 정점 버텍스 ( FVF = D3DFVF_XYZRHW | D3DFVF_TEX  ) 도 선언하고 있으며 픽셀 셰이더 적용을 위한 셰이더 테크닉 핸들도 선언하고 있습니다.
 
그럼, InitBloomResource 함수를 보도록 하죠.
 

 BOOL BloomResource ::InitBloomResource( LPDIRECT3DDEVICE9 lpD3DDevice ) {

 

 //스크린 사이즈 구하기 
 POINT ptScreen;
 ptScreen.x = d3ddm.Width;
 ptScreen.y = d3ddm.Height;

 

 //텍스처 정점 설정
 FTexture vertices[ 4 ];

 vertices[ 0 ].vPosition = D3DXVECTOR4(  ptScreen.x - 0.5f, ptScreen.y - 0.5f, 0, 1 );
 vertices[ 0 ].vTexCoord = D3DXVECTOR2( 1, 1 );
 vertices[ 1 ].vPosition = D3DXVECTOR4( -0.5f, ptScreen.y - 0.5f, 0, 1 );
 vertices[ 1 ].vTexCoord = D3DXVECTOR2( 0, 1 );
 vertices[ 2 ].vPosition = D3DXVECTOR4( -0.5f, -0.5f, 0, 1 );
 vertices[ 2 ].vTexCoord = D3DXVECTOR2( 0, 0 );
 vertices[ 3 ].vPosition = D3DXVECTOR4( ptScreen.x - 0.5f, -0.5f, 0, 1 );
 vertices[ 3 ].vTexCoord = D3DXVECTOR2( 1, 0 );

 

 HRESULT  hResult = E_FAIL;

 UINT vertexSize = sizeof( vertices );

 hResult = lpD3DDevice->CreateVertexBuffer(  vertexSize,  0,  vertices[ 0 ].FVF,  D3DPOOL_DEFAULT,   &lpD3DTexVB,  NULL );

 

 if( FAILED( hResult ) ) {   return FALSE; }

 

VOID* pVertices = NULL;

 

 hResult = lpD3DTexVB->Lock( 0, vertexSize, static_cast< VOID** >( &pVertices ), 0 );

 

 if( FAILED( hResult ) ) {  return FALSE; }

 

 memcpy( pVertices, vertices, vertexSize );

 

 lpD3DTexVB->Unlock();


 //셰이더 설정
 hResult = D3DXCreateEffectFromFile( lpD3DDevice,   szFileName,  NULL,  NULL,  D3DXSHADER_DEBUG,  NULL,  &lpD3DEffect,  NULL );

 

 hTechBloom = lpD3DEffect->GetTechniqueByName( "GOneBloom" );

 FLOAT fTexSizeX = static_cast< FLOAT >( ptScreen.x );
 FLOAT fTexSizeY = static_cast< FLOAT >( ptScreen.y );

 D3DXHANDLE hMapCX = lpD3DEffect->GetParameterByName( NULL, "map_cx" );
 lpD3DEffect->SetValue( hMapCX, &fTexSizeX, sizeof( FLOAT ) );

 D3DXHANDLE hMapCY = lpD3DEffect->GetParameterByName( NULL, "map_cy" );
 lpD3DEffect->SetValue( hMapCY, &fTexSizeY, sizeof( FLOAT ) );

 

//서피스 및 텍스처 생성 

D3DXCreateTexture(lpD3DDevice,  TEXTURE_SIZE, TEXTURE_SIZE, 0, D3DUSAGE_RENDERTARGET, d3ddm.Format, D3DPOOL_DEFAULT, &lpD3DTexture[ 0 ] );

lpD3DTexture[ 0 ]->GetSurfaceLevel( 0, &lpD3DSurface[ 0 ] );


 D3DXCreateTexture(lpD3DDevice,  ptScreen.x,  ptScreen.y, 0, D3DUSAGE_RENDERTARGET, d3ddm.Format,

D3DPOOL_DEFAULT, &lpD3DTexture[ 1 ] );

 lpD3DTexture[ 1 ]->GetSurfaceLevel( 0, &lpD3DSurface[ 1 ] );

 

return TRUE;

 

}//BOOL InitBloomResource( LPDIRECT3DDEVICE9 lpD3DDevice )

 
초기화 같은 경우는 별로 할 일이 없습니다.
 
화면 사이즈를 구하고
최종 결과물을 화면에 뿌려주기 위해 정점 설정을 하고 ( 시점 변환을 생략하기 위해서 RHW4 포멧의 정점임을 주의할 것! )
셰이더 설정을 하고
서피스와 텍스처를 생성합니다.
 
TEXTURE_SIZE 같은 경우는 실제 화면 해상도 이하로 잡으면 됩니다. 물론 클수록 화면 퀄리티가 좋으나 성능 부하가 커지겠죠. 저 같은 경우는 500 으로 설정했습니다.
 
그럼 렌더링 부분을 보도록 하죠.
 

 VOID BloomResource ::RenderBloom( LPDIRECT3DDEVICE9  lpD3DDevice ) {
 

 LPDIRECT3DSURFACE9       lpCurrentSurface = NULL;     //현재 화면 장치 
 lpD3DDevice->GetRenderTarget( 0, &lpCurrentSurface );
 lpD3DDevice->StretchRect(  lpCurrentSurface, NULL, lpD3DSurface[ 0 ], NULL, D3DTEXF_LINEAR );
 lpD3DDevice->StretchRect(  lpCurrentSurface, NULL, lpD3DSurface[ 1 ], NULL, D3DTEXF_LINEAR );

 lpD3DDevice->SetRenderTarget( 0, lpCurrentSurface );
 SAFE_RELEASE( lpCurrentSurface ); 

 

 lpD3DDevice->SetFVF( FTexture ::FVF );
 lpD3DDevice->SetTextureStageState( 0, D3DTSS_COLORARG1, D3DTA_TEXTURE );
 lpD3DDevice->SetTextureStageState( 0, D3DTSS_COLOROP, D3DTOP_SELECTARG1 );
 lpD3DDevice->SetTexture( 0, lpD3DTexture[ 0 ]);
 lpD3DDevice->SetTexture( 1, lpD3DTexture[ 1 ]);

 

 lpD3DEffect->SetTechnique( hTechBloom );

 UINT iPass = 0;

 lpD3DEffect->Begin( &iPass,  NULL  );
 lpD3DEffect->BeginPass( 0 );
 {
  lpD3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET, D3DCOLOR_XRGB( 255, 255, 255 ), 1.0f, 0 );
  lpD3DDevice->SetStreamSource( 0, lpD3DTexVB, 0, sizeof( FTexture ) );
  lpD3DDevice->DrawPrimitive( D3DPT_TRIANGLEFAN, 0, 2 );
 
  lpD3DEffect->EndPass();

 }//IF

 

 lpD3DEffect->End();
 
 lpD3DDevice->SetTexture( 0, NULL );
 lpD3DDevice->SetTexture( 1, NULL );

}

 
빨간 색 소스 부분을 보시면 알겠지만 핵심은 기존의 렌더링한 결과를 0번 서피스에 저장한 후에 이를 블룸 효과 적용해서 1번 서피스에 그려줍니다. 그리곤 0번과 1번의 결과물을 합성해서 화면에 뿌려주면 되죠. 그래서 RenderBloom 함수 같은 경우는 기존 모델들을 그린 후에 최종적으로 호출해 주면 됩니다. 의사 코드는 다음과 같겠죠.
 

VOID Render() {

 

  LPDIRECT3DDEVICE9 lpD3DDevice = m_lpD3DDevice;

 

  //카메라 변환에 의한 뷰행렬 설정

 

  //기본 화면 렌더링 
  if( SUCCEEDED( lpD3DDevice->BeginScene() ) ) {

     lpD3DDevice->Clear( 0, NULL, D3DCLEAR_TARGET | D3DCLEAR_STENCIL | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB( 

                                 255, 255, 255 ), 1.0f, 0 );


      //여기서 화면에 뿌리고 싶은 모델들 렌더링

 

      lpD3DDevice->EndScene();
  }//if

 

   //블룸 효과를 마지막 렌더링 m_pBloomResource는 BloomResource 객체.
   m_pBloomResource->RenderBloom( lpD3DDevice );

 

   lpD3DDevice->Present( NULL, NULL, m_hWnd, NULL );


}

 
의심할 여지가 없네요. 다음은 실제 블룸 효과 셰이더 소스를 보도록 합시다.
 

float map_cx;                        //텍스처 크기  가로 
float map_cy;                        //텍스처 크기  세로

sampler Sampler0;
sampler Sampler1;


float2 rcpres = { 0.0025, 0.0033333333333333333333333333333333 };

 

//텍스처 의 임의의 위치의 픽셀에 접근하귀 위한 좌표 ( 가로 방향)
float2 PixelKernelH[13] =
{
    { -6, 0 },    { -5, 0 },    { -4, 0 },    { -3, 0 },    { -2, 0 },    { -1, 0 },    {  0, 0 },    {  1, 0 },    {  2, 0 },    {  3, 0 },    {  4, 0 },
    {  5, 0 },    {  6, 0 },
};

 

//텍스처의 임의의 위치의 픽셀에 접근하기 위한 좌표 ( 세로 방향)

float2 PixelKernelV[13] =
{
    { 0, -6 },    { 0, -5 },    { 0, -4 },    { 0, -3 },    { 0, -2 },    { 0, -1 },    { 0,  0 },    { 0,  1 },    { 0,  2 },    { 0,  3 },    { 0,  4 },
    { 0,  5 },    { 0,  6 },
};

 

//미리 계산해 둔 가우스 필터의 마스크 값
float BlurWeights[13] = 
{
    0.002216,    0.008764,    0.026995,    0.064759,    0.120985,    0.176033,    0.199471,    0.176033,    0.120985,    0.064759,
    0.026995,    0.008764,    0.002216,

};

 

 

float4 PSBlur( float2 Tex : TEXCOORD ) : COLOR {

 

    float4 Color = tex2D( Sampler0, Tex );
    Color = pow( Color, 32 );
    
    float4 Color2 = -0.84;
 
    for( int index = 0; index < 13; ++index ) {
        Color2 += tex2D( Sampler0, Tex + ( PixelKernelH[ index ] * rcpres ) ) * BlurWeights[ index ];
        Color2 += tex2D( Sampler0, Tex + ( PixelKernelV[ index ] * rcpres ) ) * BlurWeights[ index ];

    }
    
    Color2 *= 0.48;
    
    float4 Color3 = tex2D( Sampler1, Tex );

    return  Color + Color2 + Color3;
    
}

 

 

technique GOneBloom {

    pass P0 {
        CullMode = CCW;      
        PixelShader = compile ps_3_0 PSBlur();
    
    }
}

 
상수는 가우스 분포 함수 부분에서 설명했던 것 처럼 미리 계산해 두었으며( 저는 레지던트 이블4 패치 파일에서 참고함) 좌, 우, 상, 하 방향에 대해 픽셀 6칸씩 블러 효과를 적용하고 있습니다. 그 부분이 바로 빨간색 소스 부분이며 최종적으로 기존 화면(Color3)과 블룸효과를 적용한 화면을(Color1 + Color2) 합성해서 화면에 뿌려주죠. 상수 값은 변화시켜보면서 적절한 결과물을 찾아보세요. 정답은 없으니깐.
 
다음은 블룸 효과 적용 전과 후의 결과 입니다.
 
 


-블룸 효과 적용 전-

 
 


-블룸 효과 적용 후-



출처 : http://blog.naver.com/hermet/58261272

728x90

+ Recent posts