グラフィクス

ジオメトリシェーダでファーシェーダを実装した話

こんにちは、mo-takusanです。
今回は題名の通りファーシェーダを実装しました。

https://kcs1959.jp/archives/1928/research/unity%E3%81%A7fur-shader

こちらの先輩の記事を見て私も実装してみようと思い、今回取り組んでみました。
またググってみると、unityでファーシェーダを実装している記事は多くあるのですが、ジオメトリシェーダを利用しているものが見当たらなかったため、今回はこれを利用したものにすることを目標としました。

完成図は図のようになります。左側が今回実装したファーシェーダになります。左右どちらの球も同じテクスチャを貼っているのですが、違いは一目瞭然ですね。

2018-09-16_11h12_28

なお、実装においてはこちらの記事を大いに参考にしました。
https://qiita.com/edo_m18/items/75db04f117355adcadbb

また、実装において利用したテクスチャは次のサイトから使わせて頂きました。
http://photoshopvip.net/25382

ファーシェーダとは

ファーシェーダとは、その名の通り毛並みのふわふわ感を表現するシェーダを指します。
ファーシェーダは主にシェル法やフィン法などの実装方法がありますが、今回はシェル法を用いていきます。

シェル法

シェル法においては毛並みを一本一本描画していくわけではなく、層状に毛の断面を描画していくことで、全体として毛並みのように表現する手法です。
具体的には、次のように行います。

  1. 各頂点に対してそれぞれの法線方向に一定距離だけ移動した頂点を生成し、元のメッシュより一回り大きいメッシュを生成する。
  2. メインテクスチャとは別に毛束の断面のようなテクスチャを用意し、そのテクスチャのRGB値が一定を超えたテクセルに対してのみメインテクスチャの色を乗せ、それ以外の部分は描画を行わないようにする。
  3. メインテクスチャを貼る閾値を少し大きくしていきながら、1.及び2.の操作をくり返す。

1.の操作をジオメトリシェーダで、2.の操作をフラグメントシェーダによって実装していきます。

先述のようにシェル法では毛を一本一本描画するわけではないため、層の数が十分でない時は毛束に見えません。以下の図に示すように層が目立ってしまうことが分かると思います。

2018-09-16_11h19_14

実装

それでは実装を示していきます。
先述のように、1.をジオメトリシェーダで、2.をフラグメントシェーダで実装します。

まずは1.です。

[maxvertexcount(81)] //これ以上多くの頂点を生成することはできない
void geom(triangle appdata input[3], inout TriangleStream outStream)
{
    const float spacing = 0.35;
    const int start = _Iterator * START;
    const int end = start + _Iterator;

    [unroll(27)]
    for(int x = start; x < end; x++)
    {       
        float shellPos = _ShellInterval * x;

        [unroll]
        for(int y = 0; y < 3; y++)
        {
            g2f o;

            appdata v = input[y];

            float3 forceDir = 0;
            float4 pos = v.vertex;

            //ここの値はなんでも良さそうなのでそのまま流用させて頂いた
            forceDir.x = sin(_Time.y + pos.x * 0.05) * 0.2;
            forceDir.y = cos(_Time.y * 0.7 + pos.y * 0.04) * 0.2;
            forceDir.z = sin(_Time.y * 0.7 + pos.y * 0.04) * 0.2;
            forceDir += _Gravity - _Inertia;

            float factor = pow(shellPos, 3.0);
                        
            float3 norm = v.normal;
            norm += forceDir * factor;
            norm = normalize(norm) * shellPos * spacing;

            pos.xyz += norm;
            pos.w = 1.0; 

            o.position = UnityObjectToClipPos(pos);

            o.uv = v.texcoord;

            TANGENT_SPACE_ROTATION;
            o.lightDir = normalize(mul(rotation, ObjSpaceLightDir(pos)));

            TRANSFER_VERTEX_TO_FRAGMENT(o);

            o.iter = x;

            outStream.Append(o);
        }
        outStream.RestartStrip();
    }
}

