私はC++でOpenGL4.0を使用するパーティクルシステムに取り組んでいます。
現在、私のプログラムのパーティクルは12個のフロートコンポーネントで構成されています。
[vec4の位置、vec4の色、vec3の速度、寿命] = [xPos、yPos、zPos、wPos、r、g、b、a、xVel、yVel、zVel、寿命]
私の現在のアプローチは、1つの配列(vertexDataと呼ばれる)にすべての粒子を配置することです。配列の構造は次のようになります。[粒子、粒子、粒子、...]ここで、各粒子は上記のレイアウトになります。
私の計画は、この初期情報を頂点シェーダーに提供し、初期情報に基づいて位置オフセットと色を計算させることです。ほとんどの計算を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
}
}
注:現時点では、パーティクルの値はハードコーディングされています。初期値を計算する関数を実装していません。
次に、すべてのパーティクルのすべてのデータを保持する1つのバッファを生成します。次の関数は、初期化時に1回呼び出されます。
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"\
};
注: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のストライドとオフセットに間違った値を渡したことが判明しました。
重複している可能性があります: OpenglVertex属性ストライド