い、い、、いっくし!!

各記事はkwskってコメントすると詳しく書き直します

彼らが最初

彼らが「改正健康増進法」を成立させたとき、私は彼らを受け容れた 私は喫煙者ではなかったから

隣の島で「ネット・ゲーム依存症対策条例」が可決されたとき、私は声をあげなかった 私は島民ではなかったから

彼らがxxxxxたちを攻撃したとき、私は声をあげなかった 私はxxxxxではなかったから

そして、彼らが私をyyyしたとき 私のために声をあげる者は、誰一人yyyyyyyy

Shader Graphを触ってみる

この記事は、ボイゲ Advent Calendar 2019 - Adventar 5日目の記事です。

ひとつ前(4日目): www.nicovideo.jp ひとつ後(6日目): hassakulab.com

まとめ

  • 背景
    • Shaderとか難しい、触ったことない、でも触れたらなんかすごそう
  • 手法
    • UnityでShader Graphというのが実装されたから使ってみる
  • 結果
    • Shader Graph怖くない

Shaderとは

普通のプログラムが使うCPUではなく、GPUで描画させるために必要な処理を記述したもの

Shader Graphって何がいいの

今までのShaderは、専用の(Cライクな)言語を覚える必要があった。 しかもShaderを記述するためには、行列演算を主とした幾何・代数への知識が必要だった。 そのため、実際に映像表現を作る人にとってハードルが高かった。

Shader Graphでは、Shaderのすべてをグラフ記述することが可能になり、学習コストが下がった。

// Shader Graphのスクリーンショット貼る

なので今まで足踏みしてた人も、使ってみようよ!という記事です。

Shader Graphを習う

あくまでも自分の場合だけど、

Youtubeにサンプル解説動画があるので、たくさん見よう ほとんど英語だけど、動画にUnity Editorの操作画面が出てるから、とりあえずトレースする

実は、Shader Graphを触る前に、WebGLを使ったレンダリングフルスクラッチで書いてて、 その時の知見がいくつか有用だったので紹介。

VertexとFragmentシェーダー

WebGLOpenGLDirectXでも)では、GPUでの描画プロセスで、Vertexシェーダーを実行したあとに、Fragmentシェーダーを実行される。 Vertexシェーダーでは、描画される座標が計算されて、Fragmentシェーダーでは、その座標における色が計算される。

下の図では、Scene上にビームサーベルが配置されている。プレビュー画面では、カメラに映ったビームサーベルが表示される。 何気なく受け入れてしまうが、ビームサーベル(のメッシュ)の座標(x,y,z)を画面座標(X,Y)に変換しなければならない。 これがVertexシェーダーの役割だ。

f:id:ixsiid:20191204223719p:plain
シェーダーの役割

普通に、Unityを触ってる限り、Vertexシェーダーはテンプレからいじる必要はない。 画面上にうまく表示されるようにカメラやオブジェクトの位置を調整するからで、 Vertexシェーダーを変にいじると、表示するオブジェクトが歪んだり、ぶっ飛んで行ったり、形が崩れたりする。

Fragmentシェーダーについてはいったん置いといて、Shader Graphでは、Vertex, Fragmentシェーダーを同一のグラフ上で記述する。 Unlit Shader Graphを作成すると、グラフ上にUnlit Masterというノードが追加される。 このMasterノードが最終的な描画内容を受けるアウトプットに相当する。

f:id:ixsiid:20191203233901p:plain
Unlit Master

Vertexシェーダーの役割は、このMasterのPositionに相当する。 普通はいじる必要がないから、デフォルト値である「Object Space」を接続しておけばいい。

逆に、Vertexシェーダーを使う場合を考えてみる。

f:id:ixsiid:20191206225126p:plain
座標変換

左のPositionノードは各頂点の座標(Transformで指定するやつ)、これが実描画の時に通常は、Object→Viewへと座標変換される。 ここでTransformノードで、View→Objectと逆変換を先に入れておくと、座標変換がキャンセルされ、常に同じ座標に表示されることになる。1

f:id:ixsiid:20191206225437p:plain
キューブのレンダリング位置(赤い四角)が、見る視点によらず、Transform座標上に表示される

更に、timeノードを使うと、時間経過に応じた動きを追加することもできる。 試しに回転行列を作り、先ほどのPositionに掛けてみる。

f:id:ixsiid:20191206230218p:plain
回転行列を掛ける

ゲーム画面上でも、レンダリングされるものが回転しだす。

f:id:ixsiid:20191206230325p:plain
回転するキューブ

シェーダーでできることは、基本的にスクリプトでも実現可能だ。 回転するキューブであれば、FixedUpdate内でTransformにAddRotationし続ければいい。 シェーダーで実行することができれば、その独立度合が高くなる。 各スクリプトのタイミング問題や、処理速度の問題に頭を悩ませなくていい。

// 閉会式動画が作られるらしいので、やっつけでもうちょっと見た目のインパクトがあるものを作ってみる

f:id:ixsiid:20191211212125p:plain
のこぎり波倍するシェーダー

上の図はのこぎり波をフーリエ変換したものを8次までシェーダー内で再現した。

