Sign in to follow this  

XNA Lighting and Normals

This topic is 3465 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

Hi, Well, I've been trying to get this working for the last two hours and just can't figure it out. Basically I have generated terrain with a height map and generated normals for it. The terrain shows but I can't get any lighting to work. I havn't sorted out texture coordinates but that shouldn't be the problem (I have tested with lighting off and the colors show etc). Heres what I have so far (partially based off Reimers tutorials)
using System;
using System.Collections.Generic;
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.Net;
using Microsoft.Xna.Framework.Storage;


namespace CotD
{
    struct MyOwnVertexFormat
    {
        private Vector3 position;
        private Vector2 texCoord;
        private Vector3 normal;

        public MyOwnVertexFormat(Vector3 newPosition, Vector2 newTexCoord, Vector3 newNormal)
        {
            position = newPosition;
            texCoord = newTexCoord;
            normal = newNormal;
        }

        public Vector3 getNormal()
        {
            return normal;
        }

        public Vector3 getPosition()
        {
            return position;
        }

        public void setNormal(Vector3 newNormal)
        {
            normal += newNormal;
            normal.Normalize();
        }

        public static VertexElement[] VertexElements =
        {
            new VertexElement(
                0, 
                0, 
                VertexElementFormat.Vector3, 
                VertexElementMethod.Default, 
                VertexElementUsage.Position, 
                0),
            new VertexElement(
                0, 
                sizeof(float)*3, 
                VertexElementFormat.Vector2, 
                VertexElementMethod.Default, 
                VertexElementUsage.TextureCoordinate, 
                0),
            new VertexElement(
                0, 
                sizeof(float)*5, 
                VertexElementFormat.Vector3, 
                VertexElementMethod.Default, 
                VertexElementUsage.Normal, 
                0),
        };

        public static int SizeInBytes = sizeof(float) * (3 + 2+ 3);
    }

    public class Game1 : Microsoft.Xna.Framework.Game
    {
        GraphicsDeviceManager graphics;
        GraphicsDevice device;

        Effect effect;
        Matrix viewMatrix;
        Matrix projectionMatrix;

        // Create buffers
        VertexBuffer vertexBuffer;
        IndexBuffer indexBuffer;

        MyOwnVertexFormat[] vertices;
        int[] indices;

        VertexDeclaration vertexDeclaration;
        Vector3 cameraPos;

        Texture2D streetTexture;

        private float[,] heightData;
        private int terrainWidth = 4;
        private int terrainHeight = 3;

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            Content.RootDirectory = "Content";
        }

        protected override void Initialize()
        {
            graphics.PreferredBackBufferWidth = 500;
            graphics.PreferredBackBufferHeight = 500;
            graphics.IsFullScreen = false;
            graphics.ApplyChanges();
            Window.Title = "Terrain Test";

            base.Initialize();
        }

        protected override void LoadContent()
        {
            device = GraphicsDevice;

            effect = Content.Load<Effect>("Effects/CustomEffect");

            streetTexture = Content.Load<Texture2D>("Textures/streettexture");

            Texture2D heightMap = Content.Load<Texture2D>("Textures/heightmap");
            LoadHeightData(heightMap);

            SetUpVertices();
            SetUpIndices();
            
            SetUpCamera();

            CalculateNormals();
        }

        private void LoadHeightData(Texture2D heightMap)
        {
            terrainWidth = heightMap.Width;
            terrainHeight = heightMap.Height;

            Color[] heightMapColors = new Color[terrainWidth * terrainHeight];
            heightMap.GetData(heightMapColors);

            heightData = new float[terrainWidth, terrainHeight];
            for (int x = 0; x < terrainWidth; x++)
                for (int y = 0; y < terrainHeight; y++)
                    heightData[x, y] = heightMapColors[x + y * terrainWidth].R / 5.0f;
        }

        private void SetUpVertices()
        {
            vertices = new MyOwnVertexFormat[terrainWidth * terrainHeight];
            for (int x = 0; x < terrainWidth; x++)
            {
                for (int y = 0; y < terrainHeight; y++)
                {
                    vertices[x + y * terrainWidth] = new MyOwnVertexFormat(
                        new Vector3(x, heightData[x, y], -y),
                        new Vector2(0, 0), 
                        new Vector3(0, 0, 0)); 
                }
            }

            vertexBuffer = new VertexBuffer(device, vertices.Length * MyOwnVertexFormat.SizeInBytes, BufferUsage.WriteOnly);
            vertexBuffer.SetData(vertices);

            vertexDeclaration = new VertexDeclaration(device, MyOwnVertexFormat.VertexElements);
        }

        private void CalculateNormals()
        {
            for (int i = 0; i < indices.Length / 3; i++)
            {
                int index1 = indices[i * 3];
                int index2 = indices[i * 3 + 1];
                int index3 = indices[i * 3 + 2];

                Vector3 side1 = vertices[index1].getPosition() - vertices[index3].getPosition();
                Vector3 side2 = vertices[index1].getPosition() - vertices[index2].getPosition();
                Vector3 normal = Vector3.Cross(side1, side2);

                vertices[index1].setNormal(normal);
                vertices[index2].setNormal(normal);
                vertices[index3].setNormal(normal);
            }
        }

