我正在使用OpenGL 4.0和C ++开发粒子系统。
目前,我程序中的粒子由12个浮点组件组成:
[vec4位置,vec4颜色,vec3速度,寿命] = [xPos,yPos,zPos,wPos,r,g,b,a,xVel,yVel,zVel,lifetime ]
我目前的方法是用一个粒子填充一个数组(称为 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应使用哪些偏移和计数来填充顶点着色器的属性变量。
在我的渲染功能中,我然后调用(这也是我认为我可能会出错的地方):
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"\
};
注意:由于velocity的值不正确,我使用虚拟变量只是为了确保着色器编译器不会丢弃该属性。
如果您需要查看更多代码,我会更新帖子。我希望我能解决问题并清楚我的情况。
对@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中为stride和offset传递了错误的值。
答案 0 :(得分:1)
glVertexAttribPointer
的步幅参数必须为PARTICLE_COMPONENTS_COUNT * sizeof (GLfloat)
。
offset参数以字节为单位。你的所有价值都是sizeof (GLfloat)
。{/ p>
您使用花式索引而不是具有所有正确字段的结构数组的原因是什么?