如何在两个着色器之间切换而又不添加越来越多的绘制调用?

时间:2018-10-18 15:27:22

标签: c++ opengl 2d rendering

我想创建一个渲染器类,该类可以在2个或更多着色器之间切换,而无需添加越来越多的绘制调用。

我的意思是拥有2个着色器-A和B-以及需要着色器,位置,大小来创建例如四边形的方法。

,我想添加该数据(位置,大小)并将其传递到顶点A(因此它是第一次绘制调用),然后将另一个数据添加到顶点B(因此它是第二次绘制调用),然后再次将数据添加到着色器A(因此仍应进行2次绘制调用,因为我们之前已经在某处使用了着色器A)。最后,通过绘制调用并绘制场景。

我有一个RenderData类,它添加了绘图调用,顶点,元素数据等。

struct DrawCall
{
    //it may have more data like texture, clip rect, camera, etc.
    Shader* shader = nullptr;
};

struct Vertex
{
    Vector2 position;
}

class RenderData
{
public:
    RenderData();
    ~RenderData();

    void Free() {           
    vertexBuffer.clear();
    shader.clear();
    drawCall.clear();
    elementBuffer.clear();
    }

    void Draw(const Rect& dest);

    void AddDrawCall();
    inline DrawCall* getDrawCall() { return drawCall.size() > 0 ? &drawCall.back() : nullptr; }

    void UpdateShader();
    void PushShader(Shader* shader);
    void PopShader();
    inline Shader* getShader() { return shader.size() > 0 ? shader.back() : nullptr; }

    uint currentVertexIndex = 0;

    vector<Vertex> vertexBuffer;   // Vertex data
    vector<Shader*> shader;
    vector<DrawCall> drawCall;
    vector<uint> elementBuffer; // Index data
}

void RenderData::AddDrawCall()
{
    DrawCall dc;
    dc.shader = getShader();
    drawCall.push_back(dc);
}

void RenderData::UpdateShader()
{
    Shader* currentShader = getShader();
    DrawCall* currentDraw = getDrawCall();
    if (!currentDraw || currentDraw->shader != currentShader) {
        AddDrawCall();
        return;
    }

    DrawCall* prevDraw = drawCall.size() > 1 ? currentDraw - 1 : nullptr;
    if (prevDraw->shader == currentShader) {
        drawCall.pop_back();
    } else { currentDraw->shader = currentShader; }
}

void RenderData::PushShader(Shader* shader)
{
    this->shader.push_back(shader);
    UpdateShader();
}

void RenderData::PopShader()
{
    Custom_Assert(shader.size() > 0, "Cannot PopShader() with size < 0!\n");
    shader.pop_back();
    UpdateShader();
}

void RenderData::Draw(const Rect& dest)
{
    //dest -> x, y, w and h
    //setup vertices 
    vertexBuffer.push_back(...);
    vertexBuffer.push_back(...);
    vertexBuffer.push_back(...);
    vertexBuffer.push_back(...);

    //setup elements
    elementBuffer.push_back(...);
    elementBuffer.push_back(...);
    elementBuffer.push_back(...);
    elementBuffer.push_back(...);
    elementBuffer.push_back(...);
    elementBuffer.push_back(...);
}

和具有很少对象的Renderer2D类: vao,vbo,ebo,Ren​​derData 和几种方法:

Create()->它创建ebo和ebo

RenderClear()->它Free() RenderData,设置视口

RenderPresent->它创建并绑定vao,绑定vbo,添加vbo属性和数据,绑定ebo和添加ebo数据,并经过DrawCall& drawCall : renderData.drawCall,使用着色器程序并绘制元素;

void Renderer2D::Create()
{     
    //gens and binds
    vbo = vbo->Create(TYPE::ARRAY, USAGE::DYNAMIC_DRAW));
    //gens and binds
    ebo = ebo->Create(TYPE::ELEMENT, USAGE::DYNAMIC_DRAW));
}

void Renderer2D::RenderClear()
{
    setRenderViewport(0, 0, 1280, 720);
    renderData.Free();
}

    void Renderer2D::RenderPresent()
{
    vao = vao->Create();

    vbo->BindBuffer();

    vbo->AddAttribute(0, 2, GL_FLOAT, false, sizeof(Vertex), (const void*)offsetof(Vertex, position));

    vbo->AddData(renderData.vertexBuffer.size() * sizeof(Vertex), renderData.vertexBuffer.data());

    ebo->BindBuffer();
    ebo->AddData(renderData.elementBuffer.size() * sizeof(uint), renderData.elementBuffer.data());

    for (auto& drawCall : renderData.drawCall) {
        drawCall.shader->UseProgram();

        vao->DrawElements(drawCall.elemCount, GL_UNSIGNED_INT, nullptr);
    }

    //delete vertex array
    vao->Free();
}

它如何工作:

int main()
{
    Renderer2D renderer2D;
    renderer2D.Create();

    Shader A("shader.vtx", "shader.frag");
    Shader B("shader.vtx", "shader2.frag");

    while(!quit) {
        renderer2D.RenderClear();

        //Push A shader = add 1st draw call
        renderer2D->PushShader(&A);
        renderer2D->Draw({ 100.0f, 100.0f, 50.0f, 50.0f });
        renderer2D->PopShader();

        //Push B shader = add 2nd draw call
        renderer2D->PushShader(&B);
        renderer2D->Draw({ 200.0f, 200.0f, 50.0f, 50.0f });
        renderer2D->PopShader();

        //Push A shader = do not add 3rd draw call, use already existing one
        //This version adds 3rd draw call instead of using existing one
        renderer2D->PushShader(&A);
        renderer2D->Draw({ 400.0f, 400.0f, 50.0f, 50.0f });
        renderer2D->PopShader();

        renderer2D.RenderPresent();
    }
    return 0;
}

我想以某种方式更改它,使其按我描述的方式工作,但我不知道(如果可能的话)如何做。

0 个答案:

没有答案