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

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

ただいまの
回答率

90.52%

  • Unity

    5492questions

    Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。

  • Unity3D

    1801questions

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

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

解決済

回答 1

投稿

  • 評価
  • クリップ 0
  • VIEW 241

Kuru_teo

score 3

 前提・実現したいこと

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
イメージ説明

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

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 1

checkベストアンサー

+1

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

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

Shader "Custom"
{
    Properties
    {
        [PerRendererData] _RootScale ("Root Bone Lossy Scale", Vector) = (1, 1, 1, 0) // プロパティを追加
    }
    SubShader
    {
        Tags { "RenderType" = "Opaque" }
        LOD 200

        CGPROGRAM

        #pragma surface surf Lambert vertex:vert

        struct Input
        {
            float3 objPos;
        };

        float4 _RootScale; // 変数を追加

        void vert(inout appdata_full v, out Input o)
        {
            UNITY_INITIALIZE_OUTPUT(Input, o);
            o.objPos = v.vertex / _RootScale.xyz; // スケールを打ち消す補正を加える
        }

        void surf(Input IN, inout SurfaceOutput o)
        {
            o.Albedo = IN.objPos;
            o.Alpha = 1;
        }

        ENDCG        
    }
}

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

using System.Collections.Generic;
using System.Linq;
using UnityEngine;

[ExecuteInEditMode]
public class ScaleTransferer : MonoBehaviour
{
    private Dictionary<SkinnedMeshRenderer, Transform> rootBones;
    private MaterialPropertyBlock props;
    private static readonly int RootScale = Shader.PropertyToID("_RootScale");

    private void OnEnable()
    {
        if (this.rootBones != null)
        {
            return;
        }

        // 各SkinnedMeshRendererと対応するルートボーンを見つけておく
        // SkinnedMeshRendererがルートボーンを持たない場合、メッシュのスキニングは
        // SkinnedMeshRendererがアタッチされているオブジェクトの座標系が基準となる
        this.rootBones = this.GetComponentsInChildren<SkinnedMeshRenderer>()
            .ToDictionary(
                r => r,
                r => r.rootBone ?? r.transform);
        this.props = new MaterialPropertyBlock();
    }

    private void Update()
    {
        // プロパティをセット
        // 実際にはわざわざUpdateで毎フレームセットしなくても、もしスケールが
        // 変化するタイミングが分かっているなら、その際に行えばいいでしょう
        foreach (var pair in this.rootBones)
        {
            var skinnedMeshRenderer = pair.Key;
            var rootBone = pair.Value;
            this.props.SetVector(RootScale, rootBone.lossyScale);
            skinnedMeshRenderer.SetPropertyBlock(this.props);
        }
    }
}

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

プレビュー

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

投稿

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2018/11/27 00:18

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

    写真付きで非常にわかりやすい画像本当にありがとうございました!!!

    キャンセル

同じタグがついた質問を見る

  • Unity

    5492questions

    Unityは、ユニティテクノロジーが開発したゲームエンジンです。 主にモバイルやブラウザ向けのゲーム製作に利用されていましたが、3Dの重力付きゲームが簡単に作成できることから需要が増え、現在はマルチプラットフォームに対応しています。 言語はC言語/C++で書かれていますが、C#、JavaScript、Booで書かれたコードにも対応しています。

  • Unity3D

    1801questions

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