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

OpenGL更新模型Uniform为相同值时出现闪烁问题

OpenGL静态模型Uniform导致画面闪烁问题

我查遍了几乎所有OpenGL闪烁相关的问题,大多都和Z缓冲或透视投影有关,但我现在的情况不一样:我只渲染一个四边形,而且没开深度测试。每帧把模型Uniform设为同一个值时画面会闪烁,但如果让模型随Uniform更新在屏幕上移动,一切就正常了。

主渲染代码

mat4 model = mat4_identity();
model = mat4_translatev(make_vec3(100.0f, 200.0f, 0.0f));
vec4 color = make_vec4(1.0f, 0.8f, 0.7f, 1.0f);
mat4 projection = mat4_ortho(0.0f, 800.0f, 600.0f, 0.0f, -1.0f, 1.0f);
Shader shader("generic_shader.vs", "generic_shader.fs");
shader.use();
//shader.set_vec4("color", &color);
shader.set_mat4("model", &model);
shader.set_mat4("projection", &projection);

float vertices[] = {
        0.0f, 0.0f,
        0.0f, 200.0f,
        200.0f, 0.0f,
        200.0f, 200.0f,
};

unsigned int indices[] = {
    0, 1, 2,
    2, 1, 3,
};

unsigned int vao, vbo, ebo;
glGenVertexArrays(1, &vao);
glGenBuffers(1, &vbo);
glGenBuffers(1, &ebo);

glBindVertexArray(vao);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);
glEnableVertexAttribArray(0);
glBindVertexArray(0);

while (!glfwWindowShouldClose(window))
    {
    
    float currentFrame = static_cast<f32>(glfwGetTime());
    deltaTime = currentFrame - lastFrame;
    while(deltaTime < REQUIRED_FRAME_TIME)
    {
        currentFrame = static_cast<f32>(glfwGetTime());
        deltaTime = currentFrame - lastFrame;
    }
    deltaTime = currentFrame - lastFrame;
    lastFrame = currentFrame;
    processInput(window);

    glDisable(GL_DEPTH_TEST);
    glClearColor(1.0f, 0.5f, 0.5f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);

    shader.use();
    //model = mat4_translatev(make_vec3(16.0f * currentFrame, 12.0f * currentFrame, 0.0f));  // <- 取消注释后就不会闪烁
    shader.set_mat4("model", &model);
    glBindVertexArray(vao);
    glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
    glBindVertexArray(0);

    glfwPollEvents();
    glfwSwapBuffers(window);
}

Shader::use() 实现

void Shader::use()
{
    glUseProgram(this->program_id);
}

矩阵Uniform设置函数实现

void Shader::set_mat4(const char* uniform_name, mat4* value)
{
    *value = mat4_transpose(*value);
    glUniformMatrix4fv(glGetUniformLocation(this->program_id, uniform_name), 1, GL_TRUE, &value->E[0][0]);
}

processInput()为空函数,使用自定义数学库处理向量和矩阵运算。


问题根源

问题出在set_mat4函数里的矩阵转置操作:每次调用都会直接修改传入的model矩阵本身。第一帧调用时矩阵被转置,第二帧调用时又把已经转置过的矩阵再转置一次(相当于还原成原矩阵),如此反复交替,导致模型矩阵每帧在正确和错误状态之间切换,最终表现为画面闪烁。

而当让模型随时间移动时,每帧都会重新生成新的model矩阵,转置操作只会作用于当前新矩阵,不会有前一帧的残留影响,因此不会闪烁。

修复方案

方案1:使用临时变量存储转置结果,不修改原矩阵

void Shader::set_mat4(const char* uniform_name, mat4* value)
{
    mat4 transposed = mat4_transpose(*value); // 用临时变量保存转置后的矩阵
    glUniformMatrix4fv(glGetUniformLocation(this->program_id, uniform_name), 1, GL_TRUE, &transposed.E[0][0]);
}

方案2:利用OpenGL的矩阵存储参数避免手动转置

由于你的矩阵是列主序,可以将glUniformMatrix4fv的第三个参数改为GL_FALSE(OpenGL默认按列主序读取矩阵),这样无需手动转置:

void Shader::set_mat4(const char* uniform_name, mat4* value)
{
    glUniformMatrix4fv(glGetUniformLocation(this->program_id, uniform_name), 1, GL_FALSE, &value->E[0][0]);
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.25 16:45:44