お米 is ライス

C#やらUnityやらを勉強していて、これはメモっといたほうがええやろ、ということを書くつもりです

【日本巫女史】総論 第一章 第一節 巫女の種類とその名称

初めに

日本巫女史とは、1930年に刊行された日本の巫女に関する様々な民俗学的事項をまとめた書籍です。

すでに著作権は切れており、幸いにも篤志の手によってインターネット上に内容が公開されています。

著者は中山太郎
詳細は省きますが、民俗学の大家である柳田國男の門下生です。

この日本巫女史、非常に内容が素晴らしいというのはよく聞ききますが、
暴力的にすら思える分量にしり込みしてこれまできちんと読めたことがありません。

また何度目かの挑戦をしたいと思うんですが、どうせまた中途半端になるのは目に見えているので、
内容をまとめることで栞代わりにしようと思います。

とはいえ、全部を全部まとめるとそれはそれで大変なので、
一部のみ、簡単にまとめることとします。

というわけで、やっていきます。


総論 第一章 第一節 巫女の種類とその名称

この節では巫女の分類を行っている。

まず大きな分類として、「神和系の神子」と「口寄系の巫女」に分類される。

「神和系の神子」は神社などに属し、一定の支給を受けて奉仕する公的な存在、

「口寄系の巫女」は地方に土着し、都度報酬を得て呪術を行うような存在。

この2つは役割や呼称によってさらに細かく分類される

神和系の神子

まず「神和系の神子」に属する名称について。

「神子」とは読んで字のごとく、「神の子」というような意味を持った名称。

ミコとはそもそも「神そのもの」を指すような言葉であったのが、
時代が変わり、神と人との仲立ちをするような存在になったのがこの呼称である。

斎宮

皇族の女性が代々任命され、伊勢神宮天照大神に奉斎するのが斎宮

「斎院」

斎宮と同じように皇族の女性が代々任命され、京都の賀茂社に奉斎する女性のことを言う。

「阿礼乎止売」

斎院の別名であり、”アレ”とは産出を言葉だと考えられる。

「御巫」

”カムコ”、すなわち神の子を意味する語。

「巫覡」

「神を和むる」という意味で、神社に奉仕する巫女を通称した言葉。

他に「キネ」「姉子」「古曾」「物忌」「斎子」など、神社ごと、役割ごとに様々な呼称がある。

口寄系の巫女

次に「口寄系の巫女」に属する名称について。
巫女の呼称というのは、

一、呪術の作法からくるもの。
二、呪術の器具からくるもの。
三、巫女の風俗からくるもの。

というようにいくつかある。

「市子」「インヂコ」「イチイ」「イチジョウ」

語源は諸説あり、
一つ目は、「斎子」が転訛した語であるとする説。
二つ目に、口寄系の巫女が市場に出て呪術を行ったことからくるという説。

著者はこれに加えて、琉球語で呪詛する人を表す”イチジャマ”という語に注目し、
”イチ”という語が呪詛の意味を持つとして、それに関連する語である説を唱えている。

「イタコ」

紀州熊野で神子を”イタ”と呼んだ記録があり、それに関連する語と推測されてる。
アイヌ語の転訛説もある。

「座下し」

「七くらおろし」という呪術に由来する言葉。
クラは回数を意味している。

「県語り」

京に対する田舎という意味での「県」。
田舎にいる巫女という意味。

「笹帚き」

巫女が呪術を行うときに、両手に笹の枝をもって自分の顔を帚くという作法に由来する呼称。

「大弓」

弓を左手に持ち、右手に持った竹の棒で弦を叩きながら呪術を行うことから、
使用している器具に由来する呼称。

「梓巫女」

梓で作った弓を呪術の器具として使用することに由来する呼称。

「口寄せ」

生口、死口、神口を呪術で引き寄せるということに由来する呼称。
詳細は後述とのことだが、生者や死者、神の言葉を代わりに口にするという意味かと。

「飯綱」

飯綱権現を主神とする巫女の一派があったことからこの呼称が生まれたと考えられる。

「コンガラサマ」

巫女がぐるぐると人家を周るという風俗に由来する呼称。

「刀自話」

「刀自」は老女の意味。
老女が巫女を営んでいたことからくる呼称。

