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

OpenGL实例化绘制异常:气泡球体显示怪异形状求助

OpenGL实例化绘制气泡显示异常问题

我用OpenGL开发简单游戏,场景里包含玩家球体和多个气泡球体,但气泡的实例化功能未按预期工作,显示效果怪异。作为OpenGL实例化新手,无法定位问题根源。

预期效果:两个气泡与正常显示的玩家球体外观完全一致;当前显示效果:气泡出现扭曲、形态异常的怪异画面。

相关代码

玩家类绘制方法

void Player::draw(glm::mat4 ViewMat, GLfloat aspect, glm::vec3 light, GLfloat zoom)
{
    bindProgram();
    bindBuffers();

    glm::mat4 Projection = glm::perspective(zoom, aspect, 0.1f, 100.0f);

    glm::mat4 Model = glm::mat4(1.0f);
    glm::mat4 MVP = Projection * ViewMat * Model; 

    glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "MVP"), 1, GL_FALSE, &MVP[0][0]);
    glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "M"), 1, GL_FALSE, &Model[0][0]);
    glUniformMatrix4fv(glGetUniformLocation(shaderProgram, "V"), 1, GL_FALSE, &ViewMat[0][0]);
    glUniform3f(glGetUniformLocation(shaderProgram, "origin"), origin.x, origin.y, origin.z);
    glUniform3f(glGetUniformLocation(shaderProgram, "lightPosWorld"), light.x, light.y, light.z);
    
    glDrawArrays(GL_TRIANGLE_STRIP, 0, vertices.size() / 3); 
}

气泡类静态绘制方法

void Bubble::drawAll(glm::mat4 ViewMat, GLfloat aspect, glm::vec3 light, GLfloat zoom,
                     std::vector<Bubble *> allInstances)
{
    std::vector<float> origins;
    uint count = 0;
    for (auto b : allInstances)
    {
        origins.push_back(b->origin.x);
        origins.push_back(b->origin.y);
        origins.push_back(b->origin.z);
        count++;
    }
    allInstances[0]->bindProgram();
    allInstances[0]->bindBuffers();
    glBufferData(GL_ARRAY_BUFFER, origins.size() * sizeof(origins), origins.data(), GL_STATIC_DRAW);
    glEnableVertexAttribArray(1);
    glBindBuffer(GL_ARRAY_BUFFER, originsBuffer);
    glVertexAttribPointer(1,        // attribute. No particular reason for 0, but must match the layout in the shader.
                          3,        // size
                          GL_FLOAT, // type
                          GL_FALSE, // normalized?
                          0,        // stride
                          (void *)0 // array buffer offset
    );

    glm::mat4 Projection = glm::perspective(zoom, aspect, 0.1f, 100.0f);

    glm::mat4 Model = glm::mat4(1.0f);
    glm::mat4 MVP = Projection * ViewMat * Model;

    GLuint programID = allInstances[0]->shaderProgram;
    glVertexAttribDivisor(0, 0);
    glVertexAttribDivisor(1, 1);
    glUniformMatrix4fv(glGetUniformLocation(programID, "MVP"), 1, GL_FALSE, &MVP[0][0]);
    glUniformMatrix4fv(glGetUniformLocation(programID, "M"), 1, GL_FALSE, &Model[0][0]);
    glUniformMatrix4fv(glGetUniformLocation(programID, "V"), 1, GL_FALSE, &ViewMat[0][0]);
    glUniform3f(glGetUniformLocation(programID, "lightPosWorld"), light.x, light.y, light.z);

    glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, allInstances[0]->vertices.size() / 3, count);
}

玩家顶点着色器

#version 330 core

layout(location = 0) in vec3 vertexPosition_modelspace;

uniform mat4 MVP;
uniform mat4 M;
uniform mat4 V;
uniform vec3 origin;
uniform vec3 lightPosWorld;
uniform vec3 cameraPos;

out vec3 vertexPosWorld;
out vec3 vertexNormal;
out vec3 eyeDirectionCamera;
out vec3 lightDirectionCamera;

