2012-04-24 78 views
1

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

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

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

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

这里是什么样子,现在与四解一个画面:

Outlined Polygon

+1

怎么样使用第二渲染过程添加晒黑?如果你只是使用外部顶点,你的边框将不会是统一的。 – djmj 2012-04-24 18:30:54

+0

@djmj好主意。我不确定你会这样做吗?你能不能详细解释一下?第二个renderpass的想法是什么?我如何获得到边界的距离? – Nicholas 2012-04-24 18:54:07

回答

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

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

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

    我想rendermonkey在他们的着色器编辑器中有一些例子。它肯定是一个很好的起点,可以与之合作并尝试一些事情。

+0

谢谢,我会尝试! – Nicholas 2012-04-24 19:16:17

0

你的第二个方法可以是可能的...

标记外顶点(边境)1和内部顶点(内部)为0.

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

它应该工作。

+0

这里的问题是我没有任何内部顶点,因为我的三角剖分。对不起,如果不明确。 – Nicholas 2012-04-24 17:28:19

1

propaply你想要计算新的边界顶点列表(容易与三角形带填充示例带原件)。如果您使用不变的边框宽度和凸多边形,只需:

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