什么是api独立顶点处理的良好代码结构?

目前正在使用C#开发3D媒体引擎,我遇到了一个小难题。我找到了我的rending循环,我有一个很棒的插件架构和内容管理系统,甚至还有一个计划好的材料管道。然后引擎计划使用DirectX和OpenGL(通过'渲染器'插件),以及两个API的可编程管线。 无论如何,在本周初,我开始研究用于处理顶点的引擎抽象层(我已经担心这几周了)。正如你们中的一些人所知,图形API之间的顶点处理并不完全相关或相同。好吧有点相关;),但不一样。在OpenGL处理​​顶点非常简单,您可以创建自定义顶点结构,将其发送到GPU,然后让着色器处理其余部分。这对于灵活的图形管线是完美的,OpenGL不需要知道每个顶点包含哪些元素。另一方面,DirectX要求我们为每个顶点结构创建声明,然后将它们发送到GPU。 问题是我不知道传递了什么类型的顶点结构,我肯定希望避免创建抽象层,该抽象层涉及通过枚举和一些抽象的“VertexDeclaration”类声明顶点的每个元素;这会导致一些问题: 1)获得顶点元素至少可以说是一种痛苦。我可以使用一些'VertexSemantic'并询问顶点'a - z'的位置,但是当处理像骨骼动画这样的顶点时,它可能会有很多开销。 2)考虑到发动机的主要焦点是“新手”,不是非常人性化。我希望用户能够创建自定义顶点和网格缓冲区,而不必声明大量对象,从而消耗宝贵的开发时间。 3)更多? 现在,我可以使用属性执行某些操作,然后在DirectX渲染器内创建顶点结构的声明。例如,继续创建一些枚举:
// for getting the format layout of the element
public enum ElementFormat
{
    Float, Float2, Float3, Byte, etc, etc
}
// for determining the 'usage' 
// (here is 'another' where DirectX limits vertex structures ><)
public enum ElementUsage
{
    Position, Normal, Color, TextureCoord, etc, etc
}
现在我可以创建一个用户可以应用于顶点结构中每个元素的“字段”的属性:
    public class VertexElementAttribute : Attribute
    {
        #region Properties
        /// <summary>
        /// Gets the total size (in bytes) of the element.
        /// </summary>
        public int Size
        {
            get;
            set;
        }
        /// <summary>
        /// Gets the number of values contained with-in the element.
        /// </summary>
        public int Count
        {
            get;
            set;
        }
        /// <summary>
        /// Gets the type semantic of the element.
        /// </summary>
        public ElementType Type
        {
            get;
            set;
        }
        /// <summary>
        /// Gets the usage semantic of the element.
        /// </summary>
        public ElementUsage Usage
        {
            get;
            set;
        }
        #endregion

        #region Init
        /// <summary>
        /// Creates a new vertex element attribute.
        /// </summary>
        /// <param name="count">The number of values contained within the element.</param>
        /// <param name="size">The total size (in bytes) of the element.</param>
        /// <param name="type">The type semantic of the element.</param>
        /// <param name="usage">The usage semantic of the element.</param>
        public VertexElementAttribute(int count, int size, ElementType type, ElementUsage usage)
        {
            Count = count;
            Size = size;
            Type = type;
            Usage = usage;
        }
        #endregion
    }
自定义顶点结构的示例:
public struct VertexPositionColor
{
    [VertexElement(3, sizeof(Vector3), ElementType.FLOAT3, ElementUsage.POSITION)]
    public Vector3 Xyz;
    [VertexElement(4, sizeof(Color), ElementType.FLOAT4, ElementUsage.COLOR)]
    public Color Rgba; 

    ... etc
}
这样会很好。在DirectX插件(渲染器)中,我可以创建一个实用程序类,它可以为每个结构类型创建语义,然后缓存数据,因此不必为每个顶点重新创建声明。 我甚至可以为ELementUsage添加一个NONE枚举值,这样自定义值可以用于任何意义......但是它们只会在OpenGL中工作,因为DirectX要求你标记每个顶点...除非我有什么东西失踪。 我的问题: 有没有更好的方法来解决这个问题(除了使用attirbutes)? 有没有办法避免在DirectX中使用VertexDeclarations? 你有什么不理解'我'的问题吗? 编辑: 使用属性的问题是从每个顶点获取元素数据。假设我想在网格缓冲区中获取每个顶点的位置。由于我使用了属性,我不能只做'vertex.Position',我必须创建一个可以从顶点结构中提取字段引用的实用程序方法,如'Utility.GetElement(vertex,ElementUsage.POSITION)' 。此方法需要使用反射来首先查找属性,然后返回对字段值的引用。设定值甚至(我认为)是不可能的? 另一种方法是创建一个IElement接口并实现每个元素(Positon,Normal等)。接口可以具有Name属性,我可以直接在继承的元素结构中返回,就像PositionElements的Name属性只返回“Positon”一样。 接下来,我可以将IElement数组放在一个Vertex结构中,该结构包含AddElement(IElement),GetElement(字符串名称),GetElement(int index),Insert,Replace等方法。我将实现DirectX已知的所有元素,以便renderer插件可以解析顶点结构以创建顶点声明数组。 这个问题是我不确定数组'[]'是否可以用作顶点元素数据。比如,数组包含哪些其他字节(如果有的话)会阻碍我将Vertex结构(包含IElement数组)直接传递给DirectX,然后传递给GPU? 以这种方式实现它对于我需要它来说绝对是完美的。另一个问题是IElement(元素)的继承类型可能是一个类,还是元素值必须是值类型?     
已邀请:
自从我做了任何directx或opengl已经有很长一段时间了,所以请耐心等待我的建议,但我记得刚才这样做了。 我想我做了这样的事情:
var graphicsStream = new GraphicsStream();

var elements = graphicsStream.Create<Vector3>(Usage.Position);
graphicsStream.Create<Color>(Usage.Color);
graphicsStream.Create<Quaternion>(Usage.Fribble);

elements.SetData(new[] { new Vector3(), new Vector3() });

var vertexFormat = graphicsStream.GetFormat();
graphicsStream.Validate();  // ensure all the streams have the same length

// get a particular element by type
var p = graphicsStream.GetData(Usage.Position, 1);
您有一个图形流,它是一组应用了一个用法的类型化数据集。从中您可以生成适当的顶点格式。 API允许您更改单个元素或一次上传和替换整个顶点缓冲区。 最大的缺点是您没有一个结构代表顶点结构中的“列”。 我不知道这是不是你要找的东西。为什么这样的设计不适合?     
你可以看看OGRE如何处理这个问题。特别是有关VertexElement和顶点缓冲类的渲染系统文档。 OGRE是C ++,但我相信你能够轻松地将设计模式转换为C#。     

要回复问题请先登录注册