Bresenham의 라인 알고리즘을 부동 소수점 엔드 포인트로 일반화하려면 어떻게해야합니까?


12

두 가지를 결합하려고합니다. 게임을 작성 중이며 부동 소수점 끝 점이있는 선에 놓인 격자 사각형을 결정해야합니다.

라인 트로프 그리드

또한 나는 접촉하는 모든 그리드 사각형을 포함해야합니다 (예 : Bresenham의 선 뿐만 아니라 파란색 사각형 ).

브레 센햄 vs 풀 스윕

누군가 내가 그것을하는 방법에 대한 통찰력을 제공 할 수 있습니까? 확실한 해결책은 순진 알고리즘을 사용하는 것이지만 더 최적화 된 (빠른) 것이 있습니까?


링크가 오프라인 상태가되면 구글에서 "raytracing을위한 더 빠른 복셀 순회 알고리즘"을 검색하십시오
Gustavo Maciel

답변:


9

그리드 순회 알고리즘을 찾고 있습니다. 이 문서 는 좋은 구현을 제공합니다.

이 논문에서 발견 된 2D의 기본 구현은 다음과 같습니다.

loop {
    if(tMaxX < tMaxY) {
        tMaxX= tMaxX + tDeltaX;
        X= X + stepX;
    } else {
        tMaxY= tMaxY + tDeltaY;
        Y= Y + stepY;
    }
    NextVoxel(X,Y);
}

종이에는 3D 레이 캐스팅 버전도 있습니다.

링크가 썩는 경우 이름이 많은 미러를 찾을 수 있습니다 . 레이트 레이싱을위한 빠른 복셀 순회 알고리즘


어색 해요 나는 당신에게 대답을 전환하고 ltjax를 투표 할 것입니다. 그 논문에 대한 귀하의 링크를 바탕으로 해결했기 때문입니다.
SmartK8

5

블루의 아이디어는 좋지만 구현은 약간 어색합니다. 실제로 sqrt없이 쉽게 할 수 있습니다. 변성 사례 ( BeginX==EndX || BeginY==EndY) 를 제외 하고 1 사분면의 행 방향에만 초점을 맞추는 순간을 가정합시다 BeginX < EndX && BeginY < EndY. 하나 이상의 다른 사분면에 대한 버전도 구현해야하지만 첫 번째 사분면의 버전과 매우 유사합니다. 다른 모서리 만 확인하십시오. C'ish 의사 코드에서 :

int cx = floor(BeginX); // Begin/current cell coords
int cy = floor(BeginY);
int ex = floor(EndX); // End cell coords
int ey = floor(EndY);

// Delta or direction
double dx = EndX-BeginX;
double dy = EndY-BeginY;

while (cx < ex && cy < ey)
{
  // find intersection "time" in x dir
  float t0 = (ceil(BeginX)-BeginX)/dx;
  float t1 = (ceil(BeginY)-BeginY)/dy;

  visit_cell(cx, cy);

  if (t0 < t1) // cross x boundary first=?
  {
    ++cx;
    BeginX += t0*dx;
    BeginY += t0*dy;
  }
  else
  {
    ++cy;
    BeginX += t1*dx;
    BeginY += t1*dy;
  }
}

이제 다른 사분면의 경우 ++cxor ++cy및 loop 조건 만 변경하면 됩니다. 충돌에 이것을 사용한다면, 아마도 4 가지 버전을 모두 구현해야 할 것이다.


Gustavo Maciel이 제공 한 알고리즘이 조금 더 효율적입니다. 첫 번째 T 만 결정한 다음 세로 또는 가로에 1을 더하고 셀 크기만큼 Ts를 이동합니다. 그러나 그가 답변으로 변환하지 않았 으므로이 답변을 가장 가까운 답변으로 받아들입니다.
SmartK8

3

귀하의 가정은 반드시 셀을 찾는 것이 아니라 셀이이 그리드에서 교차하는 선을 찾는 것입니다.

예를 들어 이미지를 찍으면 셀이 아니라 교차하는 격자 선이 강조 표시됩니다.

레드 라인

그러면 그리드 선을 통과하면이 선의 양쪽에있는 셀이 채워진 셀임을 알 수 있습니다.