f:id:ixsiid:20191211212333p:plain
のこぎり波

のこぎり波のように、びょーん、びょーん、びょーんと拡大するシェーダーになる。ほんとはビートを刻むようなエフェクトっぽくしたかったのだが諦めた。2

f:id:ixsiid:20191211213135g:plain
拡縮するキューブ

Fragmentシェーダーは、Shader GraphではColorとAlphaでMasterに接続される。 簡単な例として、先ほどのキューブを、アルファグラデーションをかけてみる。

f:id:ixsiid:20191206231511p:plain
アルファグラデーション

UVノードは、メッシュ内での(x,y)座標位置、Splitノードによりメッシュ状のX座標だけを抽出している。3 ゲーム画面上では、X座標に応じて透明になるレンダリングがされる。

f:id:ixsiid:20191206231947p:plain
グラデーションのかかったキューブ

先ほどのTimeノード、それにランダム性を加えるNoiseノードを組み合わせれば、ちょっとした登場エフェクトなども作れる。

f:id:ixsiid:20191206232542p:plain
透明度をランダムに変化させる

f:id:ixsiid:20191206233246g:plain
ゲーム上でのエフェクト

ここでは、単色ばかりの簡単な例を出したが、特に2Dゲームであればテクスチャの扱い方がわかれば、応用はさせやすい。

冒頭でもいったように実用的なものは、Youtubeなどを参照してほしい。 Shader Graphの場合、ある程度のノードの塊でどんな処理か意識できるようになるのがポイントだと思う。

ゲームジャムでのシェーダーづくり

ゲームジャムをするときに、シェーダーまで手を出せないよ!という人も多いと思う。 ただ、ちょっとした座標の動き、表現の動きをシェーダーで作れると「やり方」に幅がうまれる。

例えば、最初の企画が「ここで、うわー!ってやるとババーンとなって!ドかーってなるかんじ!」って時に 当たり判定をいれて、ダメージ計算をして、破壊判定をさせるといった処理系は手を出しやすい。

それを実装する人をしり目に「じゃあ、おれそのババーンがドかーんとなるシェーダー作ります。」とか言えるとかっこいい。かっこよくない?

スクリプト+パーティクルシステムで実装すると、インターフェースを相談したり、パラメータ例を伝えたり、 結合時にタイミングを見ながら、そのパラメータを調整したりとなるが、シェーダーであれば、マテリアルまで用意しておいて入れ替えるだけで済む。

システム処理系と描画処理系の独立性が高い分、結合が簡単になるのである。

// なんか面白いオチ


  1. 簡略化のためにカメラはOrthographics(パースが効いていないカメラ)を前提にしている

  2. ほんとにのこぎり波倍で表示したいだけなら、先に倍率係数をバッファしてTransformで計算するほうがはやい気がする(未確認)

  3. Splitノードの出力がRGBAとなっているが、座標ベクトルであればXYZWにそのまま対応する

スマホを立体ディスプレイにする

https://lookingglassfactory.com/:Looking Glassの出来が思った以上によかったので、昨今のディスプレイとレンチキュラーでどこまでのものができるか作ってみました。

www.nicovideo.jp

動画では立体感があまり伝わりませんが、裸眼でもそこそこ見えます。 レンチキュラーレンズの特性上、特定の模様に弱いのと、視野角と解像度の最適化がまだできていないようです。

f:id:ixsiid:20190507212742j:plain

コードの追記数とTake数の記載

こんなことを書いたもんで、自分の投稿についてのメモを随時更新

ちなみにこんな定義 追記数: 保存した回数 Take数: 根本的にうまくいかなくて、やり方・切り口から変えた回数 実際の回数はかなりざっくりです

追記が多いものはリファレンスが意味不明だったり、使い方が応用的だったり、 処理速度やタイミングのチューニング、抽象化を何度も行ったもの Take数が多いものは、自分が何をやりたいのかがプログラミング上で具体化できてなくて試行錯誤を繰り返しているもの

RFLP的に言えば、追記はPでTakeはL

追記 ~100, Take 5 qiita.com

追記 ~300, Take 2 qiita.com

RtMidiを使って仮想Midiデバイスのルーティング

仮想MIDIバイスを登録してMIDIキーボードから受け取った信号を ちょっとだけ加工してMIDIインターフェースに送出したい

#include <iostream>
#include <cstdlib>
#include "RtMidi.h"

void mycallback(double deltatime, std::vector<unsigned char> *message, void *userData)
{
   unsigned int nBytes = message->size();
   for (unsigned int i=0; i<nBytes; i++) {
      std::cout << "Byte " << i << " = " << (int)message->at(i) << ", ";
   }

   RtMidiOut *midiout = (RtMidiOut *)userData;
   if ( nBytes > 0 ) { 
      std::cout << "stamp = " << deltatime << std::endl;

      /* これがちょっとだけ加工している内容 */
      (*message)[1] = 64;
      midiout->sendMessage(message);
   }
}


