2014年12月31日水曜日

【Unity】 Shaderはじめました。その3

こんにちは。はじまりました、その3。

Unity公式の頂点プログラムへ頂点データ流し込み / Providing vertex data to vertex programs

ここにあるサンプルを使っていきます。
トーラスノットモデル?そんなものうちにはないよ。


【VertexInputSimple】

画像の真ん中にあるキューブが無回転のもの

Shader VertexInputSimple {
    SubShader {
        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"
            
            struct v2f {
                float4 pos : SV_POSITION;
                fixed4 color : COLOR;
            };

            v2f vert (appdata_base v){
                v2f o;
                o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
                o.color.xyz = v.normal * 0.5 + 0.5;
                o.color.w = 1.0;
                return o;
            }

            fixed4 frag (v2f i) : COLOR0 { return i.color; }

            ENDCG
        }
    } 
}

回転のかかっているものは少し色が変わっているのがわかるでしょうか?
これは o.color.xyz = v.normal * 0.5 + 0.5; の v.normal が変わっているからですね。
この v.normal は3次元のベクトルであり、Global座標の値なので、オブジェクトが回転すると変化します。
無回転のキューブの場合、各面の法線ベクトルと o.color.xyz の値(RGB 0~255 換算)は下図のようになります。

+0.5 が0~255換算だと +128 にあたります。

おまけ:今作ってるピラミッドにこのシェーダを適用してみました。


ピラミッドの端の部分、色の変わり目の色の変化がなだらかですね。(そのせいで三角がたくさん見えます)
本当はもっとソリッドにキッチリキッカリ変化させたいんですけどね…これは今後の自分の課題です。
ちなみにこれ、メッシュはちゃんと直線のみで構成されています。ピクセル単位でみるとsmoothingされてしまうのか多少変わってしまうようです。


【UV 1】


Shader !Debug/UV 1 {
    SubShader {
        Pass {
            Fog { Mode Off }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // vertex input: position, UV

            struct appdata {
                float4 vertex : POSITION;
                float4 texcoord : TEXCOORD0;
            };

            struct v2f {
                float4 pos : SV_POSITION;
                float4 uv : TEXCOORD0;
            };

            v2f vert (appdata v) {
                v2f o;
                o.pos = mul( UNITY_MATRIX_MVP, v.vertex );
                o.uv = float4( v.texcoord.xy, 0, 0 );
                return o;
            }

            half4 frag( v2f i ) : COLOR {
                half4 c = frac( i.uv );
                if (any(saturate(i.uv) - i.uv))
                c.b = 0.5;
                return c;
            }

            ENDCG
        }
    }
}

画像だけみると少し前にやったシェーダにとても似ていますね。
各面、各ピクセルの色がUV座標(texcoord)の値によって変化しています。
UV座標は黒赤緑黄で示され、UV座標外(0~1以外)の値に対しては青色が適用されるようです。
UV座標のデバッグに使える便利なシェーダらしいです。

そういえば texcoord : TEXCOORD0 は float4 なんですね。使っているのは xy だけですが zw には何が入っているんでしょう?
あ、0入ってましたね。すみません。この値いじると何か変わるんですかね?


■ Fog { Mode Off }
霧(フォグ)の指定

■ v2f
このネーミング、Vertex to Fragment の略みたいですね。

half4
ベクトル型、16bit浮動小数点ベクトル。
ちなみに float4 は32bit浮動小数点ベクトル、int4 は32bit符号付き整数ベクトル。

frac()
入力された値の小数部を返す。入力は0以上1未満の値。
それ以外の値は if(any(saturate~~))で処理するのか?

any()
入力された値のいずれかの成分が0以外である場合は True, それ以外の場合は False を返す。
any(saturate(i.uv) - i.uv) の値は i.uv の値が0~1の場合0になるので False を返します。それ以外の場合 True を返し、if文が実行されます。

saturate()
入力された値を0~1の範囲にクランプする。
clamp()と同じ、clamp()は範囲の最大と最小を設定できる。

■ c.b
c.b = 0.5; この .b はなんでしょう。普通に考えれば blue の b なんですが…
もしくは half4 の上から順に a, b, c, d で…そうすると c になるから違いますね。
とりあえずここで例外(0~1以外)を青く染めます。


【UV 2】


手前が UV 2 です。多少違いますね。逆側から見るとこうなります。


