2017年4月22日

Unity 動的にメッシュを作成する ~テクスチャ手直し編~


スクリプトからメッシュを作成する方法について、だんだん自信がなくなってきた解説その5。テクスチャが綺麗に見えるよう調整をします…


※このページの内容の動作確認にはUnity5.3を使用しています。
前回はテクスチャをスクリプトから作成し、メッシュに貼り付けました。でも、よく見るとなんだか汚い箇所があります。

Wrap Modeを変更する

まずは地形の端で色がおかしくなっている部分。

山の上なのに青色が見える

これはテクスチャがループする仕様になっていて、反対側の端の色が見えてしまっているためです。直し方は簡単で、テクスチャを作成するときに次の行を加えるだけ。

texture.wrapMode = TextureWrapMode.Clamp;

TextureにはWrap Modeというパラメータがあり、テクスチャの貼り方を選択することができます。Repeatに設定するとテクスチャを繰り返して隙間なく埋める、Clampにするとテクスチャを引き伸ばしてメッシュの端にピッタリ合わせて貼る、という感じです。今回はClampに設定しましょう。もしこれが壁や床のタイリングであればRepeatを使えばいいですね。


というわけでバッチリ改善しました。

次の問題はこれ。

ぼやけている
近くで見ると色の境目がぼやぼやっとなっています。原因は明確で、単純に「テクスチャの解像度が低い」ということです。この画像の場合メッシュのサイズを64×64に設定しているのですが、現在の仕様ではテクスチャも64×64のサイズになってしまいます。さすがに「地形」に対してこのサイズのテクスチャは小さすぎます。

ブロック化

手っ取り早い解決策は

public bool blockMode;
if(blockMode) texture.filterMode = FilterMode.Point;

という感じでFilter ModeパラメータをPointにしてしまう方法。

くっきり!

Filter Modeはテクスチャを3Dのモデルに貼り付けるときにどのように拡大するかを設定する項目で、Pointにするとそれぞれのピクセルがブロック状になります。

根本的な解決になってないし、そもそも「くっきり」ってそういうことじゃねえだろ感がすごいですが、ともかくぼやけた表示を直すという問題には対処できましたし、何よりパフォーマンス的な観点でいうと良い影響しかありません。個人的にこういう解決策は大好きです。

テクスチャの解像度を高くする

では次に、テクスチャのクオリティを上げるという本質的な改善案を実装してみます。

[Range(1,16)]
public int textureDetail;
int textureSize = (size - 1) * textureDetail + 1;
Color[] colorMap = new Color[textureSize * textureSize];
for (int z = 0; z < textureSize; z++) {
 for (int x = 0; x < textureSize; x++) {
  float sampleX;
  float sampleZ;
  float y = 0;
  foreach (PerlinNoiseProperty p in perlinNoiseProperty) {
   p.scale = Mathf.Max(0.0001f, p.scale);
   sampleX = (x / (float)textureDetail + p.offset.x) / p.scale;
   sampleZ = (z / (float)textureDetail + p.offset.y) / p.scale;
   y += Mathf.PerlinNoise(sampleX, sampleZ) * p.heightMultiplier;
  }

  float percent = Mathf.InverseLerp(minHeight, maxHeight, y);
  colorMap[z * textureSize + x] = meshColorGradient.Evaluate(percent);
 }
}
Texture2D texture = new Texture2D(textureSize, textureSize);

まずテクスチャの解像度をどれだけ上げるかを指定するtextureDetailという変数を追加しました。


上の図のように、textureDetailの値が大きくなるごとにメッシュの頂点を補間する数を多くします。つまり、テクスチャのサイズをsizeではなく(size-1)*textureDetail+1として、大きめに作成するわけです。

そのあとは、メッシュの頂点を求めたのとまったく同じ方法でパーリンノイズを使い、それぞれの点でのY座標を求めます。パーリンノイズのサンプルを取得するときに、座標をtextureDetailで割るのを忘れないようにしましょう。さらに求めたY座標から表示する色を求め、テクスチャに適用するという前回と同じ流れになります。


わーい!キレイになったよ~!

…ただ、 このコードはさすがにパフォーマンス的に無理があります。たとえばメッシュのsizeが64の場合、実際の頂点数は64×64=4096個。textureDetailを4に設定した場合、テクスチャのサンプル数は253×253で64009個にもなります。textureDetailが増えるごとにそのおよそ2乗分が増えていくことになるので、そのすべてでパーリンノイズを算出して処理していくのはとんでもなくコストがかかります。指定したタイミングで1度だけ読み込むとかならいいと思いますが、たとえば高解像度のテクスチャを毎フレーム計算したりするのは現実的ではありません。

というわけで、さすがにもうテクスチャを貼り付けるだけでは間に合わなくなってきました。ここまで作っておいてなんですが、おとなしく専用のシェーダーを用意したほうがよさそうですね。

と言いつつ、今回はこのへんで。最適化の夢を見ながら寝ます…

0 件のコメント:

コメントを投稿