我正在尝试制作即时模式UI面板/容器类。
每个面板类都包含ID,名称,位置,大小,RenderData
等。
RenderData
是我保存顶点,元素,纹理数据的类。该类还负责设置顶点,元素,纹理。
GUI
类是我有小部件,Window
指针和Renderer
Renderer
是一个类,其中包含Window
个指针的指针,即vao,vbo,ebo和shader。此类负责将RenderData
的数据添加到缓冲区并绘制所有内容。
struct Vertex
{
glm::vec2 position;
glm::vec2 uv;
float texID;
}
class Panel
{
public:
Panel(std::string name, glm::vec2 pos, glm::vec2 size)
{
position = pos;
this->size = size;
renderData = std::make_unique<RenderData>();
}
~Panel() {}
RenderData* renderData;
glm::vec2 position, size;
GLuint id;
std::string name;
};
class GUI
{
public:
GUI() {}
~GUI() {}
void Create()
{
renderer.Create();
tex = tex->Create("Textures/Panel.png");
}
void Clear()
{
for(const auto& panel: panel) {
delete panel;
} panel.clear();
}
void PushPanel()
{
glm::vec2 pos = glm::vec2(100.0f, 100.0f);
glm::Vec2 size = glm::vec2(300.0f, 300.0f);
Panel* panel = CreatePanel(name, pos, size);
panel->renderData->Clear();
panel->renderData->Draw(pos, size, tex);
}
void Render()
{
tempRenderData.clear();
for(const auto& window : context.window)
{
tempRenderData.push_back(window->renderData);
}
renderer.AddRenderData(&tempRenderData);
renderer.Render();
}
Panel* CreatePanel(std::string name, glm::vec2 pos, glm::vec2 size)
{
Panel* result = new Panel(name, pos, size);
panel.push_back(result);
return result;
}
std::vector<Panel*> panel;
std::vector<RenderData*> tempRenderData;
Renderer renderer;
Texture* tex;
};
class Renderer
{
public:
Renderer();
~Renderer();
void Create()
{
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glGenBuffers(1, &ebo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
glEnableVertexAttribArray(3);
glVertexAttribPointer(0, 2, GL_FLOAT, false, sizeof(Vertex), (const void*)offsetof(Vertex, position));
glVertexAttribPointer(1, 4, GL_UNSIGNED_BYTE, true, sizeof(Vertex), (const void*)offsetof(Vertex, color));
glVertexAttribPointer(2, 2, GL_FLOAT, false, sizeof(Vertex), (const void*)offsetof(Vertex, uv));
glVertexAttribPointer(3, 1, GL_FLOAT, false, sizeof(Vertex), (const void*)offsetof(Vertex, texID));
//Creates default vertex and fragment shaders
shader.Create();
shader.Use();
int textures[32];
for(int i = 0; i < 32; ++i) textures[i] = i;
shader.uniform1iv("u_textureSampler", textures, 32);
}
void Start()
{
shader.UseProgram();
shader.UniformMat4fv("projection", glm::ortho(0.0f, displayW, 0.0f, displayH);
}
void Render()
{
glBindVertexArray(vao);
for(GLuint i = 0; i < renderDataSize; ++i) {
RenderData* currentRender = renderData[i];
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, currentRender->vertexBuffer.size() * sizeof(RenderData), currentRender->vertexBuffer.data(), GL_DYNAMIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ARRAY_BUFFER, currentRender->elementBuffer.size() * sizeof(GLuint), currentRender->elementBuffer.data(), GL_DYNAMIC_DRAW);
for(GLuint i = 0; i < texture.size(); ++i)
{
// It is just glBindTexture(..);
currentRender->texture[i]->BindTexture(i);
}
glDrawElements(GL_TRIANGLES, currentRender->elementBuffer.size(), GL_UNSIGNED_INT, nullptr);
for(GLuint i = 0; i < texture.size(); ++i)
{
currentRender->texture[i]->UnbindTexture(i);
}
}
}
void AddRenderData(std::vector<RenderData*>* data)
{
renderData = data->data();
renderDataSize = data->size();
}
Shader shader;
RenderData** renderData;
GLuint renderDataSize;
GLuint vao;
GLuint vbo;
GLuint ebo;
};
class RenderData
{
public:
RenderData() {}
~RenderData() {}
void Clear()
{
elementBuffer.clear();
vertexBuffer.clear();
texture.clear();
}
void Draw(glm::vec2 pos, glm::vec2 size, Texture* tex)
{
int elementIndex = vertexBuffer.size();
float texID = 0.0f;
bool found = false;
for (GLuint i = 0; i < texture.size(); ++i) {
if (texture[i] == tex) {
texID = static_cast<float>(i + 1);
found = true;
break;
}
}
if (!found) {
if (texture.size() >= 32) {
//Do something, maybe add another draw call, because one GPU command can handle 32 textures
}
texture.push_back(t);
texID = static_cast<float>(texture.size());
}
// texID and color for all vertices is the same.
//Push 4 vertices
vertexBuffer.push_back(...);
vertexBuffer.push_back(...);
vertexBuffer.push_back(...);
vertexBuffer.push_back(...);
elementBuffer.push_back(elementIndex);
elementBuffer.push_back(elementIndex + 1);
elementBuffer.push_back(elementIndex + 2);
elementBuffer.push_back(elementIndex + 2);
elementBuffer.push_back(elementIndex + 3);
elementBuffer.push_back(elementIndex);
}
std::vector<Vertex> vertexBuffer;
std::vector<GLuint> elementBuffer;
std::vector<Texture*> texture;
};
int main()
{
GUI gui;
gui.Create();
while(!quit)
{
gui.Clear();
gui.PushPanel();
gui.Render()
}
return 0;
}
class Texture
{
public:
Texture() {}
~Texture() {}
Texture* Create(std::string texPath)
{
return new GLTexture(texPath);
}
virtual void BindTexture(GLuint slot) {}
virtual void UnbindTexture(GLuint slot) {}
virtual void setData(const void* data) {}
};
class GLTexture : public Texture
{
public:
GLTexture(std::string texPath)
{
SDL_Surface* surface = nullptr;
surface = IMG_Load(texPath.c_str());
w = surface->w;
h = surface->h;
GLuint format = surface->format->BytesPerPixel == 4 ? GL_RGBA : GL_RGB;
glGenTextures(1, &texture));
glBindTexture(GL_TEXTURE_2D, texture));
glTexImage2D(GL_TEXTURE_2D, 0, format, w, h, 0, format, GL_UNSIGNED_BYTE, surface ? surface->pixels : nullptr));
//wrap, filters etc.
glTexParameteri(...);
glTexParameteri(...);
glTexParameteri(...);
glTexParameteri(...);
glGenerateMipmap(target));
glBindTexture(target, 0));
SDL_FreeSurface(surface);
}
void BindTexture(GLuint slot) override
{
glBindTexture(GL_TEXTURE_2D, texture);
glActiveTexture(GL_TEXTURE0 + slot)
}
void UnbindTexture(GLuint slot) override
{
glBindTexture(GL_TEXTURE_2D, 0);
glActiveTexture(GL_TEXTURE0 + slot)
}
void setData(const void* data) override
{
glBindTexture(GL_TEXTURE_2D, texture));
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, offsetY, w, h, GL_RGBA, GL_UNSIGNED_BYTE, data));
}
int w, h;
};
纹理等级100%没问题
此代码应使用纹理渲染gui面板。这应该。不幸的是,我遇到了Exception thrown: read access violation.
std::_Vector_alloc<std::_Vec_base_types<MiE::Texture *,std::allocator<MiE::Texture *> > >::_Mylast(...) returned 0xFDFDFEE5.
问题。
它来自Draw()
方法。
我认为tempRenderData
的大小不是Panel
的大小,当我尝试渲染纹理时,它返回了问题,因为它无法得到正确的RenderData
。
我检查了tempRenderData
的大小,还可以。
没有纹理的几何图形呈现良好。
错误消息:
如何解决此问题?
答案 0 :(得分:0)
我建议您创建一个名为struct
的{{1}},并保存每个GPU绘制数据的所有位置。
DrawCall
RenderData应该包含一些新的向量,例如:
struct DrawCall
{
Texture* texture = nullptr;
Shader shader;
Camera camera;
GLuint lastFreeTexture = 0;
};
vector<DrawCall> drawCall;
vector<Texture*> texture;
vector<Shader>
vector<Camera>
渲染器
void AddDrawCall()
{
DrawCall newCall;
newCall.shader = ...;
newCall.camera = ...;
newCall.texture = texture.back();
drawCall.push_back(newCall);
}
void AddTexture(Texture* tex)
{
texture.push_back(tex);
CheckTextureForDrawCall();
}
void CheckTextureForDrawCall()
{
Texture* currentTexture = texture.back();
DrawCall* currentDraw = drawCall.back();
if (!currentDraw || (currentDraw->elemCount != 0 && currentDraw->texture != currentTexture)) {
AddDrawCall();
return;
}
DrawCall * prevDraw = drawCall.size() > 1 ? currentDraw - 1 : nullptr;
if (prevDraw
&& prevDraw->shader == shader.back()
&& prevDraw->camera == camera.back()
&& prevDraw->texture == currentTexture) {
drawCall.pop_back();
} else { currentDraw->texture = currentTexture; }
}
void Draw(...)
{
AddTexture(...);
... other things
}
drawcall结构将使您对代码有更多的控制。
如果您希望每个绘图调用32个纹理,只需创建void RenderPresent()
{
... add attributes etc.
for(GLuint i all RenderData)
{
RenderData* currentRenderData = renderData[i];
... add data to the buffers
for(auto drawCall : currentRenderData->drawCall)
{
drawCall.shader.bind();
drawCall.texture.bind();
drawElements(...);
}
}
}
,
查找自由纹理索引,如果自由纹理索引大于32,则添加一个新的绘制调用,如果先前绘制调用纹理索引小于32(并检查着色器或照相机等其他内容,因为即使纹理索引小于32,但是着色器有所不同,您仍然可以保留当前的绘制调用),可以删除当前的绘制调用-只需Texture* texture[32];
。
检查最后一个空闲索引的方法可能是这样的:
drawCall.pop_back();
,然后纹理插槽应该为 enum { DrawCall_Max_Textures = 32 };
GLuint FindFreeTextureIndex()
{
for (GLuint i = lastFreeTexture; i < DrawCall_Max_Textures; ++i) {
if (texture[i] == nullptr) {
return i;
}
}
for (GLuint i = 0; i < lastFreeTexture; ++i) {
if (texture[i] == nullptr) {
return i;
}
}
return 0;
}
,如果您将float textureSlot = static_cast<float>(getDrawCall()->FindFreeTextureIndex() + 1);
丢弃在片段着色器中,则纹理插槽应该为+1(我想您是这样做的,因为我看到了一些thecherno sparky engine series的东西)。