You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于C的OpenGL/GLSL程序台式机正常笔记本黑屏求助

OpenGL纹理渲染异常:台式机正常,笔记本黑屏且纹理全黑

我用OpenGL渲染带玩家纹理的四边形,台式机上纹理显示正常,但笔记本上仅呈现黑屏。通过GLSL调试代码:

if (texture(u_Texture, Texcoord).xyz == vec3(.0f, .0f, .0f)) 
    v_Texcoord = vec4(1.0f, 1.0f, 1.0f, 1.0f);

测试后输出全白四边形,说明程序判定纹理所有像素均为黑色。已排除屏幕刷新率、分辨率问题,图片在两台设备上均可正常加载,开发时参考过The Cherno的OpenGL/GLSL教程。

相关代码片段

玩家初始化代码

void PlayerInit() {
    playerOrigScale.x /= (float)mode->width / 1000.0f;
    playerOrigScale.y /= (float)mode->height / 1000.0f;
    {
        float playerVerticiesInit[origPlrVertLen] = {
            -1.0f, -1.0f,
            1.0f, -1.0f,
            1.0f, 1.0f,
            -1.0f, 1.0f,
        };
        float texCoordInit[8] = {
            .0f, .0f,
             1.0f, .0f,
            1.0f, 1.0f,
             .0f, 1.0f,
        };
        int i;
        int texCoordLen = lengthOf(texCoordInit);
        for (i = 0; i < texCoordLen; i++) {
            playerVerticies[i + ((i - i / 2) + 1 - (i % 2)) * 2] = texCoordInit[i];
        }
        for (i = 0; i < origPlrVertLen; i++) {
            origPlrPos[i] = playerVerticiesInit[i];
            playerVerticies[i + (i - (i % 2))] = origPlrPos[i] * (i % 2 == 0 ? playerOrigScale.x : playerOrigScale.y);
        }
    }
    verticiesLen = malloc(sizeof(byte));
    *verticiesLen = lengthOf(playerVerticies);
    unsigned int indicies[] = {
        0, 1, 2,
        2, 3, 0
    };
    unsigned int vao;
    glGenVertexArrays(1, &vao);
    glBindVertexArray(vao);
    printf("%s
", glGetString(GL_VERSION));
    unsigned int vbo;
    glGenBuffers(1, &vbo);
    glBindBuffer(GL_ARRAY_BUFFER, vbo);
    glBufferData(GL_ARRAY_BUFFER, sizeof(playerVerticies), playerVerticies, GL_DYNAMIC_DRAW);
    glEnableVertexAttribArray(0);
    int* texAttrib = malloc(sizeof(int));
    glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (const void*)0);
    unsigned int ibo;
    glGenBuffers(1, &ibo);
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indicies), indicies, GL_DYNAMIC_DRAW);
    ShaderProgramSource shaderSrc;
    {
        char* filepath = "Basic.shader";
        shaderSrc = ParseShader(filepath);
    }
    printf("vertex:
%s
", shaderSrc.vertexSource);
    printf("framgent:
%s
", shaderSrc.fragmentSource);
    shader = CreateShader(shaderSrc.vertexSource, shaderSrc.fragmentSource);
    glUseProgram(shader);
    glUniform4f(glGetUniformLocation(shader, "u_Color"), .1f, .6f, .9f, 1.0f);
    glUniform1i(glGetUniformLocation(shader, "u_Texture"), 0);
    glGenTextures(1, &texture);
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
    glGenerateMipmap(GL_TEXTURE_2D);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, playerAnimator->frames[0][6]);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void*)(sizeof(float) * 2));
    shouldDraw = true;
}

主函数片段

