目次
ライティングについて
ここでは、ベーシックなライティングについてメモします。
- GLSL1.4以上(GLSL1.5以上推奨)
ライティングの計算
物体の色(光)出力を、放射輝度(emission)、環境光(ambient)、拡散光(diffuse)、鏡面光(specular)を使った式で表すと次のようになります。
色出力 = (放射輝度) + (環境光) + (拡散光) + (鏡面光)
これは3Dグラフィックスではよく使われます。
GLSLライティングの準備と実行
光源のセットアップ(OpenGL側)
//光源
struct LightParam {
GLfloat ambient[4];
GLfloat diffuse[4];
GLfloat specular[4];
GLfloat position[4];
};
上記のような構造体を準備。
GLuint lightUboId[1]; // light用
GLuint lightBlockIndex;
GLint lightBlockSize;
void setup()
{
// ・・・
enum { UBP_LIGHT=0 }; // UBO binding point 0
lightBlockIndex = glGetUniformBlockIndex(programObject, "LightParam");
glGetActiveUniformBlockiv(
programObject, lightBlockIndex,
GL_UNIFORM_BLOCK_DATA_SIZE, &lightBlockSize);
// UBOを使用
glGenBuffers(1, lightUboId);
glBindBuffer(GL_UNIFORM_BUFFER, lightUboId[0]);
glBufferData(GL_UNIFORM_BUFFER, shObj->lightBlockSize, NULL, GL_DYNAMIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, UBP_LIGHT, lightUboId[0]);
glUniformBlockBinding(programObject, lightBlockIndex, UBP_LIGHT);
}
ここでは、UBO(uniform buffer object)を使用しています。
UBOを使うことで構造体データをCPU側のメモリからGPU側のメモリへ一度に転送することができます。
1パラメータごとに転送するのは面倒ですので、UBOを使った方が効率的かと。
UBOを使うことで構造体データをCPU側のメモリからGPU側のメモリへ一度に転送することができます。
1パラメータごとに転送するのは面倒ですので、UBOを使った方が効率的かと。
UBP_LIGHTは勝手にenumを作って使っていますが、ようはuboIdとlightBlockIndexを関連付けるためのつなぎのような働きをもつように思われます。
ただ、この番号は他でつかったもの(たとえばmaterial)と重なってはいけないようです。
ただ、この番号は他でつかったもの(たとえばmaterial)と重なってはいけないようです。
ここではUBP_LIGHTとしていますが、面倒なのでlightBlockIndexを与えてもuniqueな番号になるので、とりあえず動きます。
glBufferDataの引数指定で、GL_DYNAMIC_DRAWとしたのは、常に更新することを見込んでのことです。
もし変更しないのであれば、GL_STATIC_DRAWにして設定する方法があると思います。
もし変更しないのであれば、GL_STATIC_DRAWにして設定する方法があると思います。
マテリアルのセットアップ(OpenGL側)
GLuint materialUboId[1]; // material用
GLuint materialBlockIndex;
GLint materialBlockSize;
//材質
struct MaterialParam {
GLfloat emission[4];
GLfloat ambient[4];
GLfloat diffuse[4];
GLfloat specular[4];
GLfloat shininess;
};
void setup()
{
// ・・・
enum { UBP_MATERIAL=1 }; // UBO binding point 1
materialBlockIndex = glGetUniformBlockIndex(programObject, "MaterialParam");
glGetActiveUniformBlockiv(
programObject, materialBlockIndex,
GL_UNIFORM_BLOCK_DATA_SIZE, &materialBlockSize);
// UBOを使用
glGenBuffers(1, materialUboId);
glBindBuffer(GL_UNIFORM_BUFFER, materialUboId[0]);
glBufferData(GL_UNIFORM_BUFFER, materialBlockSize, NULL, GL_DYNAMIC_DRAW);
glBindBufferBase(GL_UNIFORM_BUFFER, UBP_MATERIAL, materialUboId[0]);
glUniformBlockBinding(programObject, materialBlockIndex, UBP_MATERIAL);
}
ほぼライトのセットアップと同じです。
ただし、注意としてCPU側からGPU側へのデータ転送は16バイト単位が基本のようです(?自信無し)
MaterialParamはshininessのみ4バイトなので合計で68バイトのはずですが、実際の転送サイズは合計で80バイトになります。
MaterialParamはshininessのみ4バイトなので合計で68バイトのはずですが、実際の転送サイズは合計で80バイトになります。
レンダリング(OpenGL側)
void render()
{
// ・・・
// light
glBindBuffer(GL_UNIFORM_BUFFER, lightUboId[0]);
glBufferData(GL_UNIFORM_BUFFER, lightBlockSize, &lightParam, GL_DYNAMIC_DRAW);
// material
glBindBuffer(GL_UNIFORM_BUFFER, materialUboId[0]);
glBufferData(GL_UNIFORM_BUFFER, materialBlockSize, material, GL_DYNAMIC_DRAW);
// draw();
ここで見るかぎり、VBOやFBOとそんなに扱いは変わりません。
シェーダ記述
- simple.vert
#version 150
//layout(std140)
uniform LightParam {
vec4 ambient;
vec4 diffuse;
vec4 specular;
vec4 position;
} light;
//layout(std140)
uniform MaterialParam {
vec4 emission;
vec4 ambient;
vec4 diffuse;
vec4 specular;
float shininess;
} material;
void main()
{
// ・・・
}
layoutは構造体データのデータ並びについて指定するもののようです。
マトリクスデータでは必要そうですが、vecデータではあまり指定しても意味がなさそうです。
マトリクスデータでは必要そうですが、vecデータではあまり指定しても意味がなさそうです。