Jump to content
  • Advertisement
Sign in to follow this  
d h k

[D3D9] Deferred Rendering: Point Lights (includes PICTURES)

This topic is 2578 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

I have ambient and directional lighting working just fine in my deferred renderer, now it's time for the slightly (to my eyes, vastly) more complex point lights! The only resource I managed to find that actually goes through the motions of implementing point lights (and doesn't just end on: "now you have the g-buffer, all that's left to do is to render light geometry: quad for directional, sphere for point and cone for spotlight") was this tutorial. My first issue is that I don't fully understand the idea of rendering a sphere for a point light, take a look at this shot right from my program: defrend_point1.png This is my point light geometry (a sphere) rendered normally as if it was real geometry like the level around it. It's a sphere with radius 1, centered on the point light's position, scaled to the light's size. Here's my point light shader, taken straight from the aforementioned tutorial minus the specular component stuff: [source lang="cpp"] float4x4 mat_world; float4x4 mat_view; float4x4 mat_projection; //this is used to compute the world-position float4x4 mat_invviewproj; //color of the light float3 light_color; //this is the position of the light float3 light_position; //how far does this light reach float light_radius; //control the brightness of the light float light_intensity; texture tex_diffuse; sampler2D smp_diffuse = sampler_state { Texture = ; MinFilter = LINEAR; MagFilter = LINEAR; MipFilter = LINEAR; }; texture tex_normal; sampler2D smp_normal = sampler_state { Texture = ; MinFilter = LINEAR; MagFilter = LINEAR; MipFilter = LINEAR; }; struct VertexShaderInput { float3 position : POSITION0; }; struct VertexShaderOutput { float4 position : POSITION0; float4 screen_position : TEXCOORD0; }; VertexShaderOutput MyVertexShader ( VertexShaderInput input ) { VertexShaderOutput output; //processing geometry coordinates float4 world_position = mul(float4(input.position,1), mat_world); float4 view_position = mul(world_position, mat_view); output.position = mul(view_position, mat_projection); output.screen_position = output.position; return output; } float4 MyPixelShader(VertexShaderOutput input) : COLOR0 { //obtain screen position input.screen_position.xy /= input.screen_position.w; //obtain textureCoordinates corresponding to the current pixel //the screen coordinates are in [-1,1]*[1,-1] //the texture coordinates need to be in [0,1]*[0,1] float2 tex_coord = 0.5f * (float2(input.screen_position.x,-input.screen_position.y) + 1); //get normal data from the normalMap float4 normal_data = tex2D(smp_normal,tex_coord); //tranform normal back into [-1,1] range float3 normal = 2.0f * normal_data.xyz - 1.0f; //read depth float depth_val = tex2D(smp_normal,tex_coord).a; //compute screen-space position float4 position; position.xy = input.screen_position.xy; position.z = depth_val; position.w = 1.0f; //transform to world space position = mul(position, mat_invviewproj); position /= position.w; //surface-to-light vector float3 light_vector = light_position - position; //compute attenuation based on distance - linear attenuation float attenuation = saturate(1.0f - length(light_vector)/light_radius); //normalize light vector light_vector = normalize(light_vector); //compute diffuse light float ndl = max(0,dot(normal,light_vector)); float3 diffuse_light = ndl * light_color.rgb; //take into account attenuation and lightIntensity. return attenuation * light_intensity * float4(diffuse_light.rgb,1.0f); } [/source] And this is how I render the light geometry: [source lang="cpp"] D3DXMATRIX scaling_matrix; D3DXMatrixScaling ( &scaling_matrix, 5.0f, 5.0f, 5.0f ); D3DXMATRIX rotation_matrix_x; D3DXMatrixRotationX ( &rotation_matrix_x, D3DXToRadian ( 0.0f ) ); D3DXMATRIX rotation_matrix_y; D3DXMatrixRotationY ( &rotation_matrix_y, D3DXToRadian ( 0.0f ) ); D3DXMATRIX rotation_matrix_z; D3DXMatrixRotationZ ( &rotation_matrix_z, D3DXToRadian ( 0.0f ) ); D3DXMATRIX translation_matrix; D3DXMatrixTranslation ( &translation_matrix, 0.0f, -15.0f, 0.0f ); D3DXMATRIX srt = scaling_matrix * ( rotation_matrix_x * rotation_matrix_y * rotation_matrix_z ) * translation_matrix; window->GetDevice ( )->SetTransform ( D3DTS_WORLD, &srt ); D3DXMATRIX w; window->GetDevice ( )->GetTransform ( D3DTS_WORLD, &w ); D3DXMATRIX v; window->GetDevice ( )->GetTransform ( D3DTS_VIEW, &v ); D3DXMATRIX p; window->GetDevice ( )->GetTransform ( D3DTS_PROJECTION, &p ); D3DXMATRIX ivp; D3DXMatrixMultiply ( &ivp, &v, &p ); D3DXMatrixInverse ( &ivp, NULL, &ivp ); shader_point_light->effect->SetMatrix ( "mat_world", &w ); shader_point_light->effect->SetMatrix ( "mat_view", &v ); shader_point_light->effect->SetMatrix ( "mat_projection", &p ); shader_point_light->effect->SetMatrix ( "mat_invviewproj", &ivp ); shader_point_light->effect->SetVector ( "light_color", &D3DXVECTOR4 ( 0.18f, 0.96f, 0.91f, 1.0f ) ); D3DXVECTOR4 lightpos ( 0.0f, -15.0f, 0.0f, 0.0f ); shader_point_light->effect->SetVector ( "light_position", &lightpos ); shader_point_light->effect->SetFloat ( "light_radius", 5.0f ); shader_point_light->effect->SetFloat ( "light_intensity", 1.0f ); shader_point_light->effect->SetTexture ( "tex_diffuse", g_buffer.texture[0] ); shader_point_light->effect->SetTexture ( "tex_normal", g_buffer.texture[1] ); shader_point_light->effect->CommitChanges ( ); window->GetDevice ( )->SetRenderTarget ( 0, back_buffer ); RenderGeometryDetails details; LPDIRECT3DVERTEXBUFFER9 vb; LPDIRECT3DINDEXBUFFER9 ib; sphere->GetVertexBuffer ( &vb ); sphere->GetIndexBuffer ( &ib ); details.primitive_type = D3DPT_TRIANGLELIST; details.num_vertices = sphere->GetNumVertices ( ); details.num_primitives = sphere->GetNumFaces ( ); details.vertex_format = sphere->GetFVF ( ); details.vertex_bytesize = sphere->GetNumBytesPerVertex ( ); details.vertex_buffer = vb; details.index_buffer = ib; details.texture = NULL; details.alpha_blend_mode = AlphaBlend_Blend; float camera_to_center = D3DXVec3Length ( &D3DXVECTOR3 ( lightpos.x - camera->position.x, lightpos.y - camera->position.y, lightpos.z - camera->position.z ) ); if ( camera_to_center < 5.0f ) details.cull_mode = Cull_Clockwise; else details.cull_mode = Cull_CounterClockwise; details.z_test = true; RenderGeometry ( details ); [/source] And this is the output I'm getting: defrend_point2.png You can clearly see that it's like I'm looking through a transparent sphere where everything behind it gets shaded, almost like additional directional lighting. This is my first deferred renderer, I'm still fairly new to this all and I can't quite keep up with it yet. I'm having trouble understanding what could be wrong here. Any help would be greatly appreciated? What looks iffy to you here? Am I missing an entire step?

