実装
vec4 fetchElement(float JointIndex, int Offset, float v)
{
float texelSizeX = 1.0 / vat_ubo.texW;
vec2 st = vec2(float(JointIndex * 4 + Offset + 0.5) * texelSizeX, v);
#ifdef USE_OPENGL
vec4 val = texture(vertexAnimationTexture, st);
#else
vec4 val = texture(sampler2D(vertexAnimationTexture, vertexAnimationTextureSampler), st);
#endif
return val;
}
mat4 GetSkinMatFromVAT(uint JointIndex, int FrameIndex)
{
float f_JointIndex = float(JointIndex);
float texelSizeY = 1.0 / vat_ubo.texH;
float v = (float(FrameIndex) + 0.5) * texelSizeY;
mat4 SkinMatrix = mat4(
fetchElement(f_JointIndex, 0, v),
fetchElement(f_JointIndex, 1, v),
fetchElement(f_JointIndex, 2, v),
fetchElement(f_JointIndex, 3, v)
);
return SkinMatrix;
}
void main()
{
mat4 SkinMat =
inWeights0.x * GetSkinMatFromVAT(inJoint0.x, CurrentFrame) +
inWeights0.y * GetSkinMatFromVAT(inJoint0.y, CurrentFrame) +
inWeights0.z * GetSkinMatFromVAT(inJoint0.z, CurrentFrame) +
inWeights0.w * GetSkinMatFromVAT(inJoint0.w, CurrentFrame);
}
ポイント
// テクスチャに焼いたデータを使う時はテクセルの中心からサンプリングすることを心がける(texelSizeX * 0.5 を足す)
// これを足さない時の値はテクセルの左下、つまりテクセルとテクセルの境界線を差している
// もしこのままだと補完時にとなりのテクセルの影響を受けて、意図しない値が返ってくることがある
// この結果、ボーンの動きがブレたり、不安定になるといったことが起こる。
// VATにはGL_RGBA32F, GL_FLOATのテクスチャに対して、GL_NEAREST・CLAMP_TO_EDGEのサンプラーを適応している
// GL_NEARESTはサンプリングに最も近い値に補完して返す機能だが、テクセル境界のままだとこれで混ざってしまう
// 用語整理 /////
// - ピクセル: 画面上の最小単位(ディスプレイのドット)
// - テクセル: テクスチャ画像内の最小単位(テクスチャのピクセル)
////////////////