Shader を使って立方体を描画

XNA で Shader を使って立方体の各面に色を設定して描画します。

前田稔(Maeda Minoru)の超初心者のプログラム入門

プログラムの説明

  1. プログラムで使用する制御領域を定義しています。
    領域の説明は 立方体に色を設定して描画 を参照して下さい。
        GraphicsDeviceManager graphics; 
    
        VertexDeclaration       cubeVertexDeclaration; 
        VertexPositionColor[]   cubeVertices; 
        VertexBuffer            vertexBuffer; 
        short[]                 cubeIndices; 
        IndexBuffer             indexBuffer;
        Effect                  effect; 
        Matrix                  worldViewProjection; 
        Matrix                  worldMatrix;
        Matrix                  viewMatrix;
        Matrix                  projectionMatrix; 
        float                   modelRotation = 0.0f; 
        
  2. LoadContent() メソッドで、3D描画の初期化を行います。
    InitializeTransform() で描画環境の設定をします。
    InitializeEffect() でエフェクトを設定します。
    InitializeCube() で立方体を作成します。
        protected override void LoadContent() 
        { 
            InitializeTransform(); 
            InitializeEffect(); 
            InitializeCube(); 
        } 
        
  3. 描画環境を設定する InitializeTransform() メソッドです。
        private void InitializeTransform() 
        { 
            viewMatrix = Matrix.CreateLookAt(new Vector3(0, 2, 5), Vector3.Zero, Vector3.Up);
            projectionMatrix = Matrix.CreatePerspectiveFieldOfView( 
                (float)Math.PI / 4.0f,  // 2 π ラジアンは 360 度なので、これは 45 度になります。 
                (float)graphics.GraphicsDevice.Viewport.Width / 
                (float)graphics.GraphicsDevice.Viewport.Height, 
                1.0f, 100.0f ); 
        } 
        
  4. エフェクトを設定する InitializeEffect() メソッドです。
    とは言ってもここでは Content に格納したエフェクト(ReallySimpleEffect.fx)を呼び出すだけです。
    ReallySimpleEffect.fx は標準的なエフェクトで、このページの後部に掲載しています。
    "TransformTechnique" は ReallySimpleEffect.fx の中で定義されています。
        private void InitializeEffect() 
        {
            effect = Content.Load<Effect>("ReallySimpleEffect"); 
            effect.CurrentTechnique = effect.Techniques["TransformTechnique"]; 
        } 
        
  5. InitializeCube() メソッドで IndexBuffer を使って立方体を定義します。
    IndexBuffer の説明は ラインプリミティブの描画 を参照して下さい。
        private void InitializeCube() 
        { 
            cubeVertexDeclaration = new VertexDeclaration( 
                graphics.GraphicsDevice, VertexPositionColor.VertexElements ); 
    
            cubeVertices = new VertexPositionColor[24]; 
     
            Vector3 topLeftFront = new Vector3( -1.0f, 1.0f, 1.0f ); 
            Vector3 bottomLeftFront = new Vector3( -1.0f, -1.0f, 1.0f ); 
            Vector3 topRightFront = new Vector3( 1.0f, 1.0f, 1.0f ); 
            Vector3 bottomRightFront = new Vector3( 1.0f, -1.0f, 1.0f ); 
            Vector3 topLeftBack = new Vector3( -1.0f, 1.0f, -1.0f ); 
            Vector3 topRightBack = new Vector3( 1.0f, 1.0f, -1.0f ); 
            Vector3 bottomLeftBack = new Vector3( -1.0f, -1.0f, -1.0f ); 
            Vector3 bottomRightBack = new Vector3( 1.0f, -1.0f, -1.0f ); 
     
            cubeVertices[0] = new VertexPositionColor( topLeftFront, Color.Red ); 
            cubeVertices[1] = new VertexPositionColor( bottomLeftFront, Color.Red ); 
            cubeVertices[2] = new VertexPositionColor( topRightFront, Color.Red ); 
            cubeVertices[3] = new VertexPositionColor( bottomRightFront, Color.Red ); 
    
            cubeVertices[4] = new VertexPositionColor( topLeftBack, Color.Orange ); 
            cubeVertices[5] = new VertexPositionColor( topRightBack, Color.Orange ); 
            cubeVertices[6] = new VertexPositionColor( bottomLeftBack, Color.Orange ); 
            cubeVertices[7] = new VertexPositionColor( bottomRightBack, Color.Orange ); 
     
            cubeVertices[8] = new VertexPositionColor( topLeftFront, Color.Yellow ); 
            cubeVertices[9] = new VertexPositionColor( topRightBack, Color.Yellow ); 
            cubeVertices[10] = new VertexPositionColor( topLeftBack, Color.Yellow ); 
            cubeVertices[11] = new VertexPositionColor( topRightFront, Color.Yellow ); 
     
            cubeVertices[12] = new VertexPositionColor( bottomLeftFront, Color.Purple ); 
            cubeVertices[13] = new VertexPositionColor( bottomLeftBack, Color.Purple ); 
            cubeVertices[14] = new VertexPositionColor( bottomRightBack, Color.Purple ); 
            cubeVertices[15] = new VertexPositionColor( bottomRightFront, Color.Purple ); 
     
            cubeVertices[16] = new VertexPositionColor( topLeftFront, Color.Blue ); 
            cubeVertices[17] = new VertexPositionColor( bottomLeftBack, Color.Blue ); 
            cubeVertices[18] = new VertexPositionColor( bottomLeftFront, Color.Blue ); 
            cubeVertices[19] = new VertexPositionColor( topLeftBack, Color.Blue ); 
     
            cubeVertices[20] = new VertexPositionColor( topRightFront, Color.Green ); 
            cubeVertices[21] = new VertexPositionColor( bottomRightFront, Color.Green ); 
            cubeVertices[22] = new VertexPositionColor( bottomRightBack, Color.Green ); 
            cubeVertices[23] = new VertexPositionColor( topRightBack, Color.Green ); 
    
            cubeIndices = new short[] {
                0,  1,  2,  1,  3,  2,      // 赤色の正面 
                4,  5,  6,  6,  5,  7,      // オレンジ色の背面 
                8,  9, 10,  8, 11,  9,      // 黄色の上面 
                12, 13, 14, 12, 14, 15,     // 紫色の底面 
                16, 17, 18, 19, 17, 16,     // 青色の左面 
                20, 21, 22, 23, 20, 22 };   // 緑色の右面 
     
            vertexBuffer = new VertexBuffer( 
                graphics.GraphicsDevice, 
                cubeVertices.Length * VertexPositionColor.SizeInBytes, 
                BufferUsage.None); 
     
            vertexBuffer.SetData<VertexPositionColor>( cubeVertices ); 
     
            indexBuffer = new IndexBuffer( graphics.GraphicsDevice, 
                sizeof( short ) * cubeIndices.Length, 
                BufferUsage.None,
                IndexElementSize.SixteenBits ); 
     
            indexBuffer.SetData<short>( cubeIndices ); 
        } 
        
  6. Update() ではタイマーを取得して、モデルの回転に使用する係数(modelRotation)を設定します。
        protected override void Update( GameTime gameTime ) 
        { 
            // Xbox 360 および Windows でデフォルト ゲームの終了を許可します。 
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed  ||
                Keyboard.GetState().IsKeyDown(Keys.Escape)) this.Exit();
            // Timer で回転係数を設定
            modelRotation += (float)gameTime.ElapsedGameTime.TotalMilliseconds * MathHelper.ToRadians(0.05f);
    
            base.Update( gameTime ); 
        } 
        
  7. 立方体を描画する Draw() メソッドです。
    Y軸を基点に modelRotation で回転しながら描画します。
        protected override void Draw( GameTime gameTime ) 
        { 
            graphics.GraphicsDevice.Clear( Color.CornflowerBlue ); 
     
            graphics.GraphicsDevice.VertexDeclaration = 
                cubeVertexDeclaration; 
     
            graphics.GraphicsDevice.RenderState.CullMode = 
                CullMode.CullClockwiseFace; 
     
            graphics.GraphicsDevice.Indices = indexBuffer; 
     
            graphics.GraphicsDevice.Vertices[0].SetSource( vertexBuffer, 0, 
                VertexPositionColor.SizeInBytes ); 
     
            // エフェクトは、アプリケーションの他の場所で作成され、コンパイルされている
            effect.Begin(); 
            foreach (EffectPass pass in effect.CurrentTechnique.Passes) 
            { 
                pass.Begin(); 
                worldMatrix = Matrix.CreateRotationY(modelRotation);
                worldViewProjection = worldMatrix * viewMatrix * projectionMatrix; 
                effect.Parameters["WorldViewProj"].SetValue( worldViewProjection ); 
    
                graphics.GraphicsDevice.DrawIndexedPrimitives( 
                    PrimitiveType.TriangleList, 0, 0, 
                    cubeVertices.Length, 0, 12 ); 
                pass.End(); 
            } 
            effect.End(); 
     
            base.Draw( gameTime ); 
        } 
        
  8. Content のフォルダーに格納されている ReallySimpleEffect.fx です。
    画像の時と同じようにプロジェクトに加えて下さい。
    シェーダの説明は DirectX のページを参照して下さい。
    uniform extern float4x4 WorldViewProj :WORLDVIEWPROJECTION; 
     
    struct VS_OUTPUT 
    { 
    float4 position :POSITION; 
    float4 color :COLOR0; 
    }; 
     
    VS_OUTPUT Transform( 
    float4 Pos  :POSITION,  
    
    float4 Color :COLOR0 ) 
    { 
    VS_OUTPUT Out = (VS_OUTPUT)0; 
     
    Out.position = mul(Pos, WorldViewProj); 
    Out.color = Color; 
     
    return Out; 
    } 
     
    float4 PixelShader( VS_OUTPUT vsout ) :COLOR 
    { 
    return vsout.color; 
    } 
    
    technique TransformTechnique 
    { 
    pass P0 
        { 
    vertexShader = compile vs_2_0 Transform(); 
    pixelShader = compile ps_1_1 PixelShader(); 
        } 
    } 
    

前田稔(Maeda Minoru)の超初心者のプログラム入門

超初心者のプログラム入門(XNA(C#) game program)