3D Graphics with XNA Game Studio 4.0 bug in light map?

Posted by Eibis on Game Development See other posts from Game Development or by Eibis
Published on 2012-09-06T14:55:12Z Indexed on 2012/09/06 15:51 UTC
Read the original article Hit count: 272

Filed under:
|
|
|
|

i'm following the tutorials on 3D Graphics with XNA Game Studio 4.0 and I came up with an horrible effect when I tried to implement the Light Map

http://i.stack.imgur.com/BUWvU.jpg

this effect shows up when I look towards the center of the house (and it moves with me). it has this shape because I'm using a sphere to represent light; using other light shapes gives different results. I'm using a class PreLightingRenderer:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

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

namespace XNAFirstPersonCamera
{
public class PrelightingRenderer
{
    // Normal, depth, and light map render targets
    RenderTarget2D depthTarg;
    RenderTarget2D normalTarg;
    RenderTarget2D lightTarg;

    // Depth/normal effect and light mapping effect
    Effect depthNormalEffect;
    Effect lightingEffect;

    // Point light (sphere) mesh
    Model lightMesh;

    // List of models, lights, and the camera
    public List<CModel> Models { get; set; }
    public List<PPPointLight> Lights { get; set; }
    public FirstPersonCamera Camera { get; set; }
    GraphicsDevice graphicsDevice;
    int viewWidth = 0, viewHeight = 0;

    public PrelightingRenderer(GraphicsDevice GraphicsDevice, ContentManager Content)
    {
        viewWidth = GraphicsDevice.Viewport.Width;
        viewHeight = GraphicsDevice.Viewport.Height;

        // Create the three render targets
        depthTarg = new RenderTarget2D(GraphicsDevice, viewWidth, viewHeight, false, SurfaceFormat.Single, DepthFormat.Depth24);
        normalTarg = new RenderTarget2D(GraphicsDevice, viewWidth, viewHeight, false, SurfaceFormat.Color, DepthFormat.Depth24);
        lightTarg = new RenderTarget2D(GraphicsDevice, viewWidth, viewHeight, false, SurfaceFormat.Color, DepthFormat.Depth24);

        // Load effects
        depthNormalEffect = Content.Load<Effect>(@"Effects\PPDepthNormal");
        lightingEffect = Content.Load<Effect>(@"Effects\PPLight");

        // Set effect parameters to light mapping effect
        lightingEffect.Parameters["viewportWidth"].SetValue(viewWidth);
        lightingEffect.Parameters["viewportHeight"].SetValue(viewHeight);

        // Load point light mesh and set light mapping effect to it
        lightMesh = Content.Load<Model>(@"Models\PPLightMesh");
        lightMesh.Meshes[0].MeshParts[0].Effect = lightingEffect;
        this.graphicsDevice = GraphicsDevice;
    }

    public void Draw()
    {
        drawDepthNormalMap();
        drawLightMap();
        prepareMainPass();
    }

    void drawDepthNormalMap()
    {
        // Set the render targets to 'slots' 1 and 2
        graphicsDevice.SetRenderTargets(normalTarg, depthTarg);

        // Clear the render target to 1 (infinite depth)
        graphicsDevice.Clear(Color.White);

        // Draw each model with the PPDepthNormal effect
        foreach (CModel model in Models)
        {
            model.CacheEffects();
            model.SetModelEffect(depthNormalEffect, false);
            model.Draw(Camera.ViewMatrix, Camera.ProjectionMatrix, Camera.Position);
            model.RestoreEffects();
        }

        // Un-set the render targets
        graphicsDevice.SetRenderTargets(null);
    }

    void drawLightMap()
    {
        // Set the depth and normal map info to the effect
        lightingEffect.Parameters["DepthTexture"].SetValue(depthTarg);
        lightingEffect.Parameters["NormalTexture"].SetValue(normalTarg);

        // Calculate the view * projection matrix
        Matrix viewProjection = Camera.ViewMatrix * Camera.ProjectionMatrix;

        // Set the inverse of the view * projection matrix to the effect
        Matrix invViewProjection = Matrix.Invert(viewProjection);
        lightingEffect.Parameters["InvViewProjection"].SetValue(invViewProjection);

        // Set the render target to the graphics device
        graphicsDevice.SetRenderTarget(lightTarg);

        // Clear the render target to black (no light)
        graphicsDevice.Clear(Color.Black);

        // Set render states to additive (lights will add their influences)
        graphicsDevice.BlendState = BlendState.Additive;
        graphicsDevice.DepthStencilState = DepthStencilState.None;

        foreach (PPPointLight light in Lights)
        {
            // Set the light's parameters to the effect
            light.SetEffectParameters(lightingEffect);

            // Calculate the world * view * projection matrix and set it to
            // the effect
            Matrix wvp = (Matrix.CreateScale(light.Attenuation) * Matrix.CreateTranslation(light.Position)) * viewProjection;
            lightingEffect.Parameters["WorldViewProjection"].SetValue(wvp);

            // Determine the distance between the light and camera
            float dist = Vector3.Distance(Camera.Position, light.Position);

            // If the camera is inside the light-sphere, invert the cull mode
            // to draw the inside of the sphere instead of the outside
            if (dist < light.Attenuation)
                graphicsDevice.RasterizerState = RasterizerState.CullClockwise;

            // Draw the point-light-sphere
            lightMesh.Meshes[0].Draw();

            // Revert the cull mode
            graphicsDevice.RasterizerState = RasterizerState.CullCounterClockwise;
        }

        // Revert the blending and depth render states
        graphicsDevice.BlendState = BlendState.Opaque;
        graphicsDevice.DepthStencilState = DepthStencilState.Default;

        // Un-set the render target
        graphicsDevice.SetRenderTarget(null);
    }

