忍者ブログ

Memeplexes

プログラミング、3DCGとその他いろいろについて

かんたんXNA4.0 その22 深度バイアス

3DCGには"z-ファイティング"という現象があります。
カメラからの距離があまりにも同じような物体が2つあると、
深度バッファの計算が狂って(丸め誤差などが原因だそうです)
描画がおかしくなります。

zFighting2.jpg

zFighting.jpg


これは、同じ平面上に2つの三角形を重ねてクルクルと回転させて表示したものです。
なんだか縦にギザギザしているのがわかると思います。
これが"z-ファイティング"です。

これが問題になるのは、物が隠れて出来る"影(シャドウ)"を描画するときなどです。
戦闘機が太陽光をさえぎって地面に影を落とすとすると、
地面を表すポリゴンと影を表すポリゴンが重なることになります。
すると、このような変なギザギザが発生してしまうことがあるのです。

この問題を解決する方法のひとつとして、深度バイアスがあります。
深度が同じならプラスαの情報を使って
どっちのポリゴンが前か決めればいいのです。
例えばカメラからの距離が同じ2つの三角形があるとしましょう。
このままだとzファイティングが発生するのですが、
ここで片方の深度バイアスを0.1fに設定してやります。(デフォルトでは0)
そうすると、0.1の方がより奥にあると認識されて0の後ろに隠れるのです。
z-ファイティングになりません。

この深度バイアスをセットするには
RasterizerState.DepthBiasプロパティを使います。

public float DepthBias { get; set; }

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;


class Triangle
{
    VertexPositionColor[] vertices = new VertexPositionColor[3];

    public Triangle(Vector3 p1, Vector3 p2, Vector3 p3, Color color)
    {
        vertices[0] = new VertexPositionColor(p1, color);
        vertices[1] = new VertexPositionColor(p2, color);
        vertices[2] = new VertexPositionColor(p3, color);
    }

    public void Draw(GraphicsDevice graphicsDevice, BasicEffect effect)
    {
        effect.VertexColorEnabled = true;

        foreach (EffectPass pass in effect.CurrentTechnique.Passes)
        {
            pass.Apply();

            graphicsDevice.DrawUserPrimitives<VertexPositionColor>(
                PrimitiveType.TriangleList,
                vertices,
                0,
                1
                );
        }
    }
}

class MyGame : Game
{
    GraphicsDeviceManager graphics;
    BasicEffect effect;

    Triangle blueTriangle = new Triangle
    (
        new Vector3(0, 1, 0),
        new Vector3(1, -0.5f, 0),
        new Vector3(-1, -0.5f, 0),
        Color.Blue
    );
    Triangle redTriangle = new Triangle
    (
        new Vector3(1, 0.5f, 0),
        new Vector3(0, -1, 0),
        new Vector3(-1, 0.5f, 0),
        Color.Red
    );
    RasterizerState depthBiasEnabled;

    public MyGame()
    {
        graphics = new GraphicsDeviceManager(this);

    }

    protected override void LoadContent()
    {
        effect = new BasicEffect(GraphicsDevice)
        {
            View = Matrix.CreateLookAt
            (
                new Vector3(0, 0, 3),  //カメラの位置
                new Vector3(0, 0, 0),   //カメラの見る点
                new Vector3(0, 1, 0)    //カメラの上向きベクトル
            ),
            Projection = Matrix.CreatePerspectiveFieldOfView
            (
                MathHelper.ToRadians(45),   //視野の角度。ここでは45°
                GraphicsDevice.Viewport.AspectRatio,//画面のアスペクト比(=横/縦)
                1,      //カメラからこれより近い物体は画面に映らない
                100     //カメラからこれより遠い物体は画面に映らない
            )
        };
        depthBiasEnabled = new RasterizerState
        {
            DepthBias = 0.1f,
            CullMode = CullMode.None 
        };
    }

    protected override void UnloadContent()
    {
        effect.Dispose();
    }

    protected override void Update(GameTime gameTime)
    {
        //(カメラではなく)三角形そのものを回転させる。
        effect.World *= Matrix.CreateRotationY(MathHelper.ToRadians(1));
    }

    protected override void Draw(GameTime gameTime)
    {
        GraphicsDevice.Clear(Color.CornflowerBlue);

        
        GraphicsDevice.RasterizerState = RasterizerState.CullNone;
        blueTriangle.Draw(GraphicsDevice, effect);
        GraphicsDevice.RasterizerState = depthBiasEnabled;
        redTriangle.Draw(GraphicsDevice, effect);
    }
}
depthBias.jpg
深度バイアスをセットして、青い三角形を常に赤い三角形の上に描画するようにしました。
赤い三角形に深度バイアスをセットしたのでより遠くにあると認識されたようです。
カメラからの距離は同じですが、z-ファイティングにはなりません!


拍手[1回]

PR