int main()
{
   RtMidiOut *midiout = new RtMidiOut();
// midiout = new RtMidiOut();
   unsigned int outPorts = midiout->getPortCount();
   if (outPorts == 0) { 
      std::cout << "No out ports available!\n";
      delete midiout;
      return 0;
   }
   midiout->openPort(0);

   RtMidiIn *midiin = new RtMidiIn();
   unsigned int inPorts = midiin->getPortCount();
   if (inPorts == 0) { 
      std::cout << "No in ports available!\n";
      delete midiout;
      delete midiin; 
      return 0;
   }
   midiin->openPort(1);
   midiin->setCallback(&mycallback, midiout);

   // Don't ignore sysex, timing, or active sensing messages.
   midiin->ignoreTypes( false, false, false );
   std::cout << "\nReading MIDI input ... press <enter> to quit.\n";

   char input;
   std::cin.get(input);

   delete midiout;
   delete midiin;
   return 0;
}

Raspberry Piで作るデバイスのメモ

Fluidsynth

$ (sudo) fluidsynth -a alsa -g 3 /{サウンドフォントファイル}

MIDI Inputインターフェース待ち受け状態になる(立ち上げに数秒かかる) ※-g 3 はゲイン

MIDIインターフェース

インターフェース一覧表示

$ aconnect -o
client 14: 'Midi Through' [type=kernel]
    0 'Midi Through Port-0'
client 20: 'microKEY-25' [type=kernel,card=1]
    0 'microKEY-25 MIDI 1'
client 128: 'FLUID Synth (659)' [type=user,pid=659]
    0 'Synth input port (659:0)'

インターフェース同士を接続する

aconnect 20:0 128:0

音声出力

$ amixer -c ALSA contents
numid=3,iface=MIXER,name='PCM Playback Route'
  ; type=INTEGER,access=rw------,values=1,min=0,max=2,step=0
  : values=2
numid=2,iface=MIXER,name='PCM Playback Switch'
  ; type=BOOLEAN,access=rw------,values=1
  : values=on
numid=1,iface=MIXER,name='PCM Playback Volume'
  ; type=INTEGER,access=rw---R--,values=1,min=-10239,max=400,step=0
  : values=-2000
  | dBscale-min=-102.39dB,step=0.01dB,mute=1
numid=5,iface=PCM,name='IEC958 Playback Con Mask'
  ; type=IEC958,access=r-------,values=1
  : values=[AES0=0x02 AES1=0x00 AES2=0x00 AES3=0x00]
numid=4,iface=PCM,name='IEC958 Playback Default'
  ; type=IEC958,access=rw------,values=1
  : values=[AES0=0x00 AES1=0x00 AES2=0x00 AES3=0x00]

よくわからんがこれの PCM Playback Route の values が出力先の変更? 1だとJack 2だとHDMI こいつのnumidを参照して変更する

$ amixer -c ALSA cset numid=3 1
numid=3,iface=MIXER,name='PCM Playback Route'
  ; type=INTEGER,access=rw------,values=1,min=0,max=2,step=0
  : values=1

企業で働く外装エンジニアへ

昨今のソフトウェアの進化は早い。様々なアプリケーションが日々リリースされているが、エンジニアとして目をつけるべきをそれを可能としている環境が進化していることと思う。

 

3D CADにふれる機会は増えたし、導入のハードルはずいぶん下がった。それでもハードウェアエンジニアリングの進化はずいぶん遅い。

 

 ソフトウェア開発の高速化、効率化が進んだ主な要因にコミュニティの形成しやすさがある。Web上には言語規約からサンプルコードまで蔓延し必要なデータシートにはすぐにアクセスできる。ばかりか、有能なツール群には間違いなく、FAQやユーザーコミュニティが存在し、様々な意見交換が行われている。同時にたくさんの方法論やフレームワークが提案され、それはWeb上で淘汰、進化しまた同時にそれらに合わせたツール群が開発される。

 

ハードウェアではかつては学会がそうだったと思う。しかし、今は研究の色が強くなり、方法論、実現方法、ツールや組織運営は企業のノウハウとして蓄積されるようになった。また、企業から組織に、組織からチームに、チームから個人に、ノウハウの蓄積はより細分化され、明文化されず、局所最適の様相を示しているのではないだろうか。

 

 

まず、私達は情報を発信し、受信し、共有する下地が必要だ。最初にフレームワークの話を進めるために、フレームワークの意味の共有をする。

 

フレームワークとは(Wikipediaより)

> 開発・運用・意思決定を行う際に、その基礎となる規則・構造・アイデア・思想などの集合のこと。日本語では「枠組み」などと訳されることが多い。

 

「規則・構造・アイデア・思想などの集合」である。今のフレームワークはどうなっているか。

規則: 関連部署や、設計審査などのゲートを通過するために必要なドキュメントや承認経路である

構造: どのようなチーム編成、組織関連性で運営されているか。またどのようなツール、測定器、評価器でなされるか。

アイディア: 規則の最もらしさを担保する仮説、ないし事実である。例えば「品質には顕在化された品質と潜在的な品質が在る」などである。

思想: 組織をまとめる思考回路である。また評価関数を担保する指標でもある。「原価に適正な利益を乗せた額が売値である」、「納期厳守」などである。

 

 

続く