今回ジオメトリシェーダでは三角メッシュを受け取って三角メッシュを出力します。それぞれのメッシュは依存関係がないため、PointStreamの入出力でもよさそうですが、仕様上PointStreamで出力してしまうと、面を貼ってくれなくなってしまうため、このように記述するしかありません。
ちなみに、maxvertexcountが81になっていますが、これより大きい3の倍数でVaridation Errorになってしまうためです(maxvertexcountのリファレンスを読んだのですがこれに関する記述は見当たりませんでした)。この値はフラグメントシェーダに値を渡す構造体の大きさにも依存していたので、渡せるデータサイズの上限値があるのでしょう。

残りは先ほど提示したページの実装とほとんど同じものです。
異なる部分はライティングのためのものになりますが、今回の内容と直接の関係はないので割愛します。触りだけ説明すると、ライトの方向を計算し、ライト情報を適切にフラグメントシェーダに渡しています。
詳しくはこちらの記事をご覧ください。
https://qiita.com/edo_m18/items/21d3b37596da3fd4b32b#%E9%96%A2%E9%80%A3%E3%83%AA%E3%83%B3%E3%82%AF

2.についてもライティング以外は同様の処理です。違いといえばノーマルマップによるライティングを行っている程度です。

さて3.についてですが、先ほどmaxvertexcountの値が81だと述べましたが、これでは層の数が最大でも27層までとなってしまい、元々表現したかったフサフサ感が損なわれてしまいます。これでは本末転倒なので、今回の実装でも参考ページと同様に同じパスを何度も記述して層の量増しを行いました。
といっても一回のパスで27層描画できるので3つだけ同じパスを用意しました。

ジオメトリシェーダによる実装のメリット

ジオメトリシェーダを利用して実装を行うと、プラットフォームによっては動かない可能性があるため、望ましくないことも多々あります。しかしながら、ジオメトリシェーダによってファーシェーダを実装した場合、次のようなメリットが考えられます。

  • 全く同じパスを数十個書く必要がないためスマート
  • バッチ数を抑えられる
  • 層の数を可変にすることができる

特に、層を可変にできることでお好みのフサフサ具合を表現でき、非常に便利でしょう。バッチ数を抑えることで
パフォーマンスは改善されるのでしょうか?ジオメトリシェーダがどれほど重いか計測が面倒だったので言及しないことにします。

まとめ

ジオメトリシェーダを利用することで、比較的スマートにファーシェーダを実装することができました。シェーダはすぐにビジュアルに表れるので実装していてとても楽しいですね!

ブルースカイ・カタストロフィ

ブルースカイ・カタストロフィ(つよそう)

スクリーンショット 2018-05-05 17.10.41

↑は参考にあった Blue sky orbit in the Gavrilov-A. Shilnikov model でパラメータも同じもの.

以下は, \(\varepsilon\) をそのまま固定しながら, \(\mu\) を \([0.1, 1.1]\) の範囲で動かしたもの(0.02 間隔で 80000 回のルンゲクッタ.最初の 1000 回は捨てている).

ローレンツアトラクタとカオス

こんにちは.TRSasasusu です.

記事を更新していないだけで,KCS は活動しています.新歓の準備も進んでいるようです.

さて,自分は生命情報学科に所属していますが,この前のレポートにてカオスで遊んだので,今回はその話をします.

スクリーンショット 2018-02-01 15.46.11

これはローレンツ方程式(↓の式)のアトラクタです.この軌道に沿って点が移動していきます.


\begin{align}
\frac{dx}{dt} &= -\sigma x + \sigma y \\
\frac{dy}{dt} &= -xz + rx -y \\
\frac{dz}{dt} &= xy – bz
\end{align}