「ユタ」

琉球に島々における巫女の呼称。
預言者という意味であると言われている。

口寄系の巫女の呼称は他にもさまざまあるが省略。

【Unity・CG・Shader】通信環境が悪くてノイズが入って乱れまくってる映像風のポストエフェクトシェーダー

概要

オペレーターズサイドのプレイ動画見てて作りたくなったので

サンプル

f:id:spi_8823:20200813005620g:plain

ソースコード

for free
説明はコメントを参照
値を適当にすると適当に調整できるよ
疑似乱数関数はこのあたりから

Shader "Hidden/Noise"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        // No culling or depth
        Cull Off ZWrite Off ZTest Always

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

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                return o;
            }

            //疑似乱数生成関数
            float rand(float3 co)
            {
                return frac(sin( dot(co.xyz ,float3(12.9898,78.233,45.5432) )) * 43758.5453);
            }

            sampler2D _MainTex;

            static const float division = 768;
            static const float blackinterval = 6;
            static const int blackheight = 1;
            static const float noisewidth = 0.01;

            fixed4 frag (v2f i) : SV_Target
            {
                //画面Y座標を分割
                int divisionindex = i.uv.y * division;

                //一定間隔で横に区切ったブロックを作る
                int noiseindex = divisionindex / blackinterval;
                
                //ブロックごとに横にずらす座標を決める前処理
                //時間による乱数のシード値(timeに互いに素っぽい数をかけたのを複数用意するといい感じになる)
                float3 timenoise = float3(0, int(_Time.x * 61), int(_Time.x * 83));
                //ときどき大きくずらす(時間のシード値が変更されるたびに5%の確率でノイズが10倍になる)
                float noiserate = rand(timenoise) < 0.05 ? 10 : 1;
                
                //横にずらす大きさを乱数で決める(0~1)(時間的にも位置的にもランダムになるように位置によるシード値と時間によるシード値を別次元で与える)
                float xnoise = rand(float3(noiseindex, 0, 0) + timenoise);
                xnoise = xnoise * xnoise - 0.5;             //ずれを2乗して0.5引く(2乗しないと乱れすぎる気がした)
                xnoise = xnoise * noisewidth * noiserate;   //ずれにスケールをかける
                xnoise = xnoise * (_SinTime.w / 2 + 1.1);   //時間的にずれに波があるようにする(いい感じになる気がする)
                xnoise = xnoise + (abs((int(_Time.x * 2000) % int(division / blackinterval)) - noiseindex) < 5 ? 0.005 : 0);    //ラスタースキャンっぽいノイズ
                
                float2 uv = i.uv + float2(xnoise, 0);

                //ぼやけさせる
                fixed4 col1 = tex2D(_MainTex, uv);
                fixed4 col2 = tex2D(_MainTex, uv + float2(0.005, 0));
                fixed4 col3 = tex2D(_MainTex, uv + float2(-0.005, 0));
                fixed4 col4 = tex2D(_MainTex, uv + float2(0, 0.005));
                fixed4 col5 = tex2D(_MainTex, uv + float2(0,-0.005));
                fixed4 col = (col1 * 4 + col2 + col3 + col4 + col5) / 8;
                
                col.rgb = divisionindex % blackinterval < blackheight ? float4(0, 0, 0, 1) : col.rgb;
                return col;
            }
            ENDCG
        }
    }
}

Blazorでコンポーネントの継承をする

ロジックの継承

ロジックの継承は@inheritsディレクティブで行う
メンバ変数やら関数やらが継承される

//MyBaseComponent.razor
@code
{
    //継承元
    public string MyValue;
}
//MyInheritsComponent.razor
@inherits MyBaseComponent
@code
{
    public string Text;
    protected override void OnInitialized()
    {
        Text = MyValue;
    }
}
<p>@Text</p>

見た目の継承

@inheritsディレクティブでは見た目(HTMLとかで記述した部分)は継承されない
ただ、「このコンポーネントのこの部分だけ書き換えたページを作りたい!」みたいなことがある
そういう場合は元ページでLayoutComponentBaseを継承し、書き換えるべき箇所(というか書き加えるべき箇所)に@Bodyプロパティを呼び出しておく
新しいページでは@layoutディレクティブで元ページを指定しておき、好きにHTMLなりを記述する
多分これは継承とは言わなくて、「レイアウトコンポーネントの利用」みたいな感じになるのだと思う

