2019-07-28
■ Unity公式のHoudini HDAを使って、Unity用のPointCache(.pcache)やVectorField(.vf)を出力する

■ 【Unity】UnityへFGAをインポートしてVisual Effect GraphのVectorFieldとして使う方法

■ HoudiniでVectorField(.fga)を作成する方法(サンプルプロジェクト付き)

2019-06-08
■ Houdiniで交差点を含む複雑な道路を作るチュートリアル

https://dokaitutorials.com/complex-roads-in-houdini-1/
道路以外にも市街地やビルの生成の動画もあった
■ Houdini:Spare Input

http://nomoreretake.net/2018/08/18/houdini_spareinput/
他のノードのパラメータを参照する方法。コンパイルブロック向けに追加されたっぽいが、それ以外の場面でも使えて、エクスプレッションの記述をシンプルにできる。こんなのがあるとは知らなかった
2019-05-06
■ 極座標シェーダー実験メモ

https://twitter.com/Cyanilux/status/1123944969125998592
Shader "Unlit/PolarCorrdinates" { Properties { _MainTex ("Texture", 2D) = "white" {} _Rotate("Rotate", Float) = 0.0 _RotateSpeed("RotateSpeed", Float) = 0.0 _RadiusAdd("RadiusAdd", Float) = 0.0 _RadiusSpeed("RadiusSpeed", Float) = 0.0 } SubShader { Tags { "RenderType"="Opaque" } LOD 100 Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag // make fog work #pragma multi_compile_fog #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; UNITY_FOG_COORDS(1) float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; float _Rotate; float _RotateSpeed; float _RadiusAdd; float _RadiusSpeed; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); UNITY_TRANSFER_FOG(o,o.vertex); return o; } fixed4 frag (v2f i) : SV_Target { float2 uv = (i.uv * 2.0f) - float2(1,1); float r = 1.0 - length(uv); float theta = atan2(uv.y, uv.x)*(1.0f / (3.1415926535f*2.0f)); r = pow(r+0.9, 2.0); float2 puv; puv.x = r + _RadiusAdd + _RadiusSpeed * _Time.x; puv.y = r + theta + _Rotate + _RotateSpeed * _Time.x; // sample the texture fixed4 col = tex2D(_MainTex, puv); // apply fog UNITY_APPLY_FOG(i.fogCoord, col); return col; } ENDCG } } }
2019-05-05
■ Interleaved gradient noise

レイマーチのアーティファクトを減らすのに使えそう
https://blog.demofox.org/2017/10/31/animating-noise-for-integration-over-time/
Interleaved gradient noise, which gives middle results, is actually very similar in generation costs as white noise believe it or not, and so can also be done in real time on either the CPU or GPU.
If you have X and Y pixel coordinates (not uv coordinates), you can generate the noise value for the pixel by using this formula:
float noise = std::fmodf(52.9829189f * std::fmodf(0.06711056f*float(x) + 0.00583715f*float
■ マテリアルシェーダーの実験ソースメモ

動画:https://twitter.com/Nao_u_/status/1125247333183057920
元ネタ:https://twitter.com/MrZulubo/status/1122611573510377472
// Upgrade NOTE: replaced '_Object2World' with 'unity_ObjectToWorld' // Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)' Shader "Custom/TestShader2" { Properties { _Color ("Color", Color) = (1,1,1,1) _MainTex("Albedo (RGB)", 2D) = "white" {} _Volume("Volume", 3D) = "" {} _SparkMap("SparkMap", 2D) = "white" {} // _ParallaxMap("_ParallaxMap", 2D) = "white" {} _Glossiness ("Smoothness", Range(0,1)) = 0.5 _Metallic ("Metallic", Range(0,1)) = 0.0 _Distotion("Distotion", Float) = 0.5 _Iteration("Iteration", int) = 5 } SubShader { Tags { "RenderType"="Opaque" } LOD 200 CGPROGRAM // Physically based Standard lighting model, and enable shadows on all light types #pragma surface surf Standard fullforwardshadows #pragma vertex vert // #pragma fragment frag // Use shader model 3.0 target, to get nicer looking lighting #pragma target 3.5 sampler2D _MainTex; sampler2D _SparkMap; sampler3D _Volume; // sampler2D _ParallaxMap; half _Glossiness; half _Metallic; fixed4 _Color; struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; float3 normal : NORMAL; float4 tangent : TANGENT; }; float _Distotion; int _Iteration; int _Type; // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader. // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing. // #pragma instancing_options assumeuniformscaling UNITY_INSTANCING_BUFFER_START(Props) // put more per-instance properties here UNITY_INSTANCING_BUFFER_END(Props) struct Input { float2 uv_MainTex; float3 viewDirTangent; float4 screenPosition; float4 objPos; //UNITY_VPOS_TYPE vpos : VPOS; }; void vert(inout appdata_full v, out Input o) { UNITY_INITIALIZE_OUTPUT(Input, o); float4 objCam = mul(unity_WorldToObject, float4(_WorldSpaceCameraPos, 1.0)); float3 viewDir = v.vertex.xyz - objCam.xyz; float tangentSign = v.tangent.w * unity_WorldTransformParams.w; float3 bitangent = cross(v.normal.xyz, v.tangent.xyz) * tangentSign; o.viewDirTangent = float3( dot(viewDir, v.tangent.xyz), dot(viewDir, bitangent.xyz), dot(viewDir, v.normal.xyz) ); o.screenPosition = ComputeScreenPos(v.vertex); o.objPos = UnityObjectToClipPos(v.vertex); } void surf(Input IN, inout SurfaceOutputStandard o) { // Interleaved gradient noiseでアーティファクトを消す実験 float2 screenPos = IN.objPos.xy / IN.objPos.z; screenPos.xy *= 1; float x = screenPos.x; float y = screenPos.y*(16.0f / 9.0f); float ig_noise = frac(52.9829189f * frac(0.06711056f*float(x) + 0.00583715f*float(y))); float mm = 0.0001; // 100サンプルあるならいらないのでほとんど無効に float addNoize = -mm + (2 * mm *ig_noise); // テクスチャ歪み float2 warp = tex2D(_MainTex, -IN.uv_MainTex).rg; // パララックス float2 uv = IN.uv_MainTex; uv.x += _Time.x*-0.5; uv.y += _Time.x*-1.0; uv += warp*0.075; float parallax = 0; for (int j = 0; j < _Iteration; j++) { float ratio = (float)j / _Iteration; //float col = tex2D(_MainTex, uv + lerp(0, _Distotion, ratio+ addNoize) * normalize(IN.viewDirTangent)) * lerp(1, 0, ratio); float2 uv2 = uv + lerp(0, _Distotion, ratio + addNoize) * normalize(IN.viewDirTangent); float3 uv3 = float3(uv2.x, uv2.y, 0.5+_Time.x*2); float col = tex3D(_Volume, uv3) * lerp(1, 0, ratio)*1.0; if (col > ratio) { parallax += col; } } parallax /= _Iteration; // 適当に色を弄る fixed4 c = ((tex2D(_MainTex, IN.uv_MainTex+float2(_Time.x*-0.5,_Time.x*-1.1))-float4(0.2,0.5,0.5,0)) * 0.5) * float4(1, 0.5, 0.5, 1); o.Albedo = c.rgb + pow(parallax,1.7) * _Color.rgb*100; o.Albedo.gb = pow(o.Albedo.gb + float2(0.2, 0.1), 2.5); o.Albedo.r += c.r * 1.25; // 光の粒(3レイヤー) float4 spk_col0 = float4(1.0, 1.0, 1.0, 1); float4 spk_col1 = float4(2.0, 1.0, 2.0, 1); float4 spk_col2 = float4(2.0, 1.5, 2.0, 1); { float depth = 2.0; float2 uv = IN.uv_MainTex + float2(_Time.x * -3.1, _Time.x * -1.05); float4 spk = tex2D(_SparkMap, uv + depth * normalize(IN.viewDirTangent)) * spk_col0; o.Albedo += spk.rgb * 2; } { float depth = 0.5; float2 uv = IN.uv_MainTex + float2(_Time.x * 1.25, _Time.x * 0.25); float4 spk = tex2D(_SparkMap, uv + depth * normalize(IN.viewDirTangent)) * spk_col1; o.Albedo += spk.rgb * 3; } { float depth = 0.25; float2 uv = IN.uv_MainTex + float2(_Time.x * -0.20, _Time.x * -0.3); float4 spk = tex2D(_SparkMap, uv + depth * normalize(IN.viewDirTangent)) * spk_col2; o.Albedo += spk.rgb * 2; } // Metallic and smoothness come from slider variables o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }
2019-04-29
■ Write-up: Magical Marbles

■ Houdini PDG 基本チュートリアル「都市を構築するためのタスク ワークフローを作成する」

なんかROP_Geometry_outputとかでファイルに吐き出したあとにファイルが壊れてたりうまく読めなかったりすることがある?Houdiniを閉じて再起動したり、ちゃんとキャッシュされているかFileノードで読み込んだ後は動く、みたいなことがあった。よくわからない
2019-03-10
■ [Unity][Houdini]新居の間取りVR

新居の間取りを図面からモデリングしてVRで確認。壁や家具の配置も簡単に変えられて、トイレやロフトなど図面だけでは広さが実感しにくい所もわかりやすく体感できるので、とても役に立った
スイッチの位置や家事の動線も部屋の中を実際に歩きながら確認できるし、家具や壁紙の色を変えたらどんな雰囲気なるのかもすぐ見れる。部屋の実際の広さは図面の数字だけでは実感しづらいので、こういった用途にVRはほんと最適だと思った
新居の間取りをVRで確認しつつ決めて、今週末の引っ越しのために現地を見に行ったら、ちょうどVRで見たものと同じものがそのまま出来上がっててちょっと不思議な感覚。2Dの図面や平面のCGではわからない部屋のスケール感や光源の具合までちゃんと確認できるので、ほんとVR向きな案件だなぁ、と。
https://twitter.com/Nao_u_/status/1104573498066853888
家のモデリングにはHoudiniを使用。図面に変更があった時にも、大元の枠組みだけ変えれば簡単に修正できるので、プロシージャルモデリングの便利さを実感。
外構工事の検討もUnityでレイアウトしてVRで確認してた。Unityなら簡単に複数パターンの配置を切り替えながら試せて、HMDを通してみると2DのCGで見るのとけっこう印象が違ってたり、塀の高さを変えたら家の中がどう見えるかの違いなども検討できたりなど、ここでもVRは有用だった
2019-02-02
■ [Houdini]連続したnullをObject Mergeで参照する方法

Object Merge SOPのオブジェクト名にはワイルドカードが使える
TransformをInto This Objectにすると、そのオブジェクトのtransformを反映した座標を取ってこれる
これでnullにnormalを追加すれば、nullを編集することで回転情報のattributeが付いた連番の点が定義できるので、道路の傾きを自由に設定できるような仕組みが作れるはず。
点の増減はpythonでスクリプトを書く必要がある。
■ [Houdini]point列をカーブに変換する方法

https://www.sidefx.com/forum/topic/42371/
https://forums.odforce.net/topic/29278-make-curve-in-vex/
addのあとFit SOPでカーブにできる
FItはinterpolateでscopeをbreakpointにしないと必要以上に補間される
2019-01-25
■ 通信回りのメモ

ついでに前作Tofu Racerのランキングが正常に表示されなくなっていた問題も修正しました。一年以上放置してたら、ランキング登録数が増えた時に限界を超えるとおかしくなるバグがあることに気づいてなかった…
ランキングの実装は前作はGoogleスプレッドシートをDBとして使用してたんだけど、続編のランキング実装時に前作がちゃんと動かなくなってることに気づき、Google側の仕様が変わってたら面倒なので別のやり方を調べた結果、今回はNiftyCloudを採用。制限付きだけど無料で使えて便利そうだったので。
ニフクラはGoogleより通信速度がだいぶ早くてAPIも整ってて使いやすいし、無料版の制限もAPIアクセスの上限が100万回/月だったので問題ないだろうと思ってたら、初日だけでAPIが6万回も叩かれてて状況によっては制限に引っかかる可能性がありそうな使い方になってたところはちょっと想定外だった
ログを確認すると、初日だけで前作の累計に匹敵する2000人近いアクセスがあり、その中の上位10人だけ数えてもリトライ回数の合計が約9000回あったため、リトライのたびに状態をサーバに送ってるせいでAPIリクエスト数が想定以上に増えてたのはちょっと誤算だった。Googleは制限なしなので気楽に使える
一方でニフクラだと送受信が早いうえにDBに配列を入れたりファイルを送ることもできるので、リプレイの保存などのGoogleでは厳しい仕様も実装できるのは明確なメリット。次があるならゴーストは実装してみたいけど、容量制限はあるので工夫無しになんでもかんでもサーバに送ると簡単に破綻しそう。
ネットワークが絡むゲームも作ってみたいけどリアルタイム通信が必要だと簡単に過疎るのが目に見えてるので、他人のプレイが非同期に影響するようなものの方がよさそうかなとか以前から思ってて、ニフクラの仕様をちゃんと見てないけどこれを使えばいろいろやりたかったことが実装できそうな気はする
■ 絵に関するメモ

今回このゲームを作った目的の一つに「Houdiniである程度の規模の背景を作るノウハウを溜める」があって、この点については一定レベルの成果は出せたように思う。あと「テクスチャを一切貼らない」ことでマテリアルについてはほぼ何も考えなくてもよくなるので、相当なコストが削減できた
一人でゲームを作るときにはAssetStoreがすごく便利で、前作も既成コースのアセットをそのまま使ってたけどこれに頼りきるとアセットの改変が一切できなくて困る。かといってちゃんとしたアセットの自作はかなり大変なので、生ポリゴンでそれっぽく見える表現ができるなら製作コストを大幅に抑えられる
今回はコンセプト的にフラットシェーディング+影くらいまでならOKだが、スペキュラやGI要素を加えるとちょっと欲しい絵とは違うものになったのでやらなかったけど、「テクスチャを貼らない」縛りだけで今時風のいろんな要素を加えた形で低コストでそれなりに見える絵にできないかは模索してみたい
2018-12-08
■ ボクセルから回転したドット絵のパターンを作る実験

ボクセル化したR-9のドット絵を傾けて正射影レンダリングした時にどのくらい元のドット絵と一致するかを実験。右上がオリジナルのドット絵。もっと破綻するかと思ってたけど案外いけてる。このボクセルをベースにモデリングすれば、低解像度描画時にドット絵と一致するモデルが作れたりするかな?
奥行方向は再現できてなくて、黄色い銃やブースターは左右で分離する必要がありそう。とはいえ適当に立体化して傾けただけでもそれなりに一致してるので、32x16の限られたドットに傾いた絵の情報が立体としてちゃんと把握できる構造になってて、ドット絵に込められた情報量の凄さをあらためて再認識。
ボクセル化はHoudiniでAttribute from Mapで取り込んだ絵を適当にPolyExpandで伸ばしたものをUnityでレンダリング。気分転換という名目の現実逃避にちょうどいい作業量だったので、余裕ができたらこれをベースにちゃんとモデリングしてみたい
ちょうど15度傾けたところで上下の端はそのままに内部構造だけが1ドット上に上がってて、元画像の上傾きの絵と動きがだいたい一致してた。ドット絵を描く人はこういうのを脳内で計算しながらやってるんだなぁ、と。
2018-11-16
■ 「無限に都市が生成されるアルゴリズム」で生成された都市を自由に歩き回ってみた

「Wave Function Collapse algorithm(波動関数崩壊アルゴリズム)」と名付けられたアルゴリズムは、入力ビットマップに類似したビットマップをローカルに生成するという仕組み。RPGのマップのようなマップを描くことも可能で、三次元マップにも応用できる
2018-11-15
■ 【入門者向け解説】プーリング処理入門(TensorFlowで説明)

■ レトロゲームエンジン Pyxel でプログラミングを始めよう!

かなりシンプルに絵が出せそうなので、機械学習結果のビジュアライズや、学習データを作るための簡単なゲーム開発環境として使えたりしないか?
2018-11-12
2018-11-04
Colaboratryで作ったphythonコードをローカルのJupyter Notebookで動かそうとしてハマったときのメモ。
■ Windows10でTensorflowとKerasをインストールしようとしてハマった話

pip install tensorflow
でインストールしたtensorflowが「ダイナミック リンク ライブラリ (DLL) 初期化ルーチンの実行に失敗しました。」というエラーで動かず、
conda install tensorflow
で動いた。
たぶん vs2015_runtime: 14.0.25123-0 --> 14.0.25420-0 が入ったおかげかも。
こういう罠にはまらないのでColaboratoryは便利。
■ mnistのサンプルで TypeError: softmax() got an unexpected keyword argument ‘axis’というエラーが出る

model.add(Dense(10, activation='softmax'))
を実行すると、kerasがバージョン2.1.6だとエラーが出るらしい。2.1.3に戻すとそのまま通る
pip install keras==2.1.3
■ Deep learningで画像認識⑦〜Kerasで畳み込みニューラルネットワーク

Kerasでは、「ImageDataGenerator」というクラスが用意されており、元画像に移動、回転、拡大・縮小、反転などの人為的な操作を加えることによって、画像数を増やすことができます
2018-06-30

フォトグラメトリの実験として、iPhoneで撮った動画を3DF Zephyrで3D化したものをUnityでVR表示してみた。デフォルト設定でそのまま変換しただけでも予想以上に高精度で形状の破綻も少なく、まつ毛くらいの細かさまでちゃんと識別できるくらいに再現できてる
今回は一分半くらいの動画から500枚を切り出して加工したんだけど、このくらいの枚数があれば十分っぽい。写真だとシャッター音で目が覚めることもあるし、枚数を取るのも大変なので動画からの切り出しでどこまでできるか気になってた。切り出した画像に多少ボケたものが混ざっててもあまり問題はないっぽい
子供のスケール感は時間がたつと忘れてしまいそうなので、今のうちに3D化して保存しておいて、後でVRで比較できるようにしておきたくて、これまでも定期的にいろんな角度からの写真だけ撮り溜めてたけど、写真を大量に撮るより動画のほうがだいぶ楽なので今後は気軽に撮れそう。欠点は寝てるときしか撮れないことか
VRが当たり前になったら、適当に撮った動画をサーバに送って3D化してVRで見る、みたいなサービスも当たり前になったりするのかな。周りからぐるぐる撮るだけでいいので「ちょっとめんどくさいパノラマ写真」くらいの感覚で3D空間を切り取って保存したりできそう。
静止画で見てもぱっと見で写真と区別がつかないくらいの品質のものが、そのままくるくる回せるのは結構なインパクトが。フォトグラメトリも4年前に試してた時より色々進歩してる。Houdiniが使えるようになったおかげで後処理もかなりやりやすくなった