교차점 알고리즘을 사용하여 포인트를 픽셀로 스케일링하여 부동 소수점 라인이 이들을 교차하는지 확인할 수 있습니다. 부동 좌표 : 픽셀의 비율이 1.0 : 1 인 경우 정렬되어 있으며 직접 변환 할 수 있습니다. 선분 교차 알고리즘을 사용하여 왼쪽 하단 선 (1,7) (2,7)이 선 (1.3,6.2) (6.51,2.9)와 교차하는지 확인할 수 있습니다. http://alienryderflex.com/intersect/

c에서 C #으로의 일부 번역이 필요하지만 그 논문에서 아이디어를 얻을 수 있습니다. 링크가 끊어 질 경우 아래 코드를 작성하겠습니다.

//  public domain function by Darel Rex Finley, 2006

//  Determines the intersection point of the line defined by points A and B with the
//  line defined by points C and D.
//
//  Returns YES if the intersection point was found, and stores that point in X,Y.
//  Returns NO if there is no determinable intersection point, in which case X,Y will
//  be unmodified.

bool lineIntersection(
double Ax, double Ay,
double Bx, double By,
double Cx, double Cy,
double Dx, double Dy,
double *X, double *Y) {

  double  distAB, theCos, theSin, newX, ABpos ;

  //  Fail if either line is undefined.
  if (Ax==Bx && Ay==By || Cx==Dx && Cy==Dy) return NO;

  //  (1) Translate the system so that point A is on the origin.
  Bx-=Ax; By-=Ay;
  Cx-=Ax; Cy-=Ay;
  Dx-=Ax; Dy-=Ay;

  //  Discover the length of segment A-B.
  distAB=sqrt(Bx*Bx+By*By);

  //  (2) Rotate the system so that point B is on the positive X axis.
  theCos=Bx/distAB;
  theSin=By/distAB;
  newX=Cx*theCos+Cy*theSin;
  Cy  =Cy*theCos-Cx*theSin; Cx=newX;
  newX=Dx*theCos+Dy*theSin;
  Dy  =Dy*theCos-Dx*theSin; Dx=newX;

  //  Fail if the lines are parallel.
  if (Cy==Dy) return NO;

  //  (3) Discover the position of the intersection point along line A-B.
  ABpos=Dx+(Cx-Dx)*Dy/(Dy-Cy);

  //  (4) Apply the discovered position to line A-B in the original coordinate system.
  *X=Ax+ABpos*theCos;
  *Y=Ay+ABpos*theSin;

  //  Success.
  return YES; }

라인 세그먼트가 언제 (그리고 어디에) 교차하는지 알아야하는 경우 다음과 같이 기능을 수정할 수 있습니다.

//  public domain function by Darel Rex Finley, 2006  

//  Determines the intersection point of the line segment defined by points A and B
//  with the line segment defined by points C and D.
//
//  Returns YES if the intersection point was found, and stores that point in X,Y.
//  Returns NO if there is no determinable intersection point, in which case X,Y will
//  be unmodified.

bool lineSegmentIntersection(
double Ax, double Ay,
double Bx, double By,
double Cx, double Cy,
double Dx, double Dy,
double *X, double *Y) {

  double  distAB, theCos, theSin, newX, ABpos ;

  //  Fail if either line segment is zero-length.
  if (Ax==Bx && Ay==By || Cx==Dx && Cy==Dy) return NO;

  //  Fail if the segments share an end-point.
  if (Ax==Cx && Ay==Cy || Bx==Cx && By==Cy
  ||  Ax==Dx && Ay==Dy || Bx==Dx && By==Dy) {
    return NO; }

  //  (1) Translate the system so that point A is on the origin.
  Bx-=Ax; By-=Ay;
  Cx-=Ax; Cy-=Ay;
  Dx-=Ax; Dy-=Ay;

  //  Discover the length of segment A-B.
  distAB=sqrt(Bx*Bx+By*By);

  //  (2) Rotate the system so that point B is on the positive X axis.
  theCos=Bx/distAB;
  theSin=By/distAB;
  newX=Cx*theCos+Cy*theSin;
  Cy  =Cy*theCos-Cx*theSin; Cx=newX;
  newX=Dx*theCos+Dy*theSin;
  Dy  =Dy*theCos-Dx*theSin; Dx=newX;

  //  Fail if segment C-D doesn't cross line A-B.
  if (Cy<0. && Dy<0. || Cy>=0. && Dy>=0.) return NO;

  //  (3) Discover the position of the intersection point along line A-B.
  ABpos=Dx+(Cx-Dx)*Dy/(Dy-Cy);

  //  Fail if segment C-D crosses line A-B outside of segment A-B.
  if (ABpos<0. || ABpos>distAB) return NO;

  //  (4) Apply the discovered position to line A-B in the original coordinate system.
  *X=Ax+ABpos*theCos;
  *Y=Ay+ABpos*theSin;

  //  Success.
  return YES; }