//MyBaseLayoutComponent.cs
<p>hogehoge</p>
@Body
<p>fugafuga</p>
//MyInheritsLayoutComponent.cs
@layout MyBaseLayoutComponent
<p>this is Body of MyBaseLayoutComponent!!</p>

こうすると、結果時にはこんな感じの見た目になる

<p>hogehoge</p>
<p>this is Body of MyBaseLayoutComponent!!</p>
<p>fugafuga</p>

どっちも継承

ロジックと見た目を同時に継承することもできる
inheritsディレクティブとlayoutディレクティブをべっこに指定するだけ
考えてみりゃそうなんだが、こういうやり方もあるよというメモ

//MyBaseComponent.razor
@inherits LayoutComponentBase
@code
{
    //継承元
    public string MyValue;
}
<p>hogehoge</p>
@Body
<p>fugafuga</p>
//MyInheritsComponent.razor
@inherits MyBaseComponent
@layout MyBaseComponent
@code
{
    public string Text;
    protected override void OnInitialized()
    {
        Text = MyValue;
    }
}
<p>inherits !!!</p>

【Blazor】WebAssembly版のWEBページを公開する

Blazorちゃん、ようやくWeb Assembly版が正式リリースされましたね!!(遅い)
というわけで、ASP.NET CoreでホストしないBlazorちゃんを公開する方法をメモ

プロジェクト作成

適当に作ってくれ。
ASP.NET Coreでホストしない」を想定してるので「ASP.NET Core hosted」のチェックは外す。

プロジェクトの発行

プロジェクトのディレクトリまで行ってdotnetコマンドを以下のように実行する。

dotnet publish -o hoge

するとhogeディレクトリにビルドしたものが出力される。
web.configファイルとwwwrootディレクトリがあるが、wwwrootに入ってるものが本体で、この中にindex.htmlとかも出力されている。

公開

ASP.NET Core hostedではない場合、Blazorちゃんは単体で動くようになっている。
したがって、wwwroot以下に作成されたファイルをWEBサーバーのhtmlドキュメントのルートディレクトリに置けばそれだけで動く。

rsync wwwroot/ /var/www/html/

ページを見に行く

これでちゃんとhttpd的なものが動いていればlocalhost:80とかでBlazorページを見に行くことができる。

3Dプログラミングで使う色々なベクトル計算を直観的に身体に覚えさせてあげますのよ【基本編】

【基礎編】に続いて、3Dプログラミングで使うことが多い基本的なベクトル計算を身体に覚えさせてあげますのよ!
spi8823.hatenablog.com

内積外積

これに関しては【基礎編】で触れたわよ。

点と直線の距離

| (p-q)-\{(p-q) \cdot r\} r |
f:id:spi_8823:20200607185634p:plain:w250

直線に下した垂線の交点へのベクトル

q+\{(p-q)\cdot r\} r

「点と直線の距離」と同様の考え方で簡単に求められますわね!

直線に下した垂線のベクトル

(p-q)-\{(p-q) \cdot r\} r

こちらも「点と直線の距離」の式で絶対値を求める過程を抜くだけですわ!
向きは直線側から点への方向だから、符号には気を付けるんですのよ!

直線に対する線対称位置

p-2\times 点から直線に下した垂線のベクトル

「点と直線の距離」の応用ですわ!
点と直線の相対位置が分かったのだから、直線から点pとは逆の方向に垂線を伸ばせばそれが対象な位置ですわね!

点と平面の距離

|\{(p-q)\cdot n\} n|
f:id:spi_8823:20200607212319p:plain:w250
ある点pと、点qを通り法線がnである平面との距離ですわ!
「点と直線の距離」の、直線の方向ベクトルrを平面の法線ベクトルnに読み替えて考えるのよ!あなたなら大丈夫ですわよね!(ただし直線に平行なのと、平面に垂直という風に微妙に関係性は変わっているわ)

平面に下した垂線のベクトル

\{(p-q)\cdot n\} n

「点と平面の距離」の応用ですわ!

