イラスト、モデリング、Unity、VR関連

unityとかblenderとかvr関連の作業メモ

ShadersLaboratryのサンプルを雑に読んでいく-Miscellaneous

ーShaders

 

++省略後

頂点シェーダ:VS

フラグメントシェーダ:FS

ローカル座標のポジション:LPOS

ワールド座標のポジション:WPOS

 

+++++

Demo 97 - Texture switch depending player pos

頂点のWPOSを取得し、FSにてプライヤーとの距離を計算、一定範囲なら別のテクスチャを使用することで、プレイヤーの周りだけテクスチャを変更している

 

Demo 96 - UV rotation with Matrix

頂点のUV座標にインスペクタのパラメータから生成した回転行列を乗算することでテクスチャを回転させている。

v.texcoord.xy - pivot;

o.uv += pivot;

はUV座標の原点が左下にあるが、テクスチャの中心を軸として回転させるには都合が悪いため、原点をテクスチャの中心に移動させている

座標平面上における回転の公式 - 具体例で学ぶ数学

 

Demo 95 - Flag with lighting

TimeとSin波を用いて頂点のLPOSを周期的に変化

頂点移動後、cross(外積)を用いて法線ベクトルを取得し、ランバート反射を行ってる。xとzの0.01の加算は多分こういうことだと思う。

ベクトルの外積の結果は、二つのベクトルの垂直ベクトル、つまり法線になる。旗の面がx,z成分で構成されているため、x,z成分を加算したベクトルの外積を取れば旗に対する垂直な線・法線が取得できる。

f:id:arumogina:20190531155507p:plain

 

ランバートについてはこちらを参考に

【Unityシェーダ入門】ランバート拡散照明モデルを試す - おもちゃラボ

mul(normal, (float3x3) unity_WorldToObject);

 

は法線ベクトルをワールド座標に変換する。

数学的な原理は忘れたが、

UnityObjectToWorldNormalの中身は

return normalize(mul(norm, (float3x3)unity_WorldToObject));

である

 

Demo 92 - Outline and screenspace texture

1回目のパスでクリップ空間における法線方向に頂点座標を膨張してアウトラインとなる色で塗りつぶす、つまり本来より大きいサイズで描写される

2回目のパスでテクスチャをスクロールしつつテクスチャを描写

float3 normal = mul((float3x3) UNITY_MATRIX_IT_MV, v.normal);
normal.x *= UNITY_MATRIX_P[0][0];
normal.y *= UNITY_MATRIX_P[1][1];

が少々ややこしいが、パースペクティブ変換行列の要素を成分ごとに乗算してるだけなので

mul(UNITY_Matrix_MVP,normal);

