Menggunakan lampu untuk menggambar poligon

Halaman Diperbarui :
Tanggal pembuatan halaman :

ringkasan

Lampu (sumber cahaya) digunakan untuk menaungi poligon.

ライトを使用してポリゴンを描画する

Lingkungan operasi

Prasyarat

Versi XNA yang Didukung
  • 4.0
Platform yang Didukung
  • Windows (XP SP2 atau lebih baru, Vista, 7)
  • Xbox 360
  • Telepon Windows 7
Versi Vertex Shader yang Diperlukan Windows 2.0
Versi Pixel Shader yang Diperlukan Windows 2.01

Lingkungan operasi

balei-balei
  • Jendela 7
  • Xbox 360
  • Windows Phone 7 Emulator

zat

Tentang Lampu

Berikut adalah beberapa hal yang dapat Anda lakukan tentang menggunakan lampu.

bahan

Secara sederhana, bahan adalah warna suatu zat. Material sering digunakan bersama dengan Lights, dan BasicEffects juga memungkinkan Anda untuk mengatur parameter material dan cahaya. Namun, ini tidak berlaku jika Anda menulis program shader Anda sendiri, dan Anda dapat menyesuaikannya dengan bebas. Juga, perhatikan bahwa warna material berbeda dari warna simpul.

Bahan umumnya memiliki item berikut.

Menyebar Warna dasar zat
Ambient Warna warna saat terkena cahaya sekitar (terlihat meskipun cahaya tidak menyinari langsung di atasnya)
Spekular Cahaya pantulan spekular (bersinar kuat seperti kilap mobil, dll.)
Kekuatan Spekuler Kekuatan Reflektif (Kekuatan Spekular)
Emisif Cahaya yang berbeda (bersinar dengan sendirinya)

Lampu dan Normal

Jika Anda ingin menggunakan lampu, Anda memerlukan sesuatu yang disebut "normal". Posisi cahaya dalam kaitannya dengan normal menentukan kecerahan objek. Normal akan ditetapkan sebagai data simpul.

面の方向と明るさ

Lebih terang jika wajah menghadap ke arah cahaya, dan lebih gelap jika sebaliknya. Ini juga berlaku jika Anda mengganti arah wajah dengan simpul. Orientasi wajah dan simpul ini disebut "normal".

Sekarang, arah normal tidak ditentukan secara eksplisit, dan ada dua normal utama yang harus diatur dalam kotak: di bawah ini.

面の方向と明るさ

Ada perbedaan antara kiri dan kanan saat cahaya diterapkan.

Dalam kasus metode di sebelah kiri, ruang di antara wajah akan tampak bersudut. Ini karena sepenuhnya berorientasi ke arah yang sama dengan normal wajah. Namun, metode ini memiliki kelemahan bahwa simpul tidak dapat dibagikan.

Dengan metode di sebelah kanan, ruang antar permukaan akan tampak sedikit membulat tergantung pada bagaimana cahaya diterapkan. Karena simpul dibagikan, ada keuntungan bahwa jumlah data berkurang. Kerugiannya adalah normal simpul tidak sama dengan arah wajah, jadi meskipun cahaya bersinar langsung dari atas, misalnya, permukaan atas tidak akan 100% terpengaruh oleh cahaya.

Sulit untuk dipahami bahkan jika Anda menjelaskannya dalam sebuah kalimat, jadi periksa diagram di bawah ini untuk melihat perbedaannya.

面の方向と明るさ面の方向と明るさ
Itu ditampilkan dengan perangkat lunak pemodelan yang disebut Metasequoia

Anda dapat melihat bahwa penampilannya sangat berbeda. Dalam sampel, kita akan membuat kotak dengan cara yang benar sehingga kodenya tidak berlebihan.

kebun

/// <summary>
/// 基本エフェクト
/// </summary>
private BasicEffect basicEffect = null;

/// <summary>
/// 頂点バッファ
/// </summary>
private VertexBuffer vertexBuffer = null;

/// <summary>
/// インデックスバッファ
/// </summary>
private IndexBuffer indexBuffer = null;

/// <summary>
/// インデックスバッファの各頂点番号配列
/// </summary>
private static readonly Int16[] vertexIndices = new Int16[] {
    2, 0, 1, 1, 3, 2, 4, 0, 2, 2, 6, 4, 5, 1, 0, 0, 4, 5,
    7, 3, 1, 1, 5, 7, 6, 2, 3, 3, 7, 6, 4, 6, 7, 7, 5, 4 };

Kotak dibuat menggunakan buffer simpul dan buffer indeks.

Penciptaan

// エフェクトを作成
this.basicEffect = new BasicEffect(this.GraphicsDevice);

// エフェクトでライトを有効にする
this.basicEffect.LightingEnabled = true;

