2
\$\begingroup\$

Recently, I implemented an RenderBatch class, which renders any objects, using a specific shader (with its configuration). I'd like to improve on the way my render process works.

RenderBatch.h:

#pragma once
#include "alpha/Shader.h"
#include "alpha/RenderComponent.h"
#include <glm/glm.hpp>
#include "alpha/Mesh.h"
#include "alpha/Transform.h"
#include "alpha/Model.h"

class ShaderConfiguration
{
public:
    ShaderConfiguration();
    virtual ~ShaderConfiguration();
    virtual void Update(Shader& shader);
    void SetProjectionView(const glm::mat4& projectionView){
        m_projectionView = projectionView;
    }
    glm::mat4 GetProjectionView() const{
        return m_projectionView;
    }
    void SetTransform(const Transform& transform){
            m_transform = transform;
    }
    Transform GetTransform(){
        return m_transform;
    }
protected:
    glm::mat4 m_projectionView;
    Transform m_transform;
};
typedef ShaderConfiguration DefaultShaderConfiguration;

///Render Batch is really low lovel... It is only advised to use it
///in case you want to experiment with GLSL shaders : for more basic uses,
///ModelBatch is actually integrated in the GameObject system...
class RenderBatch : RenderComponent
{
public:
    RenderBatch(){
        m_shader.loadFromFile(GL_VERTEX_SHADER, "res/basicShader.glslv");
        m_shader.loadFromFile(GL_FRAGMENT_SHADER, "res/basicShader.glslf");
        m_shader.CreateAndLink();
    }
    RenderBatch(Shader shader)
        : m_shader(shader){}
    virtual ~RenderBatch(){}
    void SetShader(Shader shader){this->m_shader = shader;}
    Shader GetShader()const {return m_shader;}

    ///Genereal way of updating -----------------> using polymorphism
    void UpdateShaderConfiguration(ShaderConfiguration* config){
        m_shader.Bind();
            config->Update(m_shader);
        m_shader.UnBind();
    }
    ///Update for simple rendering --------------> ProjectionView + Transform ()
    ///Equivalent of :
    ///DefaultShaderConfiguration defaultShaderConfiguration
    ///UpdateShaderConfiguration(&defaultShaderConfiguration);
    void Update(glm::mat4 projectionView, Transform transform){
        m_shader.Bind();
            glUniformMatrix4fv(
                m_shader.GetUniformLocation("MVP"),
                1,
                GL_FALSE,
                glm::value_ptr(projectionView * transform.GetModelView())
                );
        m_shader.UnBind();
    }
    void Render(Mesh& mesh){
        m_shader.Bind();
            mesh.Draw();
        m_shader.UnBind();
    }
    glm::mat4 GetProjectionViewMatrix(){
        return m_projectionView;
    }
    void SetProjectionViewMatrix(glm::mat4 projectionView){
        this->m_projectionView = projectionView;
    }
protected:
    Shader m_shader;
    glm::mat4 m_projectionView;
};

RenderBatch.cpp

#include "alpha/RenderBatch.h"

ShaderConfiguration::ShaderConfiguration()
    : m_projectionView(glm::mat4(1))
{}
ShaderConfiguration::~ShaderConfiguration(){}

void DefaultShaderConfiguration::Update(Shader& shader){
    glUniformMatrix4fv(
    shader.GetUniformLocation("MVP"),
    1,
    GL_FALSE,
    glm::value_ptr(m_projectionView * m_transform.GetModelView())
    );
}

Main.cpp

 ///example 1 ---->using defaults
 RenderBatch batch = RenderBatch();
 Mesh mesh("res/Test.obj");     
 Camera camera;     

 while(isRunning)
 {
     /*Update */
     batch.Update(camera.Combined(), transform)         
     /*some more update code...*/

     /*Render*/
     batch.Render(mesh);
 }

 ///example 2 ---->using custom configuration
 RenderBatch batch = RenderBatch();
 Mesh mesh("res/Test.obj");     
 Camera camera;     
 ShaderConfiguration config;     

 while(isRunning)
 {
     config.SetProjectionView(camera.combined());
     config.SetTransform(transform)
     /*Update */
     batch.Update(config);         
     /*some more update code...*/

     /*Render*/
     batch.Render(mesh);
 }
\$\endgroup\$
3
\$\begingroup\$

There is a bit of misnomer with RenderBatch. The term "batch rendering" is normally associated with grouping several geometries that share the same rendering attributes (textures/materials/etc) to render then together and avoid expensive state changes. Your class is just a wrapper around a Shader, so I don't see much reason for its existence, when all that could be part of Shader instead.

There is also an excessive amount of shader binding/unbinding going on inside RenderBatch. You definitely don't want to do that, changing shader programs is a very expensive operation.

That's one of the pitfalls of attempting to integrate OpenGL with OOP. The library was just not made to be used like that; it relies heavily on global state. This will change in the future with the new "bindless" OpenGL, but that's for version 4.5 and above.

Another potential issue that I see here is that RenderBatch is taking its Shader parameter by value, thus making a copy of it. How are you dealing with the internal GL shader program handle in this case? Even though you can copy the integer handle given to you by OpenGL, you can't copy the underlaying shader object itself. So I'm wondering if you are not winding up with duplicate shader handles through your program that will get deleted multiple times by calls to glDeleteProgram or even leak. Yet another problem of mixing a very C-ish API with C++. You have to be extra careful to avoid these sorts of mistakes. Be very aware that you are mixing two distinct programming languages in the same project.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Thansk :) The only question would on integrating shaders without having the user writing any g glsl code, or caring about updating shader uniforms ... \$\endgroup\$ – MattMatt Jul 15 '15 at 19:54
  • \$\begingroup\$ @MattMatt, well you could hide the shaders under a layer similar to the old fixed function pipeline (look at code samples from GL 1.x). That might make the code simpler for users with little experience on graphics, but it would certainly limit more advanced users that do want to write custom shaders. \$\endgroup\$ – glampert Jul 15 '15 at 19:58
  • \$\begingroup\$ Oh, you mean like gl.begin() gl.end() ? how ? an example : )? \$\endgroup\$ – MattMatt Jul 15 '15 at 20:01
  • \$\begingroup\$ @MattMatt something like that yes, you can emulate things like glTranslate and such, if you think the target audience of your code would benefit from that. More advanced users will prefer having explicitly control over things like shader/texture bindings and updates though. \$\endgroup\$ – glampert Jul 15 '15 at 20:03
  • \$\begingroup\$ Ok, so the user could attach a shader to glbegin() and draw different meshes with it ! But what about things like the transform matrix ? \$\endgroup\$ – MattMatt Jul 15 '15 at 20:09

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.