Jump to content
  • Advertisement
Sign in to follow this  
Spa8nky

Can't draw point light using screen coordinates, only with model?

This topic is 3464 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 folks, I'm having problems when trying to draw my point light using deferred rendering and avoiding using a sphere model. If I draw a quad that covers the screen coordinates and pass that into HLSL the point light doesn't light properly and disappears depending on the camera position and rotation. Point Light Incorrect This is what is use to draw the point light:
        private void DrawPointLight(Light_Point light_Point)
        {
            // Set G Buffer parameters
            effect_Light_Point.Parameters["colourMap"].SetValue(RT_Colour.GetTexture());
            effect_Light_Point.Parameters["depthMap"].SetValue(RT_Depth.GetTexture());
            effect_Light_Point.Parameters["normalMap"].SetValue(RT_Normal.GetTexture());

            effect_Light_Point.Parameters["LightPosition"].SetValue(light_Point.position);
            effect_Light_Point.Parameters["Colour"].SetValue(light_Point.colour);
            effect_Light_Point.Parameters["LightRadius"].SetValue(light_Point.radius);
            effect_Light_Point.Parameters["LightIntensity"].SetValue(light_Point.intensity);

            // Parameters for specular computations    // AGAIN THESE ARE THE SAME FOR ALL LIGHT THIS FRAME....no need to recompute!!!
            effect_Light_Point.Parameters["CameraPosition"].SetValue(camera.Position);
            effect_Light_Point.Parameters["InvViewProjection"].SetValue(Matrix.Invert(camera.ViewMatrix * camera.ProjectionMatrix));    
            
            // Size of a halfpixel, for texture coordinates alignment (DirectX 9)    
            effect_Light_Point.Parameters["HalfTexel"].SetValue(texel_Half);

            // Calculate the distance between the camera and light center
            float cameraToCenter = Vector3.Distance(camera.Position, light_Point.position);

            // If we are inside the light volume, draw the sphere's inside face (include + camera near plane distance)
            if (cameraToCenter < light_Point.radius + 0.01f)
            {
                GraphicsDevice.RenderState.CullMode = CullMode.CullClockwiseFace;
            }
            else
            {
                GraphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;
            }

            effect_Light_Point.Begin();
            effect_Light_Point.Techniques[0].Passes[0].Begin();

            // Draw a full-screen quad   
            GraphicsDevice.VertexDeclaration = new VertexDeclaration(GraphicsDevice, VertexPositionTexture.VertexElements);
            GraphicsDevice.DrawUserIndexedPrimitives<VertexPositionTexture>(PrimitiveType.TriangleStrip, Screen_Data.Vertices_FS, 0, Screen_Data.Vertices_FS.Length, Screen_Data.Indices, 0, 2);

            effect_Light_Point.Techniques[0].Passes[0].End();
            effect_Light_Point.End();

            GraphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;
        }


and these are my vertex and pixel shaders:
VertexShaderOutput VertexShader(VertexShaderInput input)
{
    VertexShaderOutput output = (VertexShaderOutput)0;

    output.Position = input.Position;
    output.ScreenPosition = input.TexCoord;
    
    return output;
}

