Proposal: Pre-computing shininess in code

For collaboration on developing the mod capabilities of VS; request new features, report bugs, or suggest improvements

Moderator: Mod Contributor

klauss
Elite
Elite
Posts: 7243
Joined: Mon Apr 18, 2005 2:40 pm
Location: LS87, Buenos Aires, República Argentina

Post by klauss »

Damn, Chuck! There's a ton ofimprovement even with "broken" shaders (and I say "broken" because your shaders are more suited to your dataset, the only fix I'd import into VS is environment x2 and bye bye ambient).

At first things look odd, highlights are too strong on high-shininess parts, but that kind of makes sense: if you get used to it, you'll notice the increased realism.

Thumbs up!
Oíd mortales, el grito sagrado...
Call me "Menes, lord of Cats"
Wing Commander Universe
chuck_starchaser
Elite
Elite
Posts: 8014
Joined: Fri Sep 05, 2003 4:03 am
Location: Montreal
Contact:

Post by chuck_starchaser »

klauss wrote:Take a peek at the previous post, I've added the right formula ;)
Peek taken, thanks! Take a peek at the edit in my last post, now :D
chuck_starchaser
Elite
Elite
Posts: 8014
Joined: Fri Sep 05, 2003 4:03 am
Location: Montreal
Contact:

Post by chuck_starchaser »

Glad to hear. (Damn!, my room-mate is going to kill me; I was supposed to do the dishes after breakfast, and that has now become impossible...).
chuck_starchaser
Elite
Elite
Posts: 8014
Joined: Fri Sep 05, 2003 4:03 am
Location: Montreal
Contact:

Post by chuck_starchaser »

Could you post your shader, klauss? I'm having trouble finding the right place to insert this, now. Like, it seems I would need to pass a new parameter to lightingLight()?

BTW, there's no such thing as a "PU dataset". Everything in PU is inherited garbage we're just beginning to fix.
Having separate diffuse and specular light is a BUG, even if it came curtesy of opengl.
Last edited by chuck_starchaser on Sun Apr 06, 2008 4:54 pm, edited 1 time in total.
klauss
Elite
Elite
Posts: 7243
Joined: Mon Apr 18, 2005 2:40 pm
Location: LS87, Buenos Aires, República Argentina

Post by klauss »

chuck_starchaser wrote: EDIT:
HECK! Shininess itself needs to be limited for light spots!!! Stars are not point light sources!
Higher shininesses than some 20 or 30 should ONLY apply to env mapping.
Maybe. But I wouldn't. There may be truly point light sources out there later on. Maybe a better thing is to use area lighting when you're very close to the sun. I have area lighting shaders over there in RenderMonkey, they're much more complex. I always wanted to use them eventually, the engine would choose which to use based on apparent star size (if the star looks big enough, it uses area lighting). But that's not possible right now because a single shader has to do all lights.
So feel free to hack it the way you see fit to compensate for that limitation. If you do limit shininess, please, make it distance-to-light-dependant?
Oíd mortales, el grito sagrado...
Call me "Menes, lord of Cats"
Wing Commander Universe
chuck_starchaser
Elite
Elite
Posts: 8014
Joined: Fri Sep 05, 2003 4:03 am
Location: Montreal
Contact:

Post by chuck_starchaser »

klauss wrote:If you do limit shininess, please, make it distance-to-light-dependant?
Got it. Not sure how, though; I'll have to pass star distance from the vp to the fp.
Or better yet, star diameter/distance, but I'm not sure how.

So far I only got this done:

Code: Select all

float limited_shininessMap(float shininess, float specmap)
{
  float limit = 50; //fixed for now
  float shine = shininessMap(shininess,specmap);
  return shine*limit/(shine+limit);
}
Could you please post your shader?
chuck_starchaser
Elite
Elite
Posts: 8014
Joined: Fri Sep 05, 2003 4:03 am
Location: Montreal
Contact:

Post by chuck_starchaser »

Your formula simplifies to,

Code: Select all

specular = 0.6*(1+shininess/10)^1.7
Correct?
What does "1+..." do? Maybe that's the problem at low shininess values.
chuck_starchaser
Elite
Elite
Posts: 8014
Joined: Fri Sep 05, 2003 4:03 am
Location: Montreal
Contact:

Post by chuck_starchaser »

Code: Select all

float limited_shininessMap(float shininess, float specmap)
{
  float limit = 20; //20^1.7 is 162. 162*0.0117 = 1.9 --enough risk of saturation!
  float shine = shininessMap(shininess,specmap);
  return shine*limit/(shine+limit);
}

float shininess_to_brightness(float shininess)
{
  //specular = 1.0 / (1.7/(1+shininess/10))^1.7;
  //specular = 0.588*(1+shininess/10)^1.7
  //but the 1+ term is dubious. If we get rid of it, we get
  //specular = 0.588*(shininess/10)^1.7 which simplifies to
  //specular = 0.011736837 * shininess^1.7
  return 0.011736837 * pow( shininess, 1.7 );
}
Thoughts?
chuck_starchaser
Elite
Elite
Posts: 8014
Joined: Fri Sep 05, 2003 4:03 am
Location: Montreal
Contact:

Post by chuck_starchaser »

DONE!

Code: Select all

float limited_shininessMap(float shininess, float specmap)
{
  float limit = 30; //30^1.7 is 324. 324*0.005 = 1.62 --enough risk of saturation!
  float shine = shininessMap(shininess,specmap);
  return shine*limit/(shine+limit);
}

float shininess_to_brightness(float shininess)
{
  //specular = 1.0 / (1.7/(1+shininess/10))^1.7;
  //specular = 0.588*(1+shininess/10)^1.7
  //but the 1+ term is dubious. If we get rid of it, we get
  //specular = 0.588*(shininess/10)^1.7 which simplifies to
  //specular = 0.011736837 * shininess^1.7 but make that .005...
  return 0.005 * pow( shininess, 1.7 );
}

float lightspot_brightness( float shininess, float specmap )
{
  return limited_shininessMap( shininess, specmap ) * shininess_to_brightness( shininess );
}

