1

我正在使用带有 C++ 的 OpenGL 4.0 开发粒子系统。

目前,我的程序中的一个粒子由 12 个浮点组件组成:

[vec4 位置,vec4 颜色,vec3 速度,寿命] = [xPos,yPos,zPos,wPos,r,g,b,a,xVel,yVel,zVel,寿命]

我目前的方法是用所有粒子填充一个数组(称为vertexData),这样数组的结构将是:[particle,particle,particle,...],其中每个粒子都具有上述布局。

我的计划是简单地将这个初始信息提供给顶点着色器,然后让它根据初始信息计算位置偏移和颜色。我希望大部分计算都在 GPU 上完成。

我想通过将所有数据放在一个缓冲区对象中来将数据传递给着色器,然后使用偏移量来获取正确的值以传递给顶点着色器。

问题:我似乎无法获得正确的数据。我觉得我误解了整个缓冲区偏移量,并且程序从我的数据中获取随机值......我得到了着色器属性的工作句柄,但数据绝对不正确。使用示例粒子,您将在下面的第一个代码块中看到,只有位置似乎正确。颜色为紫色(应为白色),寿命为 0(应为 5)。很难调试着色器代码。如果生命周期不是应该的,我刚刚做了一个检查,将点涂成另一种颜色。

我是否需要将数据排列成这样:[(所有粒子位置),(所有粒子颜色),(所有粒子速度),(所有粒子寿命)]?(就像这里所做的一样:http: //arcsynthesis.org/gltut/Basics/Tut02%20Vertex%20Attributes.html

或者我可能需要为每种类型的粒子数据创建一个不同的缓冲区,一个用于位置,一个用于颜色等?

代码: 现在了解我如何填充数据、创建缓冲区对象、渲染和着色器代码的一些代码。数组初始化和填充是这样完成的:

const int PARTICLE_COUNT = 1;
const int PARTICLE_COMPONENTS_COUNT = 12;
...
GLfloat* vertexData;
...

void InitVertexData(){

    vertexData = new GLfloat[PARTICLE_COUNT * PARTICLE_COMPONENTS_COUNT];

    for (int i = 0; i < PARTICLE_COUNT; i++)
{       
        vertexData[i * PARTICLE_COMPONENTS_COUNT + 0] = 0.5f; //0: posX
        vertexData[i * PARTICLE_COMPONENTS_COUNT + 1] = 0.5f; //1: posY
        vertexData[i * PARTICLE_COMPONENTS_COUNT + 2] = 0.0f; //2: posZ
        vertexData[i * PARTICLE_COMPONENTS_COUNT + 3] = 1.0f; //3: posW

        vertexData[i * PARTICLE_COMPONENTS_COUNT + 4] = 1.0f; //4: red
        vertexData[i * PARTICLE_COMPONENTS_COUNT + 5] = 1.0f; //5: green
        vertexData[i * PARTICLE_COMPONENTS_COUNT + 6] = 1.0f; //6: blue
        vertexData[i * PARTICLE_COMPONENTS_COUNT + 7] = 1.0f; //7: alpha

        vertexData[i * PARTICLE_COMPONENTS_COUNT + 8] = 0.0f; //8: velX
        vertexData[i * PARTICLE_COMPONENTS_COUNT + 9] = 0.0f; //9: velY
        vertexData[i * PARTICLE_COMPONENTS_COUNT + 10] = 0.0f; //10: velZ

        vertexData[i * PARTICLE_COMPONENTS_COUNT + 11] = 5.0f; //11: lifetime
    }
}

注意:粒子的值现在只是硬编码。我还没有实现计算初始值的函数。

然后,我生成一个缓冲区,我想保存所有粒子的所有数据。初始化时调用一次以下函数:

bool CreateBufferObject()
{
    //VertexShader attribute variable handles.
    GLint positionLocation = glGetAttribLocation(programId, "position");
    GLint colorLocation = glGetAttribLocation(programId, "color");
    GLint velocityLocation = glGetAttribLocation(programId, "velocity");
    GLint lifetimeLocation = glGetAttribLocation(programId, "lifetime");

    //Number of components (floats) in a particle element (such as position, color etc).
    GLint positionComponentCount = 4; //posX, posY, posZ, posW
    GLint colorComponentCount = 4; //r, g, b, a
    GLint velocityComponentCount = 3; //velX, velY, velZ
    GLint lifetimeComponentCount = 1; //lifetime (in seconds)

    //Size (in bytes) of the vertexData array and the elements inside.
    GLsizeiptr sizeofVertexDataArray = sizeof(GLfloat) * PARTICLE_COUNT * PARTICLE_COMPONENTS_COUNT; //4 * 2 * 12 = 96
    GLsizeiptr sizeofPosition = sizeof(GLfloat) * positionComponentCount; //4*4 = 16
    GLsizeiptr sizeofColor = sizeof(GLfloat) * colorComponentCount; //4*4 = 16
    GLsizeiptr sizeofVelocity = sizeof(GLfloat) * velocityComponentCount; //4*3 = 12
    GLsizeiptr sizeofLifetime = sizeof(GLfloat) * lifetimeComponentCount; //4*1 = 4

    //Generate one buffer in GPU memory and get handle.
    glGenBuffers(1, &vertexDataBufferId);

    //Generate VAO, a descriptor of vertex data (not the actual object), and get handle.
    glGenVertexArrays(1, &vaoId);
    glBindVertexArray(vaoId);

    //Bind the generated buffer to VAO as the current target.
    glBindBuffer(GL_ARRAY_BUFFER, vertexDataBufferId);

    //Copy the actual data from the vertexData array to GPU memory. TODO: Not sure what to pick instead of GL_STREAM_DRAW
    glBufferData(GL_ARRAY_BUFFER, sizeofVertexDataArray, vertexData, GL_STREAM_DRAW);

    //Guide Vertex Shader to where attributes are at in the memory, giving size and offset (in bytes) of the different elements of a particle.
    glVertexAttribPointer(positionLocation, positionComponentCount, GL_FLOAT, GL_FALSE, sizeofPosition, 0); //offset 0
    glVertexAttribPointer(colorLocation, colorComponentCount, GL_FLOAT, GL_FALSE, sizeofColor, (void*)positionComponentCount); //ofset 4
    glVertexAttribPointer(velocityLocation, velocityComponentCount, GL_FLOAT, GL_FALSE, sizeofVelocity, (void*)(positionComponentCount + colorComponentCount)); //offset 8
    glVertexAttribPointer(lifetimeLocation, lifetimeComponentCount, GL_FLOAT, GL_FALSE, sizeofLifetime, (void*)(positionComponentCount + colorComponentCount + velocityComponentCount)); //offset 11

    glEnableVertexAttribArray(positionLocation);
    glEnableVertexAttribArray(colorLocation);
    glEnableVertexAttribArray(velocityLocation);
    glEnableVertexAttribArray(lifetimeLocation);
//Error checks and debug prints omitted
}

我的理解如下:在glBufferData中,我指定了整个数据集(所有粒子)的大小,并将数据复制到GPU显存中。

使用 glVertexAttribPointer,我指定 OpenGL 应该使用哪些偏移量和计数来填充顶点着色器的属性变量。

然后在我的 Render 函数中调用(这也是我认为我可能出错的地方):

void Render()
{
    ...
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    GLuint elapsedTimeUniformLoc = glGetUniformLocation(programId, "time");

    glUniform1f(elapsedTimeUniformLoc, glutGet(GLUT_ELAPSED_TIME) / 1000.0f); //Divide by 1000 to get time in seconds.
    glDrawArrays(GL_POINTS, 0, PARTICLE_COUNT)

    glutSwapBuffers();
    glutPostRedisplay();
}

由于一个点由 1 个顶点组成,我要求它绘制与 PARTICLE_COUNT 大的点一样多的点(当前为 1)

着色器

const GLchar* VertexShader2 =
{
    "#version 400\n"\

    "uniform float time;\n"\
    "in vec4 position;\n"\
    "in vec4 color;\n"\
    "in vec3 velocity;\n"\
    "in float lifetime;\n"\

    "out vec4 myColor;\n"\

    "void main()\n"\
    "{\n"\
    "   float dummy = velocity.x;\n"
    "   float timeScale = 0.1f;\n"\
    "   float currentAge = mod(time, lifetime);\n"\
    "   vec4 accumulatedOffset = vec4(timeScale*currentAge, 0.0f, 0.0f, 0.0f);\n"
    "   gl_Position = position + accumulatedOffset;\n"\


    " if(lifetime ==0) { color.y = 1.0f; }\n"\
    "   //color.w = 1.0f - ((1.0f/lifetime) * currentAge)\n"\
    "   color.w = 1.0f - (0.2f * currentAge);\n"\
    "   myColor = color;\n"\
    "}\n"\
};

const GLchar* FragmentShader =
{
    "#version 400\n"\

    "in vec4 myColor;\n"\
    "out vec4 outColor;\n"\

    "void main()\n"\
    "{\n"\
    "   outColor = myColor;\n"\
    "}\n"\
};

注意:由于速度值不正确,我使用虚拟变量只是为了确保着色器编译器不会丢弃该属性。

如果您需要查看更多代码,我会更新帖子。我希望我把我的问题和我的情况弄清楚了。

回复@Ben Voight 好的,所以从您的评论中,我认为 stride 参数表示该属性下一个值的数据偏移量。所以它必须是

PARTICLE_COMPONENTS_COUNT * sizeof(GLfloat)对所有人glVertexAttribPointer

但是,它仍然给我错误的数据。一个由以下组成的粒子

[-0.5, 0.5, 0.5, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 5.0],

我希望位置为 (-0.5, 0.5, 0.5),颜色为白色,寿命为 5。(像以前一样忽略速度)

如果生命周期 == 0,我的顶点着色器会更改颜色,并且它报告生命周期确实为 0。我不明白它从哪里得到 0,它甚至不在数据中。颜色也错了。只有位置似乎是正确的。事实上,它的工作原理和以前完全一样。

已解决 原来我在 glVertexAttribPointer 中传递了错误的步幅和偏移值。

可能重复: Opengl Vertex 属性步幅

4

1 回答 1

1

的 stride 参数必须glVertexAttribPointerPARTICLE_COMPONENTS_COUNT * sizeof (GLfloat).

offset 参数以字节为单位。您所有的值都偏离了sizeof (GLfloat).

您有什么理由使用花哨的索引而不是具有所有正确字段的结构数组?

于 2012-12-06T03:22:29.640 回答