見えないところで起こっていることはわからないものです。
私は過去、平行世界を三回ほど救ったことがあるのですが、この世界の人々は気づいてすらいません。
さて、というわけで今回はカメラに映っていないメッシュを映っていないにもかかわらず描画することにチャレンジしてみます。
映っていないのに描画する必要があるのか?
それは誰も称賛しないのに世界を救う必要があるのか? という問いかけと同じ意味なのです。
わかりましたね。それでは描画していきますよ。
この記事にはUnity2017.3.0f3を使用しています。
事の発端は先日作ったシェーダー
話が少し変わりますが、視錐台カリングというものがあります。
視錐台をフラスタムとよんでフラスタムカリングとも言っていたような気もしますが、そんなカリングがあった気がします。
docs.unity3d.com
カメラの視錐台内のものは描画して、外のものは描画しないというものですねー。
これはとってもいいことです。
カメラに映っていないのなら、わざわざ描画の処理をする必要ないですもんね。
描画するのだってタダじゃないんですから。パソコンやスマホだってカロリー使いますよ。
他にもおくるーじょんカリングというものもあった気がしますが、それはまた別のお話です。
docs.unity3d.com
ただ今回は冒頭で話した通り、カメラに映っていないものを描画したいのです。
まあFrameDebuggerを使うならいざしらず、カメラに映っていないところを描画の有無なんざ把握しようがないんですけど。
何故こんなことをやろうと思ったのかと言えば、先日作成した描画位置をずらすシェーダーを作ったときに起きた問題が事の発端です。
www.urablog.xyz
このシェーダーを使ったマテリアルのプロパティにオフセットを設定することで描画位置を実際のオブジェクトの位置からずらすことが出来ます。
ただこのオブジェクトを移動させていくと、本来オブジェクトが描画されるあたりの位置がカメラの視錐台から外れた場合に、ずらして描画する位置がカメラの視錐台内でも描画されなくなってしまうのです。
マジかよ。
ただでさえ、作ったはいいけど何に使えばわからないシェーダーなのに、より使いづらくなってしまう。
何か手を考えるとしましょう。
バウンディングボリュームをいじって何とかする
さて、この問題を解決するにはどうすればいいのでしょう。
そもそも問題なのは私の許可なく視錐台カリングなどというもので、勝手に描画物を分別しているところでしょう。
こういう差別的な行為が、学校などのイジメにつながっていくのです。
こんな悪い視錐台カリングは無効にしてしまいましょう。
……。
うん、そんなやり方知らん!
視錐台カリングのOnOffのようなプロパティなんざ見たことないですね。
そもそも視錐台カリングを無効にした日なんかには、描画処理がパンクしてしまうでしょうね。
うーん、どうしようかな。
このオブジェクトの描画の有効範囲をもう少し大きくするだけでいいんだけどな。
これとかどうかな、バウンディングボリューム。
Renderer.bounds
をいじることで何とかならないだろうか。
docs.unity3d.com
ForceRenderer.cs
using UnityEngine; [RequireComponent( typeof( Renderer ) )] public class ForceRenderer : MonoBehaviour { [SerializeField] private float m_expandAmount = 100.0f; private void Start() { var renderer = GetComponent<Renderer>(); renderer.bounds.Expand( m_expandAmount ); } } // class ForceRenderer
何も変わらんね。
そもそもこのスクリプト、ちゃんと機能してんのかな。
ログの出力処理を追加してbounds
の値を確認してみましょう。
ForceRenderer.cs
using UnityEngine; [RequireComponent( typeof( Renderer ) )] public class ForceRenderer : MonoBehaviour { [SerializeField] private float m_expandAmount = 100.0f; private void Start() { var renderer = GetComponent<Renderer>(); Debug.LogFormat( "Before:{0}", renderer.bounds.size ); renderer.bounds.Expand( m_expandAmount ); Debug.LogFormat( "After:{0}", renderer.bounds.size ); } } // class ForceRenderer
Before:(1.0, 1.0, 1.0)
After:(1.0, 1.0, 1.0)
値が変わっとらんね。何故だ!
Bounds
についてもう少し調べてみましょう。
docs.unity3d.com
あっ、これ構造体だった。
ということは値型だねー。
それじゃRenderer.bounds
で取得したBounds
の値を変更しても何の意味もないね。
こちらでBounds
を作成し、Renderer.bounds
にセットしてあげるようにしなくてはいけないね。
……。
あー……、Renderer.bounds
は読み取り専用だわ。Getプロパティしかない。
どうやら私の戦いはここまでのようだ。
んー、Renderer
ではダメだったが、Mesh
そのものはどうだろう。
MeshFilter
経由でMesh
を取得し、Mesh
にあるbounds
をいじる方法はどうだろうか。
Mesh.bounds
はSetプロパティもあるから、好き勝手できるはずだ。
docs.unity3d.com
ForceRenderer.cs
using UnityEngine; [RequireComponent( typeof( MeshFilter ) )] public class ForceRenderer : MonoBehaviour { [SerializeField] private float m_expandAmount = 100.0f; private void Start() { var meshFilter = GetComponent<MeshFilter>(); var bounds = meshFilter.mesh.bounds; bounds.Expand( m_expandAmount ); meshFilter.mesh.bounds = bounds; } } // class ForceRenderer
やった! 描画されたぞ!
まあ、bounds
の値の設定方法はもっとマシな計算方法を使ったほうがいいだろうけど。
Expand()
に100ぶっこむとか適当にもほどがあるしな。
同じメッシュ全てに適応させていいなら、sharedMesh
の使用も検討してみてもよさそうだ。
先日作ったシェーダーのとき同様にランタイム時しか動かないから、ランタイム時以外でも対応する場合はExecuteInEditMode
を使ってスクリプトを改良する必要がありそうですねー。
しかし、長々とやってきたんだけど、このbounds
をいじくりまわして描画範囲を変更する方法は、果たして正攻法なのだろうか。
ちょーっと無理やり感があるような気がするんだけど。
もう少し方法を検証したいところだけど、私に助けを呼ぶ声が聞こえてくる。
どうやらまた並行世界の平和が乱れてしまったらしい。
やむを得まい。
プログラムはここまでにして、正義の英雄の仕事に戻ろうか。