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: 310
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:
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(); } }
- 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