// デフォルトのライトの設定を使用する
this.basicEffect.EnableDefaultLighting();

// スペキュラーを無効
this.basicEffect.SpecularColor = Vector3.Zero;

// 2番目と3番目のライトを無効
this.basicEffect.DirectionalLight1.Enabled = false;
this.basicEffect.DirectionalLight2.Enabled = false;

Ada beberapa item di BasicEffect yang mengatur cahaya.

Pertama, atur properti LightingEnabled ke true untuk menginstruksikan cahaya yang akan dihitung.

Saat Anda memanggil metode EnableDefaultLighting, warna cahaya atau material diatur secara otomatis. Namun, menggunakan lampu default pada kotak ini terlalu terang, jadi saya menonaktifkan warna spekular dan menonaktifkan lampu kedua dan ketiga.

// 頂点の数
int vertexCount = 8;

// 頂点バッファ作成
this.vertexBuffer = new VertexBuffer(this.GraphicsDevice,
    typeof(VertexPositionNormalTexture), vertexCount, BufferUsage.None);

// 頂点データを作成する
VertexPositionNormalTexture[] vertives = new VertexPositionNormalTexture[vertexCount];

vertives[0] = new VertexPositionNormalTexture(
    new Vector3(-2.0f, 2.0f, -2.0f),
    Vector3.Normalize(new Vector3(-1.0f, 1.0f, -1.0f)),
    Vector2.Zero);
vertives[1] = new VertexPositionNormalTexture(
    new Vector3(2.0f, 2.0f, -2.0f),
    Vector3.Normalize(new Vector3(1.0f, 1.0f, -1.0f)),
    Vector2.Zero);
vertives[2] = new VertexPositionNormalTexture(
    new Vector3(-2.0f, 2.0f, 2.0f),
    Vector3.Normalize(new Vector3(-1.0f, 1.0f, 1.0f)),
    Vector2.Zero);
vertives[3] = new VertexPositionNormalTexture(
    new Vector3(2.0f, 2.0f, 2.0f),
    Vector3.Normalize(new Vector3(1.0f, 1.0f, 1.0f)),
    Vector2.Zero);
vertives[4] = new VertexPositionNormalTexture(
    new Vector3(-2.0f, -2.0f, -2.0f),
    Vector3.Normalize(new Vector3(-1.0f, -1.0f, -1.0f)),
    Vector2.Zero);
vertives[5] = new VertexPositionNormalTexture(
    new Vector3(2.0f, -2.0f, -2.0f),
    Vector3.Normalize(new Vector3(1.0f, -1.0f, -1.0f)),
    Vector2.Zero);
vertives[6] = new VertexPositionNormalTexture(
    new Vector3(-2.0f, -2.0f, 2.0f),
    Vector3.Normalize(new Vector3(-1.0f, -1.0f, 1.0f)),
    Vector2.Zero);
vertives[7] = new VertexPositionNormalTexture(
    new Vector3(2.0f, -2.0f, 2.0f),
    Vector3.Normalize(new Vector3(1.0f, -1.0f, 1.0f)),
    Vector2.Zero);

// 頂点データを頂点バッファに書き込む
this.vertexBuffer.SetData(vertives);

Ini adalah kode yang agak panjang, tetapi membuat data simpul. Struktur data simpul yang digunakan kali ini adalah "VertexPositionNormalTexture" dengan data "position", "normal", dan "texture coordinates". Karena Kerangka Kerja XNA tidak menyediakan struktur hanya dengan "posisi" dan "normal", "Vector2.Zero" ditentukan untuk semua simpul untuk koordinat tekstur. (Tentu saja, jika Anda mengerti, Anda dapat membuat struktur Anda sendiri.)

Adapun normal, seperti yang ditunjukkan pada gambar sebelumnya, diatur untuk menunjuk ke arah miring. Karena normal adalah definisi data yang hanya diwakili oleh orientasi, arah ditentukan dan kemudian dinormalisasi dengan metode Vector3.Normalize.

VertexPositionNormalTexture pembangun

Buat instance struktur "VertexPositionNormalTexture" dengan data simpul untuk posisi dan koordinat normal dan tekstur.

posisi Vektor3 Posisi Simpul
biasa Vektor3 Normal simpul
Koordinat tekstur Vektor2 Koordinat tekstur simpul

Vector3.Normalize Metode

Membuat vektor satuan dari vektor yang ditentukan.

nilai Vektor3 Vektor sumber untuk menormalkan
Nilai Pengembalian Vektor3 Vektor satuan
// インデックスバッファを作成
this.indexBuffer = new IndexBuffer(this.GraphicsDevice,
    IndexElementSize.SixteenBits, 3 * 12, BufferUsage.None);

