ネコにも分かるShader入門 #2 SurfaceShader-プロパティ
今回はこのプロジェクトから続けてシェーダーの仕組みを説明します。
プロジェクトのShadersフォルダーにはSecondShaderというシェーダーを用意してあります。シェーダーの仕組みを理解しやすいために、最低限のコードを実装しております。
シェーダーの構成は主にA、B、C三つのエリアです。
エリアA:プロパティ
エリアB:SubShaderロジック
エリアC:SubShaderサポートできない場合適用させるシェーダー
これから順番に説明します。
ーーーーーエリアAーーーーー
プロパティのフォーマット:
_Name("Display Name", type) = defaultValue[{options}]
SecondShaderの5行目の_Color ("Color", Color) = (1,1,1,1)というと、このようです。
*
A−1. _Name
_NameはSubShaderと通信用のもの、一般的にはアンダーバースタート+英数になります。SubShaderでこのプロパティと同じ名前のパラメーターを登録すると、プロパティのデータを利用することができます。
*
A−2. "Display Name"
マテリアルパネルに表示用の文字です。"Color"を"First Color"に変更してみてください。
*
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関連のソース全部コメントアウトします。
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にすると、グレーになります。
_MyR ("Red",Range(0,1)) = 0.5
_MyG ("Green",Range(0,1)) = 0.5
_MyB ("Blue",Range(0,1)) = 0.5
ソースを保存して、Unityへ見に行きます。
三つのスライダーがうまくできたが、マテリアルは真黒になりました。原因はまだパラメーターの値をシェーダーへ渡してないです。
3. パラメーターの値をSubShaderへ渡します
仕組みを忘れた場合は上の「A−1. _Name」に戻ってください
エリアBのSubShaderに適当な場所で、パラメーターのNameと同じの変数を定義したら、SubShader内でパラメーターの値を取得することができます。
float _MyR;
float _MyG;
float _MyB;
4. _MyR/_MyG/_MyBを使って、RGB色を作ります
float3 myRGB = float3(_MyR,_MyG,_MyB);
o.Albedo = myRGB;
Unityへ見に行って、マテリアルはグレーになったんでしょう。
スライダーをドラッグしてみて、色が変わりますか?
パラメーターの練習はここまで終わり、#3のSubShaderを説明した後で複雑なシェーダーをちゃんと作りましょう。
完成したプロジェクトはここです。
ーーーーーーーーーーーーーーー特別授業ーーーーーーーーーーーーーーーーー
プログラムでシェーダーをコントロール
ーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーーー
目的:Planeの位置座標(XYZ)でシェーダーの色(RGB)をコントロールします。つまり、Planeを移動したら、色も変わります。
こんな感じです。
スクリプトMaterialCtrlはオブジェクトのマテリアルをアクセスして、マテリアルのパラメーターへ値を渡すことが可能です。
MaterialCtrlのところでダブルクリックしてスクリプトをオープンします。
最初は何にもないです。
マテリアルをコントロールする為に、まずオブジェクトのマテリアル取得することが必要です。毎回毎回取得すると、効率はよくないですから、Start関数実行する時取得しましょう。
void Start()
{
MeshRenderer myMeshRender = gameObject.GetComponent<MeshRenderer>();
Material myMaterial = myMeshRender.material;
}
マテリアルmyMaterial成功に取得しました。でも、こうすると、毎回実行するUpdate()関数はmyMaterial変数をアクセスすることができないでしょうか。そして、myMaterialをグローブ変数にしましょう。
次は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へ効果を見に行きましょう。
ゲームを実行してから、オブジェクトをドラッグしてみます。
完成したプロジェクターはここです。
次回はエリアBのSubShaderの部分を紹介します。