UNITYシェーダ研究 Ambient
コード
https://github.com/YukiMiyatake/UnityLesson/tree/Shader_5
前回のシェーダ
ディフューズとスペキュラを実装し、だいぶライティングができた
テクスチャマッピングもしている
でも、Ambientも与えていません
Ambientの設定
UNITYのバージョンにより違うけど
最近のバージョンでは
Window-Lighting-Setting から行う
Environment Lightingのあたり
Ambientの取得
Ambinetカラーの取得は定数が用意されているので簡単だ
UNITY_LIGHTMODEL_AMBIENT.rgb;
これを計算式に加えるが
テクスチャ * Ambientの値を今までのものに足せばよいので最終的には
最終カラー = Ambientテクスチャ + Diffuseテクスチャ + スペキュラ
= (Ambient+Diffuse) * テクスチャ + スペキュラ
にした
float3 frag(v2f i) : SV_Target { float3 L = normalize(_WorldSpaceLightPos0.xyz); float3 V = normalize(_WorldSpaceCameraPos - i.vertexW.xyz); float3 N = i.normal; float3 H = normalize(L + V); //Ambient float3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb; // texture albedo float4 tex = tex2D(_MainTex, i.uv); // Diffuse(HalfLambert) float3 NdotL = dot(N, L); float3 diffuse = (NdotL*0.5 + 0.5) * _LightColor0.rgb; // Speculer // float3 specular = pow(max(0.0, dot(reflect(-L, N), V)), _Spec1Power) * _Spec1Color.xyz; // reflection float3 specular = pow(max(0.0, dot(H, N)), _Spec1Power) * _Spec1Color.xyz; // Half vector return (ambient + diffuse) * tex + specular; }
カレーハッキング
これからは ニジマスの手に入るシーズンになるので、ニジマスカレーを極めようとおもう
1回作ったけど 普通に美味しいのだけど、これからさらにプラスしていく
まず前回はゴールデンカレールウを使った。普通に美味しいけど、ビーフ等にあう、濃厚なルーであった
また スパイスも自分で頑張りたいので、スパイス研究科 中村繭子パイセンや友達のアドバイスもらいつつ
自分のニジマスカレーを完成させたい
知見
一度作った時の知見であるが、野菜の多いカレーが好きだが、歯ざわりや触感が統一されてないと ん?? ってなる
例えばヤングコーン入れたがそれだけ固く ん?? だったので、煮込む野菜以外は 蒸し野菜でトッピングする
ニジマスはおろして骨などを取り除くと食べやすいが、身がくずれたり、骨のエキスが美味しいので、いまはぶつ切りで考える
将来的には、あら汁と分けたり、トッピングで乗せ身が崩れなくするなど 考えたい
デミ系のカレー好きだけど、私の求めるものではないので、ルウにと溶かす野菜は慎重にえらぶ
次への
インドのベンガル地方は川魚のカレーが多いので参考にする
川魚用のスパイス
フェンネル、クミン、ニゲラ、ブラウンマスタード、フェニュグリーク
魚はターメリックと塩とレモン汁で漬けておいて使うこと多い
酸味苦手ではあるが、酸につけて少し骨を柔らかくできるといいな
カレー 上野不忍池 サルガム
何か料理を極めたいのでカレーにしてみた
カレーは好きだし、みんな食べられる
前提として小麦アレルギーなので、ご飯にあうカレー
お米はジャポニカ米がベストだけど、場合によればインディカ米買ってもいい
今は色々なものを作るのが先 敵を知るにはまず己を知る必要がある
不忍池近くの インド&ネパール料理
こういう店は、インド料理ではなくネパールがメインのはず
とりあえず すべて入ってるコース頼む
バターチキンカレー、キーマカレー、サラダ、デザート、ナン、ライス、シシカバブ、タンドリーチキン、ラッシー
バターチキン、キーマカレーは甘い。辛口MAXだけど 私は辛さには強いので物足りないが、とにかく砂糖甘さ
デザートはインドらしく 激甘
ナン、ラッシーは甘さ控えめ
ナンは甘いのが好きだったので、残念
カレーも 味は普通だけど、好みというか目指す方向と大きく違う
そして 小麦アレルギーで速攻おなか壊し、営業に送れるという ダメなパターン・・
UNITYシェーダ研究 Blinn-Phong
コード
https://github.com/YukiMiyatake/UnityLesson/tree/Shader_4
はじめに
ディフューズを入れて 基本的なLambertモデル実装しました
次はこれにスペキュラを追加します(念のため DirectSpecularのみです)
Blinn-Phong的な事をします(定義間違ってたら教えて下さい)
スペキュラ
鏡面反射です
前回のディフューズは、ライトが面に当たった時のライティングで、View(カメラの位置)とは関係ありません
スペキュラでは、光の反射モデルなので 鏡に光源が写った時= 光の反射を直視した時のみ強烈に光ります
3DCGでは こういう事になります
光の入射角と法線のreflectionベクトル(面法線に対してライトが同じ角度で反射)と Viewベクトルの内積と正の相関関係
スペキュラの場合は、相関は Powでかけます。細かい物理的な理由は調べてないけど
スペキュラのパラメータは今回は、スペキュラ色と Powerにします
property
Properties { _MainTex ("Texture", 2D) = "white" {} _Spec1Power("Specular Power", Range(0, 30)) = 1 _Spec1Color("Specular Color", Color) = (0.5,0.5,0.5,1) }
今回はスペキュラカラーとPowerが増えた
宣言
struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; float2 uv : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; float4 vertexW: TEXCOORD0; float2 uv : TEXCOORD1; float3 normal : TEXCOORD2; }; uniform sampler2D _MainTex; uniform float4 _MainTex_ST; uniform float _Spec1Power; uniform float4 _Spec1Color;
念のため 今回はフラグメントシェーダで計算します。一部頂点シェーダで計算して補完してもいいものがありますが
スペキュラのパラメータ以外に フラグメントシェーダに vertexWを送ってます
vertexには ビューポート変換まで行うので、World座標での位置情報が必要なためです
頂点シェーダ
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.vertexW = mul(unity_ObjectToWorld, v.vertex);
o.uv = TRANSFORM_TEX(v.uv, _MainTex);
o.normal = UnityObjectToWorldNormal(v.normal);
return o;
}
vertexWに対して World座標系への変換をかけています
フラグメントシェーダ
float3 frag (v2f i) : SV_Target { float3 L = normalize(_WorldSpaceLightPos0.xyz); float3 V = normalize(_WorldSpaceCameraPos -i.vertexW.xyz); float3 N = i.normal; // texture albedo float4 tex = tex2D(_MainTex, i.uv); // Diffuse(HalfLambert) float3 NdotL = dot(N, L); float3 diffuse = (NdotL*0.5 + 0.5) * _LightColor0.rgb ; // Speculer float3 specular = pow(max(0.0, dot(reflect(-L, N), V)), _Spec1Power) * _Spec1Color.xyz; // reflection return diffuse*tex + specular; }
Vは ViewVectorです。 _WorldSpaceCameraPosは World座標系でのカメラのポジションで World座標系でのポジションをマイナスすると、ピクセルからカメラに向かうベクトルになります
dot(reflect(-L, N), V) これが、反射ベクトルと Viewベクトルの内積部分
reflectというのは組み込み関数で この場合は 法線を中心として 反射したライトベクトルが取れる
それを maxで 0未満(裏面)の計算を無効にし
スペキュラの強さで累乗しスペキュラ色を乗算
スペキュラは加算なので単純にディフューズに掛け合わせる
カメラを動かすと スペキュラのライティングだけ変わります
ディフューズはカメラ位置とは無関係なのもわかると思います
ハーフベクトル
上記の計算は 正確な反射ベクトルを計算した
具体的には reflect(-L,N)の部分だが、反射ベクトルは多少計算が重い
R = L - (2V・N) N
L - 2 * dot( V*2, N ) * N
スペキュラは ハーフベクトルを使って近似が出来る事が広く知られているのでそれを実装する
ハーフベクトルとは、ライトベクトルとViewベクトルの間のベクトルであり、それと法線の内積で近似出来る
ハーフベクトルは normalize(L+V); で表される
reflectより圧倒的に計算量が低い
float3 H = normalize(L+V); // Speculer // float3 specular = pow(max(0.0, dot(reflect(-L, N), V)), _Spec1Power) * _Spec1Color.xyz; // reflection float3 specular = pow(max(0.0, dot(H, N)), _Spec1Power) * _Spec1Color.xyz; // Half vector
計算間違えてるのか、ライトの影響パワーが大きく異なるが、スペキュラはかかった
UNITYシェーダ研究 Lambertモデル実装
前回は 新規で作ったUNITシェーダの説明したので
簡単なシェーダ作ります
コードはココ
https://github.com/YukiMiyatake/UnityLesson/tree/Shader_3
基本シェーダ(Albedo)
先ほどのシェーダからFOGを削除しシンプルにします
テクスチャは・・・雰囲気欲しいからつけておく
Shader "Unlit/yUnit" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); return o; } fixed4 frag (v2f i) : SV_Target { // sample the texture fixed4 col = tex2D(_MainTex, i.uv); return col; } ENDCG } } }
テクスチャ(Albedo)のみですね
Lambert
最も簡単なライティングモデルの ランバート反射を作ります
先ほどのAlbedoはライトの影響をうけなかったけど今度はうけます
ランバート反射は、面にライトが当たる角度により影響の強さが変わる
具体的には 法線とライトベクトルの内積を乗算する
Shader "Unlit/yUnit" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } Pass { Tags{ "LightMode" = "ForwardBase" } Cull Back CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" #include "Lighting.cginc" struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; float2 uv : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; float2 uv : TEXCOORD0; float3 normal : TEXCOORD1; }; sampler2D _MainTex; float4 _MainTex_ST; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); o.normal = UnityObjectToWorldNormal(v.normal); return o; } float3 frag (v2f i) : SV_Target { // sample the texture float4 col = tex2D(_MainTex, i.uv); // Diffuse float3 lightDir = normalize(_WorldSpaceLightPos0.xyz); float3 NdotL = dot( i.normal, lightDir); float3 diffuse = max(0.0, NdotL ) * _LightColor0.rgb * col.xyz; return diffuse; } ENDCG } } }
LightMode
Tags{ "LightMode" = "ForwardBase" }
これを付けないと安定しなかったので付けた
LightModeにより呼ばれるパスが変わる
ForwardBaseは Forwardレンダリングのメインのライトのパス
ForwardAddは Forwardレンダリングのメイン以外のライトがそれぞれ呼ばれる
Deferredは Deferredレンダリングのパス
ShadowCasterは 影のパス
など
構造体
struct appdata { float4 vertex : POSITION; float3 normal : NORMAL; float2 uv : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; float2 uv : TEXCOORD0; float3 normal : TEXCOORD1; };
法線が必要なので NORMALが追加
シェーダ中身
v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); o.normal = UnityObjectToWorldNormal(v.normal); return o; } float3 frag (v2f i) : SV_Target { // sample the texture float4 col = tex2D(_MainTex, i.uv); // Diffuse float3 lightDir = normalize(_WorldSpaceLightPos0.xyz); float3 NdotL = dot( i.normal, lightDir); float3 diffuse = max(0.0, NdotL ) * _LightColor0.rgb * col.xyz; return diffuse; }
o.normal = UnityObjectToWorldNormal(v.normal);
法線がローカル座標系なので World座標系に変換する
WorldSpaceLightPos0、LightColor0 はビルトイン定数(Lighting.cginc のinclude必要)
WorldSpaceLightPos0: 0番目のライトのポジション(ディレクショナルライトは方向)
LightColor0: 0番目のライトの色
float3 NdotL = dot( i.normal, lightDir);
ここで 法線とライトの内積を計算する
ディフューズは ライトと法線の内積に ライトカラーとテクスチャカラーを掛ければ求められる
内積は負の場合(裏面)があるので その場合は0にしておく
ライトを動かせば ライティングも変わるようになった
でも少し暗い
またSphereを置けばわかりやすいが、陰のつき方が90度でわかれている
実際のオブジェクトは光の回析や吸収反射などで もっとゆるやかに出るべきである
ハーフランバート
先ほどのランバートだと、ライティングが90度で終わる
もう少しゆるやかにしたいのでハーフランバートを適用してみる 先ほどのランバート反射では ライトと法線の内積が -1~1 であったが 0~1の範囲にリマップすれば ゆるやかになる
// float3 diffuse = max(0.0, NdotL ) * _LightColor0.rgb * col.xyz; float3 diffuse = (NdotL*0.5 + 0.5) * _LightColor0.rgb * col.xyz;
// float3 diffuse = max(0.0, NdotL ) * _LightColor0.rgb * col.xyz; // float3 diffuse = (NdotL*0.5 + 0.5) * _LightColor0.rgb * col.xyz; float3 diffuse = pow(NdotL*0.5 + 0.5, 2) * _LightColor0.rgb * col.xyz;
他にも float3 diffuse = (NdotL*0.75 + 0.25) * _LightColor0.rgb * col.xyz;
とか色々と考えれるので、好みで使い分けよう
UNITYシェーダー研究 UNITシェーダを読む
今回のソース
https://github.com/YukiMiyatake/UnityLesson/tree/Shader_2
前回のあらすじ
Surfaceシェーダを新規で作る
中野シスターズに適用
Surfaceシェーダの解説
たいした内容じゃなくてすみません
でも 初歩からやりたいの
UNITシェーダを適用する
本格的なシェーダーを作るには UNITシェーダは欠かせないので Surfaceシェーダと同じように作る
今度は名前を Unlit/yUnit にする
Shader "Unlit/yUnit" { Properties { _MainTex ("Texture", 2D) = "white" {} } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag // make fog work #pragma multi_compile_fog #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; UNITY_FOG_COORDS(1) float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); UNITY_TRANSFER_FOG(o,o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { // sample the texture fixed4 col = tex2D(_MainTex, i.uv); // apply fog UNITY_APPLY_FOG(i.fogCoord, col); return col; } ENDCG } } }
今度は明らかに、レンダリング結果が変わった
シェーダーの中身
今回は前回と違い、テクスチャしかパラメーターがありません!
Pass
Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag // make fog work #pragma multi_compile_fog
UNITシェーダでは 複数のPassを持つことができる
輪郭線とか影とか
今回は1個
pragmaは 前回はSurfaceでサーフェースシェーダを指定したが、今回は vertex、fragmentと2つのシェーダを指定している
もちろん、それらの後に続くのは エントリポイントである
今回はfogが有効になっている
変数宣言等
#include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; UNITY_FOG_COORDS(1) float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST;
UnityCG.cginc をincludeしているが、色々と便利なマクロや関数等が入ったヘッダなので使う
構造体は今回2個
頂点毎の入力(attribute)の appdata
フラグメントシェーダに渡す Varyingの v2f
そして シェーダーの引数 uniformの _MainTex
シェーダー中身
v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); UNITY_TRANSFER_FOG(o,o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { // sample the texture fixed4 col = tex2D(_MainTex, i.uv); // apply fog UNITY_APPLY_FOG(i.fogCoord, col); return col; }
頂点シェーダ
UnityObjectToClipPosの定義を見てみよう
// Tranforms position from object to homogenous space inline float4 UnityObjectToClipPos(in float3 pos) { // More efficient than computing M*VP matrix product return mul(UNITY_MATRIX_VP, mul(unity_ObjectToWorld, float4(pos, 1.0))); } inline float4 UnityObjectToClipPos(float4 pos) // overload for float4; avoids "implicit truncation" warning for existing shaders { return UnityObjectToClipPos(pos.xyz); }
オブジェクトのローカル座標からビューポート座標に変換している
TRANSFORM_TEX
// Transforms 2D UV by scale/bias property #define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
なんと、トークンマクロで
TRANSFORM_TEX(v.uv, MainTex); は
v.uv.xy * MainTex_ST.xy + MainTex_ST.zw
になる。
MainTex_ST.xyにTiling、zwにOffsetの値が入ってくるようだ
UNITY_TRANSFER_FOG
// SM3.0 and PC/console: calculate fog distance per-vertex, and fog factor per-pixel #define UNITY_TRANSFER_FOG(o,outpos) o.fogCoord.x = (outpos).z
ビューポート座標系でのZ座標をFOGのパラメータに入れている。距離によるFOG。ここは さらっと読み飛ばしてもいい
結論からいうと、ローカル座標をビューポート座標に変換し、テクスチャのUVを補正して、Zフォグのパラメータを渡しているだけのようだ
フラグメントシェーダ
// sample the texture fixed4 col = tex2D(_MainTex, i.uv); // apply fog UNITY_APPLY_FOG(i.fogCoord, col); return col;
非常にシンプルで、テクスチャマッピングして、FOGを適用しているだけのようだ
ライトの計算も何も入っていない(実際DirectionalライトをON/OFFしても変化しない)
UNITYシェーダー研究 環境構築~サーフェースシェーダ軽く
このソースは GitHub - YukiMiyatake/UnityLesson at Shader_1
はじめに
UNITYのシェーダの知見が少したまってきたので、自分でアウトプットしておかなければ忘れてしまう
ので メモをしていたが
もっと基礎から知りたいという人が多かったので
超基礎からやります。
UNITYシェーダ入門 タグでしばらく書きます
準備
UNITYでプロジェクト作成します。 今回は UNITY2017.1.0f3 を使用した
ビルトインシェーダのコードは色々と勉強になるので UNITYのAdditionalDownloadから落としておくとよい
https://unity3d.com/jp/get-unity/download/archive
ここから ダウンロードタブを開くと 色々なものをダウンロードできるが ビルトインシェーダを落とす
落とさなくてもビルトインシェーダは使えるが、きっとコードを確認したくなるので。
キャラクタモデルは UNITYちゃんを使おうと思ったけど、さらに権利のゆるそうな 中野シスターズにしました
中野シスターズダウンロードしておいてね!
中野シスターズをインポートし、画面に配置します
UNITYメニューより Import->NewAssetで ダウンロードファイルの naka.fbx、kano.fbxをインポート
そして naka(あるいはkano)のプレファブを画面にドラッグし Positionは(0, 0, 0) Rotateは (0, 180, 0)に
デフォルトではカメラが遠いので、アップになるようカメラを近づけて実行
表示された
シェーダーを変更
中野シスターズは Standardというシェーダーが付いている
これは ビルトインシェーダ―の Standard という名前のシェーダーで、物理ベースの標準的なシェーダーである
これを新規のシェーダーに置き換える
Create->Shaderをすると たくさんのリストがでる
- Standard Surface Shader
サーフェースシェーダという独自のシェーダ
頂点シェーダとフラグメントシェーダを一緒に 簡単に記述できる反面、細かい制御は出来ない - Unit Shader
頂点シェーダとフラグメントシェーダを記述する。
今回はコレ - Image Effect Shader ポストプロセスに使うシェーダ
- Compute Shader GPGPUを行う コンピュートシェーダを記述
- Shader Variant Collection ヘッダのようなもの
SurfaceShader
上記でSurfaceShaderを作ろう
ファイル名が NewSurfaceShaderになっているが、自由にかえてよい
ySurfaceに変更してみた
シェーダーの中身は下記
Shader "Custom/NewSurfaceShader" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _Glossiness ("Smoothness", Range(0,1)) = 0.5 _Metallic ("Metallic", Range(0,1)) = 0.0 } 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; }; half _Glossiness; half _Metallic; fixed4 _Color; // 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_CBUFFER_START(Props) // put more per-instance properties here UNITY_INSTANCING_CBUFFER_END void surf (Input IN, inout SurfaceOutputStandard o) { // Albedo comes from a texture tinted by color fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; // Metallic and smoothness come from slider variables o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }
非常に短いが名前を変えておこう
Shader "Custom/NewSurfaceShader" {
ここがシェーダー名を設定する箇所だ
好きな名前にかえてみよう。 とりあえず Custom/ySurface に変更してみた
中野シスターズモデルのマテリアルに対して、今はStandardがついているが それを上記のシェーダーに変更してみる
違いはわからないが、シェーダーが変わった
サーフェースシェーダは今回は扱わないので、軽く流して説明(メモする)
SurfaceShader解説
SurfaceShaderは実際のところ、フラグメントシェーダはビルトイン(あるいは自作)のシェーダを呼び出すという処理である
Shader名
Shader "Custom/yShader" {
この部分は上記の シェーダ名の設定
マテリアルに設定するときの名前である
/ を付けることにより、Editor上で階層構造で表示できるので便利
Property
Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _Glossiness ("Smoothness", Range(0,1)) = 0.5 _Metallic ("Metallic", Range(0,1)) = 0.0 }
シェーダーに対して渡す変数やテクスチャを宣言する
ここに書くと、Editor上に設定項目として出る
実際にシェーダ内で値を使うときには、パス内で定義しなおさなければならない
Syntaxは
変数名 (“Editor表示名”, 型 ) = デフォルト値
_Color (“Color”, Color ) = (1,1,1,1)
という風になっている
型やDrawer(属性)は色々あるので マニュアル参照
Unity - マニュアル: ShaderLab :プロパティー
SubShader
SubShader { Tags { "RenderType"="Opaque" } LOD 200
ここにシェーダーの内部実装をかく
SubShaderは複数書くことが出来る
TagsやLODは、このSubShaderを実行するための条件で、この条件に合致したときのみこのシェーダを実行する
合致しない場合には次のSubShaderを実行する
この機能により、ハードウェア依存や、LODによるシェーダの変更等を制御できる
Pragma
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
CGPROGRAM~ENDCG までの間が実際にシェーダにコンパイルされる部分である
pragmaは色々な拡張機能であるが
#pragma surface surf Standard fullforwardshadows #pragma target 3.0
SurfaceShaderで記述する。 シェーダーのエントリーポイントは surf関数である
ライティングモデルはStandardにする(Lambert、BlinnPhong、StandardSpecularも設定可能)
fullforwardshadows 影はフォワードパスですべて表示する
シェーダーモデル 3.0以上必要
変数
sampler2D _MainTex; struct Input { float2 uv_MainTex; }; half _Glossiness; half _Metallic; fixed4 _Color; UNITY_INSTANCING_CBUFFER_START(Props) // put more per-instance properties here UNITY_INSTANCING_CBUFFER_END
Editorから渡された値を、シェーダ変数(Uniform)へ格納している
また Inputという構造体を宣言している
これは頂点シェーダへの頂点毎の入力値(attribute)である
UNITY_INSTANCING_CBUFFER_START
は、CBufferを渡したいときは設定するようです
今回は不要
関数本体
void surf (Input IN, inout SurfaceOutputStandard o) { // Albedo comes from a texture tinted by color fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color; o.Albedo = c.rgb; // Metallic and smoothness come from slider variables o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; } ENDCG }
いよいよ本体
INには頂点シェーダに渡ってくる attributeで、今回はUV
inout のSurfaceOutputStandardは 組み込み構造体で中身は下記
struct SurfaceOutputStandard { fixed3 Albedo; // base (diffuse or specular) color fixed3 Normal; // tangent space normal, if written half3 Emission; half Metallic; // 0=non-metal, 1=metal half Smoothness; // 0=rough, 1=smooth half Occlusion; // occlusion (default 1) fixed Alpha; // alpha for transparencies };
まず テクスチャマップで UV座標のテクスチャカラーを取得し _Colorを乗算し oのAlbedoに設定
その他パラメータを oに設定する
これだけで、サーフェースシェーダでは pragmaで設定したライトモデル(今回はStandard)に 構造体oの値を自動で渡し終了となる
少し簡単になる
Fallback
FallBack "Diffuse"
SubShaderがどれも条件が満たさなかったら Diffuse(ビルトインシェーダ) を実行する