    void prepareMainPass()
    {
        foreach (CModel model in Models)
            foreach (ModelMesh mesh in model.Model.Meshes)
                foreach (ModelMeshPart part in mesh.MeshParts)
                {
                    // Set the light map and viewport parameters to each model's effect
                    if (part.Effect.Parameters["LightTexture"] != null)
                        part.Effect.Parameters["LightTexture"].SetValue(lightTarg);
                    if (part.Effect.Parameters["viewportWidth"] != null)
                        part.Effect.Parameters["viewportWidth"].SetValue(viewWidth);
                    if (part.Effect.Parameters["viewportHeight"] != null)
                        part.Effect.Parameters["viewportHeight"].SetValue(viewHeight);
                }
    }
}

}

that uses three effect:

  1. PPDepthNormal.fx

    float4x4 World;
    float4x4 View;
    float4x4 Projection;
    
    struct VertexShaderInput
    {
    float4 Position : POSITION0;
    float3 Normal : NORMAL0;
     };
    
    struct VertexShaderOutput
    {
    float4 Position : POSITION0;
    float2 Depth : TEXCOORD0;
    float3 Normal : TEXCOORD1;
    };
    
    VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
    {
    VertexShaderOutput output;
    
    float4x4 viewProjection = mul(View, Projection);
    float4x4 worldViewProjection = mul(World, viewProjection);
    
    output.Position = mul(input.Position, worldViewProjection);
    output.Normal = mul(input.Normal, World);
    
    // Position's z and w components correspond to the distance
    // from camera and distance of the far plane respectively
    output.Depth.xy = output.Position.zw;
    
    return output;
    }
    
    // We render to two targets simultaneously, so we can't
    // simply return a float4 from the pixel shader
    struct PixelShaderOutput
    {
    float4 Normal : COLOR0;
    float4 Depth : COLOR1;
    };
    
    
    PixelShaderOutput PixelShaderFunction(VertexShaderOutput input)
    {
       PixelShaderOutput output;
    
       // Depth is stored as distance from camera / far plane distance
       // to get value between 0 and 1
       output.Depth = input.Depth.x / input.Depth.y;
    
       // Normal map simply stores X, Y and Z components of normal
       // shifted from (-1 to 1) range to (0 to 1) range
       output.Normal.xyz = (normalize(input.Normal).xyz / 2) + .5;
    
        // Other components must be initialized to compile
        output.Depth.a = 1;
        output.Normal.a = 1;
    
            return output;
       }
    
       technique Technique1
       {
       pass Pass1
       {
            VertexShader = compile vs_1_1 VertexShaderFunction();
            PixelShader = compile ps_2_0 PixelShaderFunction();
        }
        }
    
    1. PPLight.fx
    float4x4 WorldViewProjection;
    float4x4 InvViewProjection;

    texture2D DepthTexture;
    texture2D NormalTexture;
    sampler2D depthSampler = sampler_state { 
    texture = ; 
    minfilter = point;
    magfilter = point;
    mipfilter = point;
    };
    sampler2D normalSampler = sampler_state { 
    texture = ; 
    minfilter = point;
    magfilter = point;
    mipfilter = point;
    };

    float3 LightColor;
    float3 LightPosition;
    float LightAttenuation;

    // Include shared functions
    #include "PPShared.vsi"

    struct VertexShaderInput
    {
        float4 Position : POSITION0;
    };

    struct VertexShaderOutput
    {
        float4 Position : POSITION0;
        float4 LightPosition : TEXCOORD0;
    };

    VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
    {
        VertexShaderOutput output;

        output.Position = mul(input.Position, WorldViewProjection);
        output.LightPosition = output.Position;

        return output;
    }

    float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
    {
    // Find the pixel coordinates of the input position in the depth
    // and normal textures
    float2 texCoord = postProjToScreen(input.LightPosition) + halfPixel();

    // Extract the depth for this pixel from the depth map
    float4 depth = tex2D(depthSampler, texCoord);

    // Recreate the position with the UV coordinates and depth value
    float4 position;
    position.x = texCoord.x * 2 - 1;
    position.y = (1 - texCoord.y) * 2 - 1;
    position.z = depth.r;
    position.w = 1.0f;

    // Transform position from screen space to world space
    position = mul(position, InvViewProjection);
    position.xyz /= position.w;

    // Extract the normal from the normal map and move from
    // 0 to 1 range to -1 to 1 range
    float4 normal = (tex2D(normalSampler, texCoord) - .5) * 2;

    // Perform the lighting calculations for a point light
    float3 lightDirection = normalize(LightPosition - position);
    float lighting = clamp(dot(normal, lightDirection), 0, 1);

    // Attenuate the light to simulate a point light
    float d = distance(LightPosition, position);
    float att = 1 - pow(d / LightAttenuation, 6);

        return float4(LightColor * lighting * att, 1);
    }

    technique Technique1
    {
        pass Pass1
        {
            VertexShader = compile vs_1_1 VertexShaderFunction();
            PixelShader = compile ps_2_0 PixelShaderFunction();
        }
    }