特にこのようなフラクタル構造を持つアトラクタはストレンジアトラクタと呼ばれ,カオスを生み出します.蝶の羽みたいで美しいですね.

そして,パラメータ(今回は \(\sigma,\ r,\ b\))を変化させると,カオスになったりならなかったりします.これを表すのが分岐図です.

スクリーンショット 2018-02-01 16.07.43

なんかすごいですね.そして,カオスを定量的に表すのがリアプノフ指数(多次元でまとめたものがリアプノフスペクトラム)です.力学系を表すヤコビ行列を時系列順に並べて QR 分解して(時刻 \(0\) では時刻 \(0\) のヤコビ行列のみを,それ以降ではヤコビ行列に前時刻の \(Q\) を右から掛けたものをそれぞれ QR 分解する),各時刻の \(R\) のそれぞれの対角要素の \(\log\) をとって全部足し合わせて \(N\) で割ると,以下のようになります.

スクリーンショット 2018-02-02 1.06.55

1 つでも正のリアプノフ指数があればカオスとなるようなので,この結果は分岐図のものと一致していますね.

最後に,ストレンジアトラクタの動画を載せて終わりにします(動画はレポートには載せられない).

Blender Cycles “Lamborghini Gallardo LP570-4″作成 Part2:Material

こんにちは、画像の使いすぎでメディアを占領してしまい申し訳なくなってるmoumouです。
前回のモデリング編に続いて、Part2:マテリアル編を書いていきたいと思います。

応用が効きそうなことだけ書いているので、だんだんランボルギーニ関係なくなってます(笑)

 

このような流れで書いています。

  1. 全体の設計&モデリング編(+ドローン動画)
  2. マテリアル設定 (←今ここ)
  3. ライティング& 環境設定
  4. コンポジットによる実写合成

今回の目次

  1. フレネル効果
  2. バンプマップを利用したノード構成
  3. 綺麗な床のマテリアル

cyclesを扱ううえで知っておくと良いことを書いていきます。

続きを読む

Blender Cycles “Lamborghini Gallardo LP570-4″作成 Part1:Modeling (+ ドローン動画)

こんにちは、moumouです。

今更ですがBlender Cycles講習会、楽しんでもらえたみたいでよかったです。

今回は、みなさんご存じ超高級車”ランボルギーニ“をBlender Cyclesにて作成しました。

 

春休みには作り終えてたモデルなのですが、記事書くのがめんどかった,いや, ある程度長くなる記事だったので,  今更ですが連載記事として投稿したいと思います。

連載記事ということで、以下のような流れに分けて書いていきます。

  1. 全体の設計&モデリング編(←今ここ)
  2. マテリアル設定
  3. ライティング& 環境設定
  4. コンポジットによる実写合成

Blender経験者なら分かるように書いていくので、説明記事みたいになるかもしれません。

基本的に役に立ったことをメインで書いています。

今回の目次

今回のモデリング編は3ページの構成です。最後はオマケです。

このページ:  3Dモデル,モデリングの流れ
2ページ目: 小ネタ, モデリングの際に役立ったこと
3ページ目: ドローンお土産動画

 では早速、Part1.モデリング編を書いていきたいと思います。が、ぱっと見てさっと記事閉じる人向けに(笑)、まずはじめに今回作成した画像と3Dモデルをひとつ載せておきますね。

3Dモデル

↓が3Dモデルです。▶を押して約15秒後3Dモデルを見ることができます。マウス操作で360°回転,拡大できます。また、右下の設定ボタンでワイヤフレームを見たりMatCapで見たりできます。

Lamborghini gallardo lp570-4
by moumou
on Sketchfab

………………………….

続きを読む

反応拡散系シミュレーション

Simulation 1

\[\frac{\partial{u}}{\partial{t}}=20 Hill(u,0.2,2)/v – 80U +1 \]

\[\frac{\partial{v}}{\partial{t}}=20 (u-v) \]

Simulation 2

