見出し画像

ネコにも分かるShader入門 #2 SurfaceShader-プロパティ

  今回はこのプロジェクトから続けてシェーダーの仕組みを説明します。

  プロジェクトのShadersフォルダーにはSecondShaderというシェーダーを用意してあります。シェーダーの仕組みを理解しやすいために、最低限のコードを実装しております。​

画像2

 シェーダーの構成は主にA、B、C三つのエリアです。
 エリアA:プロパティ
 エリアB:SubShaderロジック
 エリアC:SubShaderサポートできない場合適用させるシェーダー
 これから順番に説明します。

ーーーーーエリアAーーーーー

プロパティのフォーマット:

 _Name("Display Name", type) = defaultValue[{options}]

SecondShaderの5行目の_Color ("Color", Color) = (1,1,1,1)というと、このようです。

画像2

A−1. _Name

  _NameはSubShaderと通信用のもの、一般的にはアンダーバースタート+英数になります。SubShaderでこのプロパティと同じ名前のパラメーターを登録すると、プロパティのデータを利用することができます。

画像3

A−2. "Display Name"

マテリアルパネルに表示用の文字です。"Color"を"First Color"に変更してみてください。

画像4

A−3. Type

利用可能なタイプ主に7種類:
(1)Float:実数値
(2)Vector:4つのFloat値、使う時にx,y,z,wで順番に取得
(3)Range(min, max) : min〜maxのスライダーを表示します。主に範囲限定の時に使うタイプです。
(4)Color :RGBA(Red/Green/Blue/Alpha)で色を定義します。 範囲は0~1です。
(5)2D :2のベキ乗テクスチャを渡す時に使うタイプです。
(6)Rect :自由サイズテクスチャを渡す時に使うタイプです。
(7)Cube :Cubeテクスチャを渡す時に使うタイプです。Cubeテクスチャは関連性がある6枚の2Dテクスチャで合わせるもの、主にSkyや反射などで利用します。

A-4. defaultValue

タイプによってデフォルト値とoptionが異なります。

defaultValueは4種類:
(1)Float,Range :実数値を指定します。
例:1.2
(2)Vector : XYZWの順番に4つのFloat値を指定します
例:(1.0, 2.0, 1.5, 3.0)
(3)Color : RGBAの順番に0~1範囲内4つのFloat値を指定します
例:(0.5, 0.2, 0.6, 1.0)
(4)2D/Rect/Cube :システム認識できるTintストリングを指定します
例:white

A-5. {option} 

 {option}はTexGen(Texture coordinate generation)の為に提供して、テクスチャ(2D/Rect/Cube)しか使えないパラメーターです。利用可能なバリューはObjectLinear, EyeLinear, SphereMap, CubeReflect, CubeNormal5種類です。上記のバリューを{}に設定したら、オブジェクトの元UVは無効になります。普段は {option} あまり使わないものです。

OpenGLのtexgen modesで{option}と同じ効果を実現することができます。


プロパティの内容はここまで終わり、次は練習です。エリアBのSubShaderもちょっと触ります。

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

練習:スライダーでマテリアルの色を調節します。

1. まず_Color関連のソース全部コメントアウトします。

画像5

Shader "Dreamo/SecondShader"
{
   Properties
   {
       //_Color ("First Color", Color) = (1,1,1,1) 
   }
   SubShader
   {
       Tags { "RenderType"="Opaque"}
       LOD 200
       CGPROGRAM
       #pragma  surface surf Standard
       
       //fixed4 _Color;
       
       struct Input
       {
           float2 uv_MainTex;
       };
       void surf (Input IN, inout SurfaceOutputStandard o)
       {
           //o.Albedo = _Color.rgb;
           //o.Alpha = _Color.a;
       }
       ENDCG
   }
   FallBack "Diffuse"
}

2. スライダーを追加します。
 色はRGBだから、スライダー三つを追加します。
 デフォルトは全部0.5にすると、グレーになります。

スクリーンショット 2020-10-29 23.04.29

       _MyR ("Red",Range(0,1)) = 0.5
       _MyG ("Green",Range(0,1)) = 0.5
       _MyB ("Blue",Range(0,1)) = 0.5

ソースを保存して、Unityへ見に行きます。

スクリーンショット 2020-10-29 23.05.54

三つのスライダーがうまくできたが、マテリアルは真黒になりました。原因はまだパラメーターの値をシェーダーへ渡してないです。

3. パラメーターの値をSubShaderへ渡します

仕組みを忘れた場合は上の「A−1. _Name」に戻ってください

エリアBのSubShaderに適当な場所で、パラメーターのNameと同じの変数を定義したら、SubShader内でパラメーターの値を取得することができます。

スクリーンショット 2020-10-29 23.23.43

       float _MyR;
       float _MyG;
       float _MyB;

4. _MyR/_MyG/_MyBを使って、RGB色を作ります

スクリーンショット 2020-10-29 23.37.35

           float3 myRGB = float3(_MyR,_MyG,_MyB);
           o.Albedo = myRGB;

