RayCast, 번역을 어찌할 지 몰라 그냥 레이캐스트라 쓸까라고도 했으나 내가 이해가 안 되어 광선 계산(탐지, 추적)이라 표현하였다.
이를 이용하는 과정에서는 직선광을 방향과 거리를 지정하여 사출시키고 이 직선광에 충돌하는 고정체에 대한 정보를 구하는 절차가 포함되게 된다.
정보란, 거리, 법선벡터가 주를 이룬다.
광선 탐지는 한 물체가 사출되는 광선으로부터 이와 접촉하는지를 판단할 때 쓰인다. 특히 접촉할경우 광선 시발점과 접촉점 사이의 거리를 구하기 위해서도 주로 쓰인다.
사실 광선이 고정체에 접촉한다고 표현하였으나 메서드는 이를 주객을 바꾸어 개념을 정의한다. 고정체가 광선과 접촉하느냐로 시점을 바꾼다.
가령 광선->충돌하는가(고정체) 가 아니라 고정체->광선에충돌하는가(광선,...) 의 방식이다.
광선 계산을 위한 메서드는 다음과 같다.
bool b2Fixture::RayCast(b2RayCastOutput* output, const b2RayCastInput& input, int32 num); //셋째 인자는 무슨 영문인지는 모르지만, 존재해도 사용되지 않는다. 불편하다.
입력에 해당되는 input은 다음과 같은 멤버를 갖는 구조체이다.
struct b2RayCastInput
{
b2Vec2 p1, p2;
float32 maxFraction;
};
p1은 발사할 시작 위치, p2는 도착 위치이다. 이 기본적인 p2와 p1 사이의 거리를 1로 규정하며, maxFraction은 이 거리에 곱해질 수치로, 최종적인 광선의 길이는 p2와 p1 사이의 거리에 이 값을 곱한 값으로 결정된다.
출력에 해당되는 output은 다음과 같은 멤버를 갖는 구조체이다.
struct b2RayCastOutput
{
b2Vec2 normal;
float32 fraction;
};
normal은 충돌 지점의 단위법선벡터로 크기가 1이다.
fraction은 충돌한 광선이 실제 p2에서 p1 사이의 거리에 얼마를 곱해야 자신과 만나는지를 알려주는 값이다.
[input 설명]
[output 설명]
후에 다시 참조하기 위해 연습한 코드를 기재한다.
#ifndef __MYRAYCASTINGTEST_H__
#define __MYRAYCASTINGTEST_H__
//광선이 계속 회전하도록 현재 각을 저장한다. (이후에 이 값을 증가시켜 각도를 적용한다)
float currentRayAngle = 0;
class MyRayCastingTest : public Test {
public:
MyRayCastingTest(){
b2BodyDef myBodyDef;
myBodyDef.type = b2_staticBody;
myBodyDef.position.Set(0, 0);
b2Body* staticBody = m_world->CreateBody(&myBodyDef);
b2PolygonShape polygonShape;
b2EdgeShape edgeShape;
b2FixtureDef myFixtureDef;
myFixtureDef.shape = &edgeShape;
b2Vec2 bl(-20, 0);
b2Vec2 br(20, 0);
b2Vec2 tl(-20, 40);
b2Vec2 tr(20, 40);
edgeShape.Set( bl, br );
staticBody->CreateFixture(&myFixtureDef);
edgeShape.Set( tl, tr );
staticBody->CreateFixture(&myFixtureDef);
edgeShape.Set( bl, tl );
staticBody->CreateFixture(&myFixtureDef);
edgeShape.Set( br, tr );
staticBody->CreateFixture(&myFixtureDef);
//이상까지 사각형 영역을 생성한다.
//아래에서 사각형과 원형의 동적 강체들을 추가한다.
myBodyDef.type = b2_dynamicBody;
myBodyDef.position.Set(0, 20);
polygonShape.SetAsBox(2, 2);
myFixtureDef.shape = &polygonShape;
myFixtureDef.density = 1;
for(int i = 0; i < 5; i++){
m_world->CreateBody(&myBodyDef)->CreateFixture(&myFixtureDef);
}
b2CircleShape circleShape;
circleShape.m_radius = 2;
myFixtureDef.shape = &circleShape;
for(int i = 0; i <5; i++){
m_world->CreateBody(&myBodyDef)->CreateFixture(&myFixtureDef);
}
//움직임을 잘 살피기 위해 중력을 없앤다.
m_world->SetGravity( b2Vec2(0, 0) );
}
void Step(Settings* settings){
Test::Step(settings);
//매번 각도를 증가시킨다.
currentRayAngle += 360 / 20.0 / 60.0 * DEGTORAD;
float rayLength = 25;
b2Vec2 p1(0, 20);
b2Vec2 p2 = p1 + rayLength * b2Vec2( sinf(currentRayAngle), cosf(currentRayAngle) );
//set up input
glColor3f(1,1,1); //white
drawReflectedRay(p1, p2); //광선 계산한다. 고정체에 닿을 경우 반사되어 다음 고정체를 탐지한다. 이 과정은 광선의 최대 길이(maxFraction값을 곱한 크기)까지만 진행된다.
}
void drawReflectedRay( b2Vec2 p1, b2Vec2 p2 )
{
//set up input
b2RayCastInput input;
input.p1 = p1;
input.p2 = p2;
input.maxFraction = 1;
//check every fixture of every body to find closest
//이러한 순회는 좋지 않은 방법이나, 지금은 이와 같이 하였다.
// 잘 살펴보면, 모든 고정체마다 광선 계산을 하고 있다. 일일이 접촉하는지를 체크해야 하기 때문이다.
float closestFraction = 1; //start with end of line as p2
b2Vec2 intersectionNormal(0,0);
for (b2Body* b = m_world->GetBodyList(); b; b = b->GetNext()) {
for (b2Fixture* f = b->GetFixtureList(); f; f = f->GetNext()) {
b2RayCastOutput output;
if ( ! f->RayCast( &output, input, 3 ) )
continue;
if ( output.fraction < closestFraction ) {
closestFraction = output.fraction;
intersectionNormal = output.normal;
}
}
}
b2Vec2 intersectionPoint = p1 + closestFraction * (p2 - p1);
//draw this part of the ray
glBegin(GL_LINES);
glVertex2f( p1.x, p1.y );
glVertex2f( intersectionPoint.x, intersectionPoint.y );
glEnd();
if ( closestFraction == 1 )
return; //ray hit nothing so we can finish here
if ( closestFraction == 0 )
return;
//광선의 반사를 위해 직선에 대칭인 점을 구하는 공식이 사용되었다.
b2Vec2 remainingRay = (p2 - intersectionPoint);
b2Vec2 projectedOntoNormal = b2Dot(remainingRay, intersectionNormal) * intersectionNormal;
b2Vec2 nextp2 = p2 - 2 * projectedOntoNormal;
//recurse
drawReflectedRay(intersectionPoint, nextp2);
}
void MouseDown(const b2Vec2& p){
b2Vec2 pp = p;
pp.y = pp.y - 1;
Test::MouseDown(pp);
}
static Test* Create(){
return new MyRayCastingTest();
}
};
#endif
결과는
가운데가 광선의 시발점이다. 위로 가자마자 사각형에 접촉하고 여기서 반사되어 아래로 가다가 다시 아래 사각형의 우측 모서리 부근에서 반사되어 원에 충돌하고 .... 충돌하고... 하는 과정이 나타나 있다.
댓글 없음:
댓글 쓰기