PixelShaderOutput PixelShader(VertexShaderOutput input)
{
	PixelShaderOutput output = (PixelShaderOutput)0;
	
	// ===============================================
	// --[Texture Coordinates]------------------------
	// ===============================================
	float2 texCoord = input.ScreenPosition;
	
	// Align texels to pixels (DX9)
	texCoord -= HalfTexel;
	// ===============================================
	
	// ===============================================
	// --[Normal]-------------------------------------
	// ===============================================
	// Get normal data from the normal map
	float4 normalData = tex2D(NormalSampler, texCoord);
	
	// Transform normal from [0, 1] texture coordinate range back into [-1, 1] range
	float3 N = normalData.xyz * 2.0f - 1.0f;
	N = normalize(N);
	// ===============================================
	
	// ===============================================
	// --[World Position]-----------------------------
	// ===============================================	
	float depth = tex2D(DepthSampler, texCoord).r;
	
	float4 position_World;
	//position_World.xy = input.ScreenPosition.xy;
	position_World.x = texCoord * 2.0f - 1.0f;
	position_World.y = -(texCoord * 2.0f - 1.0f);
	position_World.z = depth;
	position_World.w = 1.0f;
	
	position_World = mul(position_World, InvViewProjection);
	position_World /= position_World.w;	
	// ===============================================
	
	// 3D normal and depth are now known for this pixel 
	// (lighting caluclations can now commence)
	
	// ===============================================
	// --[Lighting Calculations]----------------------
	// ===============================================
	
	// Surface to light vector
	float3 L = LightPosition - position_World;
	
	// Compute attenuation based on distance from centre of sphere - linear attenuation
	float attenuation = pow(saturate((LightRadius - length(L)) / LightRadius), 2);
	
	// Normalise light vector (can't do this until after length value has been used otherwise length will be incorrect)
	L = normalize(L);
	
	/* ===============================================
	 * --[Diffuse Light]------------------------------
	 * ===============================================
	 * I = Di * Dc * N.L
	 *
	 * Where:
	 *
	 * Di = Diffuse Intensity
	 * Dc = Diffuse Colour
	 * N = Surface Normal
	 * L = Light Direction Vector
	 *
	 */
	float NdotL = dot(L, N);
	float3 Diffuse_Light = LightIntensity * Colour * saturate(NdotL);
	// ===============================================
	
	/* ===============================================
	 * --[Specular Light]-----------------------------
	 * ===============================================
	 * I = Si * Sc * (R.V)^n
	 * 
	 * Where:
	 *
	 * Si = Specular Intensity
	 * Sc = Specular Colour
	 * R = Reflection Vector = 2 * (N.L) * N-L  
	 *	   reflect(L, N) = L - 2 * N * dot(L, N) [HLSL Built-in Function]	// Reflection vector of light ray
	 * V = View Vector = Camera.WorldPosition - Pixel.WorldPosition			// Camera to surface vector
	 * n = Shininess factor
	 *
	 */	
	float Si = tex2D(ColourSampler, texCoord).a;
	
	// If Sc is white then there is no need for Sc
	//float4 Sc = float4(1.0f, 1.0f, 1.0f, 1.0f);
	
	float3 R = normalize(reflect(-L, N));
	float3 V = normalize(CameraPosition - position_World);
	float n = normalData.a * 255;

	float Specular_Light = Si * pow(saturate(dot(R, V)), n);
	// ===============================================
			
	/* ===============================================
	 * --[Point/Omni Light]---------------------------
	 * ===============================================
	 *
	 * Attenuation = Affects the range of the light rays (over distance)
	 *
	 */	
	output.Colour = attenuation * float4(Diffuse_Light.rgb, Specular_Light);
	// ===============================================
	
	return output;
}


However, if I use/draw a sphere model for the point light, everything is lit correctly:
        private void DrawPointLight(Light_Point light_Point)
        {
            // Set G Buffer parameters
            effect_Light_Point.Parameters["colourMap"].SetValue(RT_Colour.GetTexture());
            effect_Light_Point.Parameters["depthMap"].SetValue(RT_Depth.GetTexture());
            effect_Light_Point.Parameters["normalMap"].SetValue(RT_Normal.GetTexture());

            // Compute light's world matrix
            // Scale according to light radius and translate it to light position
            effect_Light_Point.Parameters["World"].SetValue(light_Point.World_Transform);
            effect_Light_Point.Parameters["View"].SetValue(camera.ViewMatrix);
            effect_Light_Point.Parameters["Projection"].SetValue(camera.ProjectionMatrix);

            effect_Light_Point.Parameters["LightPosition"].SetValue(light_Point.position);
            effect_Light_Point.Parameters["Colour"].SetValue(light_Point.colour);
            effect_Light_Point.Parameters["LightRadius"].SetValue(light_Point.radius);
            effect_Light_Point.Parameters["LightIntensity"].SetValue(light_Point.intensity);

            // Parameters for specular computations    // AGAIN THESE ARE THE SAME FOR ALL LIGHT THIS FRAME....no need to recompute!!!
            effect_Light_Point.Parameters["CameraPosition"].SetValue(camera.Position);
            effect_Light_Point.Parameters["InvViewProjection"].SetValue(Matrix.Invert(camera.ViewMatrix * camera.ProjectionMatrix));    
            
            // Size of a halfpixel, for texture coordinates alignment (DirectX 9)    
            effect_Light_Point.Parameters["HalfTexel"].SetValue(texel_Half);

            // Calculate the distance between the camera and light center
            float cameraToCenter = Vector3.Distance(camera.Position, light_Point.position);

            // If we are inside the light volume, draw the sphere's inside face (include + camera near plane distance)
            if (cameraToCenter < light_Point.radius + 0.01f)
            {
                GraphicsDevice.RenderState.CullMode = CullMode.CullClockwiseFace;
            }
            else
            {
                GraphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;
            }

            effect_Light_Point.Begin();
            effect_Light_Point.Techniques[0].Passes[0].Begin();

            foreach (ModelMesh mesh in model_Sphere.Model.Meshes)
            {
                foreach (ModelMeshPart meshPart in mesh.MeshParts)
                {
                    GraphicsDevice.VertexDeclaration = meshPart.VertexDeclaration;
                    GraphicsDevice.Vertices[0].SetSource(mesh.VertexBuffer, meshPart.StreamOffset, meshPart.VertexStride);
                    GraphicsDevice.Indices = mesh.IndexBuffer;
                    GraphicsDevice.DrawIndexedPrimitives(PrimitiveType.TriangleList, meshPart.BaseVertex, 0, meshPart.NumVertices, meshPart.StartIndex, meshPart.PrimitiveCount);
                }
            }

            effect_Light_Point.Techniques[0].Passes[0].End();
            effect_Light_Point.End();

            GraphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;
        }


