Small change to my proposed shader. The glow map should have a gamma of 0.5. Reason being, in a glow map there's only a few spots where light is maxed out... the lights. The rest is pretty dark; --just the radiosity baking from those very lights. Here's an example of a glow map, and it includes ambient light baking (which my proposed shader eliminates the need for):
But dark also implies inefficient use of the already scant bits per channel.
Gamma of 0.5 is equivalent to taking the square root of the per-channel intensity of each pixel, in normalized 0-1 range. So, full brightness being 1, square root is 1. Half brightness, 0.5, square root is 0.7172. So the texture is brightened in a non-linear way, better utilizing the numerical range. This is done off line (by the LaGrande Noodle) and then the dds is generated.
Once the shader gets an interpolated color for the glow map, for a given pixel, in floating point resolution, it merely needs to square it, to de-gamma. To square the values of rgb, it only needs to dot the incoming rgb vector by itself.
But in my texture packing, the alpha channel of the glow map has the ambient occlusion, and we need to square the ambient occlusion for modulating the specularity, anyways, so, two birds of a shot
Here's the updated draft:
Code: Select all
//invariants:
float3 computed_ambient_color; //(see Note 1a)
float3 xmesh_diffuse_factor;
float3 xmesh_specular_factor;
float3 system_background_factor; //(see Note 1b)
float3 light; //color of the star //(see Note 1c)
float xmesh_min_alpha;
//variants:
float3 L; //Light vector
float3 V; //View vector
float3 N; //Normal vector
float3 T; //Tangent vector
float damage;
float cloak;
//and the texture interpolants...
main()
{
float4 temp;
float3 diffuse_mtl_color;
float3 specular_mtl_color;
float3 emissive_mtl_color;
float3 albedo_mtl_color;
float3 mtl_normal; //mtl_normal.x = du; mtl_normal.y = dv; mtl_normal.z = z
float3 normal;
float3 R; //Reflected_vector.
float mtl_alpha;
float mtl_gloss;
float mtl_ambient_exposure; //the ao baking value
float ambient_diffuse_factor;
float ambient_specular_factor;
float ambient_contribution;
float diffuse_contribution;
float specular_contribution;
float tempf;
float NdotL;
temp = normal_map_read();
mtl_normal.x = 0.3333 * (temp.r+temp.g+temp.b) - 0.5;
mtl_normal.y = temp.a - 0.5;
mtl_normal.z = 0.25; //(see Note 2)
mtl_normal.normalize();
/***************start*of*changes***************/
//vectors and dots (moved up here now)
normal = wiggle(N by mtl_normal);
LdotN = dot(L,normal);
VdotN = dot(V,normal);
R = 2.0 * VdotN * N - V;
LdotR = dot(L,R);
//Glow texture and ambient factors (see Note 4) (also moved up)
temp = glow_texture_read();
mtl_ambient_exposure = temp.a; //"ao bake"
tempf = max( mtl_ambient_exposure, VdotN );
temp.a = tempf;
ambient_diffuse_factor = sqrt( mtl_ambient_exposure );
temp = dot( temp, temp ); //de-gamma
emissive_mtl_color = temp.rgb;
ambient_specular_factor = temp.a; //the square of the ao
/*****************end*of*changes***************/
temp = diffuse_texture_read();
diffuse_mtl_color = temp.rgb * xmesh_diffuse_factor;
mtl_alpha = max( temp.a, xmesh_min_alpha ); //.a comes from 1 bit alpha
temp = specular_texture_read();
specular_mtl_color = temp.rgb * xmesh_specular_factor;
/***************start*of*changes*2*************/
/* shininess (gloss) should also be encoded with gamma = 0.5 */
mtl_gloss = temp.a * temp.a * 256.0; //changed!
/*****************end*of*changes*2*************/
temp = damage_texture_read();
damage_mtl_color = temp.rgb * xmesh_damage_factor;
mtl_is_dielectric = temp.rgb; //.a comes from 1 bit alpha
tempf = 1.0 - damage;
diffuse_mtl_color *= tempf;
specular_mtl_color *= tempf;
emissive_mtl_color *= tempf;
mtl_gloss *= tempf;
diffuse_mtl_color += (damage * damage_mtl_color);
temp = detail_texture_read();
diffuse_mtl_color *= temp.rgb; //(see Note 3)
mtl_gloss *= temp.a;
albedo_mtl_color = diffuse_mtl_color + specular_mtl_color;
ambient_contribution = albedo_mtl_color * mtl_ambient_exposure * ambient_light_color;
ambient_contribution += emissive_mtl_color;
diffuse_mtl_color *= ambient_diffuse_factor;
specular_mtl_color *= ambient_specular_factor;
//Fresnel (see Note 5):
tempf = 1.0 - VdotN; //or, 1 - cosine_of_view_to_normal_angle;
tempf *= tempf;
fresnel_reflection_factor = 0.0625 + 0.9375 * tempf;
fresnel_reflection_factor *= mtl_is_dielectric; //antialiased boolean ;-)
fresnel_transparency_factor = 1.0 - fresnel_reflection_factor;
diffuse_mtl_color *= fresnel_transparency_factor; //(see Note 6)
mtl_alpha *= fresnel_transparency_factor; //(see Note 7)
specular_mtl_color *= fresnel_transparency_factor; //(see Note 8)
specular_mtl_color += float3(fresnel_factor,fresnel_factor,fresnel_factor);//(see Note 9)
diffuse_contribution = diffuse_mtl_color * light * max(LdotN, 0);
tempf = max( LdotR, 0 );
speculating_light = light * pow( tempf, gloss );
mipmap_level = max( 0.0, 7.0 - log2( gloss + 1.0 ) );
speculating_light += textureCubeLod( envMap, R, mipmap_level ).rgb * system_background_factor;
specular_contribution = specular_mtl_color * speculating_light;
temp = float3( ambient_contribution + diffuse_contribution + specular_contribution );
temp.a = cloak * mtl_alpha;
color = temp;
}
Probably the code will need to be rearranged to optimize it to separate dependencies, but for now I'm trying to keep it in an easier to understand, logical flow.
EDIT:
Made another change: Shininess ("gloss") should also be encoded with gamma = 0.5, as a change of gloss between 1 and 2 is a lot more
significant than a change between 250 and 251.
See "/***************start*of*changes*2*************/".
EDIT 2:
Klauss, for PRTP and PRTN, I would suggest we can make them half the resolution as the diffuse, specular, etc.; but WITH an alpha channel, and that we use the alpha channel for luma and the rgb for chroma. IOW, we scale rgb to make sure that r^2 + g^2 + b^2 = 1, and put the correction factor in the alpha channel.
White would then be 0.577, 0.577, 0.577, 1.0 (0.577 being sqrt(1/3)). Thus, 1.0 in alpha represents 1.732 (sqrt(3)).
Actually, it's a little more complicated than that, since allowing the alpha channel to go so low as to use less bits than rgb would defeat the purpose; and Blender nodes don't have like "if" statements, only math ops...
So we'd need a continuous function that yields alpha ~= sqrt( 1/3 ) * ( r^2+g^2+b^2) when the latter term is high, but that falls no lower than about 0.2 or something. I'll figure it out.
Heck, maybe just luma = sqrt((r^2+g^2+b^2)/3); then scale r, b and g by sqrt(1/3)/luma.
I can write a Blender noodle to do this encoding with my eyes closed, by now...
In fact, we can do the prt bakings at 4 or 8 times the resolution and then have LaGrande scale them down --at floating point precision per channel--, and also perhaps scale the rgb numbers it gets for prtn+prtp for a given texel so they add to the same number we got for the ao bake (assuming the ao bake is higher quality), then do this chroma/luma encoding calculation; and only then, finally, quantize back down to 8-bits per channel to save a png.