void lightingLight(
   in vec3 light, in vec3 normal, in vec3 vnormal, in vec3 eye, in vec3 reflection, 
   in vec4 lightDiffuse, in float lightAtt, 
   in vec4 diffusemap, in vec4 specmap, in float shininess,
   in vec4 ambientProduct,
   inout vec3 diffuse, inout vec3 specular)
{
   float NdotL = dot(normal,light);
   float VNdotL= dot(vnormal,light);
   float RdotL = dot(reflection,light);
   float selfshadow = selfshadowStep(VNdotL);
   
   diffuse  += selfshadow * clamp(NdotL,0.0,1.0) * lightDiffuse.rgb * lightAtt ;
   specular += pow(clamp(RdotL,0.0,1.0), lightspot_brightness(shininess,specmap)) * lightDiffuse.rgb * lightAtt;
}
Yeah, looks a lot more realistic. Even with subtle differences in shininess, such as between the bronze in the exhaust and the stainless in the body of the engine, one can see the increased brightness of the light spot on the bronze:

Image

I was mentioning elsewhere the bug with the light source appearing to turn around with the ship when you turn the ship in F7 view. Here I caught the bug again on the evil deed... In the picture below:

Image

Notice how, judging for where the refletions are, the light source would be behind us and to the right?
Look what happens when I press the roll key for a second:

Image

The light that was behind us and to the right seems to have moved to a totally different place.

I'm beginning to form a thought that THIS is why the shader seems to work better without that self-shadow term. Not because the term is unwarranted, but because there's a bug with the lighting.

Image

Nice and shiny, those thinner, aluminium pipes...
chuck_starchaser
Elite
Elite
Posts: 8014
Joined: Fri Sep 05, 2003 4:03 am
Location: Montreal
Contact:

Post by chuck_starchaser »

Yep. I commented out the selfshadow term in the direct light contribution and that improves the visuals.
I'm not exactly sure what is improving, but it looks better.
I think, either this self-shadow thing is using wrong incoming light direction data, or else it's buggy.
Removed for now...

Image

Image

Looks more "natural" to me...

How's the self-shadow thing supposed to work, anyways?

Code: Select all

//float selfshadowStep(float VNdotL) { return step(0.0,VNdotL); } // fast but hard selfshadow function
float selfshadowStep(float VNdotL) { return smoothstep(0.0,0.25,VNdotL); } // costly but soft and nice selfshadow function
I've no clue what VNdotL is supposed to mean. If I look where it comes from, it comes from

Code: Select all

   float VNdotL= dot(vnormal,light);
and vnormal comes from main, where,

Code: Select all

  vec3 vnormal=iNormal;
and iNormal comes from,

Code: Select all

  // Retrieve normals
  vec3 iNormal=gl_TexCoord[1].xyz;
  vec3 iTangent=gl_TexCoord[2].xyz;
  vec3 iBinormal=gl_TexCoord[3].xyz;
