Specular highlights appear flat even though diffuse lighting is smooth

Started by
12 comments, last by Norman Barrows 8 years, 5 months ago

Hi!

I've implemented the Blinn-Phong shading model some time ago and noticed that even though the diffuse component looks smooth, the specular one doesn't (it looks good on flat surfaces). Look at the screenshot (ignore the crappy shadows):

[attachment=29608:Captura de Tela (10).png]

These are my vertex and pixel shaders (I've simplified it a lot and removed shadow mapping):


cbuffer cbChangesOnTime: register(b0)
{
	float4 SunColor;
	float3 SunDir;
	float4 AmbientColor;
}

cbuffer cbChangesEveryFrame: register(b1)
{
	matrix World;
	matrix View;
	matrix Projection;
	matrix LightView;
	matrix LightProjection;
	float3 CameraPosition;
	float4 DiffuseColor;
	float4 SpecularColor;
	float SpecularPower;
	float AmbientIntensity;
}

struct VSInput
{
	float4 pos: POSITION;
	float2 uv: TEXCOORD;
	float3 norm: NORMAL;
};

struct PSShadowedInput
{
	float4 pos: SV_POSITION;
	float2 uv: TEXCOORD;
	float3 norm: NORMAL;
	float4 worldPos: POSITION;
	float4 lightViewPos: POSITION2;
};

///////////////////////////////
// This is the Vertex Shader //
///////////////////////////////

PSShadowedInput VSShadowed(VSInput input)
{
	PSShadowedInput output;

	output.pos = mul(World, input.pos);
	output.pos = mul(View, output.pos);
	output.pos = mul(Projection, output.pos);

	output.uv = input.uv;

	output.norm = normalize(mul((float3x3)World, input.norm));

	output.worldPos = mul(World, input.pos);

	/* SHADOW MAPPING */

	return output;
}

//////////////////////////////
// This is the Pixel Shader //
//////////////////////////////

float4 PSShadowedLambertian(PSShadowedInput input) : SV_Target
{
	float3 ViewDirection = normalize(CameraPosition - input.worldPos.xyz);

	float4 Diffuse = max(dot(-SunDir, input.norm), 0.0f) * SunColor * DiffuseColor;
	float4 Specular = pow(max(dot(input.norm, normalize(-SunDir + ViewDirection)), 0.0f), SpecularPower) * SpecularColor;
	float4 Ambient = AmbientColor * AmbientIntensity * DiffuseColor;

	/* SHADOW MAPPING */

	return pow(max(Diffuse + Specular + Ambient, 0.0f), 1.0f / 2.2f);
}

What am I doing wrong? What's missing?

Thank you for spending your time reading this topic.

Advertisement

Not sure it'll be the cause, but you can't normalize your normal in the vertex shader and expect it still to be normalised in the pixel shader after interpolation. Try normalizing input.norm and see if that helps?

Adam Miles - Principal Software Development Engineer - Microsoft Xbox Advanced Technology Group

Not sure it'll be the cause, but you can't normalize your normal in the vertex shader and expect it still to be normalised in the pixel shader after interpolation. Try normalizing input.norm and see if that helps?

But it's already in the vertex shader, in the line "output.norm = normalize(mul((float3x3)World, input.norm));". See the function name: "VSShadowed"? It's a Vertex Shader. Sorry, I should've made that more clear. Thanks for the quick response.

But you're not normalizing the input.norm in the pixel shader. You need to. Interpolated normal vectors may no longer be normal.

But you're not normalizing the input.norm in the pixel shader. You need to. Interpolated normal vectors may no longer be normal.

Oh, sorry I read that wrong. I read it like "can't you normalize your normal in the vertex shader...". I'm going to try that out.

Not sure it'll be the cause, but you can't normalize your normal in the vertex shader and expect it still to be normalised in the pixel shader after interpolation. Try normalizing input.norm and see if that helps?

But it's already in the vertex shader, in the line "output.norm = normalize(mul((float3x3)World, input.norm));". See the function name: "VSShadowed"? It's a Vertex Shader. Sorry, I should've made that more clear. Thanks for the quick response.

Doesn't matter if it's normalized in the vertex shader, it still has to be normalized again in the pixel shader. (To clarify, for proper results normals should be normalized in both vertex and pixel shader.)

The normals will be linearly interpolated between vertices; as a result they will be shorter than expected away from the vertices. The exponentiation for the specular component means that this manifests most clearly in the specular highlights looking very much like vertex-shaded highlights.

Not sure it'll be the cause, but you can't normalize your normal in the vertex shader and expect it still to be normalised in the pixel shader after interpolation. Try normalizing input.norm and see if that helps?

But you're not normalizing the input.norm in the pixel shader. You need to. Interpolated normal vectors may no longer be normal.

Not sure it'll be the cause, but you can't normalize your normal in the vertex shader and expect it still to be normalised in the pixel shader after interpolation. Try normalizing input.norm and see if that helps?

But it's already in the vertex shader, in the line "output.norm = normalize(mul((float3x3)World, input.norm));". See the function name: "VSShadowed"? It's a Vertex Shader. Sorry, I should've made that more clear. Thanks for the quick response.

Doesn't matter if it's normalized in the vertex shader, it still has to be normalized again in the pixel shader.

The normals will be linearly interpolated between vertices; as a result they will be shorter than expected away from the vertices. The exponentiation for the specular component means that this manifests most clearly in the specular highlights looking very much like vertex-shaded highlights.

Thank you all so much. It works perfectly now.

I didn't even realised interpolation would ruin the normalization. I could've noticed that if I wasn't so dumb. Also, thanks for the explanation, Anthony. It make a lot of sense.

Thank you so much.

PS: Also, why should they be normalized in both VS and PS?

PS: Also, why should they be normalized in both VS and PS?

Because the results will be different if you do not.

For easy imagination, imagine vertex-shader vectors [1,0] and [0,1] (both normalized). 50% between them is [0.5,0.5], which normalizes to [0.70711,0.70711] in the pixel shader (hence why you have to normalized in the pixel shader).
Now imagine vertex-shader vectors [100,0] and [0,1] (not normalized). 50% between them is [50,0.5], which normalizes in the pixel shader to [0.99995,0.0099995] (hence why you have to normalize in the vertex shader).


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

PS: Also, why should they be normalized in both VS and PS?

Yes.


L. Spiro

Um... Okay, but why? It's working just fine normalizing only in the Pixel Shader.

Um... Okay, but why? It's working just fine normalizing only in the Pixel Shader.

Read above.


L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

This topic is closed to new replies.

Advertisement