计算GPU上的多边形轮廓顶点

时间:2012-09-28 05:01:57

标签: c# arrays windows-phone-7 xna gpu

我正在尝试绘制带有宽彩色轮廓的2D多边形而不使用自定义着色器。
 (如果我要写一个,它可能比使用CPU慢,因为我不熟悉着色器)
为此,我计划像普通一样绘制多边形,然后在绘制相同的展开几何体时使用生成的深度缓冲区作为模板。

XNA“GraphicsDevice”可以在给定任何IVertexType实例数组的情况下绘制基元:

DrawUserPrimitives<T>(PrimitiveType primitiveType, T[] vertexData, int vertexOffset, int primitiveCount, VertexDeclaration vertexDeclaration) where T : struct;

我为2D坐标空间定义了一个IvertexType:

public struct VertexPosition2DColor : IVertexType
{
    public VertexPosition2DColor (Vector2 position, Color color) {
        this.position = position;
        this.color = color;
    }

    public Vector2 position;
    public Color color;

    public static VertexDeclaration declaration = new VertexDeclaration (
        new VertexElement(0, VertexElementFormat.Vector2, VertexElementUsage.Position, 0),
        new VertexElement(sizeof(float)*2, VertexElementFormat.Color, VertexElementUsage.Color, 0)
    );

    VertexDeclaration IVertexType.VertexDeclaration {
        get {return declaration;}
    }
}

我已经定义了一个用于存储多边形顶点,颜色和边法线的数组类:
我希望将此类作为GraphicDevice的DrawPrimitives函数中的T []参数传递 目标是将轮廓顶点进行GPU计算,因为它显然擅长于此类事物。

internal class VertexOutlineArray : Array
{
    internal VertexOutlineArray (Vector2[] positions, Vector2[] normals, Color[] colors, Color[] outlineColors, bool outlineDrawMode) {
        this.positions = positions;
        this.normals = normals;
        this.colors = colors;
        this.outlineColors = outlineColors;
        this.outlineDrawMode = outlineDrawMode;
    }

    internal Vector2[] positions, normals;
    internal Color[] colors, outlineColors;
    internal float outlineWidth;
    internal bool outlineDrawMode;

    internal void SetVertex(int index, Vector2 position, Vector2 normal, Color color, Color outlineColor) {
        positions[index] = position;
        normals[index] = normal;
        colors[index] = color;
        outlineColors[index] = outlineColor;
    }

    internal VertexPosition2DColor this[int i] {
        get {return (outlineDrawMode)? new VertexPosition2DColor(positions[i] + outlineWidth*normals[i], outlineColors[i]) 
                                     : new VertexPosition2DColor(positions[i], colors[i]);
        }
    }
}

我希望能够渲染形状,它的轮廓如下: 绘制展开的轮廓线几何体时,深度缓冲区用作模板

protected override void RenderLocally (GraphicsDevice device)
{
    // Draw shape
    mVertices.outlineDrawMode = true; //mVertices is a VertexOutlineArray instance

    device.RasterizerState = RasterizerState.CullNone;
    device.PresentationParameters.DepthStencilFormat = DepthFormat.Depth16;
    device.Clear(ClearOptions.DepthBuffer, Color.SkyBlue, 0, 0);
    device.DrawUserPrimitives<VertexPosition2DColor>(PrimitiveType.TriangleList, (VertexPosition2DColor[])mVertices, 0, mVertices.Length -2, VertexPosition2DColor.declaration);

    // Draw outline
    mVertices.outlineDrawMode = true;

    device.DepthStencilState = new DepthStencilState {
        DepthBufferWriteEnable = true,
        DepthBufferFunction = CompareFunction.Greater //keeps the outline from writing over the shape
    };
    device.DrawUserPrimitives(PrimitiveType.TriangleList, mVertices.ToArray(), 0, mVertices.Count -2);
}

但这不起作用,因为我无法将我的VertexArray类作为T []传递。如何在没有自定义着色器的情况下修改此操作或以其他方式实现在GPU上进行轮廓计算的目标?

1 个答案:

答案 0 :(得分:1)

我想知道你为什么不编写一个使用成对的三角形作为线条绘制轮廓的类?您可以创建一个广义折线例程,它接收2d点的输入和线宽,并且例程会吐出VertexBuffer。

我意识到这并没有回答你的问题,但我不知道尝试以你的方式做到这一点有什么好处。是否存在您想要实现的特定效果,或者您是要经常操作数据还是缩放多边形?


您可能遇到的问题是XNA4 for Windows Phone 7根本不支持自定义着色器。事实上,由于必须测试的排列数量,它们故意将其限制为一组预定义着色器。目前支持的是:

AlphaTestEffect BasicEffect EnvironmentMapEffect DualTextureEffect SkinnedEffect

你可以在这里阅读:

http://msdn.microsoft.com/en-us/library/bb203872(v=xnagamestudio.40).aspx

我没有测试创建或使用具有Vector2位置和正常的IVertexType,所以我不能评论它是否受支持。如果我这样做,我将仅使用BasicEffect和VertexPositionNormal进行主要的多边形形状渲染,并为每个多边形调整DiffuseColor。为了渲染轮廓,您可以使用现有的VertexBuffer并通过调用GraphicsDevice.Viewport.Unproject()来适当缩放它,以确定生成n像素2d屏幕距离(轮廓宽度)所需的3d坐标距离。

请记住,当您使用BasicEffect或任何效果时,您必须遍历CurrentTechnique的EffectPass数组并在进行Draw调用之前为每个传递调用Apply()方法。

        device.DepthStencilState = DepthStencilState.Default;
        device.BlendState = BlendState.AlphaBlend;
        device.RasterizerState = RasterizerState.CullCounterClockwise;

        //Set the appropriate vertex and indice buffers
        device.SetVertexBuffer(_polygonVertices);
        device.Indices = _polygonIndices;

        foreach (EffectPass pass in _worldEffect.CurrentTechnique.Passes)
        {
            pass.Apply();
            PApp.Graphics.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, _polygonVertices.VertexCount, 0, _polygonIndices.IndexCount / 3);
        }