Algorithm 沿a的线性插值;“布雷森汉姆线”;

Algorithm 沿a的线性插值;“布雷森汉姆线”;,algorithm,graphics,graphics2d,Algorithm,Graphics,Graphics2d,我使用线性插值在屏幕上的两个2d坐标之间设置对象动画。这非常接近我想要的,但是因为四舍五入,我得到了一个锯齿状的运动。在ASCII艺术中: ooo ooo ooo oo 注意它是如何在曼哈顿网格中行走的,而不是45度转弯。我想要的是沿着直线的线性插值,这将创建: oo oo oo oo 对于每个x,只有一个对应的y。(并将x/y替换为陡峭的线) 那我为什么不使用Bresenham算法呢?我当然可以,但是这个算法是迭代的,我只想知道沿着这条线的一

我使用线性插值在屏幕上的两个2d坐标之间设置对象动画。这非常接近我想要的,但是因为四舍五入,我得到了一个锯齿状的运动。在ASCII艺术中:

ooo
  ooo
    ooo
      oo
注意它是如何在曼哈顿网格中行走的,而不是45度转弯。我想要的是沿着直线的线性插值,这将创建:

oo
  oo
    oo
      oo
对于每个x,只有一个对应的y。(并将x/y替换为陡峭的线)

那我为什么不使用Bresenham算法呢?我当然可以,但是这个算法是迭代的,我只想知道沿着这条线的一个坐标


我将尝试通过线性插值x坐标,将其四舍五入到像素网格,然后找到相应的y来解决这个问题。(同样,将x/y替换为陡峭线)。不过,不管这个解决方案效果如何,我都会对其他建议和以前的经验感兴趣。

以下是我最终得出的解决方案:

public static Vector2 BresenhamLerp(Vector2 a, Vector2 b, float percent)
{
    if (a.x == b.x || Math.Abs(a.x - b.x) < Math.Abs(a.y - b.y))
    {
        // Didn't do this part yet. Basically, we just need to recurse
        // with x/y swapped and swap result on return
    }

    Vector2 result;
    result.x = Math.Round((1-percent) * a.x + percent * b.x);

    float adjustedPercent = (result.x - a.x + 0.5f) / (b.x - a.x);
    result.y = Math.Round((1-adjustedPercent) * a.y + adjustedPercent * b.y);

    return result;
}
公共静态向量2 BresenhamLerp(向量2 a、向量2 b、浮动百分比)
{
如果(a.x==b.x | | Math.Abs(a.x-b.x)
引入了Bresenham的直线算法,以比通常方法快一点的速度绘制完整的直线。它有两大优势:

  • 它适用于整数变量
  • 当绘制完整的线条时,它会以迭代的方式工作,速度很快
如果只计算一些坐标,那么第一个优势不是很大。当只计算一些坐标时,第二个优点变成了一个缺点。因此,毕竟,没有必要使用Bresenham的算法

相反,您可以使用不同的算法,这将产生相同的行。例如DDA(数字微分分析仪)。这基本上与你提到的方法相同

第一步:计算坡度

m = (y_end - y_start) / (x_end - x_start)
第二步:计算迭代步骤,简单如下:

i = x - x_start
第三步:计算相应的y值:

y = y_start + i * m
  = y_start + (x - x_start) * (y_end - y_start) / (x_end - x_start)

这就是我刚刚想出来的方法。可能不是最漂亮的插值,但它只是一个1-2浮点加法,每次迭代一次。通过计算曼哈顿矩阵上的步数工作

啊,当直线垂直(dx=0)时,它还没有捕捉到这种情况

这是朴素的bresenham,但理论上迭代也只能使用整数。如果您想去掉float color值,事情会变得更加困难,因为直线可能比色差长,因此delta color<1

void Brepolate( uint8_t* pColorBuffer, uint8_t cs, float xs, float ys, float zs, uint8_t ce, float xe, float ye, float ze )
{
    float nColSteps = (xe - xs) + (ye - ys);
    float fColInc = ((float)cs - (float)ce) / nColSteps;
    float fCol = cs;

    float dx = xe - xs;
    float dy = ye - ys;

    float fCol = cs;

    if (dx > 0.5)
    {
        float de = fabs( dy / dx );
        float re = de - 0.5f;

        uint32_t iY = ys;
        uint32_t iX;

        for (   uint32_t    iX = xs;
                            iX <= xe;
                            iX++ )
        {
            uint32_t off = surf.Offset( iX, iY );
            pColorBuffer[off] = fCol;
            re += de;
            if (re >= 0.5f)
            {
                iY++;
                re -= 1.0f;
                fCol += fColInc;
            }
            fCol += fColInc;
        }
    }
}
void Brepolate(uint8_t*pColorBuffer,uint8_t cs,float xs,float ys,float zs,uint8_t ce,float xe,float ye,float ze)
{
浮点nColSteps=(xe-xs)+(ye-ys);
float fColInc=((float)cs-(float)ce)/nColSteps;
浮点数fCol=cs;
浮点数dx=xe-xs;
float dy=ye-ys;
浮点数fCol=cs;
如果(dx>0.5)
{
浮点数de=fabs(dy/dx);
浮球re=de-0.5f;
uint32_t iY=ys;
uint32_t iX;
对于(uint32_t iX=xs;
iX=0.5f)
{
iY++;
re-=1.0f;
fCol+=fColInc;
}
fCol+=fColInc;
}
}
}

我想说,您的舍入想法听起来像是一条可行之路。我不确定采用不同的舍入方式是否能解决这个问题。你真的需要把坐标四舍五入到像素网格吗?你用什么来渲染?@Qnan:是的,在这种情况下,我需要转到像素网格,因为我正在粘贴图像。像素完美是唯一看起来不错的东西。你可能需要考虑到0.5的舍入变成1,这在原则上是不公平的。结果是一条不对称的线。在某些情况下,您无法避免(例如,当连接2x3栅格中的角点时),但在其他情况下,您可以避免(例如,在5x3栅格中)。即使是标准的windows绘图程序也会犯这个错误。一种解决方案是在线路中间切换圆0.5的方向。如果线通过两个相邻像素的中间,则会出现“代码>5”/代码>。您不能为其中一个做出唯一的决定(即一个不比另一个更正确)。四舍五入只是一种惯例,不是错误。不,在某些情况下这很好,但在其他情况下,这会导致不对称线条,看起来很难看。简单地使用上述方法,您可以获得更合适、更漂亮的线条渲染。毕竟这是一个计算机图形学,所以是的,你可以为你的传统感到骄傲…而且看起来…丑陋。啊,现在我明白你的意思了。是的,这是一个有效的方法。