|
|
|
|
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: 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 xLightsWorldViewProjection; float4x4 xWorldViewProjection; float4x4 xWorld; bool xSolidBrown; 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; };
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; }
float DotProduct(float3 lightPos, float3 pos3D, float3 normal) { float3 lightDir = normalize(pos3D - lightPos); return dot(-lightDir, normal); }
PixelToFrame OurFirstPixelShader(VertexToPixel PSIn) { PixelToFrame Output = (PixelToFrame)0;
float diffuseLightingFactor = DotProduct(xLightPos, PSIn.Position3D, PSIn.Normal); diffuseLightingFactor = saturate(diffuseLightingFactor); diffuseLightingFactor *= xLightPower; float4 baseColor = tex2D(TextureSampler, PSIn.TexCoords); if (xSolidBrown == true) baseColor = float4(0.25f, 0.21f, 0.20f, 1); 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; float3 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); if (xSolidBrown == true) baseColor = float4(0.25f, 0.21f, 0.20f, 1); 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 class Game1 : Microsoft.Xna.Framework.Game { struct MyOwnVertexFormat { private 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 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); } GraphicsDeviceManager graphics; GraphicsDevice device; Effect effect; Matrix viewMatrix; Matrix projectionMatrix; VertexBuffer vertexBuffer; VertexDeclaration vertexDeclaration; Vector3 cameraPos; Texture2D streetTexture; Model lamppostModel; Texture2D[] lamppostTextures; Model carModel; Texture2D[] carTextures;
Texture2D carLight;
Vector3 lightPos; float lightPower; float ambientPower; Matrix lightsViewProjectionMatrix; RenderTarget2D renderTarget; Texture2D shadowMap; 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"); streetTexture = Content.Load<Texture2D>
("streettexture");
carLight = Content.Load<Texture2D>
("carlight");
carModel = LoadModel("racer", out carTextures); lamppostModel = LoadModel("lamppost", out lamppostTextures); SetUpVertices(); SetUpCamera(); PresentationParameters pp = device.PresentationParameters; renderTarget = new RenderTarget2D(device, pp.BackBufferWidth, pp.BackBufferHeight, 1, SurfaceFormat.Single); } 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(device);
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, vertices.Length * MyOwnVertexFormat.SizeInBytes, BufferUsage.WriteOnly); vertexBuffer.SetData(vertices);
vertexDeclaration = new VertexDeclaration(device, MyOwnVertexFormat.VertexElements); }
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, 5.0f, 100.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.5f;
lightPos = new Vector3(-18, 5, -2); lightPower = 2.0f;
Matrix lightsView = Matrix.CreateLookAt(lightPos, new Vector3(-2, -3, -10), new Vector3(0, 1, 0)); Matrix lightsProjection = Matrix.CreatePerspectiveFieldOfView(MathHelper.PiOver2, 1f, 1f, 100f);
lightsViewProjectionMatrix = lightsView* lightsProjection; }
protected override void Draw(GameTime gameTime) { device.SetRenderTarget(0, renderTarget);
device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0); DrawScene("ShadowMap");
device.SetRenderTarget(0, null); shadowMap = renderTarget.GetTexture();
device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.Black, 1.0f, 0); DrawScene("ShadowedScene");
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);
effect.Begin(); foreach (EffectPass pass in effect.CurrentTechnique.Passes) { pass.Begin(); device.VertexDeclaration = vertexDeclaration; device.Vertices[0].SetSource(vertexBuffer, 0, MyOwnVertexFormat.SizeInBytes); device.DrawPrimitives(PrimitiveType.TriangleStrip, 0, 18); pass.End(); } effect.End(); Matrix car1Matrix = Matrix.CreateScale(4f) * Matrix.CreateRotationY(MathHelper.Pi) * Matrix.CreateTranslation(-3, 0, -15); DrawModel(carModel, carTextures, car1Matrix, technique, false); Matrix car2Matrix = Matrix.CreateScale(4f) * Matrix.CreateRotationY(MathHelper.Pi * 5.0f / 8.0f) * Matrix.CreateTranslation(-28, 0, -1.9f); DrawModel(carModel, carTextures, car2Matrix, technique, false); Matrix lamp1Matrix = Matrix.CreateScale(0.05f) * Matrix.CreateTranslation(4.0f, 1, -35); DrawModel(lamppostModel, lamppostTextures, lamp1Matrix, technique, true); Matrix lamp2Matrix = Matrix.CreateScale(0.05f) * Matrix.CreateTranslation(4.0f, 1, -5); DrawModel(lamppostModel, lamppostTextures, lamp2Matrix, technique, true); } private void DrawModel(Model model, Texture2D[] textures, Matrix wMatrix, string technique, bool solidBrown) { 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["xSolidBrown"].SetValue(solidBrown); 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 - 2008 Riemer Grootjans
|
|
|
|
|