// 頂点インデックスを書き込む
this.indexBuffer.SetData(vertexIndices);

Membuat buffer indeks tidak berbeda.

gambar

// 描画に使用する頂点バッファをセット
this.GraphicsDevice.SetVertexBuffer(this.vertexBuffer);

// インデックスバッファをセット
this.GraphicsDevice.Indices = this.indexBuffer;

// パスの数だけ繰り替えし描画
foreach (EffectPass pass in this.basicEffect.CurrentTechnique.Passes)
{
    // パスの開始
    pass.Apply();

    // ボックスを描画する
    this.GraphicsDevice.DrawIndexedPrimitives(
        PrimitiveType.TriangleList,
        0,
        0,
        8,
        0,
        12
    );
}

Karena informasi simpul diatur sebelumnya, tidak ada yang istimewa tentang kode gambar.

Semua Kode

using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
#if WINDOWS_PHONE
using Microsoft.Xna.Framework.Input.Touch;
#endif

namespace BoxReceivedLight
{
    /// <summary>
    /// ゲームメインクラス
    /// </summary>
    public class GameMain : Microsoft.Xna.Framework.Game
    {
        /// <summary>
        /// グラフィックデバイス管理クラス
        /// </summary>
        private GraphicsDeviceManager graphics = null;

        /// <summary>
        /// スプライトのバッチ化クラス
        /// </summary>
        private SpriteBatch spriteBatch = null;

        /// <summary>
        /// 基本エフェクト
        /// </summary>
        private BasicEffect basicEffect = null;

        /// <summary>
        /// 頂点バッファ
        /// </summary>
        private VertexBuffer vertexBuffer = null;

        /// <summary>
        /// インデックスバッファ
        /// </summary>
        private IndexBuffer indexBuffer = null;

        /// <summary>
        /// インデックスバッファの各頂点番号配列
        /// </summary>
        private static readonly Int16[] vertexIndices = new Int16[] {
            2, 0, 1, 1, 3, 2, 4, 0, 2, 2, 6, 4, 5, 1, 0, 0, 4, 5,
            7, 3, 1, 1, 5, 7, 6, 2, 3, 3, 7, 6, 4, 6, 7, 7, 5, 4 };


        /// <summary>
        /// GameMain コンストラクタ
        /// </summary>
        public GameMain()
        {
            // グラフィックデバイス管理クラスの作成
            this.graphics = new GraphicsDeviceManager(this);

            // ゲームコンテンツのルートディレクトリを設定
            this.Content.RootDirectory = "Content";

#if WINDOWS_PHONE
            // Windows Phone のデフォルトのフレームレートは 30 FPS
            this.TargetElapsedTime = TimeSpan.FromTicks(333333);

            // バックバッファサイズの設定
            this.graphics.PreferredBackBufferWidth = 480;
            this.graphics.PreferredBackBufferHeight = 800;

            // フルスクリーン表示
            this.graphics.IsFullScreen = true;
#endif
        }

        /// <summary>
        /// ゲームが始まる前の初期化処理を行うメソッド
        /// グラフィック以外のデータの読み込み、コンポーネントの初期化を行う
        /// </summary>
        protected override void Initialize()
        {
            // TODO: ここに初期化ロジックを書いてください

            // コンポーネントの初期化などを行います
            base.Initialize();
        }