Shader !Debug/UV 2 {
    SubShader {
        Pass {
            Fog { Mode Off }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // vertex input: position, second UV
            struct appdata {
                float4 vertex : POSITION;
                float4 texcoord1 : TEXCOORD1;
            };

            struct v2f {
                float4 pos : SV_POSITION;
                float4 uv : TEXCOORD0;
            };

            v2f vert (appdata v) {
                v2f o;
                o.pos = mul( UNITY_MATRIX_MVP, v.vertex );
                o.uv = float4( v.texcoord1.xy, 0, 0 );
                return o;
            }

            half4 frag( v2f i ) : COLOR {
                half4 c = frac( i.uv );
                if (any(saturate(i.uv) - i.uv))
                c.b = 0.5;
                return c;
            }
            ENDCG
        }
    }
}

マルチテクスチャとかやる時に使うんですかね。 Unityでは1つのオブジェクトに2つのテクスチャが使えるらしいです。 逆に言えば2つしか使えない。3つ以上使う方法もあるにはあるらしいですが僕はわかりません。

このコード… UV 1 と全く同じです。違うのは TEXCOORD0 が TEXCOORD1 になっているだけです。
この 0 と 1 で2つのテクスチャを管理しているんですね。


【Vertex color】


Shader !Debug/Vertex color {
    SubShader {
        Pass {
            Fog { Mode Off }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // vertex input: position, color
            struct appdata {
                float4 vertex : POSITION;
                fixed4 color : COLOR;
            };

            struct v2f {
                float4 pos : SV_POSITION;
                fixed4 color : COLOR;
            };

            v2f vert (appdata v) {
                v2f o;
                o.pos = mul( UNITY_MATRIX_MVP, v.vertex );
                o.color = v.color;
                return o;
            }

            fixed4 frag (v2f i) : COLOR0 { return i.color; }
            ENDCG
        }
    }
}

頂点色を表示するシェーダになります。キューブもシリンダーも何に適用しても真っ白になりました。
頂点位置と頂点色を struct appdata に持たせそれを最後まで継承し、使っています。
Unityで生成できるオブジェクト(キューブとかスフィアとか)の頂点色、デフォルトが白ってことなんですかね。


【Normals】
一番最初にやった VertexInputSimple と同じです。


【Tangents】

.normal を .tangent に変えてるだけです。出てくる色が変わります。
接線ベクトルの値で色づけしています。
真ん中のキューブの一番手前の面と上の面、同じ色ですね。
濃水色なんですがRGBの値は255換算で (0, 128, 128) でした。
接線ベクトルに直すと(-1, 0, 0)になります。
これで法線ベクトルと接線ベクトルの値がわかったので各面の従法線ベクトルの値も、それによってつく色も予測できますね。実際にやってみましょう。


【Binormals】


Shader !Debug/Binormals {
    SubShader {
        Pass {
            Fog { Mode Off }
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // 頂点入力: position, normal, tangent
            struct appdata {
                float4 vertex : POSITION;
                float3 normal : NORMAL;
                float4 tangent : TANGENT;
            };

            struct v2f {
                float4 pos : SV_POSITION;
                float4 color : COLOR;
            };

            v2f vert (appdata v) {
                v2f o;
                o.pos = mul( UNITY_MATRIX_MVP, v.vertex );
                // 従法線を計算
                float3 binormal = cross( v.normal, v.tangent.xyz ) * v.tangent.w;
                o.color.xyz = binormal * 0.5 + 0.5;
                o.color.w = 1.0;
                return o;
            }

            fixed4 frag (v2f i) : COLOR0 { return i.color; }

            ENDCG
        }
    }
}

従法線ベクトルの値で色づけしています。
フレミングの左手の法則を覚えていますか?電・磁・力 ですね。それと同じです。
法線・接線・従法線になります。Normal Tangent Binormal、 NTB、呼びづらい。

やってることは今までの Normals とか Tangents とかと同じです。
vert内の float3 binormal = cross( v.normal, v.tangent.xyz ) * v.tangent.w; で従法線の計算をしているだけなので今までのものがわかっていれば問題ないと思います。

cross()
2つの浮動小数点3Dベクトルの外積を返す。



終了です。実は前回前々回のその1その2の方が内容的には難しかったみたいです。
どちらかといえばこちらが導入的な位置付けだったのかな?今更なんですが。仕方ありませんね。ごめんなさい。

0 件のコメント:

コメントを投稿