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

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

ただいまの
回答率

88.35%

Unityでオブジェクトの一部のみの色を動的に変える

解決済

回答 1

投稿 編集

  • 評価
  • クリップ 7
  • VIEW 12K+

GeeChiki

score 12

オブジェクトの一部のみをスクリプトから色を変える方法ありますでしょうか。
例えば、

void changeObjectColor(){
     gameobject.GetComponent<Renderer>().material.color = new Color(0.3f, 0.4f, 0.9f, 0.3f);
}

が実行されれば、このスクリプトがアタッチされているオブジェクトが指定の色になるかと思います。

しかし、例えば、球体が水没するシーンがあったとして、水中にある部分と、空中にある部分が同時に存在する時、同じオブジェクトでも色が違う部分が生まれます。
この場合、どのようにすれば、オブジェクトの一部のみの色を動的に変えることができますでしょうか。

調査してみたところ、知恵袋でヒットしましたが、
detail.chiebukuro.yahoo.co.jp/qa/question_detail/q14173810644
「ポリゴン単位で良ければ、頂点カラーをscriptで制御」「シェーダーにて色が変化する部分を示すマップテクスチャを用意し、その部分にだけ、色を線形補間」という部分がどうすれば良いかわかりません。

このあたりを勉強すればいい、という方向性だけでも頂けると非常に助かります。
何卒よろしくお願いいたします。

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

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

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

    クリップを取り消します

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

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

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

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

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

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

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

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

    質問の評価を下げる

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

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

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

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

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

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

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

    詳細な説明はこちら

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

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

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

回答 1

checkベストアンサー

+10

知恵袋の回答者の方は最初に「1部だけ違う色のテクスチャを用意しましょう」とおっしゃっていますが、その方法や「頂点カラーをscriptで制御しましょう」、そしておそらく「色が変化する部分を示すマップテクスチャを用意し」といった案も、オブジェクトのモデル座標を基準に部分的に色を変えることを想定したもののように感じます。

今回ご質問者さんは、水面に浮かぶ物体のようなワールド座標を基準とした色分けを想定されているご様子です。それを知恵袋のご回答者さんの方針で進めた場合、いくらか複雑になりそうな気配がしました。

ちょっと違うアプローチですが、メニューの「Assets」→「Create」→「Shader」→「Standard Surface Shader」で新規サーフェスシェーダーを作り、そのデフォルトのコードを一部書き換えてやれば、比較的簡単にワールド座標基準の2色塗りができそうです。
方針としては...

  • 第2の色のプロパティを追加する。
  • 2つの色を分ける高さ(ご質問者さんの想定における水面の位置)を表すプロパティを追加する。
  • サーフェスシェーダーに供給される入力情報として、ピクセルのワールド座標を追加する。
  • デフォルトのコードでは、テクスチャから取得した色に一つの塗り色を掛けて最終的なアルベド色として返しているが、この掛ける塗り色をピクセルのワールド座標に応じて二つの色から選ぶようにする。

あとはこのシェーダーを使ったマテリアルを作り、スクリプトから色を変えたい場合はそれぞれの色をSetColorなどで設定してやればいいかと思います。

デフォルトのサーフェスシェーダーに、たとえば下記のように手を加えて...

