FreeStyleWiki

LWJGL

[OpenGL]

  • LWJGLを使ってPMDファイルの読み込みを試す

PMDファイルのデータ型

OpenGLESによる描画

  シェーダー

  • シェーダーとは?
    • Wikipedia - シェーダー ... シェーダー(英: shader)とは、3次元コンピュータグラフィックスにおいて、シェーディング(陰影処理)を行うコンピュータプログラムのこと。
    • シェーダは、描画処理の前半を担う 頂点シェーダ と後半部分の フラグメントシェーダ に分けられる
  • 頂点シェーダーでは、頂点を1つ受け取ってフラグメントシェーダに何か渡す処理を書く
  • フラグメントシェーダでは、色情報を出力する

  頂点リストと面頂点リスト

  • MMDのVertex部分は頂点データ(x,y,z)を格納するために用いられるDirectXのVertex Bufferに相等する
    • モデルがもつすべての頂点を保持する
  • MMDのFaceVertexはDirectXのIndex Bufferに相等する。MikuMikuDanceのモデルではMeshデータはすべて三角形で定義されており、各三角形(面)ごとの頂点番号を3個一組で順に記録している
    • 頂点リストの要素を3つずつ指示する、3点を結んで描画し3Dグラフィックにする
  • 具体的なOpenGLでの描画方法
    • 大まかに言うと、Vertex Array Object, Vertex Buffer Object, Vertex Buffer Object Indexの3つを用意する
      • Vertex Array Objectが複数のVertex Buffer Object(VBO)を持つことができ、VBOにすべての頂点データ(x,y,z)を格納する
      • Vertex Buffer Object Indexはすべての頂点データ(x,y,z)のindexを複数指定(3つ)して描画する面をOpenGLに指示する

  材質リスト

  • 色要素を設定したVBOを作ってBindする
    • いろいろ調べたのだが、シェーダーを使って面頂点に対して直接色塗りをすることはできないようだ
    • なので、受け渡し方は VBO -> vertexシェーダー(頂点) -> fragmentシェーダー(面頂点) になる、めんどくせー
  • 上記の関係から、色塗りは頂点ベースで行うことになる、頂点データ(x,y,z)の数だけ材質リストを用意する
    • しかしPMDファイルは容量削減のため材質リストには適用される面頂点の枚数しか記録されていない
  • よって
    • 材質リストの面頂点数だけ材質のリストを作成する
    • 面頂点と材質の関連づけをindexをもとに作成する
    • 頂点リストの数だけ材質のリストを作成する(どの材質を使うかは、上記の関連付けを参照する)

  テクスチャ

テクスチャ単体の実装

  • テクスチャの描画は頂点リストに入っているUV座標:u, vを使用する
  • テクスチャの貼り付け元となる画像ファイルは材質リストの中にあるので、他の材質の属性と同様の方法で読み取る

ただ、テクスチャは常に頂点の色に適用されるわけではないのでシェーダーの中で条件分岐させてテクスチャを使う・使わないを判定しなければいけない

	if((UV[0]!=0 && UV[1]!=0))
	{
           // UV座標:u, vのどちらも0でなければテクスチャ使用
        }
        else
        {
           // そうでない場合は普通に材質リストの属性で設定されている色を計算
        }

実際に、MikuMikuDanceのモデルデータ(PMD)構造について(その1)で配布されている頂点リストと材質リストを確認すると、材質リストの中でテクスチャファイルを設定されている面頂点に属する頂点リストの属性値はちゃんとUV座標が設定されている一方で、テクスチャが設定されていない頂点リストの属性値はUV座標:u, vのどちらかが0になっている。

複数テクスチャの実装

Array textures are a great way of managing collections of textures of the same size and format. They allow for using a set of textures without having to bind between them.

  • 上記のようにあるので、テクスチャは同じ大きさとフォーマットでなければならない。

例としてメタル版ミクのテクスチャ情報

初音ミクmetal.pmd width height
metal.sph 256 256
mikuhair.sph 256 256
eyeM2.bmp 106 106

これは大きさを合わせないとだめなのだろうか

  • シェーダー
#version 450 core

layout (location = 0) out vec4 color;
layout (location = 0) in vec2 tex0;

uniform sampler2DArray texarray; // <-- sampler2D が Arrayになっている
uniform uint diffuse_layer;

// レイヤー情報をfloatに変換
// https://www.khronos.org/opengl/wiki/Array_Texture を参考に
float layer2coord(uint capacity, uint layer)
{
        // 疑似コード
        // d​ はテクスチャ配列の数
        // layer はテクスチャ座標からの浮動小数点レイヤー
        //
        // actual_layer    = max(0, min(d​ - 1, floor(layer​ + 0.5)) )

	return max(0, min(float(capacity - 1), floor(float(layer) + 0.5)));
}

void main()
{
	color = texture(texarray, vec3(tex0, layer2coord(3, diffuse_layer)));
}

  • sampler2DArrayはglUniform1fvで定義すればよさそう → と思ったが、各テクスチャを別の設定で動かしたいと現在は考えてないので不使用
  • 画像ロードはglTextureStorage3D, glTextureSubImage3DとSTBによる画像読み込みだけでできてしまった

  法線ベクトル

  カメラ

  • model, view, projectionを設定しシェーダー内で使用する
  • モデル行列
    • 描画対象のモデルの座標からOpenGLのワールド座標への相対値
  • ビュー行列
    • OpenGLのワールド座標からカメラの座標への相対値 (思ってたのと逆だ…)
  • 射影行列
    • カメラの座標から映し出される(射影)ものへの相対値
    • 正直なぜこのような変換ができるのかよく分からないのだが、数学者じゃないのでよしとする

上記の行列はシェーダー内でグローバルGLSL変数として渡される

  • 頂点シェーダーでは頂点とModelViewProjection行列の積を使って、モデルが動いた後の位置を決めている

OpenGLのカメラについて

透視投影
3次元の物体を見たとおりに2次元平面に描画する、~Perspective, ~Frustum
平行投影
視点が無限遠方にあると仮定して物体と視点を結ぶ投影方法、~Ortho

この辺りは別ページに書こうと思う OpenGL関連

GLFWによるGUI制御

  • LWJGLではGLFWがGUI制御を行う

ウィンドウサイズを変更時

  • どうやらGLFWのコールバックは後勝ち設定のようなので1つのクラスにしか適用できない、コールバックを複数クラスに適用したい場合はコールバック関数を自分で複数クラスに適用するコードを書かなければいけないようだ