{
    stbi_set_flip_vertically_on_load(1);
    playerAnimator = malloc(sizeof(Animator));
    playerAnimator->frames = malloc(sizeof(unsigned char*) * 1);
    playerAnimator->animFrameNo[idle] = 7;
    *playerAnimator->frames = malloc(sizeof(unsigned char*) * playerAnimator->animFrameNo[idle]);
    Init(playerAnimator);
    {
        QueryPerformanceCounter(&pTime);
        char* save = ReadSaveFile("saves/save 1.txt", 500);
        printf("%s
", save);
        free(save);
        printf("\nLoading save file took %f seconds. Keep in mind not printing save file will improve save access time to around .00125 seconds with lorem ipsum benchmark. This time will increase however with the encryption and the decryption which is yet to be implemented.
", deltaTime());
    }
    glfwInit();
    monitor = glfwGetPrimaryMonitor();
    mode = glfwGetVideoMode(glfwGetPrimaryMonitor());
    //glfwWindowHint(GLFW_MAXIMIZED, true);
    window = glfwCreateWindow(mode->width, mode->height, title, fullScreen ? monitor : NULL, NULL);
    glfwSetWindowMonitor(window, NULL, 0, 0, mode->width, mode->height, mode->refreshRate);
    glfwMakeContextCurrent(window);
    DWORD frameWaitTime = (DWORD)(1.0f / (float)mode->refreshRate * 1000.0f);
    glfwSwapInterval(vsyncCount);
    if (glewInit() != GLEW_OK) ThrowError("your program is broken");
    PlayerInit();
    float fullScreenET = 0;
    //glUseProgram(0);
    while (!glfwWindowShouldClose(window)) {
        if (!glfwGetWindowAttrib(window, GLFW_FOCUSED)) {
            glfwPollEvents();
            Sleep(frameWaitTime * 10);
            continue;
        }
        if (pressing(GetKeyState(q)) && pressing(GetKeyState(leftControl))) break;
        //37, 39
        if (shouldDraw) {
            glClear(GL_COLOR_BUFFER_BIT);
            shouldDraw = false;
        }
        //glUseProgram(shader);
        //glBindVertexArray(vao);
        //glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ibo);
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, NULL);
        glfwSwapBuffers(window);
        glfwPollEvents();
        Update();
        printf("deltatime is %lf
", DeltaTime());
        if (fullScreenET > .2f)
        {
            if (pressing(GetKeyState(key_return)) && pressing(GetKeyState(leftAlt))) {
                fullScreen = !fullScreen;
                glfwSetWindowMonitor(window, fullScreen ? monitor : NULL, 0, 0, mode->width, mode->height, mode->refreshRate);
                glfwSwapInterval(vsyncCount);
                fullScreenET = 0;
            }
        }
        else fullScreenET += deltaTime();
        //10000000
        if (shouldDraw) glBufferData(GL_ARRAY_BUFFER, sizeof(playerVerticies), playerVerticies, GL_DYNAMIC_DRAW);
        QueryPerformanceCounter(&pTime);
        //Sleep(frameWaitTime);
    }
    glDeleteTextures(1, &texture);
}

Basic.shader代码

#shader vertex
#version 430 core
layout(location = 0) in vec4 position;
layout(location = 1) in vec2 texcoord;
out vec2 Texcoord;
void main() {
    Texcoord = texcoord;
    gl_Position = position;
}
#shader fragment
#version 430 core
layout(location = 0) in vec4 color;
layout(location = 1) in vec2 Texcoord;
out vec4 v_Texcoord;
uniform vec4 u_Color;
uniform sampler2D u_Texture;
void main() {
    v_Texcoord = texture(u_Texture, Texcoord);
}
//don't add empty line at end of file otherwise it breaks the shader interpretation

问题排查与修复建议

1. 纠正纹理数据与Mipmap生成顺序

当前代码先调用glGenerateMipmap再上传纹理数据,导致Mipmap基于空纹理生成,后续上传的纹理不会同步到Mipmap层级。调整顺序:

// 先上传纹理像素数据
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, playerAnimator->frames[0][6]);
// 再生成Mipmap
glGenerateMipmap(GL_TEXTURE_2D);

2. 统一顶点属性指针配置时机

将纹理坐标属性的配置移到VBO绑定后、其他操作前,确保VAO正确记录所有顶点属性:

glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(playerVerticies), playerVerticies, GL_DYNAMIC_DRAW);
// 配置位置属性(location 0)
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (const void*)0);
// 配置纹理坐标属性(location 1)
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 4, (void*)(sizeof(float) * 2));

3. 删除片段着色器无用输入变量

片段着色器中layout(location = 0) in vec4 color;没有对应的顶点输出,属于未定义行为,直接删除:

#shader fragment
#version 430 core
layout(location = 1) in vec2 Texcoord;
out vec4 v_Texcoord;
uniform sampler2D u_Texture;
void main() {
    v_Texcoord = texture(u_Texture, Texcoord);
}

4. 恢复渲染循环中的状态绑定

OpenGL是状态机,必须在每次绘制前确保绑定正确的着色器程序和VAO:

while (!glfwWindowShouldClose(window)) {
    // ... 其他逻辑 ...
    if (shouldDraw) {
        glClear(GL_COLOR_BUFFER_BIT);
        shouldDraw = false;
    }
    // 绘制前绑定必要状态
    glUseProgram(shader);
    glBindVertexArray(vao);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, NULL);
    glfwSwapBuffers(window);
    // ... 其他逻辑 ...
}

5. 验证纹理数据有效性

在笔记本上打印playerAnimator->frames[0][6]的前几个字节,或上传纹理后用glGetTexImage读取验证:

// 上传纹理后读取验证
unsigned char* texData = malloc(width * height * 4);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, texData);
// 打印前16字节确认数据
for(int i=0; i<16; i++) printf("%d ", texData[i]);
free(texData);

内容的提问来源于stack exchange,提问作者nugget

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.18 04:27:34