Shader "Custom/MultiColored" {
    Properties {
        _Color ("Color", Color) = (1,1,1,1)
        _SecondaryColor ("Secondary Color", Color) = (1,1,1,1) // 追加...第2の色
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
        _SeparationHeight ("Separation Height", Float) = 0.0 // 追加...ワールドYがこれより上なら_Color、下なら_SecondaryColorで塗ることにする
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        LOD 200

        CGPROGRAM
        // Physically based Standard lighting model, and enable shadows on all light types
        #pragma surface surf Standard fullforwardshadows

        // Use shader model 3.0 target, to get nicer looking lighting
        #pragma target 3.0

        sampler2D _MainTex;

        struct Input {
            float2 uv_MainTex;
            float3 worldPos; // 追加...注目点のワールド空間内の座標が自動的に入れられる
            // その他追加できる変数の一覧は https://docs.unity3d.com/Manual/SL-SurfaceShaders.html の下の方にありました
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;
        fixed4 _SecondaryColor; // 追加...追加したプロパティの値を受け取る変数
        float _SeparationHeight; // 追加...追加したプロパティの値を受け取る変数

        // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
        // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
        // #pragma instancing_options assumeuniformscaling
        UNITY_INSTANCING_BUFFER_START(Props)
            // put more per-instance properties here
        UNITY_INSTANCING_BUFFER_END(Props)

        void surf (Input IN, inout SurfaceOutputStandard o) {
            // Albedo comes from a texture tinted by color
            // 変更...surf内でworldPosを受け取れるようになったので、追加したプロパティ_SeparationHeightとY座標を比較し、_Colorと_SecondaryColorを切り替えてテクスチャ色に掛ける
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * lerp(_SecondaryColor, _Color, step(_SeparationHeight, IN.worldPos.y));
            o.Albedo = c.rgb;
            // Metallic and smoothness come from slider variables
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}


スクリプトからの色の設定は下記のようにしてみました。

using UnityEngine;

[RequireComponent(typeof(Renderer))]
public class GlobeController : MonoBehaviour {

    public float HueSpeed = 0.25f;
    public float MotionSpeed = 0.25f;
    public float MotionAmplitude = 0.25f;

    private Material material;

    private void Start () {
        this.material = this.GetComponent<Renderer>().material;
    }

    private void Update () {
        // 時間とともに色合いを変える
        var hue1 = Mathf.Repeat(Time.time * this.HueSpeed, 1.0f);
        var color1 = Color.HSVToRGB(hue1, 0.5f, 1.0f);
        var hue2 = Mathf.Repeat(hue1 + 0.5f, 1.0f);
        var color2 = Color.HSVToRGB(hue2, 0.5f, 1.0f);

        // Set系メソッドでマテリアルに情報を渡す
        material.SetColor("_Color", color1);
        material.SetColor("_SecondaryColor", color2);

        // 時間とともにY位置を上下させる
        var angle = Mathf.Repeat(Time.time * this.MotionSpeed, 1.0f) * 2.0f * Mathf.PI;
        var y = Mathf.Sin(angle) * this.MotionAmplitude;

        transform.position = new Vector3(0.0f, y, 0.0f);
    }
}


すると、下図のようにワールド座標における特定の高さの上下で塗り分けられます。
プレビュー
サーフェスシェーダーについては本家マニュアルの他にも色々な方が解説記事を書いておられるようです。
たとえばサーフェイスシェーダー、頂点・フラグメントシェーダー入門 - Qiitaなどはイメージを掴むのによさそうに思いましたが、いかがでしょうか。

※補足を追記
うっかり第2の色プロパティに_SecondaryColorなんて名前を付けてしまいましたが、専門用語のSecondary color二次色)とかぶってしまいました。
_Color_SecondaryColor」より「_Color1_Color2」とか「_UpperColor_LowerColor」の方がよかったかもしれません。

投稿

編集

  • 回答の評価を上げる

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

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

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

  • 回答の評価を下げる

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

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

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

  • 2018/01/29 23:31

    素晴らしいです!!ここまで丁寧に解説して頂けるとは・・! シェーダーを触って見たことがなかったので、こちらを参考にやってみます!参考URLとGif(やりたいことそのものでした)も頂きありがとうございました。

    キャンセル

  • 2018/01/30 20:02

    シェーダーを触り始めて30分くらいで出来てしまいました・・!しかもおぼろけながら内容も分かった気に。前に公式からやってみようとした時は挫折してしまいましたが、こちらは導入としては最高ですね。本当にありがとうございます!

    キャンセル

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

  • ただいまの回答率 88.35%
  • 質問をまとめることで、思考を整理して素早く解決
  • テンプレート機能で、簡単に質問をまとめられる

関連した質問

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