Hi.

I am trying to implement a simple phong model with a gf4.

The diffuse part of it is working fine.

However, I’m having some problems getting the specular part to work.

My initial idea was to do it like this

unit 1: normalize H

unit 2: lookup normal from bumpmap

unit 3: Exponentiate via a dot3(unit 1, unit 2) and an exponent texture.

So, when i looked at it, DOT_PRODUCT_TEXTURE_1D, seemed like the thing to use, but now it appears that the input to one side of the dot product must be the coordinates bound to the unit.

Is it possible to do dot(normalize(H),bumpnormal), and use it to lookup in a texture?

If, can anyone hint me what to use?

I am using Cg, not that it seems to simplify matters, at least in this case.

Jonas

<pimpage>

I’m writing a chapter in ShaderX2 that is related to this subject

(albeit in DirectX)

It also deals with doing per-pixel specular power maps for ps.1.1 level hardware.

</pimpage>

The technique I present in the chapter constructs a ‘light space’ matrix that has it’s y axis aligned with the half vector, with the other 2 axes forming an orthonormal basis (they are also consistently interpolatable). The per-pixel normal can then be transformed from tangent space into light space using the equivalent of the DirectX texm3x3tex instruction (with 2 texm3x3pad instructions). The cube map that is looked up has a specular highlight in the +ve y direction. The specular highlight can be of any specular power though as it’s just a texture.

This means that you get a normalised (due to cube map lookup), arbitrarily high exponent specular bump mapping pass with no banding artifacts (you don’t use arithmetic instructions to do the exponentiation).

If you don’t need the half vector to be normalised, then just use what you suggested. If you do want it normalised, but don’t want to do the light space cube map stuff, then you’ll have to raise to a power using arithmetic instructions. This’ll probably give you banding for high exponent values.

HTH

Matt Halpin

I dont quite get it.

at least not all of it.

The technique I present in the chapter constructs a ‘light space’ matrix that has it’s y axis aligned with the half vector, with the other 2 axes forming an orthonormal basis (they are also consistently interpolatable). The per-pixel normal can then be transformed from tangent space into light space using the equivalent of the DirectX texm3x3tex instruction (with 2 texm3x3pad instructions). The cube map that is looked up has a specular highlight in the +ve y direction. The specular highlight can be of any specular power though as it’s just a texture.

So is the point that you are only using one of the cubemap faces, and abuse the lookup method for cubemaps?

I dont quite get it, any chance you could elaborate a bit more?

How do you construct the two orthogonal axes?

guess I have to think it over a bit more.

Jonas

Try turning on GL_NORMAL_MAP texgen on one unit and binding that to your diffuse solution (potentially, use the texture coordinate matrix to collapse to a 1D look-up texture) and GL_REFLECTION_MAP texgen on one unit and binding that to your specular solution (same thing there with collapsing 3D texture coordinate to 1D texture).

You’ll need to modulate with color in a separate pass if you only have two units. If you have three or more, you can do diffuse color on the first unit, modulate with diffuse light in the second, and add specular light in the third.

Note that REFLECTION_MAP and the half-angle vector behave slightly differently (WRT compression in the eye-to-light axis) but you can usually make up for this in your cube map.

jwatte:

I dont quite get what you mean.

I need to exponentiate the result of dot(normalize(half),bumpnormal), to get the specular part…

in the fragment program, that is.

Jonas

[This message has been edited by jonasmr (edited 05-02-2003).]

jonas,

Bind a pre-exponentiated cube map to the texture target which you map as REFLECTION_MAP.

If you need per-pixel exponentiation, then it won’t work, of course.

Originally posted by jwatte:

[b]jonas,

If you need per-pixel exponentiation, then it won’t work, of course.[/b]

I wanted perpixel…

all this trouble for this silly card.

thanks, Jonas

I was fuzzy.

My suggestion computes per-pixel lighting, and exponentiates per pixel. The only quantity you do not get per pixel is an exponent MAP, i e, where the exponent is “12” for one pixel, but “36” for the pixel between it (i e, difference in shinyness encoded in a map).

In fact, it seems you don’t have enough dependent reads or general combiners on a GF4 to do per-pixel exponent. It’s of course simple using ARB_fragment_shader

Okay, I dont get it then

What is the diffuse solution/specular solution?

How does the reflection map create the specular highlight?.

I just dont get it… maybe I’m missing something fundamentally, but perhaps you could point direct me to some samples using the technique… if they exist, that is.

Jonas

I put this in our engine at work for a prototype, when we played around with different lighting models. Can’t show you that

Specular highlights are, basically, just the light reflecting in the surface. Thus, if you paint a cube map with a round white fuzzy dot in the middle of one section, and rotate this cube map such that the dot is where the light should be, relative to the object, then REFLECTION_MAP will give you specular lighting onto the object.

Same thing for diffuse, except the cube map should be rather more white (and half black).

Once you’ve gotten this to work, if all you use is round dots, then you can use the texture matrix to collapse the cube map coordinates onto a 1D texture, orienting the matrix to point the “light” direction along the “s” coordinate in texture space. At that point, the “s” value is basically “cos phi” in the lighting equation, and you can put in whatever value you want in each texel, for what you want your lighting to be at that angle.

Originally posted by jonasmr:

[b]I dont quite get it.

at least not all of it.

So is the point that you are only using one of the cubemap faces, and abuse the lookup method for cubemaps?

I dont quite get it, any chance you could elaborate a bit more?

How do you construct the two orthogonal axes?

guess I have to think it over a bit more.

Jonas[/b]

ok, think of the cube map as an environment map with 1 light source in the (0,1,0) direction. ie, the texel values are calculated with the standard specular function: (normalised(u,v,w).(0,1,0))^p where u,v,w are the texture coordinates for the cube map texel (between -1 and 1) and p is the specular power. Now, this environment map needs to be oriented in some way so that the +ve y direction points along the half vector (so the highlight appears in the correct direction). So, create a basis that has the y-axis as the half vector, then construct the z-axis by doing (tangent cross half-vector). Then construct the x-axis by doing (z-axis cross half-vector). Once normalised, these 3 axes form a light space matrix. Now you need to construct a matrix that will transform a tangent space normal into light space. Feed this matrix into the 3 texture interpolators and use the texm3x3pad,texm3x3pad,texm3x3tex instructions to transform the tangent space normal into light space and look up the specular value.

This gives you normalised results as it uses a cube map. It also allows you to have arbitrarily high exponents by simply changing the way the cube map is generated - you don’t need any funky arithmetic to get a high exponent.

HTH

Matt

btw, don’t try and do per-pixel variable specular exponent in the usual ways - I don’t know of any way of doing it on GF4 level hardware without multipassing with temporary texture buffers.

The way I do this is by putting different specular power results in each channel of the cube map I described above. eg, red gets power 2, green gets 15, blue gets 50, alpha gets 200.

Then you can use your per-pixel specular power value (stored in the normal map alpha channel?) to interpolate between these 4 values. The arithmetic shader fragment is a bit tricky - work out on paper the weights required for each channel given the specular power value, then work out the shader instructions needed to construct these graphs.

Matt