初投稿になります。ゲームプログラマーを目指している学生として、勉強した内容や詰まった内容などを備忘録として書き残しておきます。
UBOとは
OpenGLにはGPUにデータを送信する方法としてUBO(Uniform Buffer Object)があります。
単一のデータとして送信するglUniform3f
やglUniformMatrix4fv
がありますが、こちらは一つ一つ変数をプッシュしなければならないうえ、複数のオブジェクトに共有されることもないので、相当な労力や容量が食われます。
そこで、構造体としてデータを一斉に送信でき、複数のオブジェクトに共有できるUBOがとても便利です。
ということで実装していきたいのですが、複数のサイトを参考にしても全くできなかったです...
手順通りに書くだけで簡単に実装できました...AIすごい。
コード解説
C++側のコード
/// <summary>
/// ライト用UB。
/// </summary>
struct LightUB
{
float ambient;
};
まずはUBOに使う構造体を定義します。
/// <summary>
/// 初期化処理。
/// </summary>
/// <param name="data">構造体</param>
/// <param name="size">構造体のサイズ</param>
/// <param name="shaderID">シェーダープログラムID</param>
void UniformBuffer::Init(void* data, const int size, GLuint shaderID, const char* blockName)
{
//UniformBufferを作成。
glGenBuffers(1, &m_ubo);
glBindBuffer(GL_UNIFORM_BUFFER, m_ubo);
glBufferData(GL_UNIFORM_BUFFER, size, &data, GL_STATIC_DRAW);
//UBOをシェーダーと紐付ける。
m_blockIndex = glGetUniformBlockIndex(shaderID, blockName);
glUniformBlockBinding(shaderID, m_blockIndex, 0);
glBindBufferBase(GL_UNIFORM_BUFFER, m_blockIndex, m_ubo);
}
/// <summary>
/// 更新処理。
/// </summary>
/// <param name="data">構造体</param>
/// <param name="size">構造体のサイズ</param>
void UniformBuffer::Update(void* data, const int size)
{
glBindBuffer(GL_UNIFORM_BUFFER, m_ubo);
//データを送信。
glBufferSubData(GL_UNIFORM_BUFFER, 0, size, data);
glBindBuffer(GL_UNIFORM_BUFFER, 0);
}
glBufferData
でUBOにModelUBの構造体とサイズを入れ、
glBufferSubData
でシェーダーにデータを送信します。
glGetUniformBlockIndex
でブロックインデックスを取得し、
glUniformBlockBinding
でプログラム側でブロックインデックスを何番に配置するかの割り当てを行います。
(作成に失敗するとm_blockIndex
の値がunsigned int型の最大値になります。
そのときはシェーダーの作成に失敗していないか、uniform block nameが間違っていないかを確認する)
int main()
{
m_lightUniformBuffer.Init(&m_lightUB, sizeof(LightUB), m_shaderProgram->ID, "LightUB");
//メインのループ処理。
while (!glfwWindowShouldClose(window)) {
glUseProgram(m_shaderProgram->ID);
m_lightUniformBuffer.Update(&m_modelUB, sizeof(ModelUB));
}
}
シェーダー側のコード
/// <summary>
/// ライト用定数バッファ。
/// </summary>
layout (std140) uniform LightUB
{
float ambient;
} lightUniformBuffer;
作成したUBOはlightUniformBuffer.ambient
などのように書くことでアクセスできます。
void main()
{
float ambient = lightUniformBuffer.ambient;
vec4 lig = CalcPointLight();
vec4 albedoColor = texture(g_normalMap, psIn.texCoord);
lig += albedoColor * ambient;
FragColor = lig;
}
ambientの値をC++側でいじるとピラミッドの環境光が変わっているのがわかります。
間違いなどあればコメント欄にお願いします。