【问题标题】:What is the best algorithm for a non antialiased line and a aliased line非抗锯齿线和锯齿线的最佳算法是什么
【发布时间】:2021-02-21 16:16:32
【问题描述】:

我是着色器的新手,我一直在搞乱网站着色器。我正在尝试理解图形(和图形管道),例如画线、插值、光栅化等……我编写了两个线函数,如果处理的像素在线,则返回颜色。这是使用片段着色器的shadertoy代码

struct Vertex {
    vec2 p;
    vec4 c;
};

vec4 overlay(vec4 c1, vec4 c2) {
    return vec4((1.0 - c2.w) * c1.xyz + c2.w * c2.xyz, 1.0);
}

vec4 drawLineA(Vertex v1, Vertex v2, vec2 pos) {
    vec2 a = v1.p;
    vec2 b = v2.p;
    vec2 r = floor(pos);
    
    vec2 diff = b - a;
    
    
    if (abs(diff.y) < abs(diff.x)) {
        if (diff.x < 0.0) {
            Vertex temp1 = v1;
            Vertex temp2 = v2;
            
            v1 = temp2;
            v2 = temp1;
            
            a = v1.p;
            b = v2.p;
            diff = b - a;
        
        }
        
        float m = diff.y / diff.x;
        float q = r.x - a.x;
        
        if (floor(m * q + a.y) == r.y && a.x <= r.x && r.x <= b.x) {
            float h = q / diff.x;
            return vec4((1.0 - h) * v1.c + h * v2.c);
        }
        
        
    } else {
        if (diff.y < 0.0) {
            Vertex temp1 = v1;
            Vertex temp2 = v2;
            
            v1 = temp2;
            v2 = temp1;
            
            a = v1.p;
            b = v2.p;
            diff = b - a;
        
        }
    
        float m =  diff.x / diff.y;
        float q = r.y - a.y;
        
        if (floor(m * q + a.x) == r.x && a.y <= r.y && r.y <= b.y) {
            float h = q / diff.y;
            return vec4((1.0 - h) * v1.c + h * v2.c);
        }
    
    }
    
    return vec4(0,0,0,0);
}

vec4 drawLineB(Vertex v1, Vertex v2, vec2 pos) {
    vec2 a = v1.p;
    vec2 b = v2.p;
    
    vec2 l = b - a;
    vec2 r = pos - a;
    float h = dot(l,r) / dot (l,l);
    
    vec2 eC = a + h * l;
    
    if (floor(pos) == floor(eC) && 0.0 <= h && h <= 1.0 ) {
       return vec4((1.0 - h) * v1.c + h * v2.c); 
    }
    
    return vec4(0,0,0,0);
}



void mainImage( out vec4 fragColor, in vec2 fragCoord )
{
    float t = iTime;
    float r = 300.0;
    Vertex v1 = Vertex(vec2(400,225), vec4(1,0,0,1));
    Vertex v2 = Vertex(vec2(400.0 + r*cos(t) ,225.0 + r*sin(t)), vec4(0,1,0,1));
    
    vec4 col = vec4(0,0,0,1);
    col = overlay(col,drawLineA(v1, v2, fragCoord));
    col = overlay(col,drawLineB(v1, v2, fragCoord));
    // Output to screen
    fragColor = col;
}

但是,我一直在使用的线条并不快或使用抗锯齿。哪个是抗锯齿和锯齿线最快的算法,我应该如何实现它谢谢。

【问题讨论】:

    标签: graphics glsl shader line fragment-shader


    【解决方案1】:

    片段着色器确实不是解决此问题的正确方法,shadertoy 上的很多内容实际上只是一个玩具/代码高尔夫,展示了克服平台限制的解决方案,这些限制在现实世界场景中效率极低。

    所有图形 API 都提供用于绘制线段的专用接口,只需搜索“API_NAME draw line”,例如“webgl 画线”。在那些不满足使用 MSAA 或自定义着色器内 AA 的三角形条的情况下。

    如果您真的只是在寻找一种高效的算法wikipedia page 可以满足您的要求。

    【讨论】:

    • 我这样做的原因是为了更好地理解图形,所以我不想只使用该功能,我宁愿自己实现它
    • “片段着色器确实不是解决这个问题的正确方法”
    • 那我还有其他方法可以自己实现代码吗?
    • 您现在运行的算法是“屏幕上的每个像素判断它是否在线”,而它应该是“每个线条判断哪些像素与它们相交”。
    • 因为您正在为每一行而不是每个像素执行数学运算?当你有一个状态和一个已知的固定分辨率时,找到相应的像素也简单得多。此外,GPU 远不能并行处理屏幕上的每个像素。
    【解决方案2】:

    正如另一个答案所说,着色器对此不是很好。

    如今,线光栅化是在 gfx 卡上使用硬件插值器在幕后完成的。着色器为渲染图元的每个像素调用,在您的情况下,这意味着它为屏幕的每个像素调用,这一切都是为您渲染的每一行调用的,这比原生方式慢得多。

    如果您真的想学习光栅化,请在 CPU 端执行此操作。线的最佳算法取决于您使用的计算硬件架构。

    对于顺序处理是:

    • DDA这个是亚像素精度的

    过去 Bresenham 更快,但自从 x386 以来,这不是真正的 IIRC ...

    对于并行处理,您只需计算像素到线的距离(或多或少像您现在所做的那样)。

    因此,如果您坚持为此使用着色器,您可以使用几何着色器加快处理速度,并仅处理靠近您的线的片段(像素)。见:

    因此,您只需在线条周围创建 OOBB 并通过每条线条发射 2 个三角形来渲染它,然后在片段中计算到线条的距离并相应地设置颜色...

    对于抗锯齿,您只需更改最后一个像素边缘距离上的像素颜色。所以如果你的线有一半宽度w 并且片段到线的距离是d 那么:

    if (d>w) discard; // fragment too far
    d=(w-d)/pixel_size; // distance from edge in pixels
    frag_color = vec4(r,g,b,min(1.0,d)); // use transparency/blending 
    

    如您所见,抗锯齿只是通过亚像素位置/像素相对于光栅化对象的距离调制的混合进行渲染)同样的技术可以用于 DDA。

    也有渲染线条的光线追踪方法,但它们与查找到线条的距离几乎相同......但是您检查 3D 光线而不是 2D 像素位置,这会使数学稍微复杂化。

    【讨论】:

    • 即使在现代 CPU 架构上,Bresenham 是否仍然能够为您节省几个周期,因为部门仍然是comparatively expensive?这在学习光栅化方面并不重要。
    • @LJᛃ 根本不需要...... DDA 不需要除法也不需要浮动......只有天真的实现才需要。如果您在上面的链接中查看我的line_DDA_subpixel,则没有除法或乘法,只有ints ...
    • @LJᛃ 乘法是通过重复加法来完成的,而除法是通过在循环中重复减法来完成的......所以在整行完成后,计算也完成了。所以在循环迭代中只使用+,- 操作
    • 那么,在 GPU 或 CPU 上最快的方法是什么?抱歉已经几天了,我还没有检查stackoverflow
    • @JoshuaPasa 最快取决于使用的硬件。如果您没有大的并行性,那么 DDA 就是方法。如果您有快速的 FPU 操作(例如这些天的 x86),您可以对 DDA 使用矢量方法,它甚至比整数 1 更快,因为它更少早午餐(它只是添加大小略小于 ~1pixel 或 2 步的方向向量1px in x 和 second with 1px in y 类似于 Wolfenstein raycast through map。在 GPU 上最快的总是原生硬件插值器。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多