안녕하세요, 그리드 순회는 그리드 전체에서 수천 개의 선 교차를 최적화하기위한 것입니다. 이것은 수천 개의 선 교차로 해결할 수 없습니다. 플레이어가 교차 할 수없는 접지선이있는 게임에 맵이 있습니다. 수천 개가있을 수 있습니다. 비싼 교차점을 계산할 대상을 결정해야합니다. 이것들을 결정하기 위해 플레이어 움직임 라인 (또는 광원의 빛)의 교차점 만 계산하고 싶습니다. 귀하의 경우 매 라운드 ~ 256x256x2 선 세그먼트와의 교차점을 결정해야합니다. 그것은 전혀 최적화되지 않았습니다.
SmartK8

그러나 여전히 대답 해 주셔서 감사합니다. 기술적으로 작동하고 정확합니다. 그러나 나에게는 실현 가능하지 않습니다.
SmartK8

3
float difX = end.x - start.x;
float difY = end.y - start.y;
float dist = abs(difX) + abs(difY);

float dx = difX / dist;
float dy = difY / dist;

for (int i = 0, int x, int y; i <= ceil(dist); i++) {
    x = floor(start.x + dx * i);
    y = floor(start.y + dy * i);
    draw(x,y);
}
return true;

JS 데모 :

임 구르


1
부동 소수점 숫자 오류로 인해 실패했습니다 (루프는 다음 정수에서 가장 작은 분수에 대해 추가 반복을 수행하여 라인 끝점을 '끝'위치 이상으로 밀어 넣습니다). 간단한 수정은 dist를 처음에 ceil로 계산하여 dx, dy를 루프의 정수 반복 횟수로 나눕니다 (for 루프에서 ceil (dist)를 잃을 수 있음을 의미합니다).
PeteB

0

나는 오늘 같은 문제에 부딪 쳤고 두더지 언덕에서 꽤 큰 스파게티 산을 만들었지 만 작동하는 것으로 끝났습니다 : https://github.com/SnpM/Pan-Line-Algorithm .

읽어보기에서 :

이 알고리즘의 핵심 개념은 한 축에서 1 단위 씩 증가하고 다른 축의 증가를 테스트한다는 점에서 Bresenham과 유사합니다. 그러나 분수는 상당히 증가하기 어렵고 많은 피자를 추가해야했습니다. 예를 들어 기울기가 5 인 X = .21에서 X = 1.21로 증가하면 복잡한 문제가 발생합니다 (불쾌한 숫자 사이의 좌표 패턴) 예측하기 어렵지만 기울기가 5 인 상태에서 1에서 2로 증가하면 쉬운 문제가 발생합니다. 정수 사이의 좌표 패턴은 해결하기가 매우 쉽습니다 (증분 축에 수직 인 선). 쉬운 문제를 얻기 위해 증분은 분수 단위로 모든 계산을 별도로 수행하여 정수로 오프셋됩니다. 따라서 .21에서 증가를 시작하는 대신

ReadMe는 솔루션을 코드보다 훨씬 잘 설명합니다. 두통을 덜 유발하도록 수정하려고합니다.

나는이 질문에 1 년 늦었다는 것을 알고 있지만이 문제에 대한 해결책을 찾고있는 다른 사람들에게 도움이되기를 바랍니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.