2D多边形的阴影边界不同

时间:2012-04-24 16:56:15

标签: graphics xna drawing shader

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

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

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

你们对我想要达到的目标有更好的想法吗?

这里是一张四合一解决方案的图片:

Outlined Polygon

3 个答案:

答案 0 :(得分:1)

  1. 您可以渲染两次对象。第一个背后更大的拉伸版本。不是那么理想,因为复杂的对象无法均匀地拉伸以创建边框。

  2. 如果您可以访问屏幕缓冲区,则可以将辉光组件渲染到渲染目标中,并将全屏四边形与视口对齐,并为其添加全屏2D轮廓滤镜。

    通过这种方式,您可以通过定义半径,颜色和模糊来获得对边缘的完美控制。使用其他输出值(例如来自对象渲染过程的RGB值),您甚至可以使用不同的高级光源。

    我认为rendermonkey在着色器编辑器中有一些例子。它绝对是一个很好的起点,可以使用和试用。

答案 1 :(得分:1)

预计你想要计算新的边界顶点列表(带有原件的三角形条带的简单填充示例)。如果你使用恒定的边框宽度和凸多边形,只需:

B_new = B - (BtoA.normalised()+ BtoC.normalised())。normalized()* width;

enter image description here

如果没有,那么它可能会变得更复杂,有我旧的但非常普遍的解决方案:


//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);
}

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); }

答案 2 :(得分:0)

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

将外部顶点(在边框中)标记为1,将内部顶点(内部)标记为0。

在像素着色器中,您可以选择突出显示,其值大于0.9f或0.8f。

它应该有用。