平面に下した垂線の交点へのベクトル

q+(p-q)-\{(p-q)\cdot n\} n

これも「点と平面の距離」の応用すればすぐですわね!

平面に対する鏡面位置

p-2\times点から平面に下した垂線のベクトル

「点と平面の距離」の応用でちょちょいのちょいですわ!!

鏡面反射ベクトル

q = p + 2(p \cdot n)n
f:id:spi_8823:20200531235900p:plain:w350

まとめ

鷹揚な上流階級にはなれたかしら???

3Dプログラミングで使う色々なベクトル計算を直観的に身体に覚えさせてあげますのよ【基礎編】

初めに申し上げておきますわ!

お~っほほほほ!!
よくいらっしゃったわね!迷える子犬ちゃんたち!
この記事では3D空間上のオブジェクトを扱うのに便利なベクトル計算についてまとめたいと思いますのよ!
例えば、「ベクトルの回転」あるいは「面と点との距離」なんていうものに興味があるんじゃないかしら?
ベクトルというのはとてもよく考えられた仕組みで、こういったものが簡単に、たとえあなたのような子犬ちゃんにでも計算できるようになっているのよ。
そして私たちがやりたいと思う大抵のベクトル計算については公式を誰かが作ってくれているものよ。
え?じゃあその公式を使えばいいじゃないか、ですって?

シイイイィィッッット!!!!!!!

そんなだからいつまで経ってもヘボヘボのまんまなんですのよ!
貴族たるもの、いついかなる時でも優雅に、そして鷹揚としていなければなりませんわ。鷹揚としているにはどうすればいいのか。そう、応用ですの!!
覚えた公式をただコピペして使っているだけでは何かあったときに応用できずに鷹揚とできなくなるのですわ!!
Oh you!!応用!!鷹揚!!

でも安心なさい?迷える子犬ちゃんたちを救ってあげるのも淑女の役目というものですわ。
今からあなたたちにみっちりきっちり教えてあげますわ!
それも公式の導出方法ではなく、視覚的かつ直観的に身体に覚えさせてあげますのよ!
直観的に理解してしまえば、公式の導出なんてあとはちょちょいのちょいですの!
よろしくって?では行きますのよ!

基礎的事項の確認

以下、単純化のために2次元ベクトルで考えていきますわ。

ベクトルとは

