質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.50%
Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

1回答

3688閲覧

SurfaceShaderにおけるSkinnedMeshRendererとオブジェクト空間座標の関係について

Kuru_teo

総合スコア11

Unity3D

Unity3Dは、ゲームや対話式の3Dアプリケーション、トレーニングシュミレーション、そして医学的・建築学的な技術を可視化する、商業用の開発プラットフォームです。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

1クリップ

投稿2018/11/26 03:57

前提・実現したいこと

SkinnedMeshモデルにおける頂点毎のオブジェクト空間の座標を取得したい。

発生している問題・エラーメッセージ

オブジェクト空間の頂点座標を用いて何らかの処理を行っていた際、SkinnedMeshを使用したモデルはスケールを変更するとv.vertexの数値が変化することに気が付きました。スケールが変化するとオブジェクト空間のローカル座標は変化するのでしょうか?

最初はバッチング処理が関わっていると考えました。しかし、バッチング処理はSkinnedmeshには関係ありませんでした。
次に、MVP行列変換について考えました。しかし、SurfaceShaderにおける変換処理がどのように行われているかわからず、現在も試行中です。
もし、こちらの挙動がわかる方がいらっしゃれば、教えていただけると幸いです。

該当のソースコード

こちらのシェーダーのマテリアルがアタッチされたSkinnedMeshのモデルは、スケールを変化させると色が変化します。

私はスケールが変化しても、オブジェクト空間の値は変わらないはずなので、色は変わらないと考えていました。

Shader "Custom" { Properties { } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM #pragma surface surf Lambert vertex:vert struct Input { float3 objPos; }; void vert (inout appdata_full v, out Input o) { UNITY_INITIALIZE_OUTPUT(Input,o); o.objPos = v.vertex; } void surf (Input IN, inout SurfaceOutput o) { o.Albedo = IN.objPos; o.Alpha = 1; } ENDCG } }

試したこと

スケールがすべて1の時(マテリアルが適用されているのは下半身部分)
イメージ説明
スケールすべて7
イメージ説明

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答1

0

ベストアンサー

MeshRendererの場合は、おっしゃるように「スケールが変化しても、オブジェクト空間の値は変わらないはずなので、色は変わらない」という認識で問題ないはずですが、なにぶんSkinnedMeshRendererですので、シェーダーに与えられる頂点データ(v.vertexv.normal...)はすでにスキニング処理が加えられており、元々のメッシュの頂点データとは異なっているはずです。
このとき、Unityのスキニング処理の場合では、シーン上のオブジェクトの平行移動・回転・拡大縮小のうち拡大縮小だけはスキニング処理の方に組み込まれるようになっており(すみません、どこかのサイトでそんなことを読んだ覚えがあったのですが、情報ソースを見失ってしまいました)、シェーダーに与えられるモデル変換行列unity_ObjectToWorldには平行移動成分・回転成分しか含まれていなかったと思います(しかも、モデル座標がルートボーンを基準とする座標系に変換されてしまっていたはずです)。

しょうがないので、シェーダーに拡大縮小率のプロパティを追加し、それでv.vertexを逆スケーリングして...

ShaderLab

1Shader "Custom" 2{ 3 Properties 4 { 5 [PerRendererData] _RootScale ("Root Bone Lossy Scale", Vector) = (1, 1, 1, 0) // プロパティを追加 6 } 7 SubShader 8 { 9 Tags { "RenderType" = "Opaque" } 10 LOD 200 11 12 CGPROGRAM 13 14 #pragma surface surf Lambert vertex:vert 15 16 struct Input 17 { 18 float3 objPos; 19 }; 20 21 float4 _RootScale; // 変数を追加 22 23 void vert(inout appdata_full v, out Input o) 24 { 25 UNITY_INITIALIZE_OUTPUT(Input, o); 26 o.objPos = v.vertex / _RootScale.xyz; // スケールを打ち消す補正を加える 27 } 28 29 void surf(Input IN, inout SurfaceOutput o) 30 { 31 o.Albedo = IN.objPos; 32 o.Alpha = 1; 33 } 34 35 ENDCG 36 } 37}

オブジェクトにはシェーダーにルートボーンのlossyScaleを渡すようなスクリプトを付けてやるのでどうでしょうか?

C#

1using System.Collections.Generic; 2using System.Linq; 3using UnityEngine; 4 5[ExecuteInEditMode] 6public class ScaleTransferer : MonoBehaviour 7{ 8 private Dictionary<SkinnedMeshRenderer, Transform> rootBones; 9 private MaterialPropertyBlock props; 10 private static readonly int RootScale = Shader.PropertyToID("_RootScale"); 11 12 private void OnEnable() 13 { 14 if (this.rootBones != null) 15 { 16 return; 17 } 18 19 // 各SkinnedMeshRendererと対応するルートボーンを見つけておく 20 // SkinnedMeshRendererがルートボーンを持たない場合、メッシュのスキニングは 21 // SkinnedMeshRendererがアタッチされているオブジェクトの座標系が基準となる 22 this.rootBones = this.GetComponentsInChildren<SkinnedMeshRenderer>() 23 .ToDictionary( 24 r => r, 25 r => r.rootBone ?? r.transform); 26 this.props = new MaterialPropertyBlock(); 27 } 28 29 private void Update() 30 { 31 // プロパティをセット 32 // 実際にはわざわざUpdateで毎フレームセットしなくても、もしスケールが 33 // 変化するタイミングが分かっているなら、その際に行えばいいでしょう 34 foreach (var pair in this.rootBones) 35 { 36 var skinnedMeshRenderer = pair.Key; 37 var rootBone = pair.Value; 38 this.props.SetVector(RootScale, rootBone.lossyScale); 39 skinnedMeshRenderer.SetPropertyBlock(this.props); 40 } 41 } 42}

手前の二人はスケール(1, 1, 1)、奥の二人は(2, 2, 2)で、左の二人はスケール補正なし、右の二人はスケール補正ありです。
左奥のユニティちゃんはスケールが2倍なので、頂点座標も2倍となるため色が鮮やかめになっていますが、右奥はスケール補正が効いているので、右手前と同じ色合いになっています。

プレビュー

ご質問者さんが問題としているのがスケールだけでしたら、おそらくこれで拡大縮小に影響されない色が着くかと思います。
ですが、ルートボーン座標系がメッシュの座標系と異なる場合は、生のメッシュから想像される着色とは異なった着色になるでしょうし、手足を動かせばモデル座標が変わるので手足の色がころころ変わることもあるでしょう。
そうではなく、もしモデルの初期姿勢...バインドポーズを基準にした着色をしたい場合は、メッシュの追加の頂点属性としてバインドポーズ時の頂点座標を埋め込んでおく...といった手を使う必要がありそうです。

投稿2018/11/26 13:40

Bongo

総合スコア10807

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Kuru_teo

2018/11/26 15:18

丁寧な説明本当にありがとうございます。こちらの方法でローカル座標を使用するとともに、やはりMVP行列あたりをもうちょっと勉強しなおそうと思います。 写真付きで非常にわかりやすい画像本当にありがとうございました!!!
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.50%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問