ちょっとずつやっていきます。
###モデル紹介
モデルにはこちらを使用します。かわいい。
https://www.assetstore.unity3d.com/jp/#!/content/6808
###モデルデフォルトのマテリアルを調べる
デフォルトの状態でかわいいです。
デフォルトで設定されているマテリアル
Unity付属のUnlit Transparent Cutout シェーダーが指定されていますね。
Unlit => Un lighting ライティングなし
Transparent => 透過あり
Cutout => アルファ情報が指定値以上の場合のみ透過する
Unlit系のシェーダーはUIに使うものだと思っていたけど、こういう使い方もできるのね。
デフォルメ系のモデリングだからテクスチャ自体に陰影をつけることでそれっぽく表現できる。
ただ、テクスチャを見てもアルファがないからUnit/Textureに変更しても見た目は変化しなかった。
Unit/Transparentに指定すると描画がおかしくなった。
うーん。なんでだろう。
###Standard shaderを指定してみる。
ライティング処理が行われ、ライトによる陰影がついた。
###自作シェーダー
こんな感じのシェーダーを書いてみる
Shader "Custom/Test1" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
float4 vert(float4 v:POSITION) : SV_POSITION {
return mul(UNITY_MATRIX_MVP,v);
}
float4 frag() : COLOR {
return fixed4(1,1,1,1);
}
ENDCG
}
}
}
結果はこんな感じ
frag関数でColor.whiteを返してるから真っ白に描画される。
ライティングなしなのでアルファ部分を0にしても真っ白に描画される。
###pragmaってなに
pragma(プラグマ)
コンパイラに指示を与えるやつ
#pragma vertex vert
でvertexを処理する関数がvertという名前の関数であることをコンパイラに指示している。
###SubShaderを二個にしてみた
Shader "Custom/Test1" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
float4 vert(float4 v:POSITION) : SV_POSITION {
return mul(UNITY_MATRIX_MVP,v);
}
float4 frag() : COLOR {
return fixed4(1,0,0,1);//赤
}
ENDCG
}
}
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
float4 vert(float4 v:POSITION) : SV_POSITION {
return mul(UNITY_MATRIX_MVP,v);
}
float4 frag() : COLOR {
return fixed4(0,1,0,1);//緑
}
ENDCG
}
}
}
1つ目のSubShaderが実行されて赤色が描画された。
###Passを増やす
Shader "Custom/Test1" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
float4 vert(float4 v:POSITION) : SV_POSITION {
return mul(UNITY_MATRIX_MVP,v);
}
float4 frag() : COLOR {
return fixed4(1,0,0,1);//赤
}
ENDCG
}
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
float4 vert(float4 v:POSITION) : SV_POSITION {
return mul(UNITY_MATRIX_MVP,v);
}
float4 frag() : COLOR {
return fixed4(0,1,0,1);//緑
}
ENDCG
}
}
}
一度赤色が描画された後、緑色が描画された。
###vertexをいじってみた
Shader "Custom/Test1" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
float4 vert(float4 v:POSITION) : SV_POSITION {
// return mul(UNITY_MATRIX_MVP,v);
return v;
}
float4 frag() : COLOR {
return fixed4(1,0,0,1);//赤
}
ENDCG
}
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
float4 vert(float4 v:POSITION) : SV_POSITION {
return mul(UNITY_MATRIX_MVP,v);
}
float4 frag() : COLOR {
return fixed4(0,1,0,1);//緑
}
ENDCG
}
}
}
1つ目のPassであえてMVP変換しないでfragmentに処理を回すとこうなるのね。
Pass間で情報を共有したい場合はどうすれば良いんだろう...
###vertexの引数を変える(引数を増やす)
vertexの引数は任意に変えることができます。
1つだけのパラメータを引数で受け取ったり、複数のパラメータを受け取ったりできます。
普通の関数のようにカンマ区切りで欲しい引数を定義しましょう。
Shader "Custom/Test1" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
float4 vert(float4 pos : POSITION , float4 uv : TEXCOORD0) : POSITION {
return mul(UNITY_MATRIX_MVP, pos);
}
float4 frag() : COLOR {
return fixed4(1,0,0,1);//赤
}
ENDCG
}
}
}
###vertexの引数を変える(struct定義)
POSITIONのパラメータ(頂点のPOSITION)をstruct経由で受け取ってみましょう。
Shader "Custom/Test1" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct appdata {
float4 pos : POSITION;
};
float4 vert(appdata data) : POSITION {
return mul(UNITY_MATRIX_MVP, data.pos);
}
float4 frag() : COLOR {
return fixed4(1,0,0,1);//赤
}
ENDCG
}
}
}
ポイント1
structで必要なパラメータを定義しています。semanticsが必要なので注意
struct appdata {
float4 pos : POSITION;
};
ポイント2
vert関数の引数を変更します
float4 vert(appdata data) : POSITION {
これでdata.posで頂点データにアクセスできるようになりました。
vert関数の戻り値の型はfloat4でsemanticsはPOSITIONになっています。
これもvertの引数のように自由に変えることができます。(ただしPOSITIONの情報を返すのはほぼ必須です)
semantics関連の説明はこちら
https://docs.unity3d.com/ja/current/Manual/SL-ShaderSemantics.html
###vert関数でPOSITIONを返さない
Shader "Custom/Test1" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
struct v2f {
float4 uv : TEXCOORD0;
};
v2f vert(float4 pos: POSITION,out float4 outputs : POSITION) {
v2f o;
outputs = mul(UNITY_MATRIX_MVP , pos);
return o;
}
float4 frag() : COLOR {
return float4(1,0,0,1);
}
ENDCG
}
}
}
ポイント
out修飾子をつける。
v2f vert(float4 pos: POSITION,out float4 outputs : POSITION) {
最大で1つしかout修飾子をつけれないので注意。
out修飾子をつけた引数の方が前に来ても良い。
v2f vert(out float4 outputs : POSITION , float4 pos: POSITION) {
これはだめ。out修飾子をつけられた引数は取得できない。代入だけ
v2f vert(out float4 pos: POSITION) {
v2f o;
pos = mul(UNITY_MATRIX_MVP , pos);
vert関数ではvertex(float4のPOSITION)さえ加工できれば良いため、返り値なしでもコンパイルは通る。
なのでこう書いても良い。
void vert(out float4 outputs : POSITION , float4 pos: POSITION) {
outputs = mul(UNITY_MATRIX_MVP , pos);
}
###Texture貼りたい
プロパティを増やしましょう。
Shader "Custom/Test1" {
Properties {
_MainTex("main Texture" , 2D) = "white" {}
}
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
uniform sampler2D _MainTex;
struct v2f {
float4 uv : TEXCOORD0;
float4 wpos : SV_POSITION;
};
v2f vert(float4 pos: POSITION, float4 uv : TEXCOORD0) {
v2f o;
o.wpos = mul(UNITY_MATRIX_MVP , pos);
o.uv = uv;
return o;
}
float4 frag(v2f i) : COLOR {
float4 tex = tex2D(_MainTex,i.uv);
return tex;
}
ENDCG
}
}
}
vert関数でその頂点のuvとposを含むv2fを返し、
frag関数でv2fを受け取っている。
tex2D関数でuvからtextureの色を抜き出して戻り値にしている。
マテリアルにTextureを指定してあげることでデフォルトのやつと同じようになった。
uniformってなんだろう。
uniform sampler2D _MainTex;
###uniformって何?
- uniformはvert関数/frag関数で自由に使える変数。
- uniformはPropertiesで定義した変数をvert関数/frag関数でアクセスできるようにするやつ。
- uniformで定義することでスクリプトから変数を制御することができるようになる。
- 頂点やピクセル毎の変数ではなく、マテリアル毎の変数となる。
###テクスチャを重ねてみる
Shader "Custom/Test1" {
Properties {
_MainTex("main" , 2D) = "white" {}
_SubTex("sub",2D) = "white" {}
}
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
uniform sampler2D _MainTex;
uniform sampler2D _SubTex;
float4 vert(out float4 wpos : POSITION , float4 pos : POSITION , float4 uv : TEXCOORD0) : TEXCOORD0 {
wpos = mul(UNITY_MATRIX_MVP,pos);
return uv;
}
float4 frag(float4 uv : TEXCOORD0) : COLOR {
float4 mainTex = tex2D(_MainTex,uv);
float4 subTex = tex2D(_SubTex,uv);
return mainTex * subTex;
}
ENDCG
}
}
}
###マテリアルのTilingとOffsetを反映させる
Tiling => タイリング ? タイル ? => 何枚敷くか
Offset => オフセット => どれくらいずらすか
普通にtex2D関数だけでtextureの色情報を持ってきただけではtilingやoffsetが反映されません。
float4 mainTex = tex2D(_MainTex,uv);
float4 subTex = tex2D(_SubTex,uv);
StanderdShaderでは、マテリアルのtilingやoffsetを操作すると、モデルのuvが加工されています。
Shader "Custom/Test1" {
Properties {
_MainTex("main" , 2D) = "white" {}
_SubTex("sub",2D) = "white" {}
}
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
uniform sampler2D _MainTex;
uniform sampler2D _SubTex;
uniform float4 _MainTex_ST;
float2 vert(out float4 wpos : POSITION , float4 pos : POSITION , float2 uv : TEXCOORD0) : TEXCOORD0 {
wpos = mul(UNITY_MATRIX_MVP,pos);
float2 tiling_offset_uv = float2(uv.xy * _MainTex_ST.xy + _MainTex_ST.zw);
return tiling_offset_uv;
}
float4 frag(float2 uv : TEXCOORD0) : COLOR {
float4 mainTex = tex2D(_MainTex,uv);
float4 subTex = tex2D(_SubTex,uv);
return mainTex * subTex;
}
ENDCG
}
}
}
ポイント
テクスチャの名前 + _STをつけたfloat4のuniformを定義することでそのTextureのtilingやoffset情報を取ってくることができます。一見何も代入されていないように見えますが、変数の中身はunity側によって事前に代入されます。
uniform float4 _MainTex_ST;
_MainTex_STを使ってUVを加工します。uv加工の式はお決まりの定型文です。
float2 tiling_offset_uv = float2(uv.xy * _MainTex_ST.xy + _MainTex_ST.zw);
return tiling_offset_uv;
またCGPROGRAM内に#include "UnityCG.cginc"を書くことでunity組み込みのuv加工関数を使えるようになります。
//float2 tiling_offset_uv = float2(uv.xy * _MainTex_ST.xy + _MainTex_ST.zw);
float2 tiling_offset_uv = TRANSFORM_TEX(uv,_MainTex);
return tiling_offset_uv;
###SubTextureもTiling対応
こんな感じでいいのかな?
TRANSFORM_TEX関数を使うとうまく対応できなかった・・・
float2 tiling_offset_uv = float2(uv.xy * _MainTex_ST.xy * _SubTex_ST + _MainTex_ST.zw + _SubTex_ST.zw);
// float2 tiling_offset_uv = TRANSFORM_TEX(uv,_MainTex);
return tiling_offset_uv;
###Colorを使ってみる
Shader "Custom/Test1" {
Properties {
_MainTex("main" , 2D) = "white" {}
_Color ("color" , Color) = (1,1,1,1)
}
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
uniform sampler2D _MainTex;
uniform float4 _Color;
float2 vert(out float4 wpos : POSITION , float4 pos : POSITION , float2 uv : TEXCOORD0) : TEXCOORD0 {
wpos = mul(UNITY_MATRIX_MVP,pos);
return uv;
}
float4 frag(float2 uv : TEXCOORD0) : COLOR {
float4 mainTex = tex2D(_MainTex,uv);
return mainTex * _Color;
}
ENDCG
}
}
}
###時間経過によって色を変える
_SinTimeを使って時間経過を取得し、それを使ってColor値を変えます。
ゲームを再生しないと_SinTimeの値が変化しないので注意。
Shader "Custom/Test1" {
Properties {
_MainTex("main" , 2D) = "white" {}
_Color ("color" , Color) = (1,1,1,1)
}
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
uniform sampler2D _MainTex;
uniform float4 _Color;
float2 vert(out float4 wpos : POSITION , float4 pos : POSITION , float2 uv : TEXCOORD0) : TEXCOORD0 {
wpos = mul(UNITY_MATRIX_MVP,pos);
return uv;
}
float4 frag(float2 uv : TEXCOORD0) : COLOR {
float4 mainTex = tex2D(_MainTex,uv);
_Color.x = (_SinTime.w + 1.0 ) * 0.5;
return mainTex * _Color;
}
ENDCG
}
}
}
思ったより_SinTimeが遅かったので早くしてみる。
二倍角の公式を使う。
float4 frag(float2 uv : TEXCOORD0) : COLOR {
float4 mainTex = tex2D(_MainTex,uv);
//2倍速
//_Color.x = (2.0 *_SinTime.w * _CosTime .w);
//3倍速
_Color.x = (3.0 * _SinTime.w - 4.0 * _SinTime.w * _SinTime.w *_SinTime.w);
return mainTex * _Color;
}
これでもちょっと遅いな・・・もっと爆速で点滅して欲しい。
###_SinTimeじゃなくて_Timeを使う
これで任意のスピードでチカチカしてくれる。
float speed = 10;
//10倍じゃ!
_Color.x = (sin(_Time.w * speed) + 1.0) * 0.5;
return mainTex * _Color;
###Semantics SV_POSITIONとPOSITIONの違い
こちらによると
https://msdn.microsoft.com/en-us/library/windows/desktop/bb509647.aspx
Migration from Direct3D 9 to Direct3D 10 and later
The following issues should be considered when migrating code from Direct3D 9 to Direct3D 10 and later:
Mapping to Direct3D 9 Semantics
A few of the Direct3D 10 and later semantics map directly to Direct3D 9 semantics.
Direct3D 10 Semantic Direct3D 9 Equivalent Semantic
SV_Depth DEPTH
SV_Position POSITION
SV_Target COLOR
Note to Direct3D 9 developers:
For Direct3D 9 targets, shader semantics must map to valid Direct3D 9 semantics. For backwards compatibility POSITION0 (and its variant names) is treated as SV_Position, COLOR is treated as SV_TARGET.
Direct3D 9 だと DEPTHやPOSITIONやCOLORといったSemanticsが使えたけど、
Direct2D 10 から SV_DepthやSV_PositionやSV_Targetに変わった。
"SV_"を接頭につけることで10に対応することができるってことかな。
積極的にSV_系のSemanticsを使っていきたい。