The main difference being that the 2D screen position coordinates now need to be calculated in the HLSL vertex and pixel shader:
VertexShaderOutput VertexShader(VertexShaderInput input)
{
    VertexShaderOutput output = (VertexShaderOutput)0;

	// Processing geometry coordinates
	float4 worldPosition = mul(float4(input.Position, 1.0), World);
	float4 viewPosition = mul(worldPosition, View);	
	
	// Position in screen space is required by GPU pipeline
    output.Position = mul(viewPosition, Projection);
     
    // Pass the screen space coordinates to pixel shader 
    output.ScreenPosition = output.Position;				// THIS IS NOW A float4
    
    return output;
}

PixelShaderOutput PixelShader(VertexShaderOutput input)
{
	PixelShaderOutput output = (PixelShaderOutput)0;
	
	input.ScreenPosition.xy /= input.ScreenPosition.w;
	
	// ===============================================
	// --[Texture Coordinates]------------------------
	// ===============================================
	// Get the texture coordinates corresponding to the current pixel
	// Convert from texture coordinate range [0, 1] to screen coordinate range [-1, 1]
	float2 texCoord = input.ScreenPosition;
	texCoord.x = input.ScreenPosition.x * 2.0f - 1.0f;
	texCoord.y = -(input.ScreenPosition.y * 2.0f - 1.0f);
	
	// Align texels to pixels (DX9)
	texCoord -= HalfTexel;
	// ===============================================
	
	// ===============================================
	// --[Normal]-------------------------------------
	// ===============================================
	// Get normal data from the normal map
	float4 normalData = tex2D(NormalSampler, texCoord);
	
	// Transform normal from [0, 1] texture coordinate range back into [-1, 1] range
	float3 N = normalData.xyz * 2.0f - 1.0f;
	N = normalize(N);
	// ===============================================
	
	// ===============================================
	// --[World Position]-----------------------------
	// ===============================================	
	float depth = tex2D(DepthSampler, texCoord).r;
	
	float4 position_World;
	position_World.xy = input.ScreenPosition.xy;
	position_World.z = depth;
	position_World.w = 1.0f;
	
	position_World = mul(position_World, InvViewProjection);
	position_World /= position_World.w;	


The lighting calculations are the same in both cases but the position_World has changed. What have I done wrong when avoiding using a sphere model? Is it not possible to draw a point light without using a model and only using a full screen quadrangle? Thank you.

Share this post


Link to post
Share on other sites
Advertisement
Ok part of the problem was:

position_World.x = texCoord * 2.0f - 1.0f;
position_World.y = -(texCoord * 2.0f - 1.0f);

should be:

position_World.x = texCoord.x * 2.0f - 1.0f;
position_World.y = -(texCoord.y * 2.0f - 1.0f);

but now I can't see the light unless I'm outside its radius?!

EDIT:

That can be resolved by removing the following line:


// Calculate the distance between the camera and light center
float cameraToCenter = Vector3.Distance(camera.Position, light_Point.position);

// If we are inside the light volume, draw the sphere's inside face (include + camera near plane distance)
if (cameraToCenter < light_Point.radius + 0.01f)
{
GraphicsDevice.RenderState.CullMode = CullMode.CullClockwiseFace;
}
else
{
GraphicsDevice.RenderState.CullMode = CullMode.CullCounterClockwiseFace;
}


I think I've solved this problem, but feel free to correct me if you can see anything wrong.

Thanks very much.

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!