\[\frac{\partial{u}}{\partial{t}}=20 Hill(u,0.2,2)/v – 80U +1 \]

\[\frac{\partial{v}}{\partial{t}}=30 (u-v) \]

Simulation 3

\[\frac{\partial{u}}{ \partial{t}}=0.5 \Delta u +20 Hill(u,0.2,2)/v – 80U +1 \]

\[\frac{\partial{v}}{ \partial{t}}=5.0 \Delta u +30 (u-v) \]

(dx=0.1,dt=0.0005)

 

samneiluSAMUNEIRU

Zバッファを可視化するプログラムをUnityで作った.Zバッファを可視化するプログラムをUnityで作った.

これは,Z-Bufferを可視化するプログラムです.

Z-Bufferとは,簡単に言いますと,距離センサーのことです.

カメラからどれくらい離れてるかをしめすんですね.

このZ-Bufferを可視化すると次のようになりました.

キャプチャ.JPGasdfasfasdfafサムネイル

フラクタルギャラリー2

フラクタルギャラリー1」の続き。

要旨

ST法により等角写像を繰り返し用いた3Dフラクタルを実装できる。

等角写像を用いる順番、種類、回数を変えることにより、様々な3Dフラクタルを実装できる。

描画結果

f5f4f3f2f1f

最後の奴は、何かの膜で包まれているようにも見える。

フラクタル・ギャラリー1

f
サムネイル

前回の記事もそうだったように、レイトレーシングの手法の一つにSphereTracing法というものがある。

このSphereTracing法の距離関数を上手く設定することで、3Dフラクタルを現実的な速度で書く事ができます。

3Dフラクタル描画の問題点

 

ff
(図1)開発中のフラクタル・エディタ(2Dver)

一般的に、GPUを使って2Dフラクタルを実装する際は、フラクタルを描画したい閉区間をピクセルにラスタライズして、各点がそのフラクタルの内部にあるか外部にあるか確かめねばならない。(図1)

これを常識的に拡張すれば、当然3Dのピクセル、すなわちボクセルの集合体として閉空間をラスタライズし、フラクタル内部の点かどうか確かめ適当な射影行列で2Dにマッピングするという考えに至るだろう。

しかし、これには問題がある。それは計算量の問題である。2Dフラクタルの計算量は時間計算量・空間計算量ともにO(n^2)であり、これを拡張した3DフラクタルではO(n^3)となっている。これは非常に大きい。

距離関数の設計

そこで、SphereTracing法である。すなわち、ある点が与えられた時、その点から最も近いフラクタル立体の距離がわかれば、ST法を用いて計算量O(n^2)で2Dマッピングができるというわけだ。

詳細な距離関数の設計は他所(Hypercomplex Iterations,Distance Estimation and Higher Dimensional Fractals, Yumei et al.)に譲るとして、フラクタルの構成の際に、すべてが等角写像であれば、距離関数d(r)は

d(r) > sd(r) = 0.5rlog(f(r))/abs(grad(f(r)))

で計算できることが知られている。ここで等号ではなく不等号が使われているのは、距離関数の近似を用いているためである。ST法ではsum(n=0…∞)d(r_n)=sum(n=0…∞)sd(r_n)が成り立つとき、dをsdで代用できる(自明である)。

これを用いて、実際にフラクタルを実装してみた結果が、次のようである。

描画結果

これらは、現在開発中の「フラクタル・エディタ(3Dver)」の出力結果である。解像度がころころ変わっているのは気にしないで欲しい。「フラクタル・エディタ(3Dver)」はリアルタイム式(Unity)と高解像式(CUDA)の二種類を開発している。しかし、やっていることは変わらないのであんまり意味は無い。しいて言えば、CUDA式のほうが拡張性が高いといったところである。現在、フラクタル・エディタに進化計算システムを追加しようとしている。

imagee5

mandelbox_mmm

ff