代码之家  ›  专栏  ›  技术社区  ›  zackery.fix

什么是独立于api的顶点处理的良好代码结构?[关闭]

  •  12
  • zackery.fix  · 技术社区  · 15 年前

    目前正在使用c开发一个3d媒体引擎,我遇到了一个小难题。我有我的渲染循环,我有一个伟大的插件架构和内容管理系统,甚至一个材料管道都计划好了。然后,引擎计划使用directx和opengl(通过“renderer”插件),以及两个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插件(renderer)中,我可以创建一个实用程序类,它可以为每个结构类型创建语义,然后缓存数据,这样就不必为每个顶点重新创建声明。

    我甚至可以向elementUsage添加一个none枚举值,以便自定义值可以用于任何意义上的…但是它们只在opengl中工作,因为directx要求您标记每个顶点…除非我少了什么。

    我的问题:

    有没有更好的方法来解决这个问题(除了使用阁楼外)? 有没有办法避免在DirectX中使用VertexDeclarations? 关于“我的”问题,你有什么不明白的吗?

    编辑:

    使用属性的一个问题是从每个顶点获取元素数据。假设我想得到网格缓冲区中每个顶点的位置。由于我使用了属性,我不能只做“vertex.position”,我必须创建一个实用程序方法,可以从顶点结构中提取字段引用,比如“utility.getelement(vertex,elementusage.position)”。此方法需要使用反射首先查找属性,然后返回对字段值的引用。设定这个值是不可能的吗?

    另一种方法是创建一个IElement接口并实现每个元素(position、normal等)。接口可以有name属性,我可以直接在继承的元素结构中返回,就像positionelements的name属性只返回“position”。

    接下来,我可以将IElement数组保存在一个顶点结构中,该结构包含addelement(IElement)、getelement(string name)、getelement(int index)、insert、replace等方法。顶点声明数组。

    这个问题是,我不确定数组'[]'是否可以用作顶点元素数据。例如,一个数组还包含哪些字节(如果有的话),这些字节会阻碍我将顶点结构(包含IElement数组)直接传递给DirectX,然后再传递给GPU?

    以这种方式实现它绝对是我所需要的完美。另一个问题是,IElement的继承类型(元素)是类,还是元素值必须是值类型?

    2 回复  |  直到 7 年前
        1
  •  1
  •   jonnii    14 年前

    我已经很久没有做过任何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可以让您一次性更改单个元素或上载并替换整个顶点缓冲区。

    最大的缺点是没有一个结构表示顶点结构中的“列”。

    我不知道这是不是你要找的那种东西。为什么这样的设计不合适?

        2
  •  1
  •   Vincent Mimoun-Prat Krishnan    14 年前

    你可以看看 OGRE 处理这个。特别地 The render system documentation 关于顶点和 vertex buffer classes . OGRE是C++的,但我相信你能很容易地把设计模式转换成C语言。

    推荐文章