|
|
|
|
Changing the shape of our light |
OK, we’ve got a square light, casting shadows on our scene. How can we make it look more like a real light? A real light shines a round beam of light, so let’s start with that. To determine the pixels that are part of the round beam, we could add some mathematical checks (see Recipe 6-8). That would be OK for a normal light, but for the headlights of a car? This should have 2 beams, so more maths..
In my opinion, a much easier method is to throw in another texture that can be used by our pixel shader:

By looking at the image above I think you get the idea: only the white part will be lit. You can download it by right-clicking on it or by clicking here. We can already add the texture sampler to our HLSL code:
Texture xCarLightTexture;
sampler CarLightSampler = sampler_state { texture = <xCarLightTexture>
; magfilter = LINEAR; minfilter=LINEAR; mipfilter = LINEAR; AddressU = clamp; AddressV = clamp;};
Now we can simply sample the color value of this image, which gives us a value between 0 and 1. We simply need to multiply our final color by this value, so this is the core of our pixel shader:
diffuseLightingFactor = DotProduct(xLightPos, PSIn.Position3D, PSIn.Normal); diffuseLightingFactor = saturate(diffuseLightingFactor); diffuseLightingFactor *= xLightPower; float lightTextureFactor = tex2D(CarLightSampler, ProjectedTexCoords).r; diffuseLightingFactor *= lightTextureFactor;
That’s it for the HLSL code! In our XNA code, we’re going to load the texture file, so import it into your project and add this variable to our code:
Texture2D carLight;
And this line to our LoadContent method:
carLight = Content.Load<Texture2D>
("carlight");
Now we still need to pass the texture to our effect file. In our DrawScene method, add this line to the list of XNA-to-HLSL parameters we’re passing to the graphics device:
effect.Parameters["xCarLightTexture"].SetValue(carLight);
And this line in the DrawModel method:
currentEffect.Parameters["xCarLightTexture"].SetValue(carLight);
Et voila! That’s all there is to it. You should see the image below:

Click here to go to the forum on this chapter!
Or click on one of the topics on this chapter to go there: Custom Effect issue Hi Riemers,
Thank you for this tutorial. This sit...Shadow - models with no texture How to modify the code to use models with no textu...how to move car in hello
how to move the car in differen...Blank Screen When i add this section the whole image goes black...
You can also use shaping images with other values than completely white/black. Grey pixels will be lit with less light than white pixels!
The HLSL code:
float4x4 xWorldViewProjection; float4x4 xLightsWorldViewProjection; float4x4 xWorld; float3 xLightPos; float xLightPower; float xAmbient;
Texture xTexture;
sampler TextureSampler = sampler_state { texture = <xTexture>
; magfilter = LINEAR; minfilter = LINEAR; mipfilter=LINEAR; AddressU = mirror; AddressV = mirror;};Texture xShadowMap;
sampler ShadowMapSampler = sampler_state { texture = <xShadowMap>
; magfilter = LINEAR; minfilter = LINEAR; mipfilter=LINEAR; AddressU = clamp; AddressV = clamp;};
Texture xCarLightTexture;
sampler CarLightSampler = sampler_state { texture = <xCarLightTexture>
; magfilter = LINEAR; minfilter=LINEAR; mipfilter = LINEAR; AddressU = clamp; AddressV = clamp;};
struct VertexToPixel { float4 Position : POSITION; float2 TexCoords : TEXCOORD0; float3 Normal : TEXCOORD1; float3 Position3D : TEXCOORD2; };
struct PixelToFrame { float4 Color : COLOR0; };
float DotProduct(float3 lightPos, float3 pos3D, float3 normal) { float3 lightDir = normalize(pos3D - lightPos); return dot(-lightDir, normal); }
VertexToPixel SimplestVertexShader( float4 inPos : POSITION0, float3 inNormal: NORMAL0, float2 inTexCoords : TEXCOORD0) { VertexToPixel Output = (VertexToPixel)0; Output.Position =mul(inPos, xWorldViewProjection); Output.TexCoords = inTexCoords; Output.Normal = normalize(mul(inNormal, (float3x3)xWorld)); Output.Position3D = mul(inPos, xWorld);
return Output; }
PixelToFrame OurFirstPixelShader(VertexToPixel PSIn) { PixelToFrame Output = (PixelToFrame)0;
float diffuseLightingFactor = DotProduct(xLightPos, PSIn.Position3D, PSIn.Normal); diffuseLightingFactor = saturate(diffuseLightingFactor); diffuseLightingFactor *= xLightPower;
PSIn.TexCoords.y--; float4 baseColor = tex2D(TextureSampler, PSIn.TexCoords); Output.Color = baseColor*(diffuseLightingFactor + xAmbient);
return Output; }
technique Simplest { pass Pass0 { VertexShader = compile vs_2_0 SimplestVertexShader(); PixelShader = compile ps_2_0 OurFirstPixelShader(); } }
struct SMapVertexToPixel { float4 Position : POSITION; float4 Position2D : TEXCOORD0; };
struct SMapPixelToFrame { float4 Color : COLOR0; };
SMapVertexToPixel ShadowMapVertexShader( float4 inPos : POSITION) { SMapVertexToPixel Output = (SMapVertexToPixel)0;
Output.Position = mul(inPos, xLightsWorldViewProjection); Output.Position2D = Output.Position;
return Output; }
SMapPixelToFrame ShadowMapPixelShader(SMapVertexToPixel PSIn) { SMapPixelToFrame Output = (SMapPixelToFrame)0;
Output.Color = PSIn.Position2D.z/PSIn.Position2D.w;
return Output; }
technique ShadowMap { pass Pass0 { VertexShader = compile vs_2_0 ShadowMapVertexShader(); PixelShader = compile ps_2_0 ShadowMapPixelShader(); } }
struct SSceneVertexToPixel { float4 Position : POSITION; float4 Pos2DAsSeenByLight : TEXCOORD0; float2 TexCoords : TEXCOORD1; float3 Normal : TEXCOORD2; float4 Position3D : TEXCOORD3; };
struct SScenePixelToFrame { float4 Color : COLOR0; };
SSceneVertexToPixel ShadowedSceneVertexShader( float4 inPos : POSITION, float2 inTexCoords : TEXCOORD0, float3 inNormal : NORMAL) { SSceneVertexToPixel Output = (SSceneVertexToPixel)0;
Output.Position = mul(inPos, xWorldViewProjection); Output.Pos2DAsSeenByLight = mul(inPos, xLightsWorldViewProjection); Output.Normal = normalize(mul(inNormal, (float3x3)xWorld)); Output.Position3D = mul(inPos, xWorld); Output.TexCoords = inTexCoords;
return Output; }
SScenePixelToFrame ShadowedScenePixelShader(SSceneVertexToPixel PSIn) { SScenePixelToFrame Output = (SScenePixelToFrame)0;
float2 ProjectedTexCoords; ProjectedTexCoords[0] = PSIn.Pos2DAsSeenByLight.x/PSIn.Pos2DAsSeenByLight.w/2.0f +0.5f; ProjectedTexCoords[1] = -PSIn.Pos2DAsSeenByLight.y/PSIn.Pos2DAsSeenByLight.w/2.0f +0.5f; float diffuseLightingFactor = 0; if ((saturate(ProjectedTexCoords).x == ProjectedTexCoords.x) && (saturate(ProjectedTexCoords).y == ProjectedTexCoords.y)) { float depthStoredInShadowMap = tex2D(ShadowMapSampler, ProjectedTexCoords).r; float realDistance = PSIn.Pos2DAsSeenByLight.z/PSIn.Pos2DAsSeenByLight.w; if ((realDistance - 1.0f/100.0f) <= depthStoredInShadowMap) { diffuseLightingFactor = DotProduct(xLightPos, PSIn.Position3D, PSIn.Normal); diffuseLightingFactor = saturate(diffuseLightingFactor); diffuseLightingFactor *= xLightPower;
float lightTextureFactor = tex2D(CarLightSampler, ProjectedTexCoords).r; diffuseLightingFactor *= lightTextureFactor;
} } float4 baseColor = tex2D(TextureSampler, PSIn.TexCoords); Output.Color = baseColor*(diffuseLightingFactor + xAmbient);
return Output; }
technique ShadowedScene { pass Pass0 { VertexShader = compile vs_2_0 ShadowedSceneVertexShader(); PixelShader = compile ps_2_0 ShadowedScenePixelShader(); } }
And the XNA code:
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 XNAseries3 { public struct MyOwnVertexFormat { public Vector3 position; private Vector2 texCoord; private Vector3 normal; public MyOwnVertexFormat(Vector3 position, Vector2 texCoord, Vector3 normal) { this.position = position; this.texCoord = texCoord; this.normal = normal; } public readonly static VertexDeclaration VertexDeclaration = new VertexDeclaration ( new VertexElement(0, VertexElementFormat.Vector3, VertexElementUsage.Position, 0), new VertexElement(sizeof(float) * 3, VertexElementFormat.Vector2, VertexElementUsage.TextureCoordinate, 0), new VertexElement(sizeof(float) * (3+2), VertexElementFormat.Vector3, VertexElementUsage.Normal, 0) ); } public class Game1 : Microsoft.Xna.Framework.Game { GraphicsDeviceManager graphics; GraphicsDevice device; Effect effect; Matrix viewMatrix; Matrix projectionMatrix; VertexBuffer vertexBuffer; Vector3 cameraPos; Texture2D streetTexture; Model lamppostModel; Texture2D[] lamppostTextures; Model carModel; Texture2D[] carTextures; Vector3 lightPos; float lightPower; float ambientPower; Matrix lightsViewProjectionMatrix; RenderTarget2D renderTarget; Texture2D shadowMap;
Texture2D carLight; 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 = "Riemer's XNA Tutorials -- Series 3"; base.Initialize(); } protected override void LoadContent() { device = GraphicsDevice;
effect = Content.Load<Effect>
("OurHLSLfile"); SetUpVertices(); SetUpCamera();
streetTexture = Content.Load<Texture2D>
("streettexture"); carModel = LoadModel("car", out carTextures);
PresentationParameters pp = device.PresentationParameters; renderTarget = new RenderTarget2D(device, pp.BackBufferWidth, pp.BackBufferHeight, true, device.DisplayMode.Format, DepthFormat.Depth24);
carLight = Content.Load<Texture2D>
("carlight");
} private Model LoadModel(string assetName, out Texture2D[] textures) {
Model newModel = Content.Load<Model>
(assetName); textures = new Texture2D[7]; int i = 0; foreach (ModelMesh mesh in newModel.Meshes) foreach (BasicEffect currentEffect in mesh.Effects) textures[i++] = currentEffect.Texture;
foreach (ModelMesh mesh in newModel.Meshes) foreach (ModelMeshPart meshPart in mesh.MeshParts) meshPart.Effect = effect.Clone();
return newModel; }
private void SetUpVertices() { MyOwnVertexFormat[] vertices = new MyOwnVertexFormat[18];
vertices[0] = new MyOwnVertexFormat(new Vector3(-20, 0, 10), new Vector2(-0.25f, 25.0f), new Vector3(0, 1, 0)); vertices[1] = new MyOwnVertexFormat(new Vector3(-20, 0, -100), new Vector2(-0.25f, 0.0f), new Vector3(0, 1, 0)); vertices[2] = new MyOwnVertexFormat(new Vector3(2, 0, 10), new Vector2(0.25f, 25.0f), new Vector3(0, 1, 0)); vertices[3] = new MyOwnVertexFormat(new Vector3(2, 0, -100), new Vector2(0.25f, 0.0f), new Vector3(0, 1, 0)); vertices[4] = new MyOwnVertexFormat(new Vector3(2, 0, 10), new Vector2(0.25f, 25.0f), new Vector3(-1, 0, 0)); vertices[5] = new MyOwnVertexFormat(new Vector3(2, 0, -100), new Vector2(0.25f, 0.0f), new Vector3(-1, 0, 0)); vertices[6] = new MyOwnVertexFormat(new Vector3(2, 1, 10), new Vector2(0.375f, 25.0f), new Vector3(-1, 0, 0)); vertices[7] = new MyOwnVertexFormat(new Vector3(2, 1, -100), new Vector2(0.375f, 0.0f), new Vector3(-1, 0, 0)); vertices[8] = new MyOwnVertexFormat(new Vector3(2, 1, 10), new Vector2(0.375f, 25.0f), new Vector3(0, 1, 0)); vertices[9] = new MyOwnVertexFormat(new Vector3(2, 1, -100), new Vector2(0.375f, 0.0f), new Vector3(0, 1, 0)); vertices[10] = new MyOwnVertexFormat(new Vector3(3, 1, 10), new Vector2(0.5f, 25.0f), new Vector3(0, 1, 0)); vertices[11] = new MyOwnVertexFormat(new Vector3(3, 1, -100), new Vector2(0.5f, 0.0f), new Vector3(0, 1, 0)); vertices[12] = new MyOwnVertexFormat(new Vector3(13, 1, 10), new Vector2(0.75f, 25.0f), new Vector3(0, 1, 0)); vertices[13] = new MyOwnVertexFormat(new Vector3(13, 1, -100), new Vector2(0.75f, 0.0f), new Vector3(0, 1, 0)); vertices[14] = new MyOwnVertexFormat(new Vector3(13, 1, 10), new Vector2(0.75f, 25.0f), new Vector3(-1, 0, 0)); vertices[15] = new MyOwnVertexFormat(new Vector3(13, 1, -100), new Vector2(0.75f, 0.0f), new Vector3(-1, 0, 0)); vertices[16] = new MyOwnVertexFormat(new Vector3(13, 21, 10), new Vector2(1.25f, 25.0f), new Vector3(-1, 0, 0)); vertices[17] = new MyOwnVertexFormat(new Vector3(13, 21, -100), new Vector2(1.25f, 0.0f), new Vector3(-1, 0, 0));
vertexBuffer = new VertexBuffer(device, MyOwnVertexFormat.VertexDeclaration, vertices.Length, BufferUsage.WriteOnly); vertexBuffer.SetData(vertices); }
private void SetUpCamera() { cameraPos = new Vector3(-25, 13, 18); viewMatrix = Matrix.CreateLookAt(cameraPos, new Vector3(0, 2, -12), new Vector3(0, 1, 0)); projectionMatrix = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver4, device.Viewport.AspectRatio, 1.0f, 200.0f); }
protected override void UnloadContent() { }
protected override void Update(GameTime gameTime) { if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed) this.Exit();
UpdateLightData();
base.Update(gameTime); }
private void UpdateLightData() { ambientPower = 0.2f;
lightPos = new Vector3(-18, 5, -2); lightPower = 2f;
Matrix lightsView = Matrix.CreateLookAt(lightPos, new Vector3(-2, 3, -10), new Vector3(0, 1, 0)); Matrix lightsProjection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver2, 1f, 5f, 100f);
lightsViewProjectionMatrix = lightsView * lightsProjection; }
protected override void Draw(GameTime gameTime) { device.SetRenderTarget(renderTarget); device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0);
DrawScene("ShadowMap");
device.SetRenderTarget(null); shadowMap = (Texture2D)renderTarget;
device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0); DrawScene("ShadowedScene"); shadowMap = null;
base.Draw(gameTime); }
private void DrawScene(string technique) { effect.CurrentTechnique = effect.Techniques[technique]; effect.Parameters["xWorldViewProjection"].SetValue(Matrix.Identity * viewMatrix * projectionMatrix); effect.Parameters["xTexture"].SetValue(streetTexture); effect.Parameters["xWorld"].SetValue(Matrix.Identity); effect.Parameters["xLightPos"].SetValue(lightPos); effect.Parameters["xLightPower"].SetValue(lightPower); effect.Parameters["xAmbient"].SetValue(ambientPower); effect.Parameters["xLightsWorldViewProjection"].SetValue(Matrix.Identity * lightsViewProjectionMatrix); effect.Parameters["xShadowMap"].SetValue(shadowMap);
effect.Parameters["xCarLightTexture"].SetValue(carLight);
foreach (EffectPass pass in effect.CurrentTechnique.Passes) { pass.Apply(); device.SetVertexBuffer(vertexBuffer); device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 16); } Matrix car1Matrix = Matrix.CreateScale(4f) * Matrix.CreateRotationY(MathHelper.Pi) * Matrix.CreateTranslation(-3, 0, -15); DrawModel(carModel, carTextures, car1Matrix, technique); Matrix car2Matrix = Matrix.CreateScale(4f) * Matrix.CreateRotationY(MathHelper.Pi * 5.0f / 8.0f) * Matrix.CreateTranslation(-28, 0, -1.9f); DrawModel(carModel, carTextures, car2Matrix, technique); } private void DrawModel(Model model, Texture2D[] textures, Matrix wMatrix, string technique) { Matrix[] modelTransforms = new Matrix[model.Bones.Count]; model.CopyAbsoluteBoneTransformsTo(modelTransforms); int i = 0; foreach (ModelMesh mesh in model.Meshes) { foreach (Effect currentEffect in mesh.Effects) { Matrix worldMatrix = modelTransforms[mesh.ParentBone.Index] * wMatrix; currentEffect.CurrentTechnique = currentEffect.Techniques[technique]; currentEffect.Parameters["xWorldViewProjection"].SetValue(worldMatrix * viewMatrix * projectionMatrix); currentEffect.Parameters["xTexture"].SetValue(textures[i++]); currentEffect.Parameters["xWorld"].SetValue(worldMatrix); currentEffect.Parameters["xLightPos"].SetValue(lightPos); currentEffect.Parameters["xLightPower"].SetValue(lightPower); currentEffect.Parameters["xAmbient"].SetValue(ambientPower); currentEffect.Parameters["xLightsWorldViewProjection"].SetValue(worldMatrix * lightsViewProjectionMatrix); currentEffect.Parameters["xShadowMap"].SetValue(shadowMap);
currentEffect.Parameters["xCarLightTexture"].SetValue(carLight);
} mesh.Draw(); } } } }
- Website design & XNA + DirectX code : Riemer Grootjans - ©2003 - 2011 Riemer Grootjans
|
|
|
|
|