Share this post


Link to post
Share on other sites
Advertisement
I'll bump this one single time as it has been 36 hours, almost 100 views and the thread is about to fall off the front page. Am I asking too much here? Not posting enough code? Does everything look correct code- and shader-wise? I'm not sure why the silence... :)

If you need more information or code, just let me know.

Share this post


Link to post
Share on other sites
I'll keep looking at this for a bit, but my first suggestion is to just use the world position passed in from the vertex shader instead of trying to compute the world matrix from the view matrix, since that just adds in extra complex computation that you don't need.

turn this:


struct VertexShaderOutput
{
float4 position : POSITION0;
float4 screen_position : TEXCOORD0;
};

to this:


struct VertexShaderOutput
{
float4 worldPos;
float4 position : POSITION0;
float4 screen_position : TEXCOORD0;
};

and this:

[font="Consolas,"]

VertexShaderOutput MyVertexShader ( VertexShaderInput input )

[/font][font="Consolas,"]

{

[/font][font="Consolas,"]

VertexShaderOutput output;

[/font][font="Consolas,"]

[/font][font="Consolas,"]

//processing geometry coordinates

[/font][font="Consolas,"]

float4 world_position = mul(float4(input.position,1), mat_world);

[/font][font="Consolas,"]

float4 view_position = mul(world_position, mat_view);

[/font][font="Consolas,"]

output.position = mul(view_position, mat_projection);

[/font][font="Consolas,"]

output.screen_position = output.position;

[/font][font="Consolas,"]

[/font][font="Consolas,"]

return output;

[/font][font="Consolas,"]

}

[/font]

to this:

[font="Consolas,"]

VertexShaderOutput MyVertexShader ( VertexShaderInput input )

[/font][font="Consolas,"]

{

[/font][font="Consolas,"]

VertexShaderOutput output;

[/font][font="Consolas,"]

[/font][font="Consolas,"]

//processing geometry coordinates

[/font][font="Consolas,"]

float4 world_position = mul(float4(input.position,1), mat_world);

[/font][font="Consolas,"]

float4 view_position = mul(world_position, mat_view);

[/font][font="Consolas,"]

output.[color="#1C2837"]worldPos = world_position;

[/font][font="Consolas,"]

output.position = mul(view_position, mat_projection);

[/font][font="Consolas,"]

output.screen_position = output.position;

[/font][font="Consolas,"]

[/font][font="Consolas,"]

return output;

[/font][font="Consolas,"]

}

[/font]
turn this:
[font="Consolas,"]

//transform to world space

[/font][font="Consolas,"]

position = mul(position, mat_invviewproj);

[/font][font="Consolas,"]

position /= position.w;

[/font]

to this:

[font="Consolas,"]

//transform to world space

[/font][font="Consolas,"]

position = input.[color="#1C2837"]worldPos;

[/font][font="Consolas,"]


[/font][font="Consolas,"]

Hope that helps



Oh yeah, and one other thing, to tell you the truth, I can't quite see whats wrong with your picture, it looks fine to me except maybe the walls and ceiling turn blue far off, is that what the problem is?

[/font]

Share this post


Link to post
Share on other sites
First of all, thanks a TON for writing! Really helps a lot.

I committed those changes but it didn't change anything (thanks for the simplification though). I guess I can also delete these lines?


position.xy = input.screen_position.xy;
position.z = depth_val;
position.w = 1.0f;


And I had to bind your float4 worldPos to TEXCOORD1 in order to get the shader to compile, I guess that's okay?

I have investigated further and I'm pretty sure the problem lies with the attenuation! That's the term that should make sure the stuff in the back, as can be seen in the shot in my first post, should NEVER be lit (as that stuff is too far away from the light and its radius) and it should also make sure the floor itself, which never gets lit currently, SHOULD be lit, right?

To answer your question, that's what is not working and making me think there's something wrong, the light sphere sits right on the floor plane of the environment, but it lights the background like a see-through sphere instead of leaving the background and point-lighting that floor.

Inverting the depth (which is stored in the alpha channel of my view-space normal render target in the G-Buffer just fine, I can check it in NVidia PerfHUD) like this doesn't change anything surprisingly:


float depth_val = tex2D(smp_normal,tex_coord).a;
depth_val = -depth_val + 1;


My G-Buffer fill shader stores the depth like this:


// in the vertex shader
// get the position of the vertex in view-space, retrieve depth and pass it on
float3 position_view = mul ( input.position, mat_worldview );
output.depth = position_view.z;

// in the pixel shader
// linear depth
output.gbuffertex2.a = input.depth / render_distance;


Since my render distance is large and that environment pretty small, the depth values stored in the G-Buffer are, according to PerfHUD, 0.00 and sometimes 0.01 - is that maybe a problem? I can lower the render distance and get much more variety between these numbers.

Share this post


Link to post
Share on other sites
Hidden
try this maybe. Pass in a float 3 containing 3 attenuation factors, or modifiers, one is a constant modifier, another being the linear modifier, and the last being an exponential modifier.


Attenuation = att0 + (att1 * d) + (att2 * d²)

This is what i have for a pixel shader in my lesson on point lights, it could possibly be of use:

float4 PS(VS_OUTPUT input) : SV_TARGET
{
input.normal = normalize(input.normal);

float4 diffuse = ObjTexture.Sample( ObjSamplerState, input.TexCoord );

float3 finalColor = float3(0.0f, 0.0f, 0.0f);

//Create the vector between light position and pixels position
float3 lightToPixelVec = light.pos - input.worldPos;

//Find the distance between the light pos and pixel pos
float d = length(lightToPixelVec);

//Create the ambient light
float3 finalAmbient = diffuse * light.ambient;

//If pixel is too far, return pixel color with ambient light
if( d > light.range )
return float4(finalAmbient, diffuse.a);

//Turn lightToPixelVec into a unit length vector describing
//the pixels direction from the lights position
lightToPixelVec /= d;

//Calculate how much light the pixel gets by the angle
//in which the light strikes the pixels surface
float howMuchLight = dot(lightToPixelVec, input.normal);

//If light is striking the front side of the pixel
if( howMuchLight > 0.0f )
{
//Add light to the finalColor of the pixel
finalColor += howMuchLight * diffuse * light.diffuse;

//Calculate Light's Falloff factor
finalColor /= light.att[0] + (light.att[1] * d) + (light.att[2] * (d*d));
}

//make sure the values are between 1 and 0, and add the ambient
finalColor = saturate(finalColor + finalAmbient);

//Return Final Color
return float4(finalColor, diffuse.a);
}



I pass this in as the attenuation factor for the light (of course you will want to change it, and it can take a little time to find a good combination of attenuation factors:
light.att = XMFLOAT3(0.0f, 0.2f, 0.0f);


notice how i also use a variable for the range of the point light, you might consider doing that too, but if the attenuation lets the light go to far, then you can see a definite cut off at the range of the light (which can be useful sometimes)

(sorry if its hard to read without formatting, i can never get the code or source tags to work right...

[color="#cccccc"]

Share this post


Link to post
Yes, that link wasn't very helpful I'm afraid... Thanks for trying though! :D

Any other ideas, anyone?

Share this post


Link to post
Share on other sites
have a look at catalins xna blog here

i also made my baby-steps in deferred rendering based on his tutorial and it's really well written and rather easy to understand

Share this post


Link to post
Share on other sites

have a look at catalins xna blog here

i also made my baby-steps in deferred rendering based on his tutorial and it's really well written and rather easy to understand


This is even less helpful than the previous response. You have linked the same blog the OP was following.

@ op; I know how frustrating it is seeing the view count go up and the reply count stand dormant. I've looked at your post but as I'm still forward rendering I won't be as helpful as those with time in the trenches.

First, have you tried shrinking the size of your view frustum, there by increasing resolution in the Depth buffer? you had mentioned it in a previous post, and it struck me that this could be a depth issue( especially with the grainy vertical blue lines on the right of the second picture, although upon closer inspection it appears to be the pleats of the curtains). If you have and it wasn't the issue than I would try some simpler geometry that we can solve by pen and paper if need be. Maybe a sphere light with a simple box around it?

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!