ベクトルが何かも知らない人なんてここには来ていないでしょうけど、念には念を入れて一応説明しておきますのよ。
ベクトルとは「大きさと向きを持った量」である(presented by Wikipedia
この一言に尽きますわね。「あっちの方にバーーっと」というのがベクトルですわ。
ただこんな言い方をされても一部の選ばれし人間を除くほとんどの場合は、あっち?どっち?そっち?となるのが関の山ですわ。
だから北に一条、東に三坊、という風にちゃんとした基準となる方向と大きさを決めて、その組み合わせで指定するのが貴族というものですわ。
ベクトルの世界では基準となる方向をX軸方向やY軸方向、大きさを数字(スカラーと呼びますの)で表すといった風に決めていますわ。
f:id:spi_8823:20200525232437p:plain:w250
図のようなpというベクトルは数式でp = \begin{pmatrix} 0.5 \\ 1.2 \end{pmatrix}と表されることが多いんですの。
ベクトルを基準となる方向毎に分解したものをベクトルのX成分、Y成分という風に呼ぶことがあるわ。
それぞれベクトルを表す変数に添え字でどの方向に対する成分なのかを書いて、p_xp_yといった風に書くことが多いわ。

ベクトルの操作

ベクトルに対する操作は、各成分に対する操作に分解できることが多いわ。
例えばベクトルの大きさをa倍したい場合、ap = \begin{pmatrix} ap_x \\ ap_y \end{pmatrix}と各成分をa倍してあげればいいのよ。
f:id:spi_8823:20200526000101p:plain:w250

ベクトルの回転

ベクトルの回転は最も基本的なベクトル操作の一つですわ。
その中でも「ある単位ベクトルがX軸方向を向くように回転させる」方法を理解しておけば応用が利きますのよ。頑張りなさい?
回転させる前のベクトルを p = \begin{pmatrix} p_x \\ p_y\end{pmatrix} としますわよ?
これ各成分\begin{pmatrix} p_x \\ 0\end{pmatrix}, \begin{pmatrix} 0 \\ p_y\end{pmatrix}を辺に持つ長方形の対角線だと考えると、ベクトルの回転はこの長方形を回転させる操作だと考えることができるわね。
ここで各ベクトル同士の角度関係を考えると、回転によって長方形の各辺は次のように移動するわ。
イメージをつかみやすいように、下の図と一緒に見てみるとよいのですわ。

  • 回転前の長方形にとってのX軸方向 → ベクトルpY成分がマイナスになった方向

     \begin{pmatrix} p_x \\ 0\end{pmatrix} => p_x * \begin{pmatrix} p_x \\ -p_y\end{pmatrix}

  • 回転前の長方形にとってのY軸方向 → ベクトルpX成分とY成分が入れ替わった方向

     \begin{pmatrix} 0 \\ p_y\end{pmatrix} => p_y * \begin{pmatrix} p_y \\ p_x\end{pmatrix}
というわけで、「ベクトルp方向からX軸方向への回転」を「各成分ベクトルの回転に分解」することができたわね。
そして、長方形を回転させるために各辺を回転させるという操作は、pじゃなくても任意のベクトルに当てはめることができるわ。

したがって同じ方法を使って、別のベクトルqに対して、ベクトルpをX軸方向に回転させるような操作を行うこともできるわ。
     \begin{pmatrix} q_x \\ q_y\end{pmatrix} => q_x * \begin{pmatrix} p_x \\ -p_y\end{pmatrix} + q_y * \begin{pmatrix} p_y \\ p_x\end{pmatrix}
f:id:spi_8823:20200531190958p:plain:w350

式を書くと説明が長くなってしまったのだけれど要するに、

  • 任意のベクトルの回転は、各成分ベクトルの回転に分解することができる
  • 各成分ベクトルの回転は、回転の度合いを表すベクトルの成分を一部マイナスにしたり、X成分とY成分を入れ替えたものをかけることで実現できる

と言えば直観的に理解できるんじゃないかしら?

内積

内積とは、2つのベクトルp, qがあったとき、pq方向とqに直角な方向の成分に分解してq方向成分の大きさを求めるという計算ですわ。
(厳密に言えばこれはqが単位ベクトルだった場合の説明ですわ。そうでない場合、q方向成分の大きさにqの大きさだけかけたものが求まりますわ)
演算子はドットで表され、次のような式になりますの。
     p \cdot q = \begin{pmatrix} p_x \\ p_y\end{pmatrix} \cdot \begin{pmatrix} q_x \\ q_y\end{pmatrix} = p_x q_x + p_y q_y
言い換えれば、ベクトルpの先端からベクトルqの線分上に垂線を下した交点と原点との長さを求めていることになりますわね。
ただし、任意のベクトルpqを考えてしまうとどうしても話がややこしくなるから、両方に対してベクトルqをX軸まで回転させるような操作をしてから考えれば、あとはベクトルp'の先端からX軸に対して垂線を下した交点と原点との距離、つまりp'のX成分を求めるという話に単純化することができるわ。
これまた具体的なイメージは下の図を見なさい。
そしてこれは「ベクトルの回転」でやった内容と全く同じ操作をしてX成分だけ抜き取ることになりますのよ。
回転の式の上部分だけを見ればp_x q_x + p_y q_yとなっているでしょう?これが内積の値ですのよ。
     \begin{pmatrix} p_x \\ p_y\end{pmatrix} => p_x * \begin{pmatrix} q_x \\ -q_y\end{pmatrix} + p_y * \begin{pmatrix} q_y \\ q_x\end{pmatrix}
f:id:spi_8823:20200531203038p:plain:w300

内積の要約

要するに、内積とは「あるベクトルを回転させたX成分を求める計算」と考えていいんじゃないかしら?
そしてこれは2つのベクトルが同じような向きを向いているほど大きな値になることから、「2つのベクトルの方向の一致度を表す」と考えてもいいわね。
もちろんこれが数学的に厳密ではないと思うけれど、3Dプログラミングで使うには一番わかりやすい考え方なんじゃないかと思うわ。

外積

外積がベクトルの基礎の中では一番直観的に理解するのが難しいですわね……。
何しろ2次元の図では説明できないのだから、とてもイメージがし辛いわ。

外積とは、2つのベクトルp, qが与えられたとき、どちらのベクトルにも垂直でかつ大きさがp, qを2辺とする平行四辺形の面積であるベクトルを求める計算ですわ。
(ただしこれは3次元の場合で、2次元上では1つのベクトルから1つのベクトルを、4次元上では3つのベクトルから1つのベクトルをというように次元によって計算に必要なベクトルの数が増えていくようですのよ)
演算子はクロス\timesで表され、次のような3次元ベクトルの式になりますの。かける順番、引く順番を間違えると正しい値を求められないので注意しなければなりませんわよ。
     p \times q = \begin{pmatrix} p_x \\ p_y \\ p_z\end{pmatrix} \times \begin{pmatrix} q_x \\ q_y \\ q_z\end{pmatrix} = \begin{pmatrix} p_y q_z - p_z q_y \\ p_z q_x - p_x q_z \\ p_x q_y - p_y q_y\end{pmatrix}

p, qを2辺とする平行四辺形の面積

まずはこちらを考えてみましょう。
実はこれ、内積とよく似た操作を行っているのよ。
p, qをXY平面上に落とし込んだベクトル\begin{pmatrix} p_x \\ p_y \end{pmatrix}\begin{pmatrix} q_x \\ q_y\end{pmatrix}の各成分同士の計算によって、クロス積のZ成分p_x q_y - p_y q_xが定められているわね。
これ、どこかで見た記憶はないかしら?
そうよ!えらいわ!「ベクトルの回転」の式の下部分だけ見れば同じような式になっているわね!(正負が入れ替わっているのは計算の順番の関係だから適宜読み替えて頂戴!)
     \begin{pmatrix} p_x \\ p_y\end{pmatrix} => p_x * \begin{pmatrix} q_x \\ -q_y\end{pmatrix} + p_y * \begin{pmatrix} q_y \\ q_x\end{pmatrix}
つまり、ベクトルpを回転させたY座標を求めている、というわけですわね!
qの大きさが1なら、これはそのままpqが成す平行四辺形の大きさを求めていることになるんですのよ!
f:id:spi_8823:20200531212311p:plain:w250

ベクトルに垂直な向き

これが外積の難しいところね。
けど、これも2次元で考えればある程度直観に落とし込むことができるわ。
2次元上のベクトルに垂直なベクトルを求めるには「X成分とY成分の比を入れ替えたベクトルを、Y軸(X軸でも可)に対してひっくり返す」という操作を行ってあげればいいのよ。
f:id:spi_8823:20200531213310p:plain:w250
この考え方を3次元にも応用すると、「XY成分、YZ成分、ZX成分の比を入れ替えたベクトルを、Z軸(X軸、Y軸でも可)に対してひっくり返す」という操作になるわ。
「XY成分」と呼んでいるのは、2次元上では軸に対して垂線を引いていたところを、3次元上では面に対して垂線を引く必要があるからね。
そしてXY成分というのがまさにp, qをXY平面上に落とし込んだ時に成す平行四辺形の面積で表されるのよ。この値をZ成分に入れることが「成分の比を入れ替える」という操作になっていますのよ。
そして「軸に対してひっくり返す」という操作の結果、外積の式で表されるような引き算の順番になっているのですわ。

外積の要約

要するに外積とは

  • 各成分に直交する成分を求める(平面上に落とし込んだ2つのベクトルが成す平行四辺形の面積を求める)
  • 求めた直交成分の大きさを、対応する成分の大きさとする(傾きの逆数を求めていると言える)
  • どれか1つの軸に対してひっくり返す

という操作を行っていると考えるといいんじゃないかしら?
ちょっと2次元から3次元への拡張の部分とか色々おおざっぱな気もするけれど、私の美しい顔に免じて許しなさい!

まとめ

というわけで、ベクトル計算の基礎的な部分について直観的に身体で理解することができたかしら?
何?出来てない?そんなことはありえませんのよ!!ムキーーッ!
というのは冗談で、内積は平行成分を求める計算、外積は直交ベクトルを求める計算という風に覚えておけば後は何とかなると思うわ。
というか正直、内積外積をこれ以上直観的に理解できる方法があるならぜひとも教えてもらいたいわね!!

【Cg Programming/Unity】テクスチャ基礎 ~ テクスチャのアルファ値を活用する【順番にやっていく】

f:id:spi_8823:20200523202113p:plain

お~~~~~ヱ”ッほっほっほっけほっけほっ!

今日はこちらのチュートリアルをやっていきますわよ!!
en.wikibooks.org
en.wikibooks.org

この節では、テクスチャのアルファ値を活用して描画する方法を学びますわよ!
前回の内容が前提になるのでまずはそちらから見るのですわよ!せっかちなのは乙女に嫌われますの。
spi8823.hatenablog.com

前回はテクスチャのRGB値だけを使って描画していたのでしたわね!
そんなことじゃあ立派な貴族にはなれませんわ!わたくしのように第四の色を使いこなせないことには、社交界でみっともない姿をさらすだけですのよ!
この間なんて、全身アルファ値ゼロの服を纏った殿方が乱入してきて大変な騒ぎだったんですから!あなたはあんなことにならないように、ちゃんとここで勉強しておくのですわよ!

アルファ値を活用したテクスチャの描画

というわけで、あなたにはコレを渡しておきますわ。
何って、陸地の部分のアルファ値を1、海の部分のアルファ値を0にした地球の画像ですわよ。どうせ貧乏なあなたのことだから画像の一枚も持っていないと思って執事に用意させたのですわ。まったくもう、いい加減自分で画像くらい用意できるようになりなさい!
f:id:spi_8823:20200522031749p:plain

というわけで、まずは前回と同じように球の表面にテクスチャを貼り付けるんですのよ。もう一人で出来るわよね?
するとこんな風になったかしら?
f:id:spi_8823:20200522035022p:plain
こんな闇に包まれた地球なんて嫌ですけれども、今はこれが正しいんですのよ。海の部分は青色の代わりに(0, 0, 0, 0)となっていますの。このうちアルファ値を無視しているのだから真っ黒くなるのは当然ですわね。

テクスチャの透過

ではまずは一番オーソドックスなアルファ値の活用方法、アルファブレンディングをやってみるのですわ。
と言ってもtex2D関数で取得してピクセルの色として出力しているテクスチャの色情報にはすでにアルファ値が含まれているんですの。ですから、頂点シェーダやフラグメントシェーダはそのままpassをコピペして、あとは適切にアルファブレンディングを指定してやるだけで出来ますわね。
アルファブレンディングについて忘れてしまったという困った人はこの記事を見るといいのですわ!
さて、上の闇地球儀をアルファブレンディングして描画するとこんな風になりますわ。日本の裏側に南米大陸が透けて見えていますわね。
ブラジルの人~~~~!!聞こえるかしら~~~~~?!
f:id:spi_8823:20200523193131p:plain
コホン……、とまあこんな感じでテクスチャから色を取得・出力すること以外はやってることは変わらないですわね。
これだけじゃあ面白くないのでもう少しアルファ値で遊んでみるのですわ。

アルファ値を反射に適用

アルファ値は何もアルファブレンディングだけに使わないといけないわけではありませんの。
例えば、アルファ値が0のところだけ反射を描画するといったこともできますのよ。
この記事でやっているように反射による表面の色を計算して、アルファ値が0の場合には出力に反射の色を足してやればいいんですのよ。
ざっくり言えばこんな感じのフラグメントシェーダを書けばいいだけですのよ。

float4 f(v2f input) : COLOR
{
    float4 texColor = tex2D(_MainTex, input.texcoord);
    float4 reflectionColor = getReflectionColor(input);

    return texColor.a == 0 ? float4(reflectionColor.xyz, 0.7) : texColor;
}

余談なのだけど、シェーダでif文を書くのはパフォーマンス的にあまり好ましくないのですわ。
その代わりに3項演算子を使えば問題ありませんのよ。

と、そんな感じで描画したのがこれになりますわ。
鏡面反射や環境光による色がアルファ値が0の部分、すなわち海の部分だけに描画されているのがわかりますわね!
f:id:spi_8823:20200523202113p:plain

コード

Shader "Custom/TransparentTextures"
{
    Properties
    {
        _MainTex ("Main", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "Queue" = "Transparent" }

        //CGINCLUDE~ENDCGの部分に書いた関数や構造体はSubShader内で共通のものとして使うことが出来ますのよ!
        //複数のパスに同じようなコードを長々と書く必要がある場合にはこのように共通処理としてまとめて差し上げればすっきりしますのよ!
        CGINCLUDE
        uniform sampler2D _MainTex;

        struct vertexInput
        {
            float4 vertexPos : POSITION;
            float2 texcoord : TEXCOORD0;
            float3 normal : NORMAL;
        };

        struct v2f
        {
            float4 sv_position : SV_POSITION;
            float2 texcoord : TEXCOORD0;
            float4 vertexPos : TEXCOORD1;
            float3 normal : TEXCOORD2;
            float4 ambientColor : TEXCOORD3;
        };

        //法線ベクトルから環境光の計算をしてますの!
        float4 getAmbientColor(float3 normal)
        {
            float4 upAmbient = lerp(unity_AmbientEquator, unity_AmbientSky, normal.y);
            float4 downAmbient = lerp(unity_AmbientEquator, unity_AmbientGround, -normal.y);

            float4 ambientColor = upAmbient * step(0, normal.y) + downAmbient * step(normal.y, 0);
            ambientColor = ambientColor;
            return ambientColor;
        }

        //頂点シェーダのインプットからフラグメントシェーダに受け渡す値をとりまとめる関数ですわ!
        v2f vertexInput2Output(vertexInput input)
        {
            v2f output;
            output.sv_position = UnityObjectToClipPos(input.vertexPos);
            output.texcoord = input.texcoord;
            output.vertexPos = input.vertexPos;
            output.normal = input.normal;
            output.ambientColor = getAmbientColor(output.normal);

            return output;
        }

        //フラグメントシェーダのインプットから鏡面反射光の色を計算しますわ!
        float4 getReflectionColor(v2f input)
        {
            float4 worldPos = mul(unity_ObjectToWorld, input.vertexPos);
            float3 cameraDirection = (_WorldSpaceCameraPos - worldPos).xyz;
            cameraDirection = normalize(cameraDirection);

            float4 incidentVector = normalize(-_WorldSpaceLightPos0); 
            float isValid = step(dot(incidentVector, input.normal), 0);
            float3 specularDirection = incidentVector.xyz - input.normal * 2 * dot(input.normal.xyz, incidentVector.xyz);
            specularDirection = normalize(specularDirection) * isValid;

            float intensity = dot(cameraDirection, specularDirection.xyz);
            intensity = max(0, intensity);
            intensity = pow(intensity, 20);
            float4 color = float4(intensity * float3(1, 1, 1) + input.ambientColor, 1);

            return color;
        }

        //フラグメントシェーダで出力すべき色を計算しますの!
        float4 getFragmentColor(v2f input)
        {
            float4 texColor = tex2D(_MainTex, input.texcoord);
            float4 reflectionColor = getReflectionColor(input);

            //3項演算子を使って、アルファ値が0の場合は反射光を、それ以外の場合はテクスチャの色を出力しますのよ!
            return texColor.a == 0 ? float4(reflectionColor.xyz, 0.7) : texColor;
        }
        ENDCG

        pass
        {
            //まず裏面の描画
            ZWrite Off
            Cull Front
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex v
            #pragma fragment f

            v2f v(vertexInput input)
            {
                return vertexInput2Output(input);
            }

            float4 f(v2f input) : COLOR
            {
                return getFragmentColor(input);
            }
            ENDCG
        }

        pass
        {
            //次に表面の描画
            ZWrite Off
            Cull Back
            Blend SrcAlpha OneMinusSrcAlpha

            CGPROGRAM
            #pragma vertex v
            #pragma fragment f

            v2f v(vertexInput input)
            {
                return vertexInput2Output(input);
            }

            float4 f(v2f input) : COLOR
            {
                return getFragmentColor(input);
            }
            ENDCG
        }
    }
}

アルファ値を活用したテクスチャの描画は以上になりますわ。
これであなたも社交界で人気者ですわね!