bambooflow Note

Lighting

最終更新:

bambooflow

- view
メンバー限定 登録/ログイン

目次


ライティングについて

ここでは、ベーシックなライティングについてメモします。

  • 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を使った方が効率的かと。


UBP_LIGHTは勝手にenumを作って使っていますが、ようはuboIdとlightBlockIndexを関連付けるためのつなぎのような働きをもつように思われます。
ただ、この番号は他でつかったもの(たとえばmaterial)と重なってはいけないようです。

ここではUBP_LIGHTとしていますが、面倒なのでlightBlockIndexを与えてもuniqueな番号になるので、とりあえず動きます。

glBufferDataの引数指定で、GL_DYNAMIC_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バイトになります。


レンダリング(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データではあまり指定しても意味がなさそうです。

タグ:

OpenGL GLSL
記事メニュー
ウィキ募集バナー