如何在shader中找到边缘像素?

问题描述 投票:0回答:1

是否可以在着色器中理解像素位于几何体的边缘?

或者至少了解三角形边缘上的像素?

也许 ddx()/ddy() 可以帮忙?

opengl glsl vulkan hlsl direct3d
1个回答
0
投票

可能但相对棘手,你需要一个几何着色器。它们几乎总是在 Direct3D 中可用:最后具有功能级别的 GPU < 11.0 was Intel Sandy Bridge from 2011, computers don’t usually live that long. However, availability in GL and especially GLES varies.

三角形的边

在几何着色器中,为三角形的每个顶点生成

float2
,三角形的 3 个顶点的值为 [ 1, 0 ]、[ 0, 1 ] 和 [ 0, 0 ]。

然后在像素着色器中,使用以下HLSL函数进行边缘检测,未经测试:

inline bool detectEdge( in float2 bary2 )
{
    // Compute minimum of these barycentric coordinates
    // The third one is redundant because they add up to 1.0
    float minBary = min( bary2.x, bary2.y );
    minBary = min( minBary, 1.0 - ( bary2.x + bary2.y ) );
    // Compute screen-space derivative of that variable
    float w = fwidth( minBary );
    // We want to return true for 1 pixels-wide line on the edge
    // Edges are shared by 2 triangles, each triangle needs 0.5 pixel wide line
    return ( minBary * 2 ) < w;
}

几何边缘

你的第一个问题比较复杂。 一种可能的方法是添加一个

DXGI_FORMAT_R8_UINT
类型的顶点属性。对于网格边缘的顶点,将这些字节设置为 1,对于其余的顶点,将这些字节设置为 0。

将这些整数从顶点传递到几何着色器。 在那里,您可以找出三角形的哪些边位于几何体的边上:

inline uint outerEdgesBitmap( uint v0, uint v1, uint v2 )
{
    uint res = 0;
    if( v0 != 0 && v1 != 0 )
        res = 1;    // v0-v1 edge is external
    if( v1 != 0 && v2 != 0 )
        res |= 2;   // v1-v2 edge is external
    if( v2 != 0 && v0 != 0 )
        res |= 4;   // v2-v0 edge is external
    return res;
}

通过图形管道传递该位图,您可以在像素着色器中检测几何体外边缘上的像素:

inline bool detectEdge( in float2 bary2, uint outerBitmap )
{
    float minBary = 1.0;
    if( 0 != ( outerBitmap & 1 ) )
        minBary = bary2.x;
    if( 0 != ( outerBitmap & 2 ) )
        minBary = min( minBary, bary2.y );
    if( 0 != ( outerBitmap & 4 ) )
        minBary = min( minBary, 1.0 - ( bary2.x + bary2.y ) );

    // For open edges, we want the entire 1-pixel wide line inside the triangle
    return minBary < fwidth( minBary );
}

附注我没有测试过这些 HLSL 函数,但我过去使用过类似的方法,它有效。

© www.soinside.com 2019 - 2024. All rights reserved.