so, this would seem to boil down to dot(light,normal), which is what diffuse lighting already does.
Are we multiplying the whole lighting by (1+0.25*cos(light's angle)) or something? :-/
Last edited by chuck_starchaser on Sun Apr 06, 2008 7:01 pm, edited 3 times in total.
DualJoe
ISO Party Member
ISO Party Member
Posts: 387
Joined: Tue Mar 21, 2006 2:37 pm
Location: Netherlands
Contact:

Post by DualJoe »

:shock: Very impressive.

One question, is the material on the hull supposed to look metallic or should it look like gray paint?
chuck_starchaser
Elite
Elite
Posts: 8014
Joined: Fri Sep 05, 2003 4:03 am
Location: Montreal
Contact:

Post by chuck_starchaser »

Good question. I haven't the faintest clue what the hull is made of, I confess.
It's basically a grey material with pretty low specularity; that's all I can say.
And the reason it's that way is that that is what Nate and I concluded the material in the original Tarsus from Origin was
like, judging from the reference images. So, it's just trying to be "canonical material" :D

Giving it low specularity, with the current shader, implies low shininess, which implies it's metallic, or some matte paint.
If we wanted to make it be a baked or enamel paint, we'd probably want even lower specularity; but it would be a
dielectric... with much higher shininess ... plus fresnel...

Next shader will be able to do that.
DualJoe
ISO Party Member
ISO Party Member
Posts: 387
Joined: Tue Mar 21, 2006 2:37 pm
Location: Netherlands
Contact:

Post by DualJoe »

chuck_starchaser wrote: If we wanted to make it be a paint, we'd probably want even lower specularity, but much higher shininess...
...plus fresnel...

Next shader will be able to do that.
Or it's some kind of grey matte paint, like primer. No need for high shininess then.
chuck_starchaser
Elite
Elite
Posts: 8014
Joined: Fri Sep 05, 2003 4:03 am
Location: Montreal
Contact:

Post by chuck_starchaser »

True; and it's looking better now, after some new shader changes...
Getting a lot lower shininess, as it should if it's a matte paint.

Squaring the spec rgb terms was not enough. I now cubed them and I get MUCH better shininesses...
Also, instead of 1.7 as the power of shininess to modulate brightness of specular spots, I changed that to 2.0.
Watch the brighness of that bar under the cockpit as it passes through the right angle. It probably saturates...

Image

Image

Image

Image

And here we see, first of all, a better differenciation between the shininesses of bronze and stainless,
and a more noticeably brighter light-spot:

Image

My current shader code:

Code: Select all

uniform int light_enabled[gl_MaxLights];
uniform int max_light_enabled;
uniform sampler2D diffuseMap;
uniform sampler2D envMap;
uniform sampler2D specMap;
uniform sampler2D glowMap;
uniform sampler2D normalMap;
uniform sampler2D damageMap;
uniform sampler2D detail0Map;
uniform sampler2D detail1Map;
uniform vec4 cloaking;
uniform vec4 damage;
uniform vec4 envColor;

vec3 matmul(vec3 tangent, vec3 binormal, vec3 normal,vec3 lightVec) {
  return vec3(dot(lightVec,tangent),dot(lightVec,binormal),dot(lightVec,normal));
}
vec3 imatmul(vec3 tangent, vec3 binormal, vec3 normal,vec3 lightVec) {
  return lightVec.xxx*tangent+lightVec.yyy*binormal+lightVec.zzz*normal;
}

vec2 EnvMapGen(vec3 f) {
   float fzp1=f.z+1.0;
   float m=2.0*sqrt(f.x*f.x+f.y*f.y+(fzp1)*(fzp1));
   return vec2(f.x/m+.5,f.y/m+.5);
}

float bias(float f){ return f*0.5+0.5; }
vec2  bias(vec2 f) { return f*0.5+vec2(0.5); }
vec3  bias(vec3 f) { return f*0.5+vec3(0.5); }
vec4  bias(vec4 f) { return f*0.5+vec4(0.5); }

float expand(float f){ return f*2.0-1.0; }
vec2  expand(vec2 f) { return f*2.0-vec2(1.0); }
vec3  expand(vec3 f) { return f*2.0-vec3(1.0); }
vec4  expand(vec4 f) { return f*2.0-vec4(1.0); }

float lerp(float f, float a, float b){return (1.0-f)*a+f*b; }
vec2  lerp(float f, vec2 a, vec2 b) { return (1.0-f)*a+f*b; }
vec3  lerp(float f, vec3 a, vec3 b) { return (1.0-f)*a+f*b; }
vec4  lerp(float f, vec4 a, vec4 b) { return (1.0-f)*a+f*b; }

//float selfshadowStep(float VNdotL) { return step(0.0,VNdotL); } // fast but hard selfshadow function
//float selfshadowStep(float VNdotL) { return smoothstep(0.0,0.25,VNdotL); } // costly but soft and nice selfshadow function
//float shininessMap(float shininess, vec4 specmap) { return clamp(specmap.a*shininess,1.0,255.0); } // alpha-based shininess modulation

float shininessMap(float shininess, vec4 specmap) // luma-based shininess modulation
{
  float3 temp = specmap.rgb;
  temp *= specmap.rgb;
  temp *= specmap.rgb;
  temp.b = (temp.r+temp.g)*0.75;
  return clamp( temp.b*shininess, 1.0, 255.0 );
}

float shininess2Lod(float shininess) { return max(0.0,7.0-log2(shininess+1.0))+3.0*(1.0+envColor.a); }

float limited_shininessMap(float shininess, float specmap)
{
  float limit = 30; //30^1.7 is 324. 324*0.005 = 1.62 --enough risk of saturation!
  float shine = shininessMap(shininess,specmap);
  return (shine*limit)/(shine+limit);
}

float shininess_to_brightness(float shininess)
{
  return 0.0025 * shininess * shininess;
}

float lightspot_brightness( float shininess, float specmap )
{
  return limited_shininessMap( shininess, specmap ) * shininess_to_brightness( shininess );
}

void lightingLight(
   in vec3 light, in vec3 normal, in vec3 vnormal, in vec3 eye, in vec3 reflection, 
   in vec4 lightDiffuse, in float lightAtt, 
   in vec4 diffusemap, in vec4 specmap, in float shininess,
   in vec4 ambientProduct,
   inout vec3 diffuse, inout vec3 specular)
{
   float NdotL = dot(normal,light);
   float VNdotL= dot(vnormal,light);
   float RdotL = dot(reflection,light);
   //float selfshadow = selfshadowStep(VNdotL);
   
   diffuse  += clamp(NdotL,0.0,1.0) * lightDiffuse.rgb * lightAtt ;
   specular += pow(clamp(RdotL,0.0,1.0), lightspot_brightness(shininess,specmap)) * lightDiffuse.rgb * lightAtt;
}

#define lighting(name, lightno_gl, lightno_tex) \
void name( \
   in vec3 normal, in vec3 vnormal, in vec3 eye, in  vec3 reflection, \
   in vec4 diffusemap, in vec4 specmap, \
   inout vec3 diffuse, inout vec3 specular) \
{ \
   lightingLight( \
      normalize(gl_TexCoord[lightno_tex].xyz), normal, vnormal, eye, reflection, \
      gl_FrontLightProduct[lightno_gl].diffuse, \
      gl_TexCoord[lightno_tex].w, \
      diffusemap, specmap, gl_FrontMaterial.shininess, \
      gl_FrontLightProduct[lightno_gl].ambient, \
      diffuse, specular); \
}

lighting(lighting0, 0, 5)
lighting(lighting1, 1, 6)

vec3 lightingClose(in vec3 diffuse, in vec3 specular, in vec3 ambient, in vec4 diffusemap, in vec4 specmap)
{
   return (diffuse + ambient) * diffusemap.rgb + specular * specmap.rgb;
}

vec3 envMapping(in vec3 reflection, in float gloss, in vec4 specmap)
{
   float envLod = shininess2Lod(gloss);//shininessMap(shininess,specmap));
   return texture2DLod(envMap, EnvMapGen(reflection), envLod).rgb * specmap.rgb * vec3(2.0);
}

void main() 
{
  // Retrieve normals
  vec3 iNormal=gl_TexCoord[1].xyz;
  vec3 iTangent=gl_TexCoord[2].xyz;
  vec3 iBinormal=gl_TexCoord[3].xyz;
  vec3 vnormal=iNormal;
  //vec3 normal=normalize(imatmul(iTangent,iBinormal,iNormal,expand(texture2D(normalMap,gl_TexCoord[0].xy).yxz)*vec3(-1.0,1.0,1.0)));
  vec3 normal=normalize(imatmul(iTangent,iBinormal,iNormal,expand(texture2D(normalMap,gl_TexCoord[0].xy).wyz)));
  
  // Other vectors
  vec3 eye = gl_TexCoord[4].xyz;
  vec3 reflection = -reflect(eye,normal);
  
  // Init lighting accumulators
  vec3 diffuse = vec3(0.0);
  vec3 specular= vec3(0.0);
  vec3 ambient = vec3(0.0);
  
  // Sample textures
  vec4 damagecolor = texture2D(damageMap , gl_TexCoord[0].xy);
  vec4 diffusecolor= texture2D(diffuseMap, gl_TexCoord[0].xy);
  vec4 speccolor   = texture2D(specMap   , gl_TexCoord[0].xy);
  vec4 glowcolor   = texture2D(glowMap   , gl_TexCoord[0].xy);
  //sanity enforcement:
  float temp = 1.0 - max( diffusecolor.r, max( diffusecolor.g, diffusecolor.b ) );
  speccolor.r = min( speccolor.r, temp );
  speccolor.g = min( speccolor.g, temp );
  speccolor.b = min( speccolor.b, temp );
  
  vec4 diffusemap  = lerp(damage.x, diffusecolor, damagecolor);
  vec4 specmap     = speccolor;
  float specdamage = clamp(1.0 - dot(damagecolor.xyz,vec3(1.0/3.0)) * damage.x * 2.0, 0.0, 1.0);
  specmap.rgb     *= specdamage;
  specmap.a       *= bias(specdamage);
  float gloss      = shininessMap(gl_FrontMaterial.shininess,specmap);
  
  // Do lighting for every active light
  if (light_enabled[0] != 0)
     lighting0(normal, vnormal, eye, reflection, diffusemap, specmap, diffuse, specular);
  if (light_enabled[1] != 0)
     lighting1(normal, vnormal, eye, reflection, diffusemap, specmap, diffuse, specular);

  vec4 result;
  result.rgb  = lightingClose(diffuse, specular, ambient, diffusemap, specmap)
              + glowcolor.rgb
              + envMapping(reflection,gloss,specmap);
  result.a = diffusemap.a;
  result *= cloaking.rrrg;
  
  gl_FragColor = result;
}
One more pic: I just caught the light just right...

Image
charlieg
Elite Mercenary
Elite Mercenary
Posts: 1329
Joined: Thu Mar 27, 2003 11:51 pm
Location: Manchester, UK
Contact:

Post by charlieg »

Very impressive work chuck.
Free Gamer - free software games compendium and commentary!
FreeGameDev forum - open source game development community
klauss
Elite
Elite
Posts: 7243
Joined: Mon Apr 18, 2005 2:40 pm
Location: LS87, Buenos Aires, República Argentina

Post by klauss »

Chuck, don't change the 1.7, it was a hand-picked fit for the mathematically correct function. Doing so would sacrifice the correctness of lower shininess values. Rather, I believe you want to raise the limit on shininess.

BTW: I don't get how that limiting function you use works. I would have expected something like clamp().

The selfshadow term is intended to black out specularity when the geometry is facing away from the light. Right now, there are two things that control the normals (and thus specular highlights): vertex normals, and the normalmap.

Normal mapping is supposed to model small features on the surface that change normal direction. They're not supposed to be big enough to shadow anything. Vertex normal captures big, gross geometry features. When vertex normals face away from the light, it's not supposed that normalmapped features will change the fact that they're in the shadowed side of the model. So, the term does that: when a pixel is in the shadowed side of a model (vertex normal pointing away from the light). no amount of normalmapping can create any lighting. So, selfshadow darkens everything. The function just does that smoothly, because doing it hardly (ie: (VNdotL > 0) ? 1 : 0 ) simply looks weird.

Perhaps it's too soft. Try making it harder, decreasing the 0.25.

BTW: Beware of misim(ex)ported vertex normals. Weird lighting tends to be the result of that.
Oíd mortales, el grito sagrado...
Call me "Menes, lord of Cats"
Wing Commander Universe
chuck_starchaser
Elite
Elite
Posts: 8014
Joined: Fri Sep 05, 2003 4:03 am
Location: Montreal
Contact:

Post by chuck_starchaser »

klauss wrote:Chuck, don't change the 1.7, it was a hand-picked fit for the mathematically correct function. Doing so would sacrifice the correctness of lower shininess values. Rather, I believe you want to raise the limit on shininess.
It looks perfect to me, at two; I can notice the difference in brightness more. On the other hand, my smooth limiting function is probably doing much of lowering that power.
BTW: I don't get how that limiting function you use works. I would have expected something like clamp().
Clamping is non-linear, and would look absolutely horrible. My function is a very smooth function yielding "lower of two numbers", namely "product over sum". The formula comes from electronics. If you have two resistors in parallel, total resistance is the product over the sum of their resistances. If the two are equal, you get half --e.g. 7x7 / (7+7) = 3.5; so when the input shininess is 30 the output shininess is 15.
The selfshadow term is intended to black out specularity when the geometry is facing away from the light. Right now, there are two things that control the normals (and thus specular highlights): vertex normals, and the normalmap.

Normal mapping is supposed to model small features on the surface that change normal direction. They're not supposed to be big enough to shadow anything. Vertex normal captures big, gross geometry features. When vertex normals face away from the light, it's not supposed that normalmapped features will change the fact that they're in the shadowed side of the model. So, the term does that: when a pixel is in the shadowed side of a model (vertex normal pointing away from the light). no amount of normalmapping can create any lighting. So, selfshadow darkens everything. The function just does that smoothly, because doing it hardly (ie: (VNdotL > 0) ? 1 : 0 ) simply looks weird.

Perhaps it's too soft. Try making it harder, decreasing the 0.25.
Ah, interesting.
Now I understand.
In the Tarsus I don't see any problems because my normalmap is only a faint randomization of surface normal. (The only place you can actually see it is on the front window, which is flat and high shininess; but only when using shininess from alpha.)

In any case, we're already computing NdotL... Doh! I see.., We do need NVdotL...
BTW: Beware of misim(ex)ported vertex normals. Weird lighting tends to be the result of that.
I've no idea what that is.

EDIT:
I'd rather make that smothstep go like

Code: Select all

float selfshadowStep(float VNdotL) { return smoothstep(-0.0625,0.0625,VNdotL); }
Phlogios
Confed Special Operative
Confed Special Operative
Posts: 298
Joined: Sun Jul 30, 2006 1:38 pm
Location: Sweden
Contact:

Post by Phlogios »

I once misimported the Tarsus' normals. Made it look like a gradient.
"Enjoy the Choice" - A very wise man from Ottawa.
chuck_starchaser
Elite
Elite
Posts: 8014
Joined: Fri Sep 05, 2003 4:03 am
Location: Montreal
Contact:

Post by chuck_starchaser »

I don't have enough jitter in the normalmapt to test this. I made the self shadow function go like,

Code: Select all

float selfshadowStep(float VNdotL) { return smoothstep(-0.0625,0.0625,VNdotL); }
and I see nothing wrong, but then again, I wouldn't.

What I do see wrong, and this is an old OGL problem, is transitions that are too fast between light and shadow. The problem of treating light sources as point sources. There must be a way of hacking the light equation to get something better than NdotL...
chuck_starchaser
Elite
Elite
Posts: 8014
Joined: Fri Sep 05, 2003 4:03 am
Location: Montreal
Contact:

Post by chuck_starchaser »

Updated code:

Code: Select all

uniform int light_enabled[gl_MaxLights];
uniform int max_light_enabled;
uniform sampler2D diffuseMap;
uniform sampler2D envMap;
uniform sampler2D specMap;
uniform sampler2D glowMap;
uniform sampler2D normalMap;
uniform sampler2D damageMap;
uniform sampler2D detail0Map;
uniform sampler2D detail1Map;
uniform vec4 cloaking;
uniform vec4 damage;
uniform vec4 envColor;

vec3 matmul(vec3 tangent, vec3 binormal, vec3 normal,vec3 lightVec) {
  return vec3(dot(lightVec,tangent),dot(lightVec,binormal),dot(lightVec,normal));
}
vec3 imatmul(vec3 tangent, vec3 binormal, vec3 normal,vec3 lightVec) {
  return lightVec.xxx*tangent+lightVec.yyy*binormal+lightVec.zzz*normal;
}

vec2 EnvMapGen(vec3 f) {
   float fzp1=f.z+1.0;
   float m=2.0*sqrt(f.x*f.x+f.y*f.y+(fzp1)*(fzp1));
   return vec2(f.x/m+.5,f.y/m+.5);
}

float bias(float f){ return f*0.5+0.5; }
vec2  bias(vec2 f) { return f*0.5+vec2(0.5); }
vec3  bias(vec3 f) { return f*0.5+vec3(0.5); }
vec4  bias(vec4 f) { return f*0.5+vec4(0.5); }

float expand(float f){ return f*2.0-1.0; }
vec2  expand(vec2 f) { return f*2.0-vec2(1.0); }
vec3  expand(vec3 f) { return f*2.0-vec3(1.0); }
vec4  expand(vec4 f) { return f*2.0-vec4(1.0); }

float lerp(float f, float a, float b){return (1.0-f)*a+f*b; }
vec2  lerp(float f, vec2 a, vec2 b) { return (1.0-f)*a+f*b; }
vec3  lerp(float f, vec3 a, vec3 b) { return (1.0-f)*a+f*b; }
vec4  lerp(float f, vec4 a, vec4 b) { return (1.0-f)*a+f*b; }

//float selfshadowStep(float VNdotL) { return step(0.0,VNdotL); } // fast but hard selfshadow function
float selfshadowStep(float VNdotL) { return smoothstep(-0.0625,0.125,VNdotL); } // costly but soft and nice selfshadow function
//float shininessMap(float shininess, vec4 specmap) { return clamp(specmap.a*shininess,1.0,255.0); } // alpha-based shininess modulation

float shininessMap(float shininess, vec4 specmap) // luma-based shininess modulation
{
  float3 temp = specmap.rgb;
  temp *= specmap.rgb;
  temp *= specmap.rgb;
  temp.b = (temp.r+temp.g)*0.75;
  return clamp( temp.b*shininess, 1.0, 255.0 );
}

float shininess2Lod(float shininess) { return max(0.0,7.0-log2(shininess+1.0))+3.0*(1.0+envColor.a); }

float limited_shininessMap(float shininess, float specmap)
{
  float limit = 50; //50^2 is 2500. 2500*0.001 = 2.5 --enough risk of saturation!
  float shine = shininessMap(shininess,specmap);
  return (shine*limit)/(shine+limit);
}

float shininess_to_brightness(float shininess)
{
  return 0.001 * shininess * shininess;
}

float lightspot_brightness( float shininess, float specmap )
{
  return limited_shininessMap( shininess, specmap ) * shininess_to_brightness( shininess );
}

void lightingLight(
   in vec3 light, in vec3 normal, in vec3 vnormal, in vec3 eye, in vec3 reflection, 
   in vec4 lightDiffuse, in float lightAtt, 
   in vec4 diffusemap, in vec4 specmap, in float shininess,
   in vec4 ambientProduct,
   inout vec3 diffuse, inout vec3 specular)
{
   float NdotL = dot(normal,light);
   float VNdotL= dot(vnormal,light);
   float RdotL = dot(reflection,light);
   float selfshadow = selfshadowStep(VNdotL);
   
   diffuse  += selfshadow * clamp(NdotL,0.0,1.0) * lightDiffuse.rgb * lightAtt ;
   specular += selfshadow * pow(clamp(RdotL,0.0,1.0), lightspot_brightness(shininess,specmap)) * lightDiffuse.rgb * lightAtt;
}

#define lighting(name, lightno_gl, lightno_tex) \
void name( \
   in vec3 normal, in vec3 vnormal, in vec3 eye, in  vec3 reflection, \
   in vec4 diffusemap, in vec4 specmap, \
   inout vec3 diffuse, inout vec3 specular) \
{ \
   lightingLight( \
      normalize(gl_TexCoord[lightno_tex].xyz), normal, vnormal, eye, reflection, \
      gl_FrontLightProduct[lightno_gl].diffuse, \
      gl_TexCoord[lightno_tex].w, \
      diffusemap, specmap, gl_FrontMaterial.shininess, \
      gl_FrontLightProduct[lightno_gl].ambient, \
      diffuse, specular); \
}

lighting(lighting0, 0, 5)
lighting(lighting1, 1, 6)

vec3 lightingClose(in vec3 diffuse, in vec3 specular, in vec3 ambient, in vec4 diffusemap, in vec4 specmap)
{
   return (diffuse + ambient) * diffusemap.rgb + specular * specmap.rgb;
}

vec3 envMapping(in vec3 reflection, in float gloss, in vec4 specmap)
{
   float envLod = shininess2Lod(gloss);//shininessMap(shininess,specmap));
   return texture2DLod(envMap, EnvMapGen(reflection), envLod).rgb * specmap.rgb * vec3(2.0);
}

void main() 
{
  // Retrieve normals
  vec3 iNormal=gl_TexCoord[1].xyz;
  vec3 iTangent=gl_TexCoord[2].xyz;
  vec3 iBinormal=gl_TexCoord[3].xyz;
  vec3 vnormal=iNormal;
  //vec3 normal=normalize(imatmul(iTangent,iBinormal,iNormal,expand(texture2D(normalMap,gl_TexCoord[0].xy).yxz)*vec3(-1.0,1.0,1.0)));
  vec3 normal=normalize(imatmul(iTangent,iBinormal,iNormal,expand(texture2D(normalMap,gl_TexCoord[0].xy).wyz)));
  
  // Other vectors
  vec3 eye = gl_TexCoord[4].xyz;
  vec3 reflection = -reflect(eye,normal);
  
  // Init lighting accumulators
  vec3 diffuse = vec3(0.0);
  vec3 specular= vec3(0.0);
  vec3 ambient = vec3(0.0);
  
  // Sample textures
  vec4 damagecolor = texture2D(damageMap , gl_TexCoord[0].xy);
  vec4 diffusecolor= texture2D(diffuseMap, gl_TexCoord[0].xy);
  vec4 speccolor   = texture2D(specMap   , gl_TexCoord[0].xy);
  vec4 glowcolor   = texture2D(glowMap   , gl_TexCoord[0].xy);
  //sanity enforcement:
  float temp = 1.0 - max( diffusecolor.r, max( diffusecolor.g, diffusecolor.b ) );
  speccolor.r = min( speccolor.r, temp );
  speccolor.g = min( speccolor.g, temp );
  speccolor.b = min( speccolor.b, temp );
  
  vec4 diffusemap  = lerp(damage.x, diffusecolor, damagecolor);
  vec4 specmap     = speccolor;
  float specdamage = clamp(1.0 - dot(damagecolor.xyz,vec3(1.0/3.0)) * damage.x * 2.0, 0.0, 1.0);
  specmap.rgb     *= specdamage;
  specmap.a       *= bias(specdamage);
  float gloss      = shininessMap(gl_FrontMaterial.shininess,specmap);
  
  // Do lighting for every active light
  if (light_enabled[0] != 0)
     lighting0(normal, vnormal, eye, reflection, diffusemap, specmap, diffuse, specular);
  if (light_enabled[1] != 0)
     lighting1(normal, vnormal, eye, reflection, diffusemap, specmap, diffuse, specular);

  vec4 result;
  result.rgb  = lightingClose(diffuse, specular, ambient, diffusemap, specmap)
              + glowcolor.rgb
              + envMapping(reflection,gloss,specmap);
  result.a = diffusemap.a;
  result *= cloaking.rrrg;
  
  gl_FragColor = result;
}
Reinstated selfshadow stuff.
Changed selfshadowing step to go from -0.0625 to +0.125 VNdotL
Changed shininess limit from 30 to 50, and multiplier for shininess_to_brightness to 0.001.
klauss
Elite
Elite
Posts: 7243
Joined: Mon Apr 18, 2005 2:40 pm
Location: LS87, Buenos Aires, República Argentina

Post by klauss »

Are you sure you want negatives on the self-shadowing step? I've played a fair bit with that, and the first things I tried were centered on 0 like you did, and they never came out right.
But it's trial and error I guess... if you get it right, so much the better.
Oíd mortales, el grito sagrado...
Call me "Menes, lord of Cats"
Wing Commander Universe
chuck_starchaser
Elite
Elite
Posts: 8014
Joined: Fri Sep 05, 2003 4:03 am
Location: Montreal
Contact:

Post by chuck_starchaser »

Well, like I said, I don't have a proper bumpmap to try this with.
I was thinking, the sun has a diameter of 0.5 degrees in the sky, from Earth.
Considering atmospheric scattering, we could push it up to one degree.
I know there's no scattering in space, but to me the important thing is whether
things shade the way we're ***used to*** seeing them shade.
With the NdotL, a flat surface turning from shadow into light makes a sudden
jump. How hard would it be to compute a modified NdotL that gives a smoother
transition?
Suppose we did something like

Code: Select all

float NdotL = dot( N * L );
float temp = 1 - NdotL*NdotL
//temp is now a sine wave twice the frequency of NdotL, shifted positive
temp = temp * temp;
temp = temp * temp;
temp = temp * temp;
//now temp shows a narrower pulse at each zero-crossing of NdotL
temp *= NdotL;
//This caused the pulses to be turned into wavelets with zero-crossings
//in phase with NdotL
float smootherNdotL = NdotL - (K * temp);
//With the right value for K, we got zero-cross distortions on NdotL that
//make its zero-crossings shallower.
Here's some pics.
NdotL is a cosine wave from the perspective of angle to the normal:

Image

1 minus the square of it:

Image

square that again:

Image

square that again

Image

Multiplying by NdotL now:

Image

And we subtract the result from NdotL:

Image

Now we can shift this function a tiny bit up, to get half a degree extra around cylinders for diffuse light; and shift it a little bit down for bump shadowing.

By the way, another way to do the self-shadowing would be,
One minus...

Image

...already has a bit of a squarish shape. Mul that by 100 and then limit it by computing it / ( it + 1 )
Not sure if this is cheaper than smooth step; but probably.
chuck_starchaser
Elite
Elite
Posts: 8014
Joined: Fri Sep 05, 2003 4:03 am
Location: Montreal
Contact:

Post by chuck_starchaser »

Correction on the previous post: The last part needs to be based on VNdotL, of course. Just a momentary inattention.

I've implemented the "softNdotL" algorithm above, with good results:

Image

Notice the soft penumbras.
More importantly, but I cannot show it here without making a movie out of it, is that, as I turn the Tarsus around,
flat faces that go from darkness to light don't "jump" so suddenly into light.

The only problem is the specular light-spots seem to be in the wrong places.
I'm not sure if this is something new. I don't believe I touched them... :-/

Here's the modified ligthingLight():

Code: Select all

void lightingLight(
   in vec3 light, in vec3 normal, in vec3 vnormal, in vec3 eye, in vec3 reflection, 
   in vec4 lightDiffuse, in float lightAtt, 
   in vec4 diffusemap, in vec4 specmap, in float shininess,
   in vec4 ambientProduct,
   inout vec3 diffuse, inout vec3 specular)
{
   float NdotL = dot(normal,light);
   float VNdotL= dot(vnormal,light);
   float s = 1.0 - (NdotL*NdotL);
   float RdotL = dot(reflection,light);
   float selfshadow = selfshadowStep(VNdotL);
   float temp = clamp( NdotL - (0.94 * s * s * s * s * NdotL) + 0.005, 0.0, 1.01 );
   specular += ( selfshadow * pow(clamp(RdotL,0.0,1.0), lightspot_brightness(shininess,specmap)) * lightDiffuse.rgb * lightAtt );
   diffuse  += ( selfshadow * temp * lightDiffuse.rgb * lightAtt );
}
EDIT:
Problem with spotlights went away; it was just messed up smooth groups in the refinery mesh.
Here's softNdotL at work on Perry (which also has messed up smooth groups, but not as bad):

Image

Image

Attention: There's no normalmap on Perry; that's all just plain texture work with bump shadows burnt into the diffuse.

EDIT2:
Klauss, what's happening with the Tangents? We really... ***really*** need them.
I can come up with some improvements to that code, by the way, like making wider angle triangles weight more
on the determination of the tangent; but I'd rather wait to hear that the basic code is working, first.
The Llama will get an instant improvement, as parts of its UV map are rotated relative to the main island, and
therefore normalmap incorrectly.
Plus, I have a "Legacy Mixer" blender noodle that can already take diffuse, spec, glow and ambient occlusion bake as
input, and produce ao-modulated diffuse and spec, glow map with baked ambient light, and shininess in spec alpha.
I can easily add a few more nodes to make a normalmap based on luma, and then make it available to Vegastrike.
But with the unwraps following no orientation rules, as they are, the normalmap can only work if we have TANGENTS...
chuck_starchaser
Elite
Elite
Posts: 8014
Joined: Fri Sep 05, 2003 4:03 am
Location: Montreal
Contact:

Post by chuck_starchaser »

Klauss, here's a MUCH cheaper way to compute bump self-shadow (and smoother, and more correct, to boot):

Code: Select all

void lightingLight(
   in vec3 light, in vec3 normal, in vec3 vnormal, in vec3 eye, in vec3 reflection,
   in vec4 lightDiffuse, in float lightAtt,
   in vec4 diffusemap, in vec4 specmap, in float shininess,
   in vec4 ambientProduct,
   inout vec3 diffuse, inout vec3 specular)
{
   float VNdotLx4= clamp( 4.0 * dot(vnormal,light), 0.0, 1.0 ); // <-***** modified
   float NdotL = clamp( dot(normal,light), -1.0, VNdotLx4 ); // <-***** modified
   float RdotL = clamp( dot(reflection,light), 0.0, VNdotLx4 ); // <-***** modified
   float s = 1.0 - (NdotL*NdotL); //soft penumbra stuff
   //float selfshadow = selfshadowStep(VNdotL); // <-***** removed
   float temp = clamp( NdotL - (0.94 * s * s * s * s * NdotL) + 0.005, 0.0, 1.01 ); //soft penumbra stuff
   specular += ( pow( RdotL, lightspot_brightness(shininess,specmap)) * lightDiffuse.rgb * lightAtt ); // <-***** modified
   diffuse  += ( temp * lightDiffuse.rgb * lightAtt );
}
IOW, I'm just preventing NdotL from ever being more than 4 * VNdotL.
Guess what NdotL has to be when VNdotL is zero...
Haven't tried it yet, hold on...
(The price for this is TANGENTS...) ;-)
Last edited by chuck_starchaser on Tue Apr 08, 2008 9:08 pm, edited 1 time in total.
chuck_starchaser
Elite
Elite
Posts: 8014
Joined: Fri Sep 05, 2003 4:03 am
Location: Montreal
Contact:

Post by chuck_starchaser »

Heh... Worked like a charm first time; no debugging:

Image

Image

Image

Image

Updated shader:

Code: Select all

uniform int light_enabled[gl_MaxLights];
uniform int max_light_enabled;
uniform sampler2D diffuseMap;
uniform sampler2D envMap;
uniform sampler2D specMap;
uniform sampler2D glowMap;
uniform sampler2D normalMap;
uniform sampler2D damageMap;
uniform sampler2D detail0Map;
uniform sampler2D detail1Map;
uniform vec4 cloaking;
uniform vec4 damage;
uniform vec4 envColor;

vec3 matmul(vec3 tangent, vec3 binormal, vec3 normal,vec3 lightVec) {
  return vec3(dot(lightVec,tangent),dot(lightVec,binormal),dot(lightVec,normal));
}
vec3 imatmul(vec3 tangent, vec3 binormal, vec3 normal,vec3 lightVec) {
  return lightVec.xxx*tangent+lightVec.yyy*binormal+lightVec.zzz*normal;
}

vec2 EnvMapGen(vec3 f) {
   float fzp1=f.z+1.0;
   float m=2.0*sqrt(f.x*f.x+f.y*f.y+(fzp1)*(fzp1));
   return vec2(f.x/m+.5,f.y/m+.5);
}

float bias(float f){ return f*0.5+0.5; }
vec2  bias(vec2 f) { return f*0.5+vec2(0.5); }
vec3  bias(vec3 f) { return f*0.5+vec3(0.5); }
vec4  bias(vec4 f) { return f*0.5+vec4(0.5); }

float expand(float f){ return f*2.0-1.0; }
vec2  expand(vec2 f) { return f*2.0-vec2(1.0); }
vec3  expand(vec3 f) { return f*2.0-vec3(1.0); }
vec4  expand(vec4 f) { return f*2.0-vec4(1.0); }

float lerp(float f, float a, float b){return (1.0-f)*a+f*b; }
vec2  lerp(float f, vec2 a, vec2 b) { return (1.0-f)*a+f*b; }
vec3  lerp(float f, vec3 a, vec3 b) { return (1.0-f)*a+f*b; }
vec4  lerp(float f, vec4 a, vec4 b) { return (1.0-f)*a+f*b; }

//float shininessMap(float shininess, vec4 specmap) { return clamp(specmap.a*shininess,1.0,255.0); } // alpha-based shininess modulation

float shininessMap(float shininess, vec4 specmap) // luma-based shininess modulation
{
  float3 temp = specmap.rgb;
  temp *= specmap.rgb;
  temp *= specmap.rgb;
  temp.b = (temp.r+temp.g)*0.75;
  return clamp( temp.b*shininess, 1.0, 255.0 );
}

float shininess2Lod(float shininess) { return max(0.0,7.0-log2(shininess+1.0))+3.0*(1.0+envColor.a); }

float limited_shininessMap(float shininess, float specmap)
{
  float limit = 50; //50^2 is 2500. 2500*0.001 = 2.5 --enough risk of saturation!
  float shine = shininessMap(shininess,specmap);
  return (shine*limit)/(shine+limit);
}

float shininess_to_brightness(float shininess)
{
  return 0.001 * shininess * shininess;
}

float lightspot_brightness( float shininess, float specmap )
{
  return limited_shininessMap( shininess, specmap ) * shininess_to_brightness( shininess );
}

void lightingLight(
   in vec3 light, in vec3 normal, in vec3 vnormal, in vec3 eye, in vec3 reflection, 
   in vec4 lightDiffuse, in float lightAtt, 
   in vec4 diffusemap, in vec4 specmap, in float shininess,
   in vec4 ambientProduct,
   inout vec3 diffuse, inout vec3 specular)
{
   float VNdotLx4= clamp( 4.0 * dot(vnormal,light), 0.0, 1.0 ); // <-***** modified
   float NdotL = clamp( dot(normal,light), -1.0, VNdotLx4 ); // <-***** modified
   float RdotL = clamp( dot(reflection,light), 0.0, VNdotLx4 ); // <-***** modified
   float s = 1.0 - (NdotL*NdotL); //soft penumbra stuff
   //float selfshadow = selfshadowStep(VNdotL); // <-***** removed
   float temp = clamp( NdotL - (0.94 * s * s * s * s * NdotL) + 0.005, 0.0, 1.01 ); //soft penumbra stuff
   specular += ( pow( RdotL, lightspot_brightness(shininess,specmap)) * lightDiffuse.rgb * lightAtt ); // <-***** modified
   diffuse  += ( temp * lightDiffuse.rgb * lightAtt );
}

#define lighting(name, lightno_gl, lightno_tex) \
void name( \
   in vec3 normal, in vec3 vnormal, in vec3 eye, in  vec3 reflection, \
   in vec4 diffusemap, in vec4 specmap, \
   inout vec3 diffuse, inout vec3 specular) \
{ \
   lightingLight( \
      normalize(gl_TexCoord[lightno_tex].xyz), normal, vnormal, eye, reflection, \
      gl_FrontLightProduct[lightno_gl].diffuse, \
      gl_TexCoord[lightno_tex].w, \
      diffusemap, specmap, gl_FrontMaterial.shininess, \
      gl_FrontLightProduct[lightno_gl].ambient, \
      diffuse, specular); \
}

lighting(lighting0, 0, 5)
lighting(lighting1, 1, 6)

vec3 lightingClose(in vec3 diffuse, in vec3 specular, in vec3 ambient, in vec4 diffusemap, in vec4 specmap)
{
   return (diffuse + ambient) * diffusemap.rgb + specular * specmap.rgb;
}

vec3 envMapping(in vec3 reflection, in float gloss, in vec4 specmap)
{
   float envLod = shininess2Lod(gloss);//shininessMap(shininess,specmap));
   return texture2DLod(envMap, EnvMapGen(reflection), envLod).rgb * specmap.rgb * vec3(2.0);
}

void main() 
{
  // Retrieve normals
  vec3 iNormal=gl_TexCoord[1].xyz;
  vec3 iTangent=gl_TexCoord[2].xyz;
  vec3 iBinormal=gl_TexCoord[3].xyz;
  vec3 vnormal=iNormal;
  //vec3 normal=normalize(imatmul(iTangent,iBinormal,iNormal,expand(texture2D(normalMap,gl_TexCoord[0].xy).yxz)*vec3(-1.0,1.0,1.0)));
  vec3 normal=normalize(imatmul(iTangent,iBinormal,iNormal,expand(texture2D(normalMap,gl_TexCoord[0].xy).wyz)));
  
  // Other vectors
  vec3 eye = gl_TexCoord[4].xyz;
  vec3 reflection = -reflect(eye,normal);
  
  // Init lighting accumulators
  vec3 diffuse = vec3(0.0);
  vec3 specular= vec3(0.0);
  vec3 ambient = vec3(0.0);
  
  // Sample textures
  vec4 damagecolor = texture2D(damageMap , gl_TexCoord[0].xy);
  vec4 diffusecolor= texture2D(diffuseMap, gl_TexCoord[0].xy);
  vec4 speccolor   = texture2D(specMap   , gl_TexCoord[0].xy);
  vec4 glowcolor   = texture2D(glowMap   , gl_TexCoord[0].xy);
  //sanity enforcement:
  float temp = 1.0 - max( diffusecolor.r, max( diffusecolor.g, diffusecolor.b ) );
  speccolor.r = min( speccolor.r, temp );
  speccolor.g = min( speccolor.g, temp );
  speccolor.b = min( speccolor.b, temp );
  
  vec4 diffusemap  = lerp(damage.x, diffusecolor, damagecolor);
  vec4 specmap     = speccolor;
  float specdamage = clamp(1.0 - dot(damagecolor.xyz,vec3(1.0/3.0)) * damage.x * 2.0, 0.0, 1.0);
  specmap.rgb     *= specdamage;
  specmap.a       *= bias(specdamage);
  float gloss      = shininessMap(gl_FrontMaterial.shininess,specmap);
  
  // Do lighting for every active light
  if (light_enabled[0] != 0)
     lighting0(normal, vnormal, eye, reflection, diffusemap, specmap, diffuse, specular);
  if (light_enabled[1] != 0)
     lighting1(normal, vnormal, eye, reflection, diffusemap, specmap, diffuse, specular);

  vec4 result;
  result.rgb  = lightingClose(diffuse, specular, ambient, diffusemap, specmap)
              + glowcolor.rgb
              + envMapping(reflection,gloss,specmap);
  result.a = diffusemap.a;
  result *= cloaking.rrrg;
  gl_FragColor = result;
}
More natural bump self-shadowing, and it should take a lot less shader instructions.
Post Reply