【发布时间】:2012-11-26 01:19:09
【问题描述】:
我正在为 OpenGL 3.1 设计一个渲染系统(目前仅限于 2D)。我想做的是真正考虑优雅的设计模式,这样我就不必担心一些维护和调试很痛苦的乱七八糟的东西。
起初,我的想法是有一个模板化的基类,它带有接受不同类型名作为参数的函数。然后子类将从基类继承,在派生时发送它们将使用的类型作为模板参数。
Then I found out this was basically impossible.
所以,我对如何正确设计它而不只是把事情搞砸有点困惑。问题是我使用的是我自己的顶点结构,它环绕glm 向量类,所以我可以在一个结构中拥有顶点对,并在调用glVertexAttribPointer() 时以内存偏移量跨越它们。
换句话说,
typedef struct colorVertex_d
{
glm::vec3 Position;
glm::vec4 Color;
}
colorVertex_t;
typedef struct textureVertex_d
{
glm::vec3 Position;
glm::vec2 UV;
}
textureVertex_t;
理想情况下,我想做的是将可用于此渲染器的实际类型限制为已定义的顶点类型。
我认为这样做的一种方法是按照this 的方式做一些事情,然后调用 Shutdown 方法(有点像异常,只是不是异常......)如果类型发送到类无效。
不过,我什至不确定在这种情况下这是否是一个好的实现。
我真正在寻找的是一种可以支持多种缓冲区类型(例如顶点、索引、法线、颜色等)的方法,这些缓冲区类型符合上面显示的顶点结构等类型。
原文出处
/**
* Is an abstract class to inherit from for defining custom buffer handler classes,
* the type name specified is the type of data stored in the buffer specifically, e.g. textureVertex_t or colorVertex_t
*/
template < typename DataT, typename ContainerT >
class RenderBuffer
{
public:
enum RenderMethod
{
Stream, // used for writing data into the buffer once per render
Dynamic, // values in the buffer are changed from time to time
Static // data is set only once, then rendered as many times as it needs to be
};
enum DrawMode
{
Triangle,
TriangleStrip,
Line,
LineStrip
};
enum BufSetType
{
Alloc,
Update
};
RenderBuffer( RenderMethod rm, DrawMode dm )
{
switch( rm )
{
case Stream: mRenderMethod = GL_STREAM_DRAW; break;
case Dynamic: mRenderMethod = GL_DYNAMIC_DRAW; break;
case Static: mRenderMethod = GL_STATIC_DRAW; break;
}
switch( dm )
{
case Triangle: mDrawMode = GL_TRIANGLES; break;
case TriangleStrip: mDrawMode = GL_TRIANGLE_STRIP; break;
case Line: mDrawMode = GL_LINES; break;
case LineStrip: mDrawMode = GL_LINE_STRIP; break;
}
}
virtual ~RenderBuffer( void )
{
}
virtual void Reserve( size_t sz ) = 0; // Reserve space for whatever container used to store the buffer data.
virtual void Bind( void ) const = 0;
virtual void UnBind( void ) const = 0;
template < typename DataT, ContainerT >
virtual void SetBufferData( const ShaderProgram& program, const ContainerT& data, BufSetType m )
{
}
template < typename DataT, ContainerT >
virtual void SetBufferData( const ShaderProgram& program, const DataT* data, const size_t len, BufSetType m )
{
}
virtual void Render( void ) const = 0;
size_t VertexCount;
protected:
GLenum mDrawMode, mRenderMethod;
};
【问题讨论】:
-
我发现“RenderBuffer”这个名字有点混乱,因为它通常指的是 FBO 中的只写附件:)
-
是的,我只是在读那些。肯定会改变这一点......不过你还有其他建议吗?说到这个,我还是觉得有点卡住。
-
关于顶点格式:由于您限制了顶点格式,我假设您还将依赖在顶点着色器中指定属性位置?我转向另一个方向,让一切都交给着色器,并在使用不同的格式或属性顺序时简单地动态生成 VAO。不过,我需要顶点格式的极大灵活性。属性名称和格式形成一个键(字符串)。绑定 VAO 时,它们的键 n 着色器被发送到 Mesh 类。如果没有具有该密钥的 VAO,则即时生成并存储以供下次使用。
-
上述方法仅在您根据着色器中的属性名称命名每个属性指针时才有效。这当然会使实施变得更加困难,但您的 VAO 可以适应这种情况。如果您支持着色器的运行时重新加载,则特别好。
-
您可能还希望在设计课程时考虑到转换反馈。