I even experimented with cubic root...
...but no matter, any roots or powers I use end up producing strange artifacts, like that darker polygon you see above, on the far side of the bubble's reflections. I've no idea what causes them, but they seem to go away when I don't use any special roots or powers, so, I'm going to leave the final alpha at plain vanilla fresnel_alpha, for now.
The glass looks dark enough to me, though, ever since I multiplied glow by alpha, anyways:
It's hard for me to concentrate on something as subtle as glass thickness effects when so much stuff looks horrible:
1) The polygonality of the bubble... --it needs a level of subdivision or two, really bad...
2) The complete lack of ambient occlusion: Those "buildings" should be painted with a darkening gradient towards the bottom, to fake the shadows of the ring ledge and other buildings.
3) Reflections of sky where the bubble should be reflecting structure... I'm going to make a specular texture for bubbles that darkens towards the rim, so one doesn't get so much reflection where, chances are, they should be reflecting darker, nearby stuff, rather than sky.
4) MOST IMPORTANTLY: The fact that the far side of the bubbles (the inside surface) is reflecting sky (from a direction below the picture), through the goddam station. Klauss, is there a way to stop that?
Here's the 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 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 vec4 diffusemap, in vec4 specmap)
{
return (diffuse*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);
// 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 );
//Fresnel:
float alpha = diffusecolor.a * gl_FrontMaterial.diffuse.a;
float fresnel_alpha = 1.0 - clamp( dot( eye, normal ), 0.0, 1.0 );
alpha *= alpha;
fresnel_alpha *= fresnel_alpha;
alpha *= alpha;
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);
fresnel_alpha = clamp( 0.0625 + ( 0.9375 * fresnel_alpha ), alpha, 1.0 );
// 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;
specular *= fresnel_alpha;
result.a = fresnel_alpha;
result.rgb = lightingClose(diffuse, specular, diffusemap, specmap)
+ (alpha*glowcolor.rgb)
+ (envMapping(reflection,gloss,specmap)*fresnel_alpha);
result *= cloaking.rrrg;
gl_FragColor = result;
}
Note: The multiplication of glowcolor by alpha is only there temporarily, until I find the way to get rid of the bubbles' glow. I just don't know where it comes from. The xmesh has emissive at zero, and there's no glow texture specified... :-/
Klauss, a second (third?) question for you: For the sake of Pyramid's work, we need to enable Fresnel for planets. We can enable it for the whole planet, since only water and ice have much specularity, anyways. To do this, we need to give the planet material's diffuse color a low value for alpha. This should be okay because their blending mode ought to be ONE ZERO, so alpha is ignored; but just so that my shader believes it's glass and applies fresnel. The question is ***where*** do we do this?
EDIT:
And yet a third (fourth?) question: How do I get a detail texture into the shader?
Let me make that very simple:
1) I ABSOLUTELY DON'T WANT the kind of detail texture that woud add to the burden on artists. I just want a special perlin noise that I'm about to start working on a special Blender noodle to produce it. Please, let's not argue about this anymore. Believe me: You're not going to get a single artist to actually produce a custom detail texture. Ever. Not even me. All I want is a bit of noise to I don't notice the pixels when I get close to something. The only purpose of it will be to mask DDS quantization and extend the range of perceived detail, globally, for all units and for all materials. So, I envision this as a 256x256 PNG in the main /textures/ folder.
2 ) We won't need any kind of scaling controls: All we need is an automatic scaling that will map it to to 16 x 16 pixels. So, in a 1024 x 2048 texture, for instance, it should tile 64 x 128 times. Why? See the Long Story further down.
3) We won't need any kind of blending and distances controls. Why? Because as the distance increases the the mipmap level of the detail texture increases; and by the fifth level it will be a solid color already.
And now,
The Long Story:
There's two things a detail texture needs to balance: Making its tiling repetitiveness unnoticeable, and providing higher frequencies; while at the same time keeping its size manageable.
The detail texture needs to add frequencies starting from twice the pixel frequency of the main texture, at the highest.
I say at the highest, because quantization noise is not only visible at the pixel frequency but, in lesser amounts, at all its sub-harmonics, like half the pixel frequency, one third, etc.
So, for a detail texture to be really good, it should add a bit of noise at 0.75 of the pixel frequency of the main texture.
My plan is to produce 4 perlin noise textures, with relative ratios of 1, 1.7, 2.9 and 5 (going by cubic root of 5 ratios). These would be average perlin frequencies, each with its own harmonics. I'll assign these four perlins to the R, G, B and A channels. The lowest, red frequency, will map to 0.76 of the main texture's pixel frequency. The green perlin will map to 1.3 times the main texture's pixel frequency, and so on... to alpha channel's perlin frequency mapping to 3.8 times main texture pixel frequency. The amplitudes of the four channel perlins will be proportional to the square roots of their frequencies, except that the green channel needs only half as much noise as red and blue, and the alpha channel 1/8th as much, thus 1, 0.65, 1.7 and 0.55 respectively, --and relatively: gain will be maxed out overall, to make best use of the 8-bits per channel, and then brought down in intensity in the shader such that the red channel amplitude is about 0.7 of a dds red channel amplitude step, --subject to experimentation.
Now, the rgb channels will modulate the main diffuse texture additively, and the main specular texture subtractively, so as to minimize the overall visibility of the detail texture, for a given amount of modulation. The alpha channel will modulate shininess subtractively.
These modulations are to the incoming texture colors, of course; prior to lighting.
Now, the question of absolute frequencies:
Like I said, I envision this detail texture as a PNG (NOT a DDS), 256 x 256, always sitting in video ram and used globally. In every case, it maps to 16 x 16 texels of the textures it modulates. Therefore, the pixel frequencies for the rgba channels in the detail texture would be 16 divided by 0.76, 1.3, 2.2 and 3.8; --namely 21, 12.3, 7.3 and 4.2 texels typical.
Even in the case of red, at 21 texels frequency, 256/21 = 12.2 hills over its width, so I don't think the repetitiveness will be noticeable (specially at the low level of modulation we're talking about. But to make sure there are no frequencies lower, or even as low as half of the main texture's 2-texel frequency, I indend for the Blender noodle to subtract a 24-texel radius blur of the result from the result, while adding 50% gray, to make sure the color stays generally flat across the the detail texture.
But so, this is why the detail texture has to map to a fixed, 16x16 tile of
any texture it modulates.