忍者ブログ

Memeplexes

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

かんたんXNA その19 マテリアル

このページは古いです
最新版はこちら

物の色はライトの色だけで決まるわけではありません。
真っ黒いものは赤い光が当たろうと緑の光が当たろうと
青い光が当たろうと白い光が当たろうと
黒です。

緑の葉っぱに白い光を当てると、
反射する光は白ではなく緑です。

どういうことかというと、
物の見かけの色はライトの色だけでなく、物そのものの色にも影響される
といういことです。
物体にどんな光が当たるかということだけではなく、
それがどれだけ物体によって跳ね返されるかにも影響されるのです。

この「物そのものの色」をマテリアルといい、

BasicEffectのプロパティで設定します。

public Vector3 DiffuseColor { get; set; }

DirectionalLightのDiffuseColorをどれだけ跳ね返すかを表します。

public Vector3 SpecularColor {get; set; }

DirectionalLightのSpecularColorをどれだけ跳ね返すかを表します。

public float SpecularPower { get; set; }

SpecularColorの色の収束っぷりを表します。
大きいほどよりよく収束します。
specularPower1.JPGSpecularPower = 1
specularPower10.JPGSpecularPower = 10
specularPower100.JPGSpecularPower = 100

public Vector3 AmbientLightColor { get; set; }

環境光による色を表します。
周りで複雑な反射を繰り返し、最終的に物体に当たる光による色です。
これは物体やカメラがどんな位置や角度にあろうと必ず物体の色に反映されます。

public Vector3 EmissiveColor { get; set; }

自分自身が放つ色です。
他のプロパティが光の反射による、惑星の光だとすれば、
このプロパティは自分から光を発する恒星の光といったところでしょうか。

using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;
using System;

class MyGame : Game
{
    GraphicsDeviceManager graphics;
    BasicEffect basicEffect;

    VertexPositionNormalTexture[] vertices;

    ContentManager content;
    Texture2D texture;

    public MyGame()
    {
        graphics = new GraphicsDeviceManager(this);
        content = new ContentManager(Services);

        int planeCount = 30;
        vertices = new VertexPositionNormalTexture[planeCount * 6];

        for (int i = 0; i < planeCount; i++)
        {
            VertexPositionNormalTexture[] plane 
                = new VertexPositionNormalTexture[4];

            plane[0] = new VertexPositionNormalTexture(
                    getPosition(i, planeCount)  + new Vector3(0, 1, 0), 
                    getPosition(i, planeCount), 
                    new Vector2((float)i / planeCount, 0)
                );
            plane[1] = new VertexPositionNormalTexture(
                    getPosition(i + 1, planeCount) + new Vector3(0, 1, 0),
                    getPosition(i + 1, planeCount),
                    new Vector2((float)(i + 1)/ planeCount, 0)
                );
            plane[2] = new VertexPositionNormalTexture(
                    getPosition(i, planeCount) + new Vector3(0, -1, 0),
                    getPosition(i, planeCount),
                    new Vector2((float)i / planeCount, 1)
                );
            plane[3] = new VertexPositionNormalTexture(
                    getPosition(i + 1, planeCount) + new Vector3(0, -1, 0),
                    getPosition(i + 1, planeCount),
                    new Vector2((float)(i + 1) / planeCount, 1)
                );

            vertices[i * 6] = plane[0];
            vertices[i * 6 + 1] = plane[1];
            vertices[i * 6 + 2] = plane[2];

            vertices[i * 6 + 3] = plane[1];
            vertices[i * 6 + 4] = plane[3];
            vertices[i * 6 + 5] = plane[2];
        }

    }

    Vector3 getPosition(int index, int count)
    {
        return new Vector3(
            (float)Math.Sin(2 * Math.PI * index / count),
            0,
            (float)Math.Cos(2 * Math.PI * index / count)
            );
    }

    protected override void LoadGraphicsContent(bool loadAllContent)
    {
        if (loadAllContent)
        {
            basicEffect = new BasicEffect(graphics.GraphicsDevice, null);
            basicEffect.View = Matrix.CreateLookAt(
                new Vector3(0, 0, 8),
                new Vector3(0, 0, 0),
                new Vector3(0, 1, 0)
                );
            basicEffect.Projection = Matrix.CreatePerspectiveFieldOfView(
                MathHelper.ToRadians(45),
                Window.ClientBounds.Width / (float)Window.ClientBounds.Height,
                1, 100
                );


            texture = content.Load<Texture2D>("FlyingSpaghettiMonster");
        }
    }

    protected override void UnloadGraphicsContent(bool unloadAllContent)
    {
        if (unloadAllContent) { content.Unload(); }
    }

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

        graphics.GraphicsDevice.VertexDeclaration = new VertexDeclaration(
            graphics.GraphicsDevice,
            VertexPositionNormalTexture.VertexElements
            );

        basicEffect.TextureEnabled = true;
        basicEffect.Texture = texture;

        basicEffect.LightingEnabled = true;
        basicEffect.DirectionalLight0.Enabled = true;
        basicEffect.DirectionalLight0.Direction = new Vector3(1, 0, -1);
        basicEffect.DirectionalLight0.DiffuseColor = new Vector3(0.5f, 0.5f, 0.5f);
        basicEffect.DirectionalLight0.SpecularColor = Color.White.ToVector3();

        basicEffect.DiffuseColor = Color.Green.ToVector3();
        basicEffect.SpecularColor = Color.Blue.ToVector3();
        basicEffect.SpecularPower = 100;
        
        basicEffect.Begin();

        foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes)
        {
            pass.Begin();

            graphics.GraphicsDevice.DrawUserPrimitives<VertexPositionNormalTexture>(
                PrimitiveType.TriangleList,
                vertices,
                0,
                vertices.Length / 3
                );

            pass.End();
        }

        basicEffect.End();
    }
}

material.JPG

このプログラムは、円筒形を表示して、そのマテリアルを設定しています。
マテリアルのDiffuseColorは緑なので、ライトの緑成分しか跳ね返しません。
(つまりライトに緑成分がなければ真っ黒に見えます。
lightDiffuseNoGreen.JPGライトのDiffuseColor = (0.5f, 0, 0.5f)


一方マテリアルのSpecularColorは青なので、
円筒のハイライト部分は青くなっています(不自然なことに!)

自然な感じにしたいのならマテリアルの
SpecularColorは白にしたほうがいいでしょうね。
specularWhite.JPG

拍手[0回]

PR