对于我正在使用 C++ 进行的 OpenGL 项目,我正在尝试将距离信息编码到一维缓冲区中,我真的很想为此使用无符号整数。我创建了一个调试步骤将缓冲区的内容导出到一个文件,这表明它正在成功写入,但是我无法从片段着色器中读取帧缓冲区纹理的内容。着色器本身都是接近文件的顶部。
对于这个测试程序,我的目标是绘制一个简单的渐变(用 UV 完成)到 1D 帧缓冲区,然后从片段着色器读取它并将它绘制到屏幕上。澄清一下,我知道如何制作基本的紫外线渐变。这不是我的实际目标,我的目标是写入无符号整数的一维缓冲区,然后从中读取。*
这是程序的实际输出: 这是我要复制的模型: 这是我渲染后写入的“buffer.bin”文件内容的截图:
这是一个 345 行的文件。抱歉缺乏可读性和大量冗余,为了减少潜在的错误来源,我创建了第二个项目。但是,我需要强调一点:此处发布的代码与我无法正常工作的代码完全相同,并且我没有为了保密而隐藏任何内容。鉴于安装了 SDL 和 GLEW,它应该在 Linux 或 Windows 上编译得很好(在 gcc 下测试)。
main.cpp(只有一个文件)
#include <iostream>
#include <SDL2/SDL.h>
#include <GL/glew.h>
#include <SDL_opengl.h>
#include <fstream>
#include <iostream>
#include <filesystem>
#include <initializer_list>
#include <exception>
#include <stdexcept>
#include <type_traits>
#include <SDL2/SDL.h>
#include <SDL2/SDL_main.h>
#include <GL/glew.h>
typedef union SizeVec2 {
struct {
size_t x = 0, y = 0;
};
struct { size_t w, h; };
} SizeVec2;
using namespace std;
SDL_Window* window = nullptr;
SDL_GLContext context;
bool isRunning = true;
SizeVec2 windowSize = { 640, 640 };
bool init() {
if (SDL_Init(SDL_INIT_EVERYTHING) < 0 ) {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Failed to initialize SDL: %s", SDL_GetError());
return false;
}
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2);
window = SDL_CreateWindow("OpenGL Test", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, (int)windowSize.w, (int)windowSize.h, SDL_WINDOW_ALLOW_HIGHDPI | SDL_WINDOW_SHOWN | SDL_WINDOW_OPENGL);
if (window == nullptr) {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Failed to create window: %s", SDL_GetError());
return false;
}
context = SDL_GL_CreateContext(window);
if (context == nullptr) {
SDL_LogCritical(SDL_LOG_CATEGORY_APPLICATION, "Failed to create OpenGL context: %s", SDL_GetError());
return false;
}
GLenum ret = glewInit();
if (ret) {
SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "glewInit() = %u", ret);
return false;
}
return true;
}
const char* vertShaderSrc = "#version 330 core\n"
"layout (location = 0) in vec2 aPosition;\n"
"layout (location = 1) in vec2 aTexCoord;\n"
"out vec2 texCoord;\n"
"\n"
"void main() {\n"
" texCoord = aTexCoord;\n"
" gl_Position = vec4(aPosition, 0.0, 1.0);\n"
"}";
// ignore this
const char* fragShaderSrc_shadowPass1 = "#version 420 core\n"
"in vec2 texCoord;\n"
"out float fragColor;\n"
"uniform sampler2D occludersTexture;\n";
const char* fragShaderSrc_shadowPass2 = "#version 420 core\n"
"in vec2 texCoord;\n"
"layout(location = 0) out uint fragDistance;\n"
// NOTE: if it weren't for this being a test, this would be taking input from pass 1
"uniform sampler2D polarOccludersTexture;\n"
"void main() {\n"
" fragDistance = uint(texCoord.x * 256.0);\n"
"}";
const char* fragShaderSrc_shadowPass3 = "#version 420 core\n"
"in vec2 texCoord;\n"
"out vec4 fragColor;\n"
"uniform usampler1D shadowMap;\n"
"void main() {\n"
// " int shadowMapWidth = textureSize(shadowMap, 0);\n"
" uvec4 samp = texture(shadowMap, texCoord.x);\n"
" uint msamp = max(samp.r, max(samp.g, max(samp.b, samp.a)));"
" fragColor = vec4(float(samp.r) / 256.0, 0.1, 0.0, 1.0);\n"
// " fragColor = vec4(texCoord, 0.0, 1.0);\n"
// " fragColor = vec4(1.0, 0.0, 0.0, 1.0);\n"
"}";
GLuint vertexShader = 0, fragShader_shadowPass1 = 0, fragShader_shadowPass2 = 0, fragShader_shadowPass3 = 0;
GLuint prog_shadowPass1 = 0, prog_shadowPass2 = 0, prog_shadowPass3 = 0;
GLuint fbo_shadowPass[] = { 0, 0, 0 };
GLuint fbt_shadowPass[] = { 0, 0, 0 };
GLfloat screenVertices[16] = {
-1.f, 1.f, 0.0f, 0.0f, // tl
1.f, 1.f, 1.0f, 0.0f, // tr
-1.f, -1.f, 0.0f, 1.0f, // bl
1.f, -1.f, 1.0f, 1.0f, // br
};
GLuint screenIndices[] = {
0, 1, 2, // tl
3, 2, 1, // br
};
GLuint screenVao = 0, screenVbo = 0, screenEbo = 0;
void tick() {
SDL_Event ev;
while (SDL_PollEvent(&ev)) {
switch (ev.type) {
case SDL_QUIT: {
isRunning = false;
return;
break;
}
}
}
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo_shadowPass[1]);
glDisable(GL_BLEND);
GLenum a = GL_COLOR_ATTACHMENT0;
glDrawBuffers(1, &a);
glClearColor(0.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, fbt_shadowPass[0]);
glUseProgram(prog_shadowPass2);
glUniform1i(glGetUniformLocation(prog_shadowPass2, "polarOccludersTexture"), 1);
glBindVertexArray(screenVao);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
void* buf = malloc(sizeof(unsigned int) * windowSize.w);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_shadowPass[1]);
// glReadPixels(0, 0, 640, 1, GL_RED_INTEGER, GL_UNSIGNED_INT, buf);
// glReadPixels(0, 0, (GLsizei)windowSize.w, 1, GL_RED, GL_UNSIGNED_INT, buf);
glReadPixels(0, 0, (GLsizei)windowSize.w, 1, GL_RED_INTEGER, GL_UNSIGNED_INT, buf);
ofstream writeout{"buffer.bin"};
writeout.write((const char *)(buf), (streamsize)(sizeof(unsigned int) * windowSize.w));
writeout.flush();
writeout.close();
free(buf);
SDL_GL_SwapWindow(window);
GLenum e = glGetError();
if (e != 0) SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "%x %s", e, glewGetErrorString(e));
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo_shadowPass[1]);
glClearColor(0.0, 0.0, 0.5, 0.0);
glClear(GL_COLOR_BUFFER_BIT);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_1D, fbt_shadowPass[1]);
// glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glUniform1i(glGetUniformLocation(prog_shadowPass3, "shadowMap"), 2);
glUseProgram(prog_shadowPass3);
glBindVertexArray(screenVao);
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
SDL_GL_SwapWindow(window);
GLenum err = glGetError();
if (err != 0) SDL_LogError(SDL_LOG_CATEGORY_VIDEO, "%x %s", err, glewGetErrorString(err));
SDL_Delay(5000);
exit(0);
}
int main(int argc, char* argv[]) {
SDL_Log("running");
if (!init()) return 1;
#ifdef NDEBUG
SDL_LogSetAllPriority(SDL_LOG_PRIORITY_DEBUG);
#endif
SDL_Log("initialized");
size_t vertexCount = 16;
size_t verticesSize = vertexCount * sizeof(GLfloat);
size_t indexCount = 6;
size_t indexesSize = indexCount * sizeof(GLuint);
glGenVertexArrays(1, &screenVao);
glGenBuffers(1, &screenVbo);
glGenBuffers(1, &screenEbo);
glBindVertexArray(screenVao);
glBindBuffer(GL_ARRAY_BUFFER, screenVbo);
glBufferData(GL_ARRAY_BUFFER, (GLsizeiptr)verticesSize, screenVertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, screenEbo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, (GLsizeiptr)indexesSize, screenIndices, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, screenVbo);
// specify vertex positions
glVertexAttribPointer(
0, // index
2, // size
GL_FLOAT, // type
GL_FALSE, // normalized
4 * sizeof(float), // stride
nullptr // pointer
);
glEnableVertexAttribArray(0);
// specify UV attribute
glVertexAttribPointer(
1, // index
2, // size
GL_FLOAT, // type
GL_TRUE, // normalized
4 * sizeof(float), // stride
(void*)(2 * sizeof(float)) // pointer
);
glEnableVertexAttribArray(1);
glGenFramebuffers(3, fbo_shadowPass);
glGenTextures(3, fbt_shadowPass);
glBindFramebuffer(GL_FRAMEBUFFER, fbo_shadowPass[1]);
glBindTexture(GL_TEXTURE_1D, fbt_shadowPass[1]);
glTexImage1D(GL_TEXTURE_1D, 0, GL_R32UI, (GLsizei)windowSize.w, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, nullptr);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glFramebufferTexture1D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_1D, fbt_shadowPass[1], 0);
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) SDL_Log("Framebuffer 2 incomplete!");
glBindFramebuffer(GL_FRAMEBUFFER, 0);
vertexShader = glCreateShader(GL_VERTEX_SHADER);
fragShader_shadowPass1 = glCreateShader(GL_FRAGMENT_SHADER);
fragShader_shadowPass2 = glCreateShader(GL_FRAGMENT_SHADER);
fragShader_shadowPass3 = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(vertexShader, 1, (const GLchar* const*)&vertShaderSrc, nullptr);
glCompileShader(vertexShader);
glShaderSource(fragShader_shadowPass1, 1, (const GLchar* const*)&fragShaderSrc_shadowPass1, nullptr);
glCompileShader(fragShader_shadowPass1);
glShaderSource(fragShader_shadowPass2, 1, (const GLchar* const*)&fragShaderSrc_shadowPass2, nullptr);
glCompileShader(fragShader_shadowPass2);
glShaderSource(fragShader_shadowPass3, 1, (const GLchar* const*)&fragShaderSrc_shadowPass3, nullptr);
glCompileShader(fragShader_shadowPass3);
// check err
prog_shadowPass1 = glCreateProgram();
glAttachShader(prog_shadowPass1, vertexShader);
glAttachShader(prog_shadowPass1, fragShader_shadowPass1);
glLinkProgram(prog_shadowPass1);
prog_shadowPass2 = glCreateProgram();
glAttachShader(prog_shadowPass2, vertexShader);
glAttachShader(prog_shadowPass2, fragShader_shadowPass2);
glLinkProgram(prog_shadowPass2);
prog_shadowPass3 = glCreateProgram();
glAttachShader(prog_shadowPass3, vertexShader);
glAttachShader(prog_shadowPass3, fragShader_shadowPass3);
glLinkProgram(prog_shadowPass3);
GLuint shaders[4] = { vertexShader, fragShader_shadowPass1, fragShader_shadowPass2,fragShader_shadowPass3};
for (int i = 0; i < 4; i++) {
GLint compiled = 0;
glGetShaderiv(shaders[i], GL_COMPILE_STATUS, &compiled);
if (!compiled) {
GLint infoLen = 0;
glGetShaderiv(shaders[i], GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 1) {
char *infoLog = (char *) malloc(infoLen * sizeof(char));
glGetShaderInfoLog(shaders[i], infoLen, nullptr, infoLog);
SDL_Log("Error compiling shader %i: %s", i, infoLog);
::free(infoLog);
} else SDL_Log("Error compiling shader %i (no further information)", i);
return 1;
}
}
GLuint progs[3] = { prog_shadowPass1, prog_shadowPass2, prog_shadowPass3};
for (int i = 0; i < 3; i++) {
GLint linked;
glGetProgramiv(progs[i], GL_LINK_STATUS, &linked);
if (!linked) {
GLint infoLen = 0;
glGetProgramiv(progs[i], GL_INFO_LOG_LENGTH, &infoLen);
if (infoLen > 1) {
char *infoLog = (char *) malloc(sizeof(char) * infoLen);
glGetProgramInfoLog(progs[i], infoLen, nullptr, infoLog);
SDL_Log("Error linking program %i: %s", i, infoLog);
::free(infoLog);
}
SDL_Log("Error linking program %i (no further information)", i);
glDeleteProgram(progs[i]);
return 1;
}
}
while (isRunning) tick();
SDL_Log("shutting down");
SDL_Quit();
SDL_Log("goodnight");
return 0;
}
罪魁祸首是
glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);
因为您只生成了 1D 纹理的基本级别,并且由于某种原因“最近的 mipmap”在渲染时不是级别 0,所以着色器尝试对未分配的数据进行采样,每次都为您提供 0 值。
手动生成 mipmap 或使用 glGenerateMipmap - OpenGL 4 参考页,或者将您的最小过滤器更改为
GL_NEAREST
.