Skip to main content
GameDev.net gamedev.net
🔒 Locked 🎮 Unity

Light vectors in tangent space

Started by Bagel Man Jan 18, 2005 at 8:30 PM 12 replies 4.9k views
Original Post
Bagel Man
Bagel Man
I just recently got a GeForce FX card, and have been experimenting with shaders. I implimented some normal mapping the way it was presented in all the tutorials I read, which is to use tangent and bi-normal vectors to transform the normal from the map from tangent space to world space, and it worked just fine. But transforming the normal this way every pixel seemed inefficient, so I tried doing the opposit: transforming the light and half vectors into tangent space, and doing it per-vertex, rather than per-pixel. It seems to work equaly well, it removes the normal transformation, and it passes 2 fewer parameters to the pixel shader. Has anyone tried doing this before? Is there some fatal flaw I have yet to discover?
Namethatnobodyelsetook
Namethatnobodyelsetook
Your method is the common way to do normal mapping in ps.1.1. In ps.2.0 and higher people seem to use the other method. It avoids the problem of having your tangent-space light vector being non-normalized.

Here I demonstrate the issue, which can be mostly fixed with a normalization cube map. Since this only "mostly" fixes it, and it requires an extra cube map texture lookup, is probably why the other method is preferred.
SimmerD
SimmerD
Yes, you have rediscovered the way most folks have been doing it for quite a while! :)

As far as I know, Doug Rogers and I were the first people to develop the currently popular method of deriving smooth tangent spaces from texture coordinates & vertex normals back in the GeForce1/2 timeframe - and we did it like you say. Move the L vector into tangent space per-vertex and let the hw interpolate it.

Depending on how you generated the tangent space, and what type of vector you are interpolating it may or may not be 100% accurate.

In general, you definitely want to move linear or almost-linear computations to the vertex shader, and moving vectors into tangent space is certainly a good one to start with.
hplus0603
hplus0603
Doing it that way is faster, but may lead to more interpolation error, especially across large or heavily bent surfaces, or objects close to the light source (with local light sources).

Also, you really don't want to take the light into the normal space -- you want to take the normal out of tangent space into model space, because then you can also use the vector for environment mapping. Where going one way is a series of DP3, going the other way is a series of MAD, so it's really no more expensive doing one vs the other (you don't need to transpose the matrix explicitly).
enum Bool { True, False, FileNotFound };
Bagel Man
Bagel Man
Quote:
Original post by Namethatnobodyelsetook
Here I demonstrate the issue, which can be mostly fixed with a normalization cube map. Since this only "mostly" fixes it, and it requires an extra cube map texture lookup, is probably why the other method is preferred.

Don't you need to re-normalize the normal in the other method too?

Quote:
Original post by SimmerD
Yes, you have rediscovered the way most folks have been doing it for quite a while! :)

Yeah, I figured it must have been done before, but I had never seen it mentioned anywhere, which is why I thought there may be a problem I hadn't seen.


Quote:
Original post by hplus0603
Where going one way is a series of DP3, going the other way is a series of MAD, so it's really no more expensive doing one vs the other (you don't need to transpose the matrix explicitly).

Yes, but putting the light and half vectors into tangent space can be done in the vertex shader instead of pixel.
Namethatnobodyelsetook
Namethatnobodyelsetook
Quote:
Original post by Bagel Man
Quote:
Original post by Namethatnobodyelsetook
Here I demonstrate the issue, which can be mostly fixed with a normalization cube map. Since this only "mostly" fixes it, and it requires an extra cube map texture lookup, is probably why the other method is preferred.

Don't you need to re-normalize the normal in the other method too?

Yeah, that occured to me sometime after I posted. In truth, I've yet to use anything above ps.1.1 so I don't know the details of the other ps.2.0 method.
SimmerD
SimmerD
Quote:
Original post by Sages
Quote:
Original post by SimmerD
As far as I know, Doug Rogers and I were the first people to develop the ...

Lindholm? Everitt? Ramey? Way off?


If by Everitt, you mean Cass Everitt, yes he learned it from us when he started at nvidia. Not sure who you mean by Lindholm ( Erik? ) or Ramey ( Will? ), but Doug Rogers and I created devloped & tested the method in february '00, then popularized it inside of nvidia. Then I helped put it into Evolva, and the Whole Experience Demo. Those were fun times...

I remember as soon as Cass started I explained to him my 1-(x^2+y^2+z^2) attenuation map technique, and he came up with the gaussian method ( which is cooler in some ways )...
Sages
Sages
Quote:
Original post by SimmerD