        /// <summary>
        /// ゲームが始まるときに一回だけ呼ばれ
        /// すべてのゲームコンテンツを読み込みます
        /// </summary>
        protected override void LoadContent()
        {
            // テクスチャーを描画するためのスプライトバッチクラスを作成します
            this.spriteBatch = new SpriteBatch(this.GraphicsDevice);

            // エフェクトを作成
            this.basicEffect = new BasicEffect(this.GraphicsDevice);

            // エフェクトでライトを有効にする
            this.basicEffect.LightingEnabled = true;

            // デフォルトのライトの設定を使用する
            this.basicEffect.EnableDefaultLighting();

            // スペキュラーを無効
            this.basicEffect.SpecularColor = Vector3.Zero;

            // 2番目と3番目のライトを無効
            this.basicEffect.DirectionalLight1.Enabled = false;
            this.basicEffect.DirectionalLight2.Enabled = false;


            // ビューマトリックスをあらかじめ設定 ((6, 6, 12) から原点を見る)
            this.basicEffect.View = Matrix.CreateLookAt(
                    new Vector3(6.0f, 6.0f, 12.0f),
                    Vector3.Zero,
                    Vector3.Up
                );

            // プロジェクションマトリックスをあらかじめ設定
            this.basicEffect.Projection = Matrix.CreatePerspectiveFieldOfView(
                    MathHelper.ToRadians(45.0f),
                    (float)this.GraphicsDevice.Viewport.Width /
                        (float)this.GraphicsDevice.Viewport.Height,
                    1.0f,
                    100.0f
                );

            // 頂点の数
            int vertexCount = 8;

            // 頂点バッファ作成
            this.vertexBuffer = new VertexBuffer(this.GraphicsDevice,
                typeof(VertexPositionNormalTexture), vertexCount, BufferUsage.None);

            // 頂点データを作成する
            VertexPositionNormalTexture[] vertives = new VertexPositionNormalTexture[vertexCount];

            vertives[0] = new VertexPositionNormalTexture(
                new Vector3(-2.0f, 2.0f, -2.0f),
                Vector3.Normalize(new Vector3(-1.0f, 1.0f, -1.0f)),
                Vector2.Zero);
            vertives[1] = new VertexPositionNormalTexture(
                new Vector3(2.0f, 2.0f, -2.0f),
                Vector3.Normalize(new Vector3(1.0f, 1.0f, -1.0f)),
                Vector2.Zero);
            vertives[2] = new VertexPositionNormalTexture(
                new Vector3(-2.0f, 2.0f, 2.0f),
                Vector3.Normalize(new Vector3(-1.0f, 1.0f, 1.0f)),
                Vector2.Zero);
            vertives[3] = new VertexPositionNormalTexture(
                new Vector3(2.0f, 2.0f, 2.0f),
                Vector3.Normalize(new Vector3(1.0f, 1.0f, 1.0f)),
                Vector2.Zero);
            vertives[4] = new VertexPositionNormalTexture(
                new Vector3(-2.0f, -2.0f, -2.0f),
                Vector3.Normalize(new Vector3(-1.0f, -1.0f, -1.0f)),
                Vector2.Zero);
            vertives[5] = new VertexPositionNormalTexture(
                new Vector3(2.0f, -2.0f, -2.0f),
                Vector3.Normalize(new Vector3(1.0f, -1.0f, -1.0f)),
                Vector2.Zero);
            vertives[6] = new VertexPositionNormalTexture(
                new Vector3(-2.0f, -2.0f, 2.0f),
                Vector3.Normalize(new Vector3(-1.0f, -1.0f, 1.0f)),
                Vector2.Zero);
            vertives[7] = new VertexPositionNormalTexture(
                new Vector3(2.0f, -2.0f, 2.0f),
                Vector3.Normalize(new Vector3(1.0f, -1.0f, 1.0f)),
                Vector2.Zero);

            // 頂点データを頂点バッファに書き込む
            this.vertexBuffer.SetData(vertives);

            // インデックスバッファを作成
            this.indexBuffer = new IndexBuffer(this.GraphicsDevice,
                IndexElementSize.SixteenBits, 3 * 12, BufferUsage.None);

            // 頂点インデックスを書き込む
            this.indexBuffer.SetData(vertexIndices);
        }

        /// <summary>
        /// ゲームが終了するときに一回だけ呼ばれ
        /// すべてのゲームコンテンツをアンロードします
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: ContentManager で管理されていないコンテンツを
            //       ここでアンロードしてください
        }

        /// <summary>
        /// 描画以外のデータ更新等の処理を行うメソッド
        /// 主に入力処理、衝突判定などの物理計算、オーディオの再生など
        /// </summary>
        /// <param name="gameTime">このメソッドが呼ばれたときのゲーム時間</param>
        protected override void Update(GameTime gameTime)
        {
            // Xbox 360 コントローラ、Windows Phone の BACK ボタンを押したときに
            // ゲームを終了させます
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
            {
                this.Exit();
            }

            // TODO: ここに更新処理を記述してください

            // 登録された GameComponent を更新する
            base.Update(gameTime);
        }

        /// <summary>
        /// 描画処理を行うメソッド
        /// </summary>
        /// <param name="gameTime">このメソッドが呼ばれたときのゲーム時間</param>
        protected override void Draw(GameTime gameTime)
        {
            // 画面を指定した色でクリアします
            this.GraphicsDevice.Clear(Color.CornflowerBlue);

            // 描画に使用する頂点バッファをセット
            this.GraphicsDevice.SetVertexBuffer(this.vertexBuffer);

            // インデックスバッファをセット
            this.GraphicsDevice.Indices = this.indexBuffer;

            // パスの数だけ繰り替えし描画
            foreach (EffectPass pass in this.basicEffect.CurrentTechnique.Passes)
            {
                // パスの開始
                pass.Apply();

                // ボックスを描画する
                this.GraphicsDevice.DrawIndexedPrimitives(
                    PrimitiveType.TriangleList,
                    0,
                    0,
                    8,
                    0,
                    12
                );
            }

            // 登録された DrawableGameComponent を描画する
            base.Draw(gameTime);
        }
    }
}