void main()
{
    gl_Position =  MVP * vec4(vertexPosition_modelspace + origin,1);

    vertexPosWorld = (M * vec4(vertexPosition_modelspace + origin,1)).xyz;

    vertexNormal = normalize(vertexPosWorld - origin);
}

气泡顶点着色器

#version 330 core

layout(location = 0) in vec3 vertexPosition_modelspace;
layout(location = 1) in vec3 origin;

uniform mat4 MVP;
uniform mat4 M;
uniform mat4 V;
uniform vec3 lightPosWorld;
uniform vec3 cameraPos;

out vec3 vertexPosWorld;
out vec3 vertexNormal;
out vec3 eyeDirectionCamera;
out vec3 lightDirectionCamera;

void main()
{
    gl_Position =  MVP * vec4(vertexPosition_modelspace + origin,1);

    vertexPosWorld = (M * vec4(vertexPosition_modelspace + origin,1)).xyz;

    vertexNormal = normalize(vertexPosWorld - origin);
}

问题定位与修复

你的气泡实例化代码存在3个关键错误:

  1. 缓冲区操作顺序与参数错误

    • 你先调用glBufferData但未绑定originsBuffer,导致实例化数据被写入到模型顶点缓冲区,直接覆盖了气泡的模型顶点数据,这是显示异常的核心原因。
    • sizeof(origins)参数错误:origins是vector<float>对象,sizeof(origins)获取的是容器本身的大小,而非单个元素的大小,应该替换为sizeof(float)。
      修复后的缓冲区操作代码:
    // 先绑定实例化数据缓冲区
    glBindBuffer(GL_ARRAY_BUFFER, originsBuffer);
    // 写入实例化数据,使用正确的元素大小
    glBufferData(GL_ARRAY_BUFFER, origins.size() * sizeof(float), origins.data(), GL_STATIC_DRAW);
    glEnableVertexAttribArray(1);
    glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);
    
  2. 顶点属性除数设置时机错误

    • glVertexAttribDivisor应该在glVertexAttribPointer之后调用,确保属性配置完成后,再设置实例化步进规则。
  3. 冗余代码优化

    • count变量可以直接用allInstances.size()替代,无需循环统计。

修正后的Bubble::drawAll核心片段:

std::vector<float> origins;
uint count = allInstances.size();
for (auto b : allInstances)
{
    origins.push_back(b->origin.x);
    origins.push_back(b->origin.y);
    origins.push_back(b->origin.z);
}

allInstances[0]->bindProgram();
allInstances[0]->bindBuffers();

// 处理实例化数据缓冲区
glBindBuffer(GL_ARRAY_BUFFER, originsBuffer);
glBufferData(GL_ARRAY_BUFFER, origins.size() * sizeof(float), origins.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 0, (void*)0);

// 配置实例化步进规则
glVertexAttribDivisor(0, 0);
glVertexAttribDivisor(1, 1);

// 矩阵与Uniform设置
glm::mat4 Projection = glm::perspective(zoom, aspect, 0.1f, 100.0f);
glm::mat4 Model = glm::mat4(1.0f);
glm::mat4 MVP = Projection * ViewMat * Model;

GLuint programID = allInstances[0]->shaderProgram;
glUniformMatrix4fv(glGetUniformLocation(programID, "MVP"), 1, GL_FALSE, &MVP[0][0]);
glUniformMatrix4fv(glGetUniformLocation(programID, "M"), 1, GL_FALSE, &Model[0][0]);
glUniformMatrix4fv(glGetUniformLocation(programID, "V"), 1, GL_FALSE, &ViewMat[0][0]);
glUniform3f(glGetUniformLocation(programID, "lightPosWorld"), light.x, light.y, light.z);

glDrawArraysInstanced(GL_TRIANGLE_STRIP, 0, allInstances[0]->vertices.size() / 3, count);

额外建议:将originsBuffer的创建(glGenBuffers)放到类的初始化方法中,避免每次绘制都重复创建缓冲区,提升性能。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.09 17:45:32