ourTextures[TexIndex]
是未定义的行为,因为ourTextures
是采样器数组,而TexIndex
是片段着色器输入。扩展着色器输入不是Dynamically uniform expression。
[使用C ++开发引擎,并基于Cherno's video series实现了批处理渲染器。似乎工作了一段时间。最近注意到,在用于该项目的2台计算机中,有1台计算机上,一种纹理怪异地渗入了精灵的另一种纹理。
<< img src =“ https://image.soinside.com/eyJ1cmwiOiAiaHR0cHM6Ly9pLmltZ3VyLmNvbS9XbzhkRFpwLnBuZyJ9” alt =“精灵纹理渗入彼此” >>>
经过一些研究,我们注意到,流血是根据它们呈现的顺序而发生的。在第一个渲染的精灵没有问题的情况下,随后的精灵大部分都是正确的,其中一小部分像素来自最后绘制的精灵的纹理。
我们认为问题与着色器有关,但是批处理渲染器也可能有问题。
顶点着色器
#version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec4 aColor; layout (location = 2) in vec2 aTexCoord; layout (location = 3) in int aTexIndex; out vec4 ourColor; out vec2 TexCoord; flat out int TexIndex; uniform mat4 uViewProjection; void main() { gl_Position = uViewProjection * vec4(aPos, 1.0); ourColor = aColor; TexCoord = aTexCoord; TexIndex = aTexIndex; }
片段着色器
#version 330 core out vec4 FragColor; in vec4 ourColor; in vec2 TexCoord; flat in int TexIndex; uniform sampler2D ourTextures[32]; void main() { FragColor = texture(ourTextures[TexIndex], TexCoord) * ourColor; }
批处理Renderer.h
#pragma once #include "glm/glm.hpp" #include "Shader.h" #include "Memory/SmartPointers.h" namespace Engine::Graphics { // TODO: move this struct to a different header. struct Vertex { glm::vec3 pos; glm::vec4 col; glm::vec2 texCoords; i32 texIndex; }; struct BatchData { GLuint VAO = 0; GLuint VB = 0; GLuint IB = 0; uint32_t indexCount = 0; Vertex* vertexBuffer = nullptr; Vertex* vertexBufferCurrentOffset = nullptr; GLint* textureSlotsArray = nullptr; i32 textureSlotIndex = 0; glm::mat4x4 currentViewMatrix = glm::mat4x4(); }; class BatchRenderer { public: void Init(SharedRef<Shader>& startShader); void Destroy(); GLuint GetVAOID(); static uint32_t GetMaxBatchSize(); static uint32_t GetNoTexID(); static uint32_t GetQuadsDrawnThisFrame(); static uint32_t GetDrawCallsThisFrame(); static void ResetQuadCounter(); static void ResetDrawCallCounter(); void BeginBatch(); void EndBatch(); void Flush(); void SetShader(SharedRef<Shader>& shaderPassed); void DrawQuad(const glm::vec2& position1, const glm::vec2& position2, const glm::vec2& position3, const glm::vec2& position4, const glm::vec4& colour); void DrawQuad(const glm::vec2& position1, const glm::vec2& position2, const glm::vec2& position3, const glm::vec2& position4, uint32_t textureID); void DrawQuad(const glm::vec2& position1, const glm::vec2& position2, const glm::vec2& position3, const glm::vec2& position4, uint32_t textureID, const glm::vec4& colour); void UpdateViewMatrix(const glm::mat4x4& newMatrix); SharedRef<Shader> shaderRef; BatchData m_batchData; friend class BatchManager; }; }
Batch Renderer.cpp
-重要功能,随时询问您认为我错过的任何内容。
void BatchRenderer::Init(SharedRef<Shader>& startShader) { #ifdef _DEBUG assert(m_batchData.vertexBuffer == nullptr && "Batch Renderer already initialized."); #endif shaderRef = startShader; shaderRef->Bind(); GLint maxTextures; glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS, &maxTextures); MAX_TEXTURES_PER_BATCH = maxTextures; if (MAX_TEXTURES_PER_BATCH > 32) { MAX_TEXTURES_PER_BATCH = 32; } m_batchData.vertexBuffer = new Vertex[MAX_VERT_COUNT_PER_BATCH]; glGenVertexArrays(1, &m_batchData.VAO); glBindVertexArray(m_batchData.VAO); shaderRef->Bind(); glGenBuffers(1, &m_batchData.VB); glBindBuffer(GL_ARRAY_BUFFER, m_batchData.VB); glBufferData(GL_ARRAY_BUFFER, MAX_VERT_COUNT_PER_BATCH * sizeof(Vertex), nullptr, GL_DYNAMIC_DRAW); glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)(0 * sizeof(float))); glEnableVertexAttribArray(0); glVertexAttribPointer(1, 4, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)(7 * sizeof(float))); glEnableVertexAttribArray(2); //glVertexAttribPointer(3, 1, GL_UNSIGNED, GL_FALSE, sizeof(Vertex), (void*)(9 * sizeof(float))); glVertexAttribIPointer(3, 1, GL_INT, sizeof(Vertex), (void*)(9 * sizeof(float))); glEnableVertexAttribArray(3); uint32_t* indices = new uint32_t[MAX_INDEX_COUNT_PER_BATCH]; uint32_t offset = 0; for (size_t i = 0; i < MAX_INDEX_COUNT_PER_BATCH; i += 6) { indices[i] = 0 + offset; indices[i + 1] = 1 + offset; indices[i + 2] = 2 + offset; indices[i + 3] = 2 + offset; indices[i + 4] = 3 + offset; indices[i + 5] = 0 + offset; offset += 4; } glGenBuffers(1, &m_batchData.IB); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_batchData.IB); glBufferData(GL_ELEMENT_ARRAY_BUFFER, MAX_INDEX_COUNT_PER_BATCH * sizeof(uint32_t), indices, GL_STATIC_DRAW); delete[] indices; m_batchData.textureSlotsArray = new GLsizei[MAX_TEXTURES_PER_BATCH]; m_batchData.textureSlotsArray[0] = noTexID; GLint* samplers = new int32_t[MAX_TEXTURES_PER_BATCH]; samplers[0] = 0; for (GLint i = 1; i < MAX_TEXTURES_PER_BATCH; i++) { m_batchData.textureSlotsArray[i] = 0; //zero out data samplers[i] = i; } glUniform1iv(shaderRef->GetUniformIndex("ourTextures"), static_cast<int>(MAX_TEXTURES_PER_BATCH), samplers); } void BatchRenderer::Destroy() { #ifdef _DEBUG assert(m_batchData.vertexBuffer != nullptr && "Batch Renderer not initialized, cannot destroy."); #endif glDeleteVertexArrays(1, &m_batchData.VAO); glDeleteBuffers(1, &m_batchData.VB); glDeleteBuffers(1, &m_batchData.IB); delete[] m_batchData.vertexBuffer; delete[] m_batchData.textureSlotsArray; } void BatchRenderer::BeginBatch() { m_batchData.vertexBufferCurrentOffset = m_batchData.vertexBuffer; } void BatchRenderer::EndBatch() { glBindVertexArray(m_batchData.VAO); shaderRef->Bind(); GLsizeiptr totalSize = (uint8_t*)m_batchData.vertexBufferCurrentOffset - (uint8_t*)m_batchData.vertexBuffer; glBindBuffer(GL_ARRAY_BUFFER, m_batchData.VB); glBufferSubData(GL_ARRAY_BUFFER, 0, totalSize, m_batchData.vertexBuffer); } void BatchRenderer::Flush() { glBindVertexArray(m_batchData.VAO); shaderRef->Bind(); shaderRef->SetMat4x4("uViewProjection", m_batchData.currentViewMatrix); for (i32 i = 0; i < m_batchData.textureSlotIndex; i++) { glBindTextureUnit(i, m_batchData.textureSlotsArray[i]); } glDrawElements(GL_TRIANGLES, m_batchData.indexCount, GL_UNSIGNED_INT, nullptr); m_batchData.indexCount = 0; m_batchData.textureSlotIndex = 1; #ifdef EDITOR drawCallsThisFrame++; #endif } void BatchRenderer::DrawQuad(const glm::vec2& position1, const glm::vec2& position2, const glm::vec2& position3, const glm::vec2& position4, uint32_t textureID, const glm::vec4& colour) { if (m_batchData.indexCount > QUICK_LOWER_INDEX_ACCESS || m_batchData.textureSlotIndex >= MAX_TEXTURES_PER_BATCH) { EndBatch(); Flush(); BeginBatch(); } i32 textureIndex = -1; for (i32 i = 0; i < m_batchData.textureSlotIndex; i++) { if (m_batchData.textureSlotsArray[i] == textureID) { textureIndex = i; break; } } if (textureIndex == -1) { textureIndex = m_batchData.textureSlotIndex; m_batchData.textureSlotsArray[m_batchData.textureSlotIndex] = static_cast<i32>(textureID); m_batchData.textureSlotIndex++; } m_batchData.vertexBufferCurrentOffset->pos = { position1.x, position1.y, 0.0f }; m_batchData.vertexBufferCurrentOffset->col = colour; m_batchData.vertexBufferCurrentOffset->texCoords = { 0.0f, 0.0f }; m_batchData.vertexBufferCurrentOffset->texIndex = textureIndex; m_batchData.vertexBufferCurrentOffset++; m_batchData.vertexBufferCurrentOffset->pos = { position2.x, position2.y, 0.0f }; m_batchData.vertexBufferCurrentOffset->col = colour; m_batchData.vertexBufferCurrentOffset->texCoords = { 1.0f, 0.0f }; m_batchData.vertexBufferCurrentOffset->texIndex = textureIndex; m_batchData.vertexBufferCurrentOffset++; m_batchData.vertexBufferCurrentOffset->pos = { position3.x, position3.y, 0.0f }; m_batchData.vertexBufferCurrentOffset->col = colour; m_batchData.vertexBufferCurrentOffset->texCoords = { 1.0f, 1.0f }; m_batchData.vertexBufferCurrentOffset->texIndex = textureIndex; m_batchData.vertexBufferCurrentOffset++; m_batchData.vertexBufferCurrentOffset->pos = { position4.x, position4.y, 0.0f }; m_batchData.vertexBufferCurrentOffset->col = colour; m_batchData.vertexBufferCurrentOffset->texCoords = { 0.0f, 1.0f }; m_batchData.vertexBufferCurrentOffset->texIndex = textureIndex; m_batchData.vertexBufferCurrentOffset++; m_batchData.indexCount += 6; #ifdef EDITOR quadCountThisFrame++; #endif }
如上所述,如果该信息有用,则错误仅发生在项目中使用的2台PC中的1台上(AMD RX 580]具有错误)。
使用C ++引擎工作,并基于Cherno的视频系列实现了批处理渲染器。似乎工作了一段时间。最近注意到,在...上工作的两台计算机中的一台...
ourTextures[TexIndex]
是未定义的行为,因为ourTextures
是采样器数组,而TexIndex
是片段着色器输入。扩展着色器输入不是Dynamically uniform expression。
请参阅您使用的GLSL版本3.30(从OpenGL Shading Language 3.30 Specification - 4.1.7 Samplers起:]
样本聚合成数组在着色器中(使用方括号[])只能使用整数常量表达式进行索引
请参阅GLSL 4.60版(最新)(摘自OpenGL Shading Language 4.60 Specification - 4.1.7. Opaque Types):(此规则适用于GLSL 4.00以后的所有版本)
当聚合到着色器中的数组中时,只能使用动态统一表达式对这些类型进行索引,否则纹理查找将导致未定义的值。
因此,无论是在您使用的GLSL版本中,还是在最新版本中,都不能通过顶点着色器输入(属性)对采样器数组进行索引。
自GLSL 4.00起,就可以通过统一方式对采样器数组进行索引,因为通过统一变量进行索引是dynamically uniform expression。
我建议使用sampler2DArray
(请参阅Sampler),而不要使用sampler2D
的数组。使用sampler2DArray
时,根本不需要任何索引,因为在纹理查找时“索引”被编码在纹理坐标的第3个分量中(请参见Texture
)。
ourTextures[TexIndex]
是未定义的行为,因为ourTextures
是采样器数组,而TexIndex
是片段着色器输入。扩展着色器输入不是Dynamically uniform expression。