So (going out on a limb here) that makes you Sim Dietrich?
If so... it's a pleasure to be conversing with you and I was unaware of your presence in these forums. I should've paid more attention to your previous posts.
Promit
Promit
Quote:
Original post by Sages
Quote:
Original post by SimmerD


So (going out on a limb here) that makes you Sim Dietrich?
If so... it's a pleasure to be conversing with you and I was unaware of your presence in these forums. I should've paid more attention to your previous posts.


Likewise -- it's cool to see we have a few VIPs here [grin]
SlimDX | Ventspace Blog | Twitter | Diverse teams make better games. I am currently hiring capable C++ engine developers in Baltimore, MD.
SimmerD
SimmerD
Yep, that's me alright.

BTW, I just implemented blending tangent space normal maps as decals into my engine, and it works great. Doing this with object space seems like it would be much harder.
Sages
Sages
Quote:
Original post by SimmerD
BTW, I just implemented blending tangent space normal maps as decals into my engine, and it works great. Doing this with object space seems like it would be much harder.

Yeah, this is really interesting stuff. I actually did the same thing a few weeks ago and would like to ask a few questions.

1) Are you only applying the normal map or do the decals also have other information (ie: diffuse, specular)?

2) Have you put any thoughts into using depth correction on the decal?

I hacked together a method to get depth correction working but I still have artifacts on corners and curves especially with larger depths. Overall the technique produces a really nice effect, however the method is not very practical or efficient. Any thoughts?
Jason Z
Jason Z
Quote:
Original post by SimmerD
Quote:
Original post by Sages
Quote:
Original post by SimmerD
As far as I know, Doug Rogers and I were the first people to develop the ...

Lindholm? Everitt? Ramey? Way off?


If by Everitt, you mean Cass Everitt, yes he learned it from us when he started at nvidia. Not sure who you mean by Lindholm ( Erik? ) or Ramey ( Will? ), but Doug Rogers and I created devloped & tested the method in february '00, then popularized it inside of nvidia. Then I helped put it into Evolva, and the Whole Experience Demo. Those were fun times...

I remember as soon as Cass started I explained to him my 1-(x^2+y^2+z^2) attenuation map technique, and he came up with the gaussian method ( which is cooler in some ways )...


How cool is that, to actually talk (kind of) with one of the guys that developed tangent space bump mapping. I am humbled... :)
Jason Zink :: DirectX MVP   Direct3D 11 engine on CodePlex: Hieroglyph 3 Direct3D Books: 
SimmerD
SimmerD
Quote:
Original post by Sages
Quote:
Original post by SimmerD
BTW, I just implemented blending tangent space normal maps as decals into my engine, and it works great. Doing this with object space seems like it would be much harder.

Yeah, this is really interesting stuff. I actually did the same thing a few weeks ago and would like to ask a few questions.

1) Are you only applying the normal map or do the decals also have other information (ie: diffuse, specular)?

2) Have you put any thoughts into using depth correction on the decal?

I hacked together a method to get depth correction working but I still have artifacts on corners and curves especially with larger depths. Overall the technique produces a really nice effect, however the method is not very practical or efficient. Any thoughts?


Since I posted, I now have 2 methods for blending in materials. Method one involves a 2-layer material specified at design time. There is a uniquely textured RGBA texture across the world that contiains R : Diffuse blending factor, G : Normal blending factor, B : Ambient occlusion , A : Sunlight occlusion factor.

I do semi-deferred shading in the engine, so first I output diffuse to a buffer for all world surfaces, including 1 or 2 layer materials. Then I alpha blend in the decals' diffuse on top. Then I copy this to a texture.

Next is the same for the normals for the world surfaces ( in tangent space ), then alpha blend decals' normals, then copy offscreen. I just doa straight srcalpha,invsrcalpha blend, don't renormalize, and don't correct depth. Of course my view is mostly top down, so the depth is not much of an issue.

Then I do the ambient & occlusion & Z pass using the offscreen surfaces as screen-aligned projected textures, rendering the actual world geometry. This is why I call it semi-deferred shading in the sense that most folks render the light geometry instead of the world gometry. I prefer my method b/c it is still fast, and it uses a similar setup to the old-non-deferred path which I may have to revive for older drivers or slower machines. Also its the only way to do it without an accurate view-space Z for all surfaces ( which can't be reasonably done in ps.1.1 ).

Then, for each light, I have an index list of just the triangles that aren't statically shadowed from that light, and I render those, again using the offscreen diffuse & normal map buffers. I store gloss in normal.alpha, and am not using diffuse.alpha for anything yet, although I am considering either a fresnel or scattering term.

The cool thing is that all of this is done in vs & ps.1.1!
Maybe I'll start a thread on this rather than hijacking this one...

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.