• Create Account

# Having problems with specular (deferred lighting)

Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

24 replies to this topic

### #1lipsryme  Members   -  Reputation: 799

Like
0Likes
Like

Posted 05 September 2012 - 10:34 AM

Ok first let me give you an example and tell me if I'm wrong somewhere on the way.

Given a single directional light shining in the direction of (0, 0, 1).
Let's say we have calculated the pixel position of the geometry I want to shade from my linear view-space depth buffer
like so:
VS:
output.PositionVS = mul(input.Position, InverseProj);

PS:
float depth = DepthTarget.Sample(LinearSampler, input.UV);
float3 viewRay = input.PositionVS.xyz;
float3 positionVS = viewRay * depth;


Now using pix I can tell you the depth will give me a value of '0.004' and viewray being (0.736, -0.414, 1.000)
Multiplied by the viewray I get a view space position of still 0.004.

Now I calculate the view direction vector like this:
float3 ViewDir = float3(0, 0, 0) - positionVS.xyz;


As far as I understand the vector resulting in the subtraction always points in the direction of the vector that is being subtracted from. So this would seem correct to me.
The result of this vector should be (0, 0, -0.004)

The last part is calculating the blinn-phong model:
float4 BlinnPhong(float3 Normal, float3 LightDir,
float3 ViewDir, float specularPower)
{
float3 N = normalize(Normal); // (0, 0, -1)
float3 L = normalize(mul(LightDir, View));  // (0, 0, 1)
float3 V = normalize(ViewDir);
float3 H = normalize(-L + V); // Result of this is (0.022, -0.008, -1.000)

float Ei = LightColor * 1.0f; // LightIntensity = '1.0f'
float NdotL = saturate(dot(N, -L));
float kd = NdotL;
float4 Lr = (kd / PI) * Ei;

float NdotH = saturate(dot(N, H)); // result = 1.000
float Specular = pow(NdotH, specularPower); // result = 93.419

return Lr + Specular;
}


The function is being called like this:
lightColor = BlinnPhong(normal, LightDirection, ViewDir, 255.0f);


What I get is something that looks more like a flashlight moving around with the mouse camera than a specular highlight.
I hope anyone can help me / tell me what's going wrong in this process of mine : /

Another question: Should tonemapping be used also on the specular output ?

Edited by lipsryme, 05 September 2012 - 12:27 PM.

marcel-schindler.tumblr.com

### #2kauna  Crossbones+   -  Reputation: 1547

Like
0Likes
Like

Posted 05 September 2012 - 11:44 AM

Excuse me but, as far as I understand things, pow(1.0, specularpower) should result as 1.0.

Otherwise, 93.413 is well over the range of value that screen can show (0.0 - 1.0) so I guess that you'll need to tone down your specular value.

Cheers!

### #3lipsryme  Members   -  Reputation: 799

Like
0Likes
Like

Posted 05 September 2012 - 11:47 AM

You're right it should probably be saturated before output. Still doesn't seem to make any difference.

marcel-schindler.tumblr.com

### #4kauna  Crossbones+   -  Reputation: 1547

Like
1Likes
Like

Posted 05 September 2012 - 11:50 AM

Saturating doesn't really help here since most of your values are over 1.0. You are just clamping most of the values to 1.0 which will result the same output as earlier.

Cheers!

### #5lipsryme  Members   -  Reputation: 799

Like
0Likes
Like

Posted 05 September 2012 - 11:59 AM

Don't see how my code is much different from this: http://content.gpwik...ng)_Blinn-Phong for example
The dot product between N and H will always be between 0 and 1 so I don't see what I'm doing wrong there.

Edited by lipsryme, 05 September 2012 - 12:01 PM.

marcel-schindler.tumblr.com

### #6kauna  Crossbones+   -  Reputation: 1547

Like
0Likes
Like

Posted 05 September 2012 - 12:05 PM

NdotH is defined as float3. Although I think it doesn't matter. Also, lot of the code you have presented doesn't have effect on the final output. Can you show a bit more of the shader code?

Cheers!

### #7lipsryme  Members   -  Reputation: 799

Like
0Likes
Like

Posted 05 September 2012 - 12:19 PM

