Jump to content
  • Advertisement
Sign in to follow this  
blaze02

Per-pixel Spotlight Shader

This topic is 4234 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 want to make a vertex/pixel shader that illuminates geometry per pixel. Unlike a per-vertex shader, I want to be able to shine the spotlight on a giant triangle and be able to illuminate the center. The problem I am running into is incorporating my bump map into the shader. To do this, I have to take the spotlight calculations into tangent (texture) space. The vertex shader uses the light position and direction, and the vertex position. I got the spotlight working fine in world space. Then I transformed everything into model space, and it was identical to the world space calculations. But when I tried to transform everything into tangent space, the lighting breaks. The overall lighting is fine, but some polys are extra dark. I'm thinking I have a problem with transforming the vertex's position into tangent space. Does it make sense to do such a transformation (vertex position multiplied with the TBN matrix)? Also, it could be broke in the fact that the pipeline is interpolating between one vertex position to the other while both are in tangent space. I haven't quite wrapped my head around what exactly is happening with that transformation. Any help is welcome :) I think I'm going to try to do my lighting in model space for the spotlight and tangent space for my bump and specular highlights. Thanks for any help.

Share this post


Link to post
Share on other sites
Advertisement
I've got some working bumpmapping shaders using tangent space calculations. If you post the code that's broken I might be able to spot the problem. What you are trying to do certainly sounds possible.

I've included one version of my bumpmapping code below. It works fine in FX Composer and DarkBASIC:

*****************************************************************************
//
// Green Gandalf's Bumpmapper v1.0
//
// Created 27 June 2006, Modified 7 September 2006
//
// Bumpmapping using a texture and normal map with a single directional light source
// plus ambient light and specular reflection.
// Uses full normalization in the pixel shader.

matrix wvp : WorldViewProjection;
matrix mworld : World;
matrix winv: WorldInverse;
float3 eyePos : CameraPosition;

texture colorTexture < string ResourceName = "lead.jpg"; >;
texture bumpTexture < string ResourceName = "spherical bumps.png"; >;
sampler2D sampler0 = sampler_state
{ texture = <colorTexture>;
mipfilter = linear;
magfilter =linear;
minfilter =linear;
};
sampler2D sampler1 = sampler_state
{ texture = <bumpTexture>;
mipfilter = linear;
magfilter =linear;
minfilter =linear;
};

float4 lightDir = {-1.0f, 0.0f, 0.0f, 1.0f};
float4 ambiColor = {0.2f, 0.2f, 0.2f, 1.0f};
float4 lightColor = {1.0f, 1.0f, 1.0f, 1.0f};
float specLevel = 0.5;
float specExpon=10.0;

struct VS_INPUT
{ float4 Pos : POSITION;
float2 Tex : TEXCOORD0;
float3 Tangent : TANGENT;
float3 Binormal : BINORMAL;
float3 Normal : NORMAL;
};

struct VS_OUTPUT
{ float4 Pos : POSITION;
float2 Tex : TEXCOORD0;
float3 Light : TEXCOORD1;
float3 View : TEXCOORD2;
};

VS_OUTPUT GGBumpVShader(VS_INPUT In, VS_OUTPUT Out)
{ Out.Pos = mul(In.Pos, wvp);
Out.Tex = In.Tex;
float3x3 TSM = {In.Tangent,-In.Binormal,In.Normal};
TSM=transpose(TSM);
float3 temp=-mul(lightDir.xyz,winv);
Out.Light=mul(temp,TSM);
temp=mul(eyePos,winv)-In.Pos;
Out.View=mul(temp,TSM);
return Out;
}

struct PS_INPUT
{ float2 Tex : TEXCOORD0;
float3 Light : TEXCOORD1;
float3 View : TEXCOORD2;
};
struct PS_OUTPUT
{ float4 col : COLOR;
};

PS_OUTPUT GGBumpPShader(PS_INPUT In, PS_OUTPUT Out)
{ float4 baseColour = tex2D(sampler0, In.Tex);
float3 normal = 2 * tex2D(sampler1, In.Tex) - 1.0;
float3 tempLightDir = normalize(In.Light);
float3 tempViewDir = normalize(In.View);
float diffuse = saturate(dot(normal, tempLightDir));
float3 reflect = 2 * diffuse * normal - tempLightDir;
float specular = diffuse * specLevel * pow(saturate(dot(reflect, tempViewDir)),specExpon);
Out.col = baseColour * (ambiColor + diffuse * lightColor) + specular * lightColor;
return Out;
}

technique t0
{ pass p0
{ VertexShader = compile vs_2_0 GGBumpVShader();
PixelShader = compile ps_2_0 GGBumpPShader();
}
}
******************************************************************************

Share this post


Link to post
Share on other sites
Quote:
Original post by Green Gandalf
I've got some working bumpmapping shaders using tangent space calculations.


I have the same thing, and I want to add a spotlight instead of directional lighting. To convert the spotlight into tangent space, you have to get the pixel position in tangent space. Then you can find the vector to the spotlight and do any cone tests for lighting. I think the problem is: the math breaks down when converting a vertex to tangent space. If the vertex can be converted to tangent space, each pixel's position can be interpolated and sent to the pixel shader. Any thoughts?

Share this post


Link to post
Share on other sites
Moving position to tangent space is not necessary. I suggest to post position in world space to pixel shader. Then calculate the direction for light and view. Now that vectors can be rotated by tangent matrix and succesfuly used to deal with light model.

Maybe some details are needed:
float3 VPos - vertex position in world space
float3 LPos - light position in world space
float3 CPos - camera/eye position in world space

// It sholud be negated in 'dot' equation, because
// it points towards to a face, but normal vector points
// outwards.
float3 LDir = normalize(VPos - LPos);

// This vector points towards, but reflected LDir points
// also towards, so simply 'dot' is enought
float3 VDir = normalize(VPos - CPos);

Do not forget to transform that vectors to tangent space.

Above calculation od light direction gives you omni/point light effect. It is also spot light effect, but without spot. VDir (in tangent space) can be used to evaluate intensity of reflected light.

To achieve spot light effect you need spot direction vector, which points towards the face. Spot light direction is constatnt like in directional light. Using that two vectors you can calculate spot multipler. Example:

// Note: all vectors with 'T' suffix are in tangent space

// We need to obtain angle between light direction (LDirT)
// and spot direction (SDirT)
float spot_alpha = dot(LDirT, SDirT);


// Now we using external spot data to determinate cones.
// Theta is inner cone angle, and Phi is outer cone angle.
float inner_alpha = cos(g_spot_theta / 2.0f);
float outer_alpha = cos(g_spot_phi / 2.0f);

// Finally, we can determine spot multipler
float spot = saturate((spot_alpha - outer_alpha) / (inner_alpha - outer_alpha));

All what you need to do with that scalar is to multiple final color/intensity of light by.

Enjoy!

Share this post


Link to post
Share on other sites
Quote:
Original post by TheDmD
Moving position to tangent space is not necessary.


I agree, but moving the light vector to tangent space is necessary for correct bump mapping. The only solution for that is sending the TBN matrix to the pixel shader which seems like a waste.

I have the shader working, but its not how I originally expected it to work. I do spotlight lighting in model space, then bump mapping and specular highlights in tangent space.

Share this post


Link to post
Share on other sites
Quote:

I agree, but moving the light vector to tangent space is necessary for correct bump mapping. The only solution for that is sending the TBN matrix to the pixel shader which seems like a waste.


I always send the TBN matrix to the pixel shader and there I do all necessary math. Of coruse some optimalizations can be done. We can choose: move light vector to tangent space or move normal vector to light vector space. You can also prepare light vector in vertex shader, transform it to tangent space and then send to pixel shader. View vector can be treated in this same way. So in pixel shader you sholud only normalize that vectors. Theoreticaly results will be identical.

All required data always must be in this same space. This guarante correct results.


Quote:

I have the shader working, but its not how I originally expected it to work. I do spotlight lighting in model space, then bump mapping and specular highlights in tangent space.


How it exacly works, and how it should works? Maybe TNB matrix is broken?

Share this post


Link to post
Share on other sites
Quote:
moving the light vector to tangent space is necessary for correct bump mapping.


That's not true. You can move the normal map normal into object space, instead, and do calculations in object space. This makes things like bump mapped environment mapping easier, too.

You still need to send the TBN to the pixel shader, but to go to object space, you multiply with the inverse of the TBN. Which, for an orthonormal matrix, is the transpose. Which, in pixel shader code, means using three multiply-add instructions instead of three dot-product instructions, so it's the same cost.

If you're not using an orthonormal tangent basis matrix, then you can still pre-calculate the inverse of the matrix, and store that as your T/B/N in the vertex stream.

Share this post


Link to post
Share on other sites
Quote:
Original post by TheDmD
All required data always must be in this same space. This guarante correct results.
How it exacly works, and how it should works? Maybe TNB matrix is broken?


I don't see why everything MUST be in the same space. I compute light intensity in model space using spotlight math. Then I compute bump mapping and specular highlights in tangent space. I also grab a little reflection in world space. I blend bump map lighting, specular highlights, and the reflection for a final color. I multiply that by the light intensity of the spot (how close to the center of the cone). It all works fine, there is nothing broken.

I just wanted to do spotlight calculations in tangent space. Which, I suppose, is possible by sending the TBN matrix to the pixel shader, but that is a lot of bandwidth added per pixel when I can do the same calculations in model space.

We are kinda going in circles because I already said this.
Quote:
Original post by blaze02
The only solution for that is sending the TBN matrix to the pixel shader which seems like a waste.

Share this post


Link to post
Share on other sites
it is pretty common to transform the normal to world space in the pixel shader. This way you do not have to transform all the other vectors involved to tangent space ... it is starting to pay off if you use several lights or a huge number of vectors and people reported that it looks better.

Share this post


Link to post
Share on other sites
Quote:
Original post by hplus0603
That's not true. You can move the normal map normal into object space, instead, and do calculations in object space.


Right! But I assume, normal map is in tangent space ;).

Quote:
Original post by blaze02
I don't see why everything MUST be in the same space.


Not absolutely everything but everyting in given equation.

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.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!