Usa le luci per disegnare poligoni

Pagina aggiornata :
Data di creazione della pagina :

sommario

Le luci (sorgenti luminose) vengono utilizzate per ombreggiare i poligoni.

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

Ambiente operativo

Prerequisiti

Versioni XNA supportate
  • 4.0
Piattaforme supportate
  • Windows (XP SP2 o successivo, Vista, 7)
  • Xbox 360
  • Windows Phone 7
Versione Vertex Shader richiesta da Windows 2.0
Versione Pixel Shader richiesta da Windows 2.01

Ambiente operativo

piattaforma
  • finestre 7
  • Xbox 360
  • Emulatore di Windows Phone 7

sostanza

Informazioni sulle luci

Ecco alcune cose che puoi fare sull'uso delle luci.

materiale

In parole povere, un materiale è il colore di una sostanza. I materiali vengono spesso utilizzati in combinazione con le luci e gli effetti di base consentono anche di impostare i parametri del materiale e della luce. Tuttavia, questo non si applica se stai scrivendo il tuo programma shader e puoi regolarlo liberamente. Inoltre, si noti che il colore del materiale è diverso dal colore dei vertici.

I materiali hanno generalmente i seguenti elementi.

Diffuso Colori di base delle sostanze
Ambientale Il colore del colore quando esposto alla luce ambientale (visibile anche se la luce non lo colpisce direttamente)
Speculare Luce a riflessione speculare (brilla fortemente come la lucentezza di un'auto, ecc.)
Potenza Speculare Forza riflettente (forza speculare)
Emissivo Luce divergente (si illumina da sola)

Luci e Normali

Se vuoi usare una luce, avrai bisogno di qualcosa chiamato "normale". La posizione della luce rispetto alla normale determina la luminosità dell'oggetto. La normale verrà impostata come dati dei vertici.

面の方向と明るさ

È più luminoso se il viso è rivolto nella direzione della luce e più scuro se è il contrario. Questo vale anche se si sostituisce la direzione della faccia con un vertice. L'orientamento di queste facce e vertici è chiamato "normale".

Ora, la direzione delle normali non è definita in modo esplicito e ci sono due normali principali da impostare nella casella: qui sotto.

面の方向と明るさ

C'è una differenza tra la sinistra e la destra quando viene applicata la luce.

Nel caso del metodo a sinistra, lo spazio tra le facce apparirà angolare. Questo perché è completamente orientato nella stessa direzione della normale del viso. Tuttavia, questo metodo ha lo svantaggio che i vertici non possono essere condivisi.

Con il metodo a destra, lo spazio tra le superfici apparirà leggermente arrotondato a seconda di come viene applicata la luce. Poiché i vertici sono condivisi, c'è un vantaggio che la quantità di dati è ridotta. Lo svantaggio è che la normale del vertice non è la stessa della direzione del viso, quindi anche se la luce viene illuminata direttamente dall'alto, ad esempio, la superficie superiore non sarà influenzata al 100% dalla luce.

È difficile da capire anche se lo spieghi in una frase, quindi controlla il diagramma qui sotto per vedere la differenza.

面の方向と明るさ面の方向と明るさ
Viene visualizzato con un software di modellazione chiamato Metasequoia

Puoi vedere che è molto diverso nell'aspetto. Nell'esempio verrà creata la casella nel modo corretto in modo che il codice non sia ridondante.

campo

/// <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 };

La casella viene creata utilizzando un buffer dei vertici e un buffer di indice.

creazione

// エフェクトを作成
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;

Ci sono diversi elementi in BasicEffect che impostano la luce.

Innanzitutto, impostare la proprietà LightingEnabled su true per indicare la luce da calcolare.

Quando si chiama il metodo EnableDefaultLighting, il colore della luce o del materiale viene impostato automaticamente. Tuttavia, l'utilizzo della luce predefinita su questa scatola è troppo luminoso, quindi ho disabilitato il colore speculare e disabilitato la seconda e la terza luce.

// 頂点の数
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);

È un pezzo di codice un po' lungo, ma crea dati sui vertici. La struttura dei dati dei vertici usata questa volta è "VertexPositionNormalTexture" con i dati "position", "normal" e "texture coordinates". Poiché XNA Framework non fornisce una struttura con solo "position" e "normal", viene specificato "Vector2.Zero" per tutti i vertici per le coordinate della trama. (Naturalmente, se capisci, puoi creare la tua struttura.)

Per quanto riguarda la normale, come mostrato nella figura precedente, è impostata in modo da puntare in direzione obliqua. Poiché le normali sono definizioni di dati rappresentate solo dall'orientamento, la direzione viene specificata e quindi normalizzata con il metodo Vector3.Normalize.

VertexPositionNormalTexture costruttore

Creare un'istanza della struttura "VertexPositionNormalTexture" con i dati dei vertici per la posizione e le coordinate normali e di trama.

posizione Vettoriale3 Posizione vertice
normale Vettoriale3 Normali ai vertici
textureCoordinate Vettore 2 Coordinate della trama dei vertici

Vector3.Normalize metodo

Crea un vettore unitario dal vettore specificato.

valore Vettoriale3 Vettore sorgente da normalizzare
Valori restituiti Vettoriale3 Versore
// インデックスバッファを作成
this.indexBuffer = new IndexBuffer(this.GraphicsDevice,
    IndexElementSize.SixteenBits, 3 * 12, BufferUsage.None);

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

La creazione di un buffer di indice non è diversa.

disegno

// 描画に使用する頂点バッファをセット
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
    );
}

Poiché le informazioni sui vertici sono impostate in anticipo, non c'è nulla di speciale nel codice di disegno.

Tutti i codici

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);
        }
    }
}