1
\$\begingroup\$

I tried to build a simple graphics engine, and faced with this problems:

i have a list of models that i need to draw, and object (renderer) that implements IRenderer interface with method DrawObject(Object* obj). Implementation of renderer depends on using graphics library (opengl/directx).

1st question: model should not know nothing about renderer implementation, but in this case where can i hold (cache) information that depends on renderer implementation? For example, if model have this definition:

class Model
{
public:
    Model();

    Vertex* GetVertices() const;

private:
    Vertex* m_vertices;
};

what is the best way to cache, for example, vertex buffer of this model for dx11? Hold it in renderer object?

2nd question: what is the best way for model to say renderer HOW it must be rendered (for example with texture, bump mapping, or may be just in one color). I thought it can be done with flags, like this:

model->SetRenderOptions(RENDER_TEXTURE | RENDER_BUMPMAPPING | RENDER_LIGHTING);

and in Renderer::DrawModel method check for each flag.

But looks like it will become uncomfortable with the options count growth...

\$\endgroup\$

3 Answers 3

0
\$\begingroup\$

To answer your first question: I'm not sure. I haven't cached vertex information before. But I would use a different interface to represent the buffers, and then have one implementation for OpenGL and one for DirectX. I would then have a separate class ( manager ) for creating and mapping buffers to objects. This way you can ask the manager for a buffer, or create a new one if there isn't one already. You might do the asking inside the renderer's DrawObject method, or you might have a separate method that first checks / creates the caches and then calls the renderer. Again, I haven't seen this done before, so my answer is just the first thing that comes to mind. Also, when designing your classes try making them highly cohesive ( they should have one and only one responsibility ). Keep this in mind when things get messy or you're not sure what goes where.

As for the second question: the information about how an object should be rendered is usually stored in a material object. The material represents the state of the renderer when rendering your object ( think of all the glEnable / glDisable commands ). Usually that's where you store whether or not depth test is active, whether or not you enable blending and what the blend function is, what shaders will be used and what binds to the shader parameters ( constants, matrices, textures, etc ). The renderer should have a method that accepts a material as it's parameter and sets it's state accordingly. You will need to store your object along with the material in a separate class. This makes sense though. You can have the same geometry with different materials represent different entities. You will then render entities instead of objects.

If you need further reference, I recommend you have a look at how other open-source engines are implemented ( Ogre3D, Irrlicht, HPL, etc ).

\$\endgroup\$
2
  • 2
    \$\begingroup\$ As a final hint to add to this answer, you should have a system that manages all renderable objects. A renderable should not try to draw itself, but rather than main graphics system should iterate over all renderable objects in one fast pass. This is important when using materials so that you can batch all objects with identical materials together, which is critical for getting the best performance on GPUs. \$\endgroup\$ Nov 19, 2012 at 0:11
  • \$\begingroup\$ @SeanMiddleditch That's a good point Sean, it's hard to resist OOP's temptation to provide a "draw yourself" member function, but as you said, if a renderer iterates over buffer data you can make some neat optimizations \$\endgroup\$
    – bobobobo
    Nov 19, 2012 at 1:56
0
\$\begingroup\$

I recently wrote a simple rendering backend, and it basically worked like below.

First, generate or load your object and pass it to the renderer. The renderer returns an ID that you can use later.

const unsigned char* bitmap = /* load this */;
unsigned width, height; /* load these */
int textureId = renderer->makeTexture(bitmap, width, height);

std::vector<Vec3> verticies = /* calculate this */;
std::vector<Vec3> normals = /* calculate this */;
std::vector<Vec3> texCoords = /* calculate this */;

int myModelId = renderer->makeTexturedModel(&verticies, 
                                            &normals,
                                            &texCoords,
                                            textureId);

Next, when you actually want to render the model, you just pass in the ID:

renderer->beginFrame();
renderer->render(myModelId);
renderer->render(otherModelId);
renderer->render(yetAnotherModelID);
renderer->endFrame();

This allows the renderer to decide the most efficient way to store and render models, and it's completely hidden from the rest of the code.

For example, in OpenGL ES the model will get converted into a VBO and uploaded to the GPU. Also, in endFrame the renderer can sort the models by textureId for efficient rendering. You don't have to worry about any of these details, though, as it's completely hidden in the renderer code.

\$\endgroup\$
0
\$\begingroup\$

You need to cache the data in a format that can be flushed out to a vertex buffer.

You need to make some assumptions about what the data format is going to be. Here I'm assuming both OGL and D3D use FLOAT for positions and colors.

struct Vec3{
  float x,y,z;
};

struct Vec4{
  float x,y,z,w;
};

struct VertexC
{
  Vec3 pos ;
  Vec4 color ;
} ;

// fully loaded v/tttt/n/cccc
struct VertexTTTTNCCCC
{
  Vec3 pos ;
  Vec4 tex[ 4 ] ;
  Vec3 norm ;
  Vec4 color[ 4 ] ;
} ;

You then load your model as VertexTTTTNCCCC, for example, and then from there you can create either D3D vertex buffers or OGL vertex buffers.

\$\endgroup\$
3
  • \$\begingroup\$ Does it mean that i anyway must create vertex buffers at every render? For example, in d3d11 i have to call ID3D11Device->CreateBuffer(...), because your VertexTTTTNCCC doesn't represent buffer - just data that this buffer will contain? Why can't i create immutable buffer just one time (for static meshes) and then attach it to pipeline every render, without recreation? \$\endgroup\$
    – acrilige
    Nov 19, 2012 at 8:06
  • \$\begingroup\$ No you don't create the vertex buffer more than once. The point of these structs is an intermediate stage. You would call ID3D11Device::CreateBuffer once if the D3D renderer is used, and glVertexPointerf if OGL is used. You could then delete the VertexTTTTNCCCC's if you want to. \$\endgroup\$
    – bobobobo
    Nov 19, 2012 at 14:55
  • \$\begingroup\$ Note that if you need to update the vertex data while the game is running, in D3D11, the GPU prefers to DISCARD D3D11_MAP::D3D11_MAP_WRITE_DISCARD the previous data, (for performance), so you may need to flush out the entire vertex buffer on every update (ie keep the VTTTTNCCCC around) \$\endgroup\$
    – bobobobo
    Nov 19, 2012 at 14:56

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .