HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2023
0405
youdaiHGIMG4でTension Map(圧力マップ)の仕組みをシェーダーで実装したいです5未解決


youdai

リンク

2023/4/5(Wed) 14:26:16|NO.99254

HGIMG4でshteeve氏のBlender AddonのTension Map 1.5(圧力マップ)の仕組みをシェーダーで実装したいです。

Tension Map (オリジナル) - Pyroevil ( Jean-Francois Gallant ) (サイトにはアクセスできないかも?)
https://pyroevil.com/

Tension Map (改良版) - ScottishCyclops
https://github.com/ScottishCyclops/tensionmap

Tension Map 1.5 (改良版の改良版) - shteeve
https://blenderartists.org/t/revised-mesh-tension-add-on/1239091

Tension Map 1.5 への直リンク( Blender 2.8x Addon )
https://blenderartists.org/uploads/short-url/7MFhPW5yuZPNVgZkenyZDDaiJ0M.py

Tension Mapは上記以外にもバリエーションがあるのですが、私はshteeve氏のTension Map 1.5を使用しています。
対応するBlenderのバージョンは2.8x系です。
今回、実装したいのはこのTension Map 1.5版の仕組みです。

Tension Mapの仕組みを大雑把に説明すると、これはアーマチュアアニメーション等によってモデルが変形した時に発生する圧力の増減を測定するAddonです。
これはTension Map(圧力マップ)と呼ばれています。

デモンストレーション動画
https://youtu.be/QfbXeP8LgEg

技術的解説動画
https://youtu.be/6G8SkBQngvw

Tension Mapの仕組み

・アーマチュアアニメーションの変形によって生ずる圧力を測定する
・圧力が増した値はVertexColorのRに出力される
・圧力が減った値はVertexColorのGに出力される
・注意点としては、レンダーのインターフェースのロックをしないとAddonをオンにできません
(原文:-I discovered that to get around crashes, there is apparently a magical ‘Lock Interface’ toggle in the Render menu. So for safety the add-on interface greys itself out and presents a prompt until this is enabled.)

これをGLSLのシェーダーで実装したいと思っているのですが、何から手をつけていいか見当がつきません。
理論上、できるかどうかも分かりません。
ですが、これが実装できるとモデルの皺をリアルタイムで表現できるようになります。表現の幅が非常に広がると思います。

要はこれは、流体力学の圧力の仕組みだと思うのですが、シェーダーでそれをどうやって表現したらいいのか、よく分かりません。

要は、アーマチュア変形前のモデルの状態と、アーマチュア変形後のモデルの状態を比較して、それを頂点単位で圧力の増減の測定をする、ということなのだと思います。
Blenderでは出力用の汎用の変数がマテリアルノード上で使えないため、VertexColorのRGBのRGに値を格納していますが、HGIMG4のシェーダー上ではvec2でいいと思います。

全く正しい形の圧力マップでなくても構いません。近似系でも、結果的に似たようなことができる、でも構いません。
ご教授お願い致します。



この記事に返信する


youdai

リンク

2023/4/5(Wed) 14:51:45|NO.99255

Addonではなくて、GeometryNodeでTensionMapを実装しているサンプルも見つけました。

技術解説動画
https://youtu.be/kjmAXqpP5tc

Github - Blender 3x系
https://github.com/cgvirus/blender-geometry-nodes-collection

こちらはまだ今発見したばかりで、内容を検証していませんが、技術解説動画を見た限りではAddon版のTensionMapと似たような効果をしていました。



kinokawa

リンク

2023/4/6(Thu) 01:23:30|NO.99258

>これをGLSLのシェーダーで実装したいと思っているのですが、
>何から手をつけていいか見当がつきません。

私はGLSLのシェーダーのことはよくわからないのですが
GPT先生に聞いてみるとコードは書いてくれるので
動くかどうかはわかりませんが何かのきっかけにはなるかもしれません。

以下
「Tension Mapを使った皺の表現をするシェーダーのコードを教えて」の返答

Tension Mapを使用して皺を表現するためのシェーダーコードの例を以下に示します。

```glsl
// Tension Mapの計算
vec3 calcTensionMap(float distance, float minDistance, float maxDistance) {
// 距離が最小値以下の場合、白色を返す
if (distance <= minDistance) {
return vec3(1.0, 1.0, 1.0);
}
// 距離が最大値以上の場合、黒色を返す
else if (distance >= maxDistance) {
return vec3(0.0, 0.0, 0.0);
}
// 距離が最小値と最大値の間の場合、グレースケールを返す
else {
float t = (distance - minDistance) / (maxDistance - minDistance);
return vec3(t, t, t);
}
}

// 皺の深さを計算する関数
float calcWrinkle(float tension, float threshold, float depth) {
if (tension > threshold) {
return (tension - threshold) * depth;
}
else {
return 0.0;
}
}

// 皺を表現するシェーダー
void main() {
// テクスチャ座標の取得
vec2 uv = gl_TexCoord[0].xy;

// 頂点間の距離を計算
float distance = length(gl_Vertex.xyz - gl_Vertex.yzw);

// Tension Mapの計算
vec3 tensionColor = calcTensionMap(distance, 0.0, 1.0);

// 皺の深さを計算
float wrinkleDepth = calcWrinkle(tensionColor.r, 0.5, 0.2);

// ノーマルベクトルの取得
vec3 normal = normalize(gl_NormalMatrix * gl_Normal);

// 皺の深さに基づいて頂点座標を移動
vec3 displacedPosition = gl_Vertex.xyz + normal * wrinkleDepth;

// 出力
gl_Position = gl_ModelViewProjectionMatrix * vec4(displacedPosition, 1.0);
gl_FrontColor = vec4(tensionColor, 1.0);
}
```

このシェーダーコードでは、頂点座標の距離を計算し、
`calcTensionMap`関数を使用して距離に基づく頂点カラーを計算しています。
また、`calcWrinkle`関数を使用して、Tension Mapの値に基づく皺の深さを計算しています。
皺の深さは、`wrinkleDepth`変数によって調整されます。
また、ノーマルベクトルを使用して、皺の深さに基づいて頂点座標を移動しています。

このシェーダーコードを使用するには、マテリアルにこのシェーダーを設定し、
Tension Mapのテクスチャをテクスチャユニットにバインドする必要があります。
また、テクスチャ座標を使用して頂点間の距離を計算するため、
メッシュはUVマッピングされている必要があります。

以上が、Tension Mapを使用して皺を表現するためのシェーダーコードの例です。
他にも多数の方法がありますので、必要に応じて調査してください。


私にはよくわからないです(^^;
色々聞いてみるほうがいいかも・・・



youdai

リンク

2023/4/6(Thu) 16:45:51|NO.99260

シェーダーの原型を作りました。
イメージとしては、このようなものを想定しています。
(これは単なるイメージなので、動作させても意味はありません)

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の間の距離を計算できるのでしょうか?
これはそういうものなのでしょうか? かなり不思議な感じを受けます。
もしこの性質についてご存知の方がいらっしゃれば、これについてもご教授お願いいたします。



youdai

リンク

2023/4/7(Fri) 12:05:08|NO.99265

> kinokawa さんへ

kinokawaさんのchatGPTのシェーダーを検証しました。
これはTensionMapではなく、DisplacementMapの一例ですね。
DisplacementMapは、TensionMapとセットで使用されることが多いです。
このchatGPTの生成したTensionMapの部分と、解説のコメントはデタラメでした。(^_^;)

引き続き、TensionMapについて、chatGPT以外でのアドバイスをお願いいたします。
もしTensionMapについてご存知の方、もしくはそれに類するアイデアを思いついたよ、という方がいらっしゃいましたら、ぜひアドバイスをご教授下さい。



youdai

リンク

2023/4/7(Fri) 21:33:49|NO.99268

GLSLで他の頂点座標を取得する方法を調べました。
あるには、あったのですが、ジオメトリシェーダーに対応している必要がありました。

GLSLによるジオメトリシェーダー
https://www.slis.tsukuba.ac.jp/~fujisawa.makoto.fu/cgi-bin/wiki/?GLSL%A4%CB%A4%E8%A4%EB%A5%B8%A5%AA%A5%E1%A5%C8%A5%EA%A5%B7%A5%A7%A1%BC%A5%C0

ジオメトリシェーダー内のgl_PositionInが配列になっていて、gl_PositionIn[int id]で情報にアクセスすることができるようです。
ですが、HGIMG4はジオメトリシェーダーには対応していないと思うので、「辺の頂点AとBの位置を直接参照する」という方法は今回は使えないみたいです。

なので、辺の頂点座標を直接取得しないで近似値(算出する方法があるかどうか分からないのですが)で、TensionMapを実装するしか今回はやりようがないみたいです。

現在でも取得できそうな要素としては

・変形前の頂点位置と、変形後の頂点位置
・頂点位置の変化量(距離)
・頂点位置が変化したベクトル(方向)
・アーマチュアの適用範囲
・アーマチュアのボーンの拡大、回転、移動

くらいかと思います。
これを数学的に処理して、TensionMap(圧力の増減値)を取得するしかなさそうなのですが、できるかどうか不明です。



記事削除

記事NO.パスワード
(質問が解決したスレッドは他の利用者に活用してもらうため、削除しないようお願いします)

NO.99254への返信

マスコット

好きなマスコットを選んでください。

名前

e-mail
HOME
  1. 初めて利用する方は、HSP3掲示板の使い方をお読みください。
  2. 不要部分の多い長いスクリプトの投稿は ご遠慮ください。
  3. 書き込みは自動改行されません。適度に改行を入れてください。
  4. スクリプトは小文字の<pre>〜</pre>で囲むと見やすく表示できます。

削除用パスワード

解決したら質問者本人がここをチェックしてください。

エラー発生時、再送信すると二重送信になることがあります。
回答が得られたら、お礼書き込み時に[解決]チェックしてください。
SPAM防止のためURLから始まる文章は投稿できません。
SPAM防止のため英文字のみの本文を投稿することはできません。

ONION software Copyright 1997-2023(c) All rights reserved.