だけで同じ、normal.x,yが得られる(はず

 

Demo 91 - Green chromaKey to transparency

TRANSFORM_TEXはテクスチャのタイリングとオフセットのためのマクロ

ceilはx以上の最小の整数、つまり切り上げ

saturate:x を [0, 1] の範囲にクランプ

なのだが、色をいじってる部分の意味が分からん

 

Demo 89 - Dissolve depending depth value

 ディゾルブに関してはこっち読んだほうが勉強になる

【Unityシェーダ入門】Dissolve(溶けるような)シェーダをつくる - おもちゃラボ

 

Demo 83 - Invert color

色の反転。rgb値が0~1なので、1から現在のrgbを引くことで反転させている

 

Demo 76 - Fog

近づくほど霧が消えて、遠ざかれば霧が濃くなる

i.pos.z / i.pos.wは深度、カメラの視錐体の奥行を0~1の間で推移する

その70 完全ホワイトボックスなパースペクティブ射影変換行列

Maskの意味はよくわからなかった。

 

Demo 72 - Texture depending lighting

lerp(x, y, s) => x + s(y - x)

lerpはx~yの差分間の値を0~1を取るパラメータsによって返す,

s=0のときはx ,s=1のときはyが返る

dotは内積

maxは引数の内、大きい方を返す

光の入射ベクトルと法線ベクトルの内積が大きいほど、ライティングは暗くなるので(二つのベクトルが垂直のとき、内積は0)

o.light = max(0, dot(worldNormal, lightDir));

lerp(col2, col1, i.light);

は明るいほどcol2を、暗いほどcol1を使用している

【Unityシェーダ入門】ランバート拡散照明モデルを試す - おもちゃラボ

 

Demo 61 - Fading when too edge

step(a, x)  => (x >= a) ? 1 : 0 

なので、

col.a *= step(_Threshold + 0.01, i.val);

は視線方向のベクトルとオブジェクトの法線ベクトルの内積がThresholdよりも大きいとき(正面からではなく、より斜めに見えているとき)1を返して透過する。

 

Demo 60 - Fur shader

Furシェーダ、もふもふに出来る。

Passブロックを何度も使用し、FurStepパラメータを使って多数回レンダリングしている。FurStepが大きくなるほどアルファ値が大きくなり、頂点も法線方向に延びるようになっている

 

Demo 53 - Vertex color depending ID

頂点カラーのサンプル

fmod(x, y)  x/y  の浮動小数点の剰余を返します。

floor(x)  x 以下の最大の整数を返します=>切り下げ

fmod(id, floor(_Val)) == 0

はidが_Valで割り切れる場合であるので、

if(fmod(id, floor(_Val)) == 0)
o.col *= _Color1;

これは周期的に特定の色を使う、というコードである。

 

Demo 51 - Discard faces depending normal and camera

カメラ(視線)のベクトルとオブジェクトの法線ベクトルの内積が大きいのみ、つまり正面にある場合のみ描画する。非正面の場合はdiscardで処理を切り上げて非表示にしている

 

Demo 48 - Alpha depending distance camera

カメラとの距離に応じてアルファ値を変える。

saturate(x)  => x を [0, 1] の範囲にクランプします。

 

Demo 45 - Section

一定以上のy座標、一定以上高い場合、描写しない

v.vertex.xyz *= _EdgeWidth;

で切り口の幅を調整しているのだが、高さとなるy座標まで増加させてるが、それも含めて一定以上の高さになるとdiscardしている。

またコードを実際に動かすとカリングされたせいか切り口が透明になっていた。

 

 

Demo 44 - Burning paper

ディゾルブの応用

ノイズテクスチャのrgb値が高い(コード中ではrが使われてるが、モノクロなのでrgbの値が同じ)、つまり白いほど低いThresholdで消える。

Threashold - 0.04

Threadhold

とすることで、完全に透過する場合と、赤色を描画する場合、加工しない場合の3パターンに分けている

Timeを加算すれば時間経過で消していくことが出来る。

 Lerpの第三引数にboolを渡してるところあるけど、0,1に暗黙の型変換が行われるんじゃないか、とのこと。

 

Demo 42 - Varying color with ramp texture

単純だが面白いシェーダ

MainTexのr値によって色の元となるRmpTexから取得する色を変えて、時間経過でさらに色を変えている

 

Demo 41 - Animated flag

Cosカーブでy座標をローカル座標で周期的に動かしたのち、クリップ空間へ変換している

 

Demo 39 - Blur effect with grab pass

Blur,ぼかし

一つ目のパスでX軸方向にぼかし、二つ目のパスでY軸方向にぼかしている

 

#pragma fragmentoption ARB_precision_hint_fastest

// フラグメント処理で、できるだけ精度を下げて実行時間を最小限に抑えたいときのオプション(GPU)

【Unity】頂点+フラグメントシェーダープログラミング 1 | albatrus.com

o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = ComputeGrabScreenPos(o.pos);

は頂点とGrabテクスチャ上の位置の関連付け

 内部的には以下のビューポート座標系への変換、と同じようなことをしているのではないかと思われる。

http://light11.hatenadiary.com/entry/2018/12/12/235852A

 

_GrabTexture_TexelSize

テクセル(英: texel)は、コンピュータグラフィックスで使用するテクスチャ空間の基本単位である[1]。texture element の略、または texture pixel の略とされる。画像(ピクチャー)がピクセルの配列で表されるように、テクスチャはテクセルの配列で表される。

{TextureName}_TexelSize - テクスチャサイズの情報を含む float4 プロパティー

x には 1.0/width が含まれます
y には 1.0/height が含まれます
z には width が含まれます
w には height が含まれます

 

Cg/HLSL でシェーダープロパティーを参照する - Unity マニュアル

なので、x,yには1テクセルあたりのサイズが入っている

 

Demo 35 - Deform with Bezier curve

ベジュ曲線をシェーダでやるというもの。

球体だとうまく動く。

計算式はそう動くもの、と思っておく

 

Demo 33 - Bilboard Bush

張り付けたテクスチャが常にカメラの方向を向く。常に正面を向くエフェクトをビルボードエフェクト、と呼ぶらしい。


mul(UNITY_MATRIX_P,
mul(UNITY_MATRIX_MV, float4(0, 0, 0, 1)) + float4(v.vertex.x, v.vertex.y, 0, 0)) ;

を分解すると

s = mul(UNITY_MATRIX_MV, float4(0, 0, 0, 1));

t = float4(v.vertex.x, v.vertex.y, 0, 0);

u =  mul(UNITY_MATRIX_P, s+t);

;

MATRIX_MVはワールド座標系=>正のz方向を向いたカメラを原点としたビュー座標系への変換

s+tはビュー座標系においてvetexの座標を加算している

https://web.wakayama-u.ac.jp/~tokoi/lecture/gg/ggbook03.pdf

によれば、平行移動や拡大縮小などの線形変換をアフィン変換と呼び、アフィン変換で行列計算を行う場合、三次元ベクトルに(x,y,z,1)と成分を一つ増やし1を置く。これを同次座標と呼び、同次座標と実座標(元の三次元座標)は 実座標の各成分=同次座標の成分/ w

の関係にある。

アフィン変換の変換行列はw成分に影響を与えないので、

sはx,yzにのみワールド変換、ビュー変換を受けている。

その55 そもそも「w」って何なのか?

言い換えれば、(0,0,0)はローカル座標におけるオブジェクトの中心点であるので(この場合は。Unityでオブジェクトを生成した場合は(0,0,0)になるが常にそうなるわけではない。ローカル座標は飽くまでここのオブジェクトが持つ座標でしかない)、sはオブジェクトの中心点のみビュー座標へと変換したことになる。

MATRIX_MVには回転の処理も含まれているが、中心点のみが回転し、それを構成する頂点にはMATRIX_MVが乗算されてないので中心点以外の頂点は回転処理が行われてないことになる.MATRIX_Pは奥行に合わせた縮小処理なので回転処理を行わない。

また、tはz=0,つまり奥行を持たないので、s+tはx軸、y軸で構成される平面において、(s.x,s.y)を中心にして、vetex.x,vertex.y(中心点から処理対象の頂点へのオフセット)のオフセットを取ることでオブジェクトが構成されることになる。だからカメラやオブジェクトを回転させてもテクスチャが回転しなくなる

(オブジェクトを回転させていても、その回転分はvertに入った段階で頂点座標として吸収されている。回転も頂点単位で見れば平行移動)

*1;

ワールド座標における法線ベクトル

normalize(_WorldSpaceCameraPos - mul*2;

は頂点を原点にした場合のカメラの視線ベクトル

float val = 1 - abs(dot(i.viewDir, i.normal)) * _RimEffect;
return _Color * _Color.a * val * val * t;

は視線ベクトルと法線ベクトルの内積が大きいほど、つまり平行に近いほどvalが小さくなるアルファ値を小さくなるので透明度になる

 

Demo 24 - Shadow with a vertex shader

 Tags { "LightMode"="ForwardBase" }

 ForwardBase : Forward Rendering で使用され、環境光、メインのディレクショナルライト、頂点/SH ライトが適用されます

ShaderLab : Pass 内の Tags - Unity マニュアル

#pragma multi_compile_fwdbase

UnityのShader Variantについて調べてみた - Qiita

LIGHTING_COORDS(0, 1)

構造体内に_LightCoordと_ShadowCoordを定義する

引数には未使用のTEXCOORD#idxを指定する

 

 

TRANSFER_VERTEX_TO_FRAGMENT

ライトの情報を適切にフラグメントシェーダに渡す」という処理

 

LIGHT_ATTENUATION
ライトの減衰率を計算するマクロ。

_LightCoordへ書き込まれた情報を元に、フェッチするテクセルの場所を計算。そして_LightTexture0(ライトの諸々の情報を格納しているテクスチャ)から適切に値を取り出します。

[Unity] AssetStoreのファーシェーダをupdateしたので分かったことを書いてみる - Qiita

 

Demo 22 - Echolocation

float dist = distance(_Center, i.worldPos);

オブジェクトの中心からの距離を計算

 

float val = 1 - step(dist, _Radius - 0.1) * 0.5;

step(a, x) (x >= a) ? 1 : 0 を返します。

  val = step(_Radius - 1.5, dist) * step(dist, _Radius) * val;

が分からない。

 

Demo 20 - Distort with Grab pass

GrabPassでレンダリングしたテクスチャを取得し、歪ませて再度レンダリングしている。

ComputeGrabScreenPos(o.pos);

頂点のGrabテクスチャ上の座標を取得

 

i.grabPos.x += sin*3;

グラブテクスチャ上のカラーを取得

tex2Dprojとtex2Dの違いは、w除算を行うか否か。Dprojは除算する

Unityで投影テクスチャマッピングを実装する際に抑えておくべき基礎情報 - 土屋つかさの技術ブログは今か無しか

 

 

Demo 19 - Outline 3D model

重ね塗り方式のアウトライン

頂点をx,y成分においてのみ法線方向に膨らましす、つまり画面の平面上にのみ膨らましてアウトラインとなる色を描写し、二回目のパスで通常通りに描写している

 

Demo 17 - Jelly effect

オブジェクトをぷよぷよさせるシェーダ

sign 正なら1,負の数なら-1を返す

 

Time.wは

_Time float4 時間 (t/20,t,t×2,t×3)

なので、t*3

 

v.vertex.x += sign(v.vertex.x) * sin(_Time.w)/50;

sin,cosは-1~ 1を行き来するので、それでプルプル震わせている

 

v.texcord

appdata_base => float4 texcoord : TEXCOORD0;

 

Demo 15 - Sprite outline

ceil(x) x 以上の最小の整数を返します。

なので

outlineC.a *= ceil(c.a);
outlineC.rgb *= outlineC.a;

アルファ値が1なら不透明、0なら透明なので、テクスチャのアルファ値が0、つまり透明部分のとき、アウトラインのrgbも0、つまり黒にしている

 

そのあとのalphaUp等で周囲のテクスチャのアルファ値を取得

ceil(alpha_up * alpha_down * alpha_right * alpha_left)はアルファが0~1であるため、0,1しか返さないので、

return lerp(outlineC, c, ceil(alpha_up * alpha_down * alpha_right * alpha_left));

にてalpha_の全て1ならテクスチャカラーを、一つでも1でなければアウトラインカラーを返している。つまり、テクスチャが透明部分のときはrgbaが(0,0,0,0)となってるアウトラインカラーを返し、テクスチャの透明部分と不透明部分の境界線では設定したアウトラインカラーを返し、それ以外ではテクスチャカラーを返している。

 

Demo 14 - Misc with Grab pass

拡大している。

w成分を加算するとは、本来よりもx,y成分を除算する数を大きくする、実際より奥行があるとして計算しているということである。

縮小率が上がるということはその分ピクセルデータが減るが、表示する領域サイズはデータが減る前と変わらないので、拡大されてぼやける、ということだと思う。

その55 そもそも「w」って何なのか?

 

Demo 13 - Lava with flow map

f:id:arumogina:20190602224719p:plain

frac(x) x の小数部を返します。

float dif1 = frac(_Time.y * 0.25 + 0.5);

は0~1を周期的に繰り返してる。

他のところも周期的な繰り返しをしてるのがコードの意味が良く分からない。ただシェーダは理屈というより、この数値だとうまくいく、みたいなことが多いっぽいので、これはその類なきがする

 

Demo 12 - Pixelation

half ratioX = 1 / _PixelNumberX;

は1ピクセルあたりのサイズ

 

(int)(i.uv.x / ratioX) * ratioX

i.uv.x /ratioX => i.uv.x * PixelNumerXとなる

uv座標は0~1なので計算結果は0~500の値を取る

これはhalf型でそれをintにキャストしてるので少数部位が切り捨てられるので、整数間の値が同じなる。

例えば、> i.uv.x * PixelNumerX = が1~ 1.9のとき、intのキャストによる常に1になるので

(int)(i.uv.x / ratioX) * ratioX  = ratioXとなる。

yも同様。

これはuv座標の計算であるので、一定範囲間は同じ色が返されることになる

 

Demo 10 - Water distortion with noise

ノイズテクスチャとTimeと手動のパラメータでuv座標を変化させている

 

 

*1:x,y,z)を(x,y,0)にするということは要するに平面に圧縮するということである)

 

Demo 32 - Volumetric sphere in a cube

レイマーチングのサンプルとなっている

 

Demo 27 - Moss on rock

Directionと法線の内積が一定値より大きい場合、つまりコケのテクスチャを表示している

 

Demo 26 - Force field with rim effect

ZWriteは

 オブジェクトのピクセルをデプスバッファに書き込むかどうかを制御します (デフォルトは On)。実線でオブジェクトを描画する場合は、これを On にします。半透明の効果を描画する場合は ZWrite Off にします。

normalize(mul((float3x3)_Object2World, v.normal.xyz

*2:float3x3)_Object2World, v.vertex.xyz

*3:_Time.y + i.grabPos.y) * _Intensity)/20;

時間経過とともにx座標を変化

 

UNITY_PROJ_COORD

4 次元ベクトルを渡すと、投影されたテクスチャ読み込みに適切なテクスチャ座標を戻します。ほとんどのプラットフォームでは渡された値そのものを戻します

 

tex2Dproj(_GrabTexture, UNITY_PROJ_COORD(i.grabPos