1

我们正在 XNA 中编写一个 2D 游戏。现在我们有了定义关卡元素的多边形。它们被三角化,以便我们可以轻松地渲染它们。现在我想编写一个着色器,将多边形渲染为轮廓纹理。所以在多边形的中间会看到纹理,在边界上它应该会发光。

我的第一个想法是沿着多边形走,并在每个线段上绘制一个具有特定纹理的四边形。这有效,但对于纹理被迫重叠的小角落看起来很奇怪。

我的第二种方法是用某种法线指向多边形外标记所有边界顶点。将其传递给着色器将在三角剖分的边缘内插法线,我可以使用内插的“法线”作为着色的值。我还不能测试它,但那行得通吗?三角剖分的一个特殊属性是所有顶点都在边界上,因此多边形内没有顶点。

你们对我想要实现的目标有更好的想法吗?

这里是四边形解决方案现在的样子:

轮廓多边形

4

3 回答 3

1
  1. 你可以渲染你的对象两次。第一个后面有一个更大的拉伸版本。不是很理想,因为一个复杂的对象不能被均匀地拉伸以创建一个边框。

  2. 如果您可以访问屏幕缓冲区,则可以将发光组件渲染到渲染目标中,并将全屏四边形与视口对齐,并向其添加全屏 2D 剪影过滤器。

    通过这种方式,您可以通过定义其半径、颜色、模糊来完美控制边缘。使用额外的输出值,例如来自对象渲染通道的 RGB 值,您甚至可以拥有不同的高级发光。

    我认为 rendermonkey 在他们的着色器编辑器中有一些例子。它绝对是一个很好的开始工作和尝试的东西。

于 2012-04-24T19:08:30.050 回答
1

建议您要计算新的边界顶点列表(使用带原件的三角形带的简单填充示例)。如果您使用恒定的边框宽度和凸多边形,它只是:

B_new = B - (BtoA.normalised() + BtoC.normalised()).normalised() * 宽度;

在此处输入图像描述

如果不是,那么它会变得更复杂,这是我的旧但非常通用的解决方案:


//Helper function. To working right, need that v1 is before v2 in vetex list and vertexes are going to (anti???) cloclwise! float vectorAngle(Vector2 v1, Vector2 v2){

    float alfa;

    if (!v1.isNormalised())
        v1.normalise();
    if (!v2.isNormalised())
        v2.normalise();

    alfa = v1.dotProduct(v2);

    float help = v1.x;
    v1.x = v1.y;
    v1.y = -help;

    float angle = Math::ACos(alfa);

    if (v1.dotProduct(v2) < 0){
        angle = -angle;
    }


    return angle;
}

//Normally dont use directly this!
Vector2 calculateBorderPoint(Vector2 vec1, Vector2 vec2, float width1, float width2){

    vec1.normalise();
    vec2.normalise();

    float cos = vec1.dotProduct(vec2);            //Calculates actually cosini of two (normalised) vectors (remember math lessons)
    float csc = 1.0f / Math::sqrt(1.0f-cos*cos);  //Calculates cosecant of angle, This return NaN if angle is 180!!!

    //And rest of the magic
    Vector2 difrence = (vec1 * csc * width2) + (vec2 * csc * width1);


    //If you use just convex polygons (all angles < 180, = 180 not allowed in this case) just return value, and if not you need some more magic. 
    //Both of next things need ordered vertex lists!



    //Output vector is always to in side of angle, so if this angle is.
    if (Math::vectorAngle(vec1, vec2) > 180.0f) //Note that this kind of function can know is your function can know that angle is over 180 ONLY if you use ordered vertexes (all vertexes goes always (anti???) cloclwise!)
        difrence = -difrence;


    //Ok and if angle was 180...
    //Note that this can fix your situation ONLY if you use ordered vertexes (all vertexes goes always (anti???) cloclwise!)
    if (difrence.isNaN()){
        float width = (width1 + width2) / 2.0;   //If angle is 180 and border widths are difrent, you cannot get perfect answer ;)

        difrence = vec1 * width;

        //Just turn vector -90 degrees
        float swapHelp = difrence.y
        difrence.y = -difrence.x;
        difrence.x = swapHelp;
    }

    //If you don't want output to be inside of old polygon but outside, just: "return -difrence;"
    return difrence;

}

//Use this =)
Vector2 calculateBorderPoint(Vector2 A, Vector2 B, Vector2 C, float widthA, float widthB){
    return B + calculateBorderPoint(A-B, C-B, widthA, widthB);
}

于 2012-11-27T00:59:53.587 回答
0

你的第二种方法是可能的......

用 1 标记外部顶点(在边界中),用 0 标记内部顶点(内部)。

在像素着色器中你可以选择高亮,那些它的值大于 0.9f 或 0.8f 的。

它应该工作。

于 2012-04-24T17:24:39.683 回答