        private void SetUpIndices()
        {
            indices = new int[(terrainWidth - 1) * (terrainHeight - 1) * 6];
            int counter = 0;
            for (int y = 0; y < terrainHeight - 1; y++)
            {
                for (int x = 0; x < terrainWidth - 1; x++)
                {
                    int lowerLeft = x + y * terrainWidth;
                    int lowerRight = (x + 1) + y * terrainWidth;
                    int topLeft = x + (y + 1) * terrainWidth;
                    int topRight = (x + 1) + (y + 1) * terrainWidth;

                    indices[counter++] = topLeft;
                    indices[counter++] = lowerRight;
                    indices[counter++] = lowerLeft;

                    indices[counter++] = topLeft;
                    indices[counter++] = topRight;
                    indices[counter++] = lowerRight;
                }
            }

            indexBuffer = new IndexBuffer(device, typeof(int), indices.Length, BufferUsage.WriteOnly);
            indexBuffer.SetData(indices);

            device.Indices = indexBuffer;
        }

        private void SetUpCamera()
        {
            cameraPos = new Vector3(10, 200, 40);
            viewMatrix = Matrix.CreateLookAt(cameraPos, new Vector3(0, 2, -2), new Vector3(0, 1, 0));
            projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, device.Viewport.AspectRatio, 1.0f, 2000.0f);
        }


        protected override void UnloadContent()
        {
        }

        protected override void Update(GameTime gameTime)
        {
            if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
                this.Exit();

            base.Update(gameTime);
        }

        protected override void Draw(GameTime gameTime)
        {
            device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.DarkSlateBlue, 1.0f, 0);
            //device.RenderState.FillMode = FillMode.WireFrame;

            Matrix worldMatrix = Matrix.CreateTranslation(-terrainWidth / 2.0f, 0, terrainHeight / 2.0f);
            effect.CurrentTechnique = effect.Techniques["Simplest"];
            effect.Parameters["xTexture"].SetValue(streetTexture);
            effect.Parameters["xViewProjection"].SetValue(viewMatrix * projectionMatrix);
            effect.Parameters["xWorld"].SetValue(worldMatrix);


            effect.Parameters["xEnableLighting"].SetValue(true);
            effect.Parameters["xLightDirection"].SetValue(new Vector3(-0.5f, -0.5f, -1));
            effect.Parameters["xAmbient"].SetValue(0.1f);

            effect.Begin();
            foreach (EffectPass pass in effect.CurrentTechnique.Passes)
            {
                pass.Begin();
                device.VertexDeclaration = vertexDeclaration;

                device.Vertices[0].SetSource(vertexBuffer, 0, MyOwnVertexFormat.SizeInBytes);
                device.DrawIndexedPrimitives(PrimitiveType.TriangleList, 0, 0, vertices.Length, 0, indices.Length / 3);


                pass.End();
            }
            effect.End();

            base.Draw(gameTime);
        }
    }
}
And the effect file
//------- Constants --------
float4x4 xViewProjection;
float4x4 xWorld;
float3 xLightDirection;
float xAmbient;
bool xEnableLighting;

//------- Texture Samplers --------

Texture xTexture;

sampler TextureSampler = sampler_state { texture = <xTexture> ; magfilter = LINEAR; minfilter = LINEAR; mipfilter=LINEAR; AddressU = mirror; AddressV = mirror;};

//------- Technique: Textured --------

struct TexVertexToPixel
{
    float4 Position         : POSITION;    
    float4 Color            : COLOR0;
    float3 Normal            : TEXCOORD0;
    float2 TextureCoords    : TEXCOORD1;
    float4 LightDirection    : TEXCOORD2;

};

struct TexPixelToFrame
{
    float4 Color : COLOR0;
};

TexVertexToPixel TexturedVS( float4 inPos : POSITION, float3 inNormal: NORMAL, float2 inTexCoords: TEXCOORD0)
{
    TexVertexToPixel Output = (TexVertexToPixel)0;
    float4x4 preWorldViewProjection = mul (xWorld, xViewProjection);
    
    Output.Position = mul(inPos, preWorldViewProjection);
    Output.Normal = mul(normalize(inNormal), xWorld);
    Output.TextureCoords = inTexCoords;
    Output.LightDirection.xyz = -xLightDirection;
    Output.LightDirection.w = 1;
    
    return Output;    
}

TexPixelToFrame TexturedPS(TexVertexToPixel PSIn)
{
    TexPixelToFrame Output = (TexPixelToFrame)0;
    
    float lightingFactor = 1;
    if (xEnableLighting)
        lightingFactor = saturate(saturate(dot(PSIn.Normal, PSIn.LightDirection)) + xAmbient);

    Output.Color = tex2D(TextureSampler, PSIn.TextureCoords)*lightingFactor;

    return Output;
}


technique Simplest
{
    pass Pass0
    {
        VertexShader = compile vs_2_0 TexturedVS();
        PixelShader = compile ps_2_0 TexturedPS();
    }
}
Any help apreciated.

Share this post


Link to post
Share on other sites

This topic is 3465 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this