LoginSignup
64
64

More than 5 years have passed since last update.

Unity でShaderの勉強 その1

Last updated at Posted at 2016-09-19

ちょっとずつやっていきます。

モデル紹介

モデルにはこちらを使用します。かわいい。
https://www.assetstore.unity3d.com/jp/#!/content/6808
スクリーンショット 2016-09-19 3.15.09.png

デフォルト状態はこれ
スクリーンショット 2016-09-19 3.11.37.png

モデルデフォルトのマテリアルを調べる

デフォルトの状態でかわいいです。
デフォルトで設定されているマテリアル
スクリーンショット 2016-09-19 3.16.39.png

Unity付属のUnlit Transparent Cutout シェーダーが指定されていますね。
Unlit => Un lighting ライティングなし
Transparent => 透過あり
Cutout => アルファ情報が指定値以上の場合のみ透過する
Unlit系のシェーダーはUIに使うものだと思っていたけど、こういう使い方もできるのね。
デフォルメ系のモデリングだからテクスチャ自体に陰影をつけることでそれっぽく表現できる。

ただ、テクスチャを見てもアルファがないからUnit/Textureに変更しても見た目は変化しなかった。
Unit/Transparentに指定すると描画がおかしくなった。
スクリーンショット 2016-09-19 3.28.30.png
うーん。なんでだろう。

Standard shaderを指定してみる。

ライティング処理が行われ、ライトによる陰影がついた。
スクリーンショット 2016-09-19 3.29.43.png

自作シェーダー

こんな感じのシェーダーを書いてみる

Test1
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

        }

    }

}

結果はこんな感じ
スクリーンショット 2016-09-19 3.43.59.png
frag関数でColor.whiteを返してるから真っ白に描画される。
ライティングなしなのでアルファ部分を0にしても真っ白に描画される。

pragmaってなに

pragma(プラグマ)
コンパイラに指示を与えるやつ

#pragma vertex vert

でvertexを処理する関数がvertという名前の関数であることをコンパイラに指示している。

SubShaderを二個にしてみた

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
        }
    }

}

スクリーンショット 2016-09-19 3.50.12.png

1つ目のSubShaderが実行されて赤色が描画された。

Passを増やす

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
        }
    }

}

スクリーンショット 2016-09-19 3.51.38.png

一度赤色が描画された後、緑色が描画された。

vertexをいじってみた

modify_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
        }
    }

}

スクリーンショット 2016-09-19 3.53.44.png

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を指定してあげることでデフォルトのやつと同じようになった。
スクリーンショット 2016-09-19 13.48.30.png

uniformってなんだろう。

uniformって・・・
uniform sampler2D _MainTex;

uniformって何?

  • uniformはvert関数/frag関数で自由に使える変数。
  • uniformはPropertiesで定義した変数をvert関数/frag関数でアクセスできるようにするやつ。
  • uniformで定義することでスクリプトから変数を制御することができるようになる。
  • 頂点やピクセル毎の変数ではなく、マテリアル毎の変数となる。

テクスチャを重ねてみる

mainTex_and_subTex
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
        }
    }
}

プレビュー
スクリーンショット 2016-09-19 14.56.07.png

スクリーンショット 2016-09-19 14.56.22.png

マテリアルのTilingとOffsetを反映させる

Tiling => タイリング ? タイル ? => 何枚敷くか
Offset => オフセット => どれくらいずらすか

普通にtex2D関数だけでtextureの色情報を持ってきただけではtilingやoffsetが反映されません。

tilingやoffsetが効かない
                float4 mainTex  = tex2D(_MainTex,uv);
                float4 subTex   = tex2D(_SubTex,uv);

StanderdShaderでは、マテリアルのtilingやoffsetを操作すると、モデルのuvが加工されています。

tiling_and_offset_enabled
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側によって事前に代入されます。

_ST
uniform float4 _MainTex_ST;

_MainTex_STを使ってUVを加工します。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加工関数を使えるようになります。

TRANSFORM_TEX関数を使ってみる
                //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を使う

これで任意のスピードでチカチカしてくれる。

_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を使っていきたい。

64
64
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
64
64