前: ユーザインターフェイスの追加 次: Texture2D クラスと Texture3D クラス

5. アンチエイリアステクスチャ

以下は、Stripes2 テクスチャを球に割り当てた別の画像です。この場合、オブジェクト空間とテクスチャ空間の間のマッピングで、ストライプがとても細くなるよう設定しています。

ご覧のように、ひどい見映えです。ストライプにズレとジャギーが発生しています。明らかに何かがおかしいです。何が起きているのでしょう? どうすれば直せるのでしょう?

この問題はエイリアシングとして知られています。そしてこれはコンピュータアーティスト全員に存在する悩みの種です。原因は、表面プロパティを単一の、無限に小さな点でしか評価していないことと、あるピクセル全体の色を決定するのにこれらのプロパティを使っていることです。この方法は、表面プロパティがとても緩慢に変化するときときのみうまく働きます。しかし鋭いふちがある場合、明らかに人工的なものを作り出します。第3章に戻り、画像を見てみましょう。そこで既に、ストライプの色の境目にジャギーが発生しています。上の図のとても薄いストライプでは、その効果が目立っているだけなのです。

この問題を解決するには、単一のピクセルに含まれる表面の領域すべてにわたり、表面プロパティを平均化する必要があります。可能な方法は多くあります。単純な解決方法の1つとして、Renderer でそのピクセルを分割して、断片それぞれで色を決定、その後それらを平均化して、そのピクセルの最終的な色を取得する方法があります。これは スーパーサンプリング と呼ばれ、アンチエイリアス問題の解決にとても効果的な方法です。例えば Raytracer で、ピクセルあたりに複数の光線を送るよう設定すればこれができます。

スーパーサンプリングには、「遅い」という大きなデメリットがあります。ほとんどの場合、もし Texture クラスが自身を平均化でき、表面上の領域にわたって平均プロパティを返せるなら、そちらのほうがよほど速いです(もっと複雑ではありますが)。

今回の Stripes テクスチャの場合、この平均化はとても簡単にできます。表面プロパティは y や z によらないので、すべきことは x 方向に沿った平均化のみです。色それぞれについてピクセルの分割のしかたを決定し、その2色の重み平均として拡散反射色を計算します。

以下は Stripes テクスチャの3番目のバージョンへの、getTextureSpec() の実装です。完全なソースコードは Stripes3.java です。

public void getTextureSpec(TextureSpec spec, double x, double y, double z, double xsize, double ysize, double zsize, double t, float param[])
{
  double halfsize = 0.5*xsize, fract1, fract2;
  
  if (xsize == 0.0)
    fract1 = (x-Math.floor(x) < width) ? 1.0 : 0.0;
  else
    fract1 = (totalStripeWidth(x+halfsize) - totalStripeWidth(x-halfsize))/xsize;
  fract2 = 1.0-fract1;
  
  spec.diffuse.setRGB((float) (fract1*color1.getRed() + fract2*color2.getRed()),
      (float) (fract1*color1.getGreen() + fract2*color2.getGreen()),
      (float) (fract1*color1.getBlue() + fract2*color2.getBlue()));
  spec.specular.setRGB(0.0f, 0.0f, 0.0f);
  spec.transparent.setRGB(0.0f, 0.0f, 0.0f);
  spec.emissive.setRGB(0.0f, 0.0f, 0.0f);
  spec.bumpGrad.set(0.0, 0.0, 0.0);
  spec.roughness = spec.cloudiness = 0.0;
}

double totalStripeWidth(double x)
{
  double xi = Math.floor(x), xf = x-xi;
  
  if (xf < width)
    return xi*width + xf;
  return xi*width + width;
}
totalStripeWidth() は、0と x の間の color1 の幅の合計を計算します。数学的に言うと、color1 ストライプで1に等しく、color2 ストライプで0に等しい関数の不定積分です。その後、幅 xsize の領域にわたって定積分を計算、xsize で割って、color1 のピクセルのトータルの割合を取得します。

結果テクスチャを以下に示します。ご覧のように、結果は非常に改善されました。

もろちん、これは例外的に単純なテクスチャです。ほとんどのテクスチャで、分析的に平均化するのはさらに難しくなります。多くの場合は不可能で、さまざまな近似が必要となります。言うまでもなく、コードはさらに複雑になり、手続きテクスチャの開発に必要な時間は増大します。

エイリアス問題の解決には、いつでもスーパーサンプリングを頼りに できる ことを思い出しましょう。ときには完全に合理的な選択肢にもなり得ます。テクスチャに1つか2つの画像を含めるだけで、それを以降は使わないなら、恐らくスーパーサンプリングが使えます。これで要求される追加レンダー時間は、最初の方法でテクスチャを作成するのに節約する時間での相殺以上です。その一方で、1つのテクスチャを多数の画像に使う場合、最初からアンチエイリアスの構築に時間をかけるなら、長く走らせるほうがはるかにマシになります。そしてもしあなたのテクスチャを他の人に使わせるなら、アンチエイリアスは絶対に必要なものと考えたほうがいいでしょう。

前: ユーザインターフェイスの追加 次: Texture2D クラスと Texture3D クラス