I've updated some of the code on my post. Basically what's there is everything that's necessary for the light calculation. The Diffuse term (Lr) works just fine.
It's just the specular part that doesn't. The return value of my BlinnPhong function is the same as the PS output for this pixel.

Edited by lipsryme, 05 September 2012 - 12:22 PM.

marcel-schindler.tumblr.com

### #8Bacterius  Crossbones+   -  Reputation: 5658

Like
0Likes
Like

Posted 05 September 2012 - 07:22 PM

float3 H = normalize(-L + V); // Result of this is (0.022, -0.008, -1.000)

Huh. Are you sure? That vector doesn't look normalized to me, even taking into account floating-point precision limits.

Your specular exponent is positive... right?

Also, probably unrelated to your problem but you may want to cast Specular to a float4 with an alpha of zero when adding it to Lr, otherwise it gets promoted to a float4 and the alpha channel gets set with the specular power (unless you want that):

return Lr + float4(Specular, Specular, Specular, 0);

Or:

return Lr + float4(float3(Specular), 0);

"The best comment is a deleted comment."

### #9CryZe  Members   -  Reputation: 768

Like
0Likes
Like

Posted 06 September 2012 - 02:32 AM

What space are your normals in? They need to be in view space as well.

Edited by CryZe, 06 September 2012 - 02:35 AM.

### #10lipsryme  Members   -  Reputation: 799

Like
0Likes
Like

Posted 06 September 2012 - 07:04 AM

@bacterius That's what PIX tells me. I've just tried normalizing it again and it stays the same. (0.010, -0.000, -1.000) this time.

The specular probably looks okay its just that the highlight seems to move around with my mouse view on the plane.
So did I forget anything ? My normals are in viewspace.
L is being multiplied by the view matrix so it should be in view space too right ?
V is being calculated by float3(0, 0, 0) - position in viewspace.
Do I have to multiply H with the view matrix too ? I don't think so ....(?)

update: I've added a small video of the light accumulation buffer output.

Edited by lipsryme, 06 September 2012 - 07:11 AM.

marcel-schindler.tumblr.com

### #11CryZe  Members   -  Reputation: 768

Like
0Likes
Like

Posted 06 September 2012 - 07:29 AM

It seems like the normals don't affect the calculation at all, meaning that they are either not saved in your G-Buffer correctly or you loaded them incorrectly.

My guess is that your transformation of the normals into view space is wrong. When transforming directions, you need to be sure, that the w-component of the vector you're transforming is 0. Otherwise translation might affect your direction vector, not just the rotational component of the transformation.

Edited by CryZe, 06 September 2012 - 07:39 AM.

### #12lipsryme  Members   -  Reputation: 799

Like
0Likes
Like

Posted 06 September 2012 - 07:51 AM

I transform them like this:
// Output view space normals
output.Normal.xyz = mul(input.Normal, (float3x3)WorldViewIT);


Is there anything wrong with that ?

Edited by lipsryme, 06 September 2012 - 07:52 AM.

marcel-schindler.tumblr.com

### #13CryZe  Members   -  Reputation: 768

Like
0Likes
Like

Posted 06 September 2012 - 08:05 AM

No, the transformation seems to be correct.
You should post code from the Geometry Pass Pixel Shader that writes the normals to the G-Buffer and code from the Pixel Shader that reads it from the G-Buffer. I bet something is wrong there.

Update: Oh wait, does IT stand for "Inverse Transform"? If that's the case, than we found our problem. With an inverse transform your normals would be transformed from view space to object space. This would result in the normals cancelling themselves out with the view direction which would result in the behaviour seen in the video.

But it could just as well still be the normals either not being saved or loaded correctly.

Edited by CryZe, 06 September 2012 - 08:22 AM.

### #14lipsryme  Members   -  Reputation: 799

Like
0Likes
Like

Posted 06 September 2012 - 08:25 AM

No WorldViewIT is the Inverse Transpose of the World and View matrix multiplied.

