어떤 방향에서도 볼 수 있는 MIP 를 만들어보자.
2018, Jul 18
이전 MIP 는 X, Y, Z 방향에서 본 화면 이었다.
이번에는 사용자가 어떤 좌표에서 보고 싶은지 정하면 해당 좌표에서 바라본 MIP를 만들어보자.
개발 순서는 다음과 같다.
- view vector 를 구한다.
- x vector 를 구한다.
- y vector 를 구한다.
- 구한 vector 평면에서 view vector 방향으로 전진하면서 MIP를 구한다.
먼저 이벤트 처리기 구현하자
VolumeRendererDoc.cpp
void CVolumeRendererDoc::OnMiprenderingAnydirection()
{
// TODO: 여기에 명령 처리기 코드를 추가합니다.
int img_width = 256;
int img_height = 256;
shared_ptr<unsigned char> image =
shared_ptr<unsigned char>(new unsigned char[img_width*img_height]);
memset(image.get(), 0, sizeof(unsigned char)*img_width*img_height);
m_pRenderer->RenderMIPAnyDirection(image.get(), img_width, img_height);
CVolumeRendererView* pView =
(CVolumeRendererView*)((CMainFrame*)(AfxGetApp()->m_pMainWnd))->GetActiveView();
pView->SetDrawImage(image.get(), img_width, img_height, 1);
pView->OnInitialUpdate();
}
핵심 함수인 RenderMIPAnyDirection 을 구현하자. 해당 함수에서 3차원 좌표가 자주 사용되므로 이를 구조체로 선언하여 간편하게 쓰도록 하자.
Renderer.h
#include "Volume.h"
...
struct float3
{
float x;
float y;
float z;
float3()
{
x = 0.f; y = 0.f; z = 0.f;
};
float3(float _x, float _y, float _z)
{
x = _x; y = _y; z = _z;
};
}
Renderer.cpp
bool Renderer::RenderMIPAnyDirection(unsigned char* image,
const int img_width, const int img_height)
{
int vol_width = p_mVolume -> GetWidth();
int vol_height = p_mVolume -> GetHeight();
int vol_depth = p_mVolume -> GetDepth();
// 눈, 센터, 업 좌표 설정
float3 eye_coord = float3(100.f, 200.f, 300.f);
float3 center_coord = float3(vol_width/2.f, vol_height/2.f, vol_depth/2.f);
float3 up_vector = float3(0.f, 0.f, -1.f);
// view vector 계산
float3 view_vector = center_coord - eye_coord;
// x vector 계산
float3 x_vector = 외적(view_vector, up_vector);
x_vector.normalize();
// y vector 계산
float3 y_vector.외적(view_vector, x_vector);
y_vector.normalize();
for(int j=0; j<img_height; j++)
{
for(int i=0; i<img_width; i++)
{
float3 cur_coord = eye_coord + x_vector*(i-img_width/2) + y_vector*(j-img_height/2);
unsigned char max_value = 0;
for(int k=0; k<1000; k++)
{
if( cur_coord.x >=0 && cur_coord.x < vol_width
&& cur_coord.y >= 0 && cur_coord.y < vol_width
&& cur_coord.z >= 0 && cur_coord.z < vol_width )
{
max_value = __max(max_value, m_pVolume->GetVoxel(cur_coord.x,cur_cood.y,cur_coord.z));
}
// view_vector 만큼 전진한다.
cur_coord += view_vector;
}
image[img_width*j + i] = max_value;
}
}
}
이제 정규화와 외적 함수를 구현하자. 정규화는 normalize, 외적은 cross 라는 이름으로 사용할 것이다.
정규화함수는 float3 구조체 내에서 선언 후 사용할 것이다. 연산자 오버로딩도 구현해놓자.
Renderer.h
#include <math.h>
struct float3
{
...
// 연산자 오버로딩
float3 operator- (float3 s2)
{
float3 res = float3();
res.x = x - s2.x;
res.y = y - s2.y;
res.z = z - s2.z;
return res;
}
float3 operator+ (float3 s2)
{
float3 res = float3();
res.x = x + s2.x;
res.y = y + s2.y;
res.z = z + s2.z;
return res;
}
float3 operator* (int i)
{
float3 res = float3();
res.x = x * i;
res.y = y * i;
res.z = z * i;
return res;
}
// 정규화
void normalize()
{
float dist = sqrt(x*x + y*y + z*z);
if(dist > 0)
x = x / dist; y= y/dist; z= z/dist;
}
}
외적구현
Renderer.cpp
float3 cross(float3 v1, float3 v2)
{
float res_x = v1.y*v2.z - v1.z*v2.y;
float res_y = v1.z*v2.x - v1.x*v2.z;
float res_z = v1.x*v2.y - v1.y*v2.x;
float3 res = float3(res_x,res_y,res_z);
return res;
}
실행하면 다음과 같이 나온다.