MeshRendererの場合は、おっしゃるように「スケールが変化しても、オブジェクト空間の値は変わらないはずなので、色は変わらない」という認識で問題ないはずですが、なにぶんSkinnedMeshRendererですので、シェーダーに与えられる頂点データ(v.vertex
、v.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 15:18