シェーダーの原型を作りました。
イメージとしては、このようなものを想定しています。
(これは単なるイメージなので、動作させても意味はありません)
tensionmap.vert
/*
Tension Map Shader
*/
//------------------------------------------------------------------------------
// attribute
attribute vec4 a_position;
attribute vec3 a_normal;
//------------------------------------------------------------------------------
// varying
varying vec2 v_tensionMap;
//------------------------------------------------------------------------------
// skinning
// skinning.vert を抜粋
#if defined(SKINNING)
// attribute - skinning
attribute vec4 a_blendWeights;
attribute vec4 a_blendIndices;
// uniform - skinning
const int SJCP = 3;
uniform vec4 u_matrixPalette[SKINNING_JOINT_COUNT * SJCP];
vec4 _skinnedPosition;
void skinPosition(float blendWeight, int matrixIndex)
{
vec4 tmp;
tmp.x = dot(a_position, u_matrixPalette[matrixIndex]);
tmp.y = dot(a_position, u_matrixPalette[matrixIndex + 1]);
tmp.z = dot(a_position, u_matrixPalette[matrixIndex + 2]);
tmp.w = a_position.w;
_skinnedPosition += blendWeight * tmp;
}
vec4 getPosition()
{
_skinnedPosition = vec4(0.0);
float blendWeight = a_blendWeights[0];
int matrixIndex = int(a_blendIndices[0]) * SJCP;
skinPosition(blendWeight, matrixIndex);
blendWeight = a_blendWeights[1];
matrixIndex = int(a_blendIndices[1]) * SJCP;
skinPosition(blendWeight, matrixIndex);
blendWeight = a_blendWeights[2];
matrixIndex = int(a_blendIndices[2]) * SJCP;
skinPosition(blendWeight, matrixIndex);
blendWeight = a_blendWeights[3];
matrixIndex = int(a_blendIndices[3]) * SJCP;
skinPosition(blendWeight, matrixIndex);
return _skinnedPosition;
}
// skinnedNormal
vec3 _skinnedNormal;
void skinTangentSpaceVector(vec3 vector, float blendWeight, int matrixIndex)
{
vec3 tmp;
tmp.x = dot(vector, u_matrixPalette[matrixIndex].xyz);
tmp.y = dot(vector, u_matrixPalette[matrixIndex + 1].xyz);
tmp.z = dot(vector, u_matrixPalette[matrixIndex + 2].xyz);
_skinnedNormal += blendWeight * tmp;
}
vec3 getTangentSpaceVector(vec3 vector)
{
_skinnedNormal = vec3(0.0);
// Transform normal to view space using matrix palette with four matrices used to transform a vertex.
float blendWeight = a_blendWeights[0];
int matrixIndex = int (a_blendIndices[0]) * 3;
skinTangentSpaceVector(vector, blendWeight, matrixIndex);
blendWeight = a_blendWeights[1];
matrixIndex = int(a_blendIndices[1]) * 3;
skinTangentSpaceVector(vector, blendWeight, matrixIndex);
blendWeight = a_blendWeights[2];
matrixIndex = int(a_blendIndices[2]) * 3;
skinTangentSpaceVector(vector, blendWeight, matrixIndex);
blendWeight = a_blendWeights[3];
matrixIndex = int(a_blendIndices[3]) * 3;
skinTangentSpaceVector(vector, blendWeight, matrixIndex);
return _skinnedNormal;
}
vec3 getNormal()
{
return getTangentSpaceVector(a_normal);
}
#endif
//------------------------------------------------------------------------------
// tensionMap
/*
解説
tensionMapとは圧力マップのこと。
アーマチュアの変形前と変形後を比較して、その頂点に対する圧力を求めたい。
in
src_position = 元の頂点位置
dts_position = Skinning変形後の頂点位置
edge_vertices_0 = 辺の頂点位置A
edge_vertices_1 = 辺の頂点位置B
weightLimit = 出力される値の制限
(引数はとりあえず関係性のありそうなものを入れたが、これが必要なものかどうかは分からない。
引数の参考はBlenderのgeometryNodeのサンプルから、関係のありそうなものを選んだ。
もしかしたら法線やSkiningのblendWeight等も必要かも?)
result
result.x = 圧力の増えた分の値
result.y = 圧力の減った分の値
*/
vec2 tensionMap_compute( vec4 src_position, vec4 dts_position, vec4 edge_vertices_0, vec4 edge_vertices_1, float weightLimit ) {
vec2 result;
// 辺の頂点Aと辺の頂点Bの距離を測定
float dist = distance( edge_vertices_0, edge_vertices_1 );
// 要は辺の頂点Aと辺の頂点Bの距離が短くなっていれば、圧力が増して、その逆は圧力が減っているということ?
// なんらかの処理
// 値の制限
result = clamp( result, 0.0, weightLimit );
return result;
}
//------------------------------------------------------------------------------
// main
void main() {
// skinning
#if defined(SKINNING)
vec4 position = getPosition();
vec3 normal = getNormal();
#else
vec4 position = a_position;
vec3 normal = a_normal;
#endif
// normal(SKINNINGのアーマチュアアニメーション後の法線)
normal = normalize( mat3( u_inverseTransposeWorldViewMatrix ) * normal );
// 辺の頂点の位置AとB。どうやって取得するかは不明
// この辺の頂点位置をどうやって取得するかがGLSLのシェーダー上では重要かも?
vec4 edge_vertices_0;
vec4 edge_vertices_1;
v_tensionMap = tensionMap_compute( a_position, position, edge_vertices_0, edge_vertices_1, 1.0 );
gl_Position = u_worldViewProjectionMatrix * position;
}
tensionmap.frag
//------------------------------------------------------------------------------
// varying
varying vec2 v_tensionMap;
//------------------------------------------------------------------------------
// main
void main() {
// テストなので係数の値を単純に出力するだけでいい
gl_FragColor = vec4( v_tensionMap, 0.0, 1.0 );
}
edge_vertices_0 = 辺の頂点位置A
edge_vertices_1 = 辺の頂点位置B
辺の頂点位置Aというのはa_positionの位置そのものだと思うのですが、それに繋がる頂点Bの位置が辺の頂点位置Bだと思うのですが、取得の仕方が分かりません。
上記の2要素の取得方法が分かれば、もしくはその近似の取得方法が分かれば一気にシェーダーが形作れそうな気もするのですが、よく分かりません。
もしシェーダーのバージョンがGLSL version 2x系でできなければ、#version 330までなら私のGPUでも対応しているので、バージョンによる不都合があるなら、バージョンを変更して貰っても構いません。
もし、辺の頂点位置Aと辺の頂点位置BがGLSL上で取得できない場合は、geometryNodeを使用してFBXの頂点カラーへ距離の値をベイクしておくという手もあるので、そちらの方法でも試してみようと思います。
> kinokawa さん
アドバイスありがとうございます。
このシェーダーには謎の回答部分があります。
// 頂点間の距離を計算
float distance = length(gl_Vertex.xyz - gl_Vertex.yzw);
この部分なのですが、これで辺の頂点Aと辺の頂点Bの間の距離を計算できるのでしょうか?
これはそういうものなのでしょうか? かなり不思議な感じを受けます。
もしこの性質についてご存知の方がいらっしゃれば、これについてもご教授お願いいたします。