Unityへ見に行って、マテリアルはグレーになったんでしょう。

スクリーンショット 2020-10-29 23.32.40

スライダーをドラッグしてみて、色が変わりますか?

スクリーンショット 2020-10-29 23.38.57

パラメーターの練習はここまで終わり、#3のSubShaderを説明した後で複雑なシェーダーをちゃんと作りましょう。

完成したプロジェクトはここです。


ーーーーーーーーーーーーーーー特別授業ーーーーーーーーーーーーーーーーー

プログラムでシェーダーをコントロール

ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー

目的:Planeの位置座標(XYZ)でシェーダーの色(RGB)をコントロールします。つまり、Planeを移動したら、色も変わります。

こんな感じです。

スクリプトMaterialCtrlはオブジェクトのマテリアルをアクセスして、マテリアルのパラメーターへ値を渡すことが可能です。

スクリーンショット 2020-10-30 22.43.14

MaterialCtrlのところでダブルクリックしてスクリプトをオープンします。

スクリーンショット 2020-10-30 22.54.37

最初は何にもないです。

スクリーンショット 2020-10-30 23.00.22

マテリアルをコントロールする為に、まずオブジェクトのマテリアル取得することが必要です。毎回毎回取得すると、効率はよくないですから、Start関数実行する時取得しましょう。

void Start()
{
   MeshRenderer  myMeshRender = gameObject.GetComponent<MeshRenderer>();
   Material myMaterial = myMeshRender.material;
}

マテリアルmyMaterial成功に取得しました。でも、こうすると、毎回実行するUpdate()関数はmyMaterial変数をアクセスすることができないでしょうか。そして、myMaterialをグローブ変数にしましょう。

スクリーンショット 2020-10-30 23.14.29

次はUpdate()の中にオブジェクトの位置座標を取得します。

void Update()
{
       Vector3 myPosition = transform.position;
}

理解しやすい為に、まずXYZ座標をRGBのように抽出します。

    void Update()
   {
       Vector3 myPosition = transform.position;
       float red   = myPosition.x;
       float green = myPosition.y;
       float blue  = myPosition.z;
   }

色の範囲は(0〜1)ですから、もう少し工夫して処理します。

マイナス値はダメですから、Mathf.Abs()関数で絶対値を取得します。

    void Update()
   {
       Vector3 myPosition = transform.position;
       float red   = Mathf.Abs(myPosition.x);
       float green = Mathf.Abs(myPosition.y);
       float blue  = Mathf.Abs(myPosition.z);
   }

1.0を超えたら意味がないです。Mathf.Clamp()関数で値の範囲を限定します。

    void Update()
   {
       Vector3 myPosition = transform.position;
       float red   = Mathf.Clamp(Mathf.Abs(myPosition.x), 0.0f, 1.0f);
       float green = Mathf.Clamp(Mathf.Abs(myPosition.y), 0.0f, 1.0f);
       float blue  = Mathf.Clamp(Mathf.Abs(myPosition.z), 0.0f, 1.0f);
   }

OK、RGBデータ全部用意しました。これからRGBの値を渡します。

スクリプトからシェーダーまで値を渡す方法:

<マテリアル変数名>.Set<シェーダーのパラメーターのタイプ>("<シェーダーのパラメーター名>",<値>)​

この例によると,
 マテリアル変数名 → myMaterial
 シェーダーのパラメーターのタイプ → float
 シェーダーのパラメーター名 → _MyR、_MyG、_MyB
 値 → red、green、blue

そして、コードはこのようになります:

myMaterial.SetFloat("_MyR", red);
myMaterial.SetFloat("_MyG", green);
myMaterial.SetFloat("_MyB", blue);

全てのソースコート:

using UnityEngine;
public class MaterialCtrl : MonoBehaviour
{
   
   private Material myMaterial;
   // Start is called before the first frame update
   void Start()
   {
       MeshRenderer myMeshRender = gameObject.GetComponent<MeshRenderer>();
       myMaterial = myMeshRender.material;
   }
   // Update is called once per frame
   void Update()
   {
       Vector3 myPosition = transform.position;
       float red   = Mathf.Clamp(Mathf.Abs(myPosition.x), 0.0f, 1.0f);
       float green = Mathf.Clamp(Mathf.Abs(myPosition.y), 0.0f, 1.0f);
       float blue  = Mathf.Clamp(Mathf.Abs(myPosition.z), 0.0f, 1.0f);
       myMaterial.SetFloat("_MyR", red);
       myMaterial.SetFloat("_MyG", green);
       myMaterial.SetFloat("_MyB", blue);
   }
}

Unityへ効果を見に行きましょう。

ゲームを実行してから、オブジェクトをドラッグしてみます。

スクリーンショット 2020-10-30 23.52.52

完成したプロジェクターはここです。

次回はエリアBのSubShaderの部分を紹介します。


この記事が気に入ったらサポートをしてみませんか?