Я хотел бы создать класс рендерера, который может переключаться между 2 или более шейдерами, не добавляя все больше и больше вызовов отрисовки.
Я имею в виду, что у нас есть 2 шейдера - A и B - и метод, который принимаетшейдер, позиция, размер, например, для создания четырехугольника.
, и я хочу добавить эти данные (положение, размер) и передать их в вершину A (так это вызовы первого рисования), а затем добавить другие данные ввершина B (так что это 2-й вызов отрисовки) и снова добавьте данные в шейдер 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, RenderData и несколько методов:
Create()
-> он создает ebo и ebo
RenderClear()
-> it 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;
}
Я бы хотел как-то изменить его на работу, как я описал, но я не знаю, как (если это вообще возможно) сделать это.