PPShared.vsi has some common functions:

float viewportWidth;
float viewportHeight;

// Calculate the 2D screen position of a 3D position
float2 postProjToScreen(float4 position)
{
    float2 screenPos = position.xy / position.w;
    return 0.5f * (float2(screenPos.x, -screenPos.y) + 1);
}

// Calculate the size of one half of a pixel, to convert
// between texels and pixels
float2 halfPixel()
{
    return 0.5f / float2(viewportWidth, viewportHeight);
}

and finally from the Game class I set up in LoadContent with:

            effect = Content.Load(@"Effects\PPModel");

            models[0] = new CModel(Content.Load(@"Models\teapot"), new Vector3(-50, 80, 0), new Vector3(0, 0, 0),
                1f, Content.Load(@"Textures\prova_texture_autocad"), GraphicsDevice);
house = new CModel(Content.Load(@"Models\house"), new Vector3(0, 0, 0), new Vector3((float)-Math.PI / 2, 0, 0), 35.0f, Content.Load(@"Textures\prova_texture_autocad"), GraphicsDevice);

            models[0].SetModelEffect(effect, true);
            house.SetModelEffect(effect, true);

            renderer = new PrelightingRenderer(GraphicsDevice, Content);
            renderer.Models = new List();

            renderer.Models.Add(house);
            renderer.Models.Add(models[0]);

            renderer.Lights = new List()
            {
                new PPPointLight(new Vector3(0, 120, 0), Color.White * .85f, 2000)
            };

where PPModel.fx is:

float4x4 World;
float4x4 View;
float4x4 Projection;
texture2D BasicTexture;

sampler2D basicTextureSampler = sampler_state
{
    texture = ;
    addressU = wrap;
    addressV = wrap;
    minfilter = anisotropic;
    magfilter = anisotropic;
    mipfilter = linear;
};

bool TextureEnabled = true;
texture2D LightTexture;

sampler2D lightSampler = sampler_state
{
    texture = ;
    minfilter = point;
    magfilter = point;
    mipfilter = point;
};

float3 AmbientColor = float3(0.15, 0.15, 0.15);
float3 DiffuseColor;

#include "PPShared.vsi"

struct VertexShaderInput
{
    float4 Position : POSITION0;
    float2 UV : TEXCOORD0;
};

struct VertexShaderOutput
{
    float4 Position : POSITION0;
    float2 UV : TEXCOORD0;
    float4 PositionCopy : TEXCOORD1;
};

VertexShaderOutput VertexShaderFunction(VertexShaderInput input)
{
    VertexShaderOutput output;
    float4x4 worldViewProjection = mul(World, mul(View, Projection));
    output.Position = mul(input.Position, worldViewProjection);
    output.PositionCopy = output.Position;
    output.UV = input.UV;
    return output;
}

float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
    // Sample model's texture
    float3 basicTexture = tex2D(basicTextureSampler, input.UV);
    if (!TextureEnabled)
    basicTexture = float4(1, 1, 1, 1);

    // Extract lighting value from light map
    float2 texCoord = postProjToScreen(input.PositionCopy) +
    halfPixel();
    float3 light = tex2D(lightSampler, texCoord);
    light += AmbientColor;
    return float4(basicTexture * DiffuseColor * light, 1);
}

technique Technique1
{
    pass Pass1
    {
        VertexShader = compile vs_1_1 VertexShaderFunction();
        PixelShader = compile ps_2_0 PixelShaderFunction();
    }
}


I don't have any idea on what's wrong... googling the web I found that this tutorial may have some bug but I don't know if it's the LightModel fault (the sphere) or in a shader or in the class PrelightingRenderer.
Any help is very appreciated, thank you for reading!

© Game Development or respective owner

Related posts about XNA

Related posts about 3d