gloss mapping?

Started by
6 comments, last by Hodgman 11 years, 4 months ago
Hey I just implemented gloss mapping or at least I think I did. Am I right in thinking that gloss mapping is when you change the specular exponent based on a texture? Also did I implement it right? here is my code (simplified of course):


specular = pow(max(dot(halfvec, finalnormal), 0.0), (texture2D(gloss_texture, transformedTexcoord).r*99.0) + 1.0);


Mostly I was wondering about the range, I want to be pretty conformant to what other engines use as to maximize the portability of my content. right now I noticed that when multiplying it by 99 I start running into the limitations of 8-bit grayscale. Does anyone have an example of how they implemented this effect?
Advertisement
Not sure about your constant, but the rest looks OK. This is how a gloss map is used to the best of my knowledge too.
Sometimes "gloss" terminology is a bit vague - some engines use it to mean the specular power, while others use it to mean the specular mask. In any case, it's good to fetch both the power and mask values from a texture to give your artists decent control over the materials.
In my engine, I call the specular-power texture the "roughness" texture.

You seem to be using the traditional Blinn-Phong specular function, and the code looks ok. In terms of style, I'd suggest a few more line breaks:
float NdotH = saturate(dot(halfvec, finalnormal)); //prefer saturate over max where able
float specPower = texture2D(gloss_texture, transformedTexcoord).r*99.0 + 1.0;
specular = pow(NdotH, specPower);


As for the "[font=courier new,courier,monospace]x*99+1[/font]" part, there is no right answer for this encoding/decoding function. Any positive value is valid for the specular power -- some materials might need "5" and others might need "50000"... This makes choosing a function that encodes this range into 8-bits particularly difficult.
If the materials in your game only need the values of 1-100, then your encoding is fine.

If you want to move more precision to the lower end, you can do something like:
float specPower = texture2D....
specPower = (specPower*specPower) * range + 1;
Or to move precision to higher values, something like:float specPower = 1.0 - texture2D....
specPower = (1.0-(specPower*specPower)) * range + 1;

Some engines use the log function for a logarithmic distribution, but basically you've just got to choose a mapping that works for you.

Other engines might not even be using Blinn-Phong, so even if you use the same mapping, you'll get different appearances of "gloss" anyway.
Sorry to intrude but I'm curious as to why you'd prefer saturate over max ?
I was having some problems using saturate for a dot product before, were it was clamping values to 1 which should not have been.
Isn't a max much safer and precise in that respect ?
It is common in game engines now to store the spec power in log space, even with 8 bit precicion.

i.e see how UE4 does it here (slide 30)

http://advances.realtimerendering.com/s2012/Epic/The%20Technology%20Behind%20the%20Elemental%20Demo%2016x9.pptx

Frostbite2 also does it this way too (albeit with maybe a slightly different range)


Also as mentioned above a saturate will be better. Its usually free whereas max is not.

Also as mentioned above a saturate will be better. Its usually free whereas max is not.


I'm using glsl, as far as I know saturate doesn't exist there, glsl has clamp() but I'm not sure if that is still "free"

[quote name='AliasBinman' timestamp='1354558493' post='5006703']
Also as mentioned above a saturate will be better. Its usually free whereas max is not.


I'm using glsl, as far as I know saturate doesn't exist there, glsl has clamp() but I'm not sure if that is still "free"
[/quote]

In that case do clamp(x,0,1) and the compiler should replace with a saturate.

Sorry to intrude but I'm curious as to why you'd prefer saturate over max ?
I was having some problems using saturate for a dot product before, were it was clamping values to 1 which should not have been.
Isn't a max much safer and precise in that respect ?
Yes if the input is possibly > 1, then saturate isn't a valid replacement. A dot product of two normal vectors can't be > 1 though, so it's possible in this case.
Saturate/clamp(x,0,1) is preferable because it's often implemented as an instruction modifier, rather than its own instruction.
E.g. Instead of "mul ...; max ...;", the GPU ASM may just be "mul_clamped ...;"

This topic is closed to new replies.

Advertisement