Alright so here we go:
GBuffer VS()
PSI VS(VSI input)
{
PSI output = (PSI)0;
// Transform geometry for 3D projection
float4 worldPos = mul(float4(input.Position.xyz, 1.0f), World);
float4 viewPos = mul(worldPos, View);
output.Position = mul(viewPos, Proj);
// Straight pass input UV's
output.UV = input.UV;
// Output view position
output.ViewPos = viewPos;
// Output view space normals
output.Normal.xyz = mul(input.Normal, (float3x3)WorldViewIT);
return output;
}


Inside of GBuffer PS()
output.Normal.rg = encode(normalize(input.Normal));


Encode/Decode by using spheremap transform (can be found here: http://aras-p.info/texts/CompactNormalStorage.html#method04spheremap)
half2 encode (half3 n)
{
half2 enc = normalize(n.xy) * (sqrt(-n.z*0.5+0.5));
enc = enc*0.5+0.5;
return enc;
}

half3 decode (half4 enc)
{
half4 nn = enc*half4(2,2,0,0) + half4(-1,-1,1,-1);
half l = dot(nn.xyz,-nn.xyw);
nn.z = -l;
nn.xy *= sqrt(l);
return nn.xyz * 2 + half3(0,0,-1);
}


This gets written to a R16G16 Buffer.
And input to the light calculation shader.
Which I've put up on my first post.

I don't believe the normals are wrong since the diffuse lighting works just fine.
It's just the specular term that is "moving"

Edited by lipsryme, 06 September 2012 - 08:28 AM.

marcel-schindler.tumblr.com

### #15CryZe  Members   -  Reputation: 768

Like
0Likes
Like

Posted 06 September 2012 - 08:42 AM

Why would you want to use an inverse transposed transformation matrix? Just multiply the normals with your World and View matrices, just like you do with the position (except that you use float3x3 or float4(input.Normal, 0)). And whether your diffuse term actually works is hard to tell on that model.

Edited by CryZe, 06 September 2012 - 08:45 AM.

### #16lipsryme  Members   -  Reputation: 799

Like
0Likes
Like

Posted 06 September 2012 - 08:54 AM

Because when using non uniform scaled transforms you have to use the inverse transpose of this matrix, which is not often the case (in my case not at all) so I guess its a little optional here but it doesn't change anything. I've noticed that when not multiplying the LightDirection with the View Matrix the diffuse term seems to work. When multiplied its view dependent like the specular. Though even when not multiplying it the specular stays the same.

marcel-schindler.tumblr.com

### #17CryZe  Members   -  Reputation: 768

Like
0Likes
Like

Posted 06 September 2012 - 09:01 AM

This proves that the Normals don't work at all. The Normals always are (0,0,-1), not transforming the Light Direction is always (0,0,-1), thus looking like diffuse would work. But it's actually just 2 bugs cancelling each other out. The Normals being (0,0,-1) is a result of the decode function having a nn value of 0. I guess it's because the decode function is called with a zero vector.

### #18lipsryme  Members   -  Reputation: 799

Like
0Likes
Like

Posted 06 September 2012 - 09:05 AM

god damn it this is gonna take some more time debugging...

marcel-schindler.tumblr.com

### #19kauna  Crossbones+   -  Reputation: 1547

Like
0Likes
Like

Posted 06 September 2012 - 09:06 AM

You should transform your light direction outside of the shader, since that's something you'll need to calculate only once per frame.

Cheers!

### #20lipsryme  Members   -  Reputation: 799

Like
0Likes
Like

Posted 06 September 2012 - 10:22 AM

I've made 2 videos showing how the normals look like on a cube.
This is direct output of normalized normals from the gBuffer to a R8G8B8A8 RenderTarget without any kind of encoding.

Multiplied by just the World Matrix:
http://youtu.be/Kk0X_nYp2aU

Multiplied by the WorldView Matrix
http://youtu.be/h0XAEVsgSRQ

Does this look okay? Because if not then there can only be something wrong with my matrices.

update:
What I've noticed with all normal encodings is that they all fail when having a negative pointing normal why is that ?
When my normal points to (0, 0, -1) I always get NaN's inside the encoding/decoding because of either something resulting in a division by 0 or a sqrt of a negative number.
If this is the case then maybe I might try to define my normals for the plane/cube differently.

Edited by lipsryme, 06 September 2012 - 11:07 AM.

marcel-schindler.tumblr.com

Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

PARTNERS