I made a PBR shader, but it looked not good, so I tried to mix the PBR and the Blinn-Phong lighting, awesome ! it unexpectedly looked great on Windows, but it lost Blinn-Phong lighting effect on Android. After my test, the calculated normal (TBN * var_normal) in PBR is okay, but in Blinn-Phong is bad, if I substituted var_normal for the calculated normal in Blinn-Phong, then looked normal, but it lost bump details.
On Windows :
On Android :
substituted var_normal for the calculated normal in Blinn-Phong on Android :
The custom standard.fp :
#extension GL_OES_standard_derivatives : enable
varying highp vec4 var_frag_pos;
varying highp vec3 var_normal;
varying highp vec2 var_texcoord0;
varying highp vec3 var_spherical_harmonic_lighting;
uniform highp vec4 view_pos;
uniform highp vec4 roughness;
uniform highp vec4 directional_light_dir;
uniform highp vec4 directional_light_ambient;
uniform highp vec4 directional_light_diffuse;
uniform highp vec4 directional_light_specular;
uniform highp vec4 point_light_pos;
uniform highp vec4 point_light_ambient;
uniform highp vec4 point_light_diffuse;
uniform highp vec4 point_light_specular;
uniform highp vec4 spot_light_pos;
uniform highp vec4 spot_light_ambient;
uniform highp vec4 spot_light_diffuse;
uniform highp vec4 spot_light_specular;
uniform highp vec4 spot_light_inout_cut_off; //x is cut off, y is outter cut off, z and w are unused.
uniform highp vec4 light_attenuation_params; //x is constant, y is linear, z is quadratic, w is unused.
uniform highp vec4 tint;
uniform highp sampler2D diffuse_map;
uniform highp sampler2D metallic_map;
uniform highp sampler2D normal_map;
uniform highp sampler2D ao_map;
const float exposure = 1.0;
const float PI = 3.14159265359;
vec3 get_normal_from_map()
{
vec3 tangent_normal = texture2D(normal_map, var_texcoord0).xyz * 2.0 - 1.0;
vec3 Q1 = dFdx(vec3(var_frag_pos));
vec3 Q2 = dFdy(vec3(var_frag_pos));
vec2 st1 = dFdx(vec2(var_texcoord0));
vec2 st2 = dFdy(vec2(var_texcoord0));
vec3 N = normalize(var_normal);
vec3 T = normalize(Q1*st2.t - Q2*st1.t);
vec3 B = -normalize(cross(N, T));
mat3 TBN = mat3(T, B, N);
return normalize(TBN * tangent_normal);
}
float distribution_GGX(vec3 N, vec3 H, float roughness)
{
float a = roughness * roughness;
float a2 = a * a;
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH * NdotH;
float nom = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return nom / denom;
}
float geometry_schlick_GGX(float NdotV, float roughness)
{
float r = (roughness + 1.0);
float k = (r * r) / 8.0;
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
float geometry_smith(vec3 N, vec3 V, vec3 L, float roughness)
{
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float ggx2 = geometry_schlick_GGX(NdotV, roughness);
float ggx1 = geometry_schlick_GGX(NdotL, roughness);
return ggx1 * ggx2;
}
vec3 fresnel_schlick(float cosTheta, vec3 F0)
{
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
vec4 Albedo(vec2 uv)
{
vec2 resolution = vec2(2048, 2048);
vec4 color1 = texture2D(diffuse_map, uv + (vec2(-1.0,-1.0)/resolution.xy), -1.0);
vec4 color2 = texture2D(diffuse_map, uv + (vec2(1.0,-1.0)/resolution.xy), -1.0);
vec4 color3 = texture2D(diffuse_map, uv + (vec2(-1.0,1.0)/resolution.xy), -1.0);
vec4 color4 = texture2D(diffuse_map, uv + (vec2(1.0,1.0)/resolution.xy), -1.0);
vec4 color5 = texture2D(diffuse_map, uv + (vec2(-1.0,0.0)/resolution.xy), -1.0);
vec4 color6 = texture2D(diffuse_map, uv + (vec2(1.0,0.0)/resolution.xy), -1.0);
vec4 color7 = texture2D(diffuse_map, uv + (vec2(0.0,-1.0)/resolution.xy), -1.0);
vec4 color8 = texture2D(diffuse_map, uv + (vec2(0.0,1.0)/resolution.xy), -1.0);
vec4 color = (color1 + color2 + color3 + color4 + color5 + color6 + color7 + color8) / 8.0;
return color;
}
vec3 calculate_directional_light(vec3 dir, vec3 normal, vec3 view_dir, vec3 frag_color)
{
vec3 light_dir = normalize(-dir);
// diffuse shading
float diff = max(dot(normal, light_dir), 0.0);
// specular shading
vec3 halfway_dir = normalize(light_dir + view_dir);
float spec = pow(max(dot(normal, halfway_dir), 0.0), 32.0);
// combine results
vec3 ambient = vec3(directional_light_ambient) * frag_color;
vec3 diffuse = vec3(directional_light_diffuse) * diff * frag_color * var_spherical_harmonic_lighting;
vec3 specular = vec3(directional_light_specular) * spec * frag_color;
return (ambient + diffuse + specular);
}
vec3 calculate_point_light(vec3 light_pos, vec3 normal, vec3 frag_pos, vec3 view_dir, vec3 frag_color)
{
vec3 light_dir = normalize(light_pos - frag_pos);
// diffuse shading
float diff = max(dot(normal, light_dir), 0.0);
// specular shading
vec3 halfway_dir = normalize(light_dir + view_dir);
float spec = pow(max(dot(normal, halfway_dir), 0.0), 32.0);
// attenuation
float distance = length(light_pos - frag_pos);
float constant = light_attenuation_params.x;
float linear = light_attenuation_params.y;
float quadratic = light_attenuation_params.z;
float attenuation = 1.0 / (constant + linear * distance + quadratic * (distance * distance));
// combine results
vec3 ambient = vec3(point_light_ambient) * frag_color;
vec3 diffuse = vec3(point_light_diffuse) * diff * frag_color;
vec3 specular = vec3(point_light_specular) * spec * frag_color;
ambient = ambient * attenuation;
diffuse = diffuse * attenuation;
specular = specular * attenuation;
return (ambient + diffuse + specular);
}
vec3 calculate_spot_light(vec3 light_pos, vec3 normal, vec3 frag_pos, vec3 view_dir ,vec3 frag_color)
{
vec3 light_dir = normalize(light_pos - frag_pos);
// diffuse shading
float diff = max(dot(normal, light_dir), 0.0);
// specular shading
vec3 halfway_dir = normalize(light_dir + view_dir);
float spec = pow(max(dot(normal, halfway_dir), 0.0), 32.0);
// attenuation
float distance = length(light_pos - frag_pos);
float constant = light_attenuation_params.x;
float linear = light_attenuation_params.y;
float quadratic = light_attenuation_params.z;
float attenuation = 1.0 / (constant + linear * distance + quadratic * (distance * distance));
// spotlight intensitys
float theta = dot(light_dir, normalize(vec3(light_pos.x, light_pos.y, light_pos.z)));
float cut_off = spot_light_inout_cut_off.x;
float outter_cut_off = spot_light_inout_cut_off.y;
float epsilon = cut_off - outter_cut_off;
float intensity = clamp((theta - outter_cut_off) / epsilon, 0.0, 1.0);
// combine results
vec3 ambient = vec3(spot_light_ambient) * frag_color;
vec3 diffuse = vec3(spot_light_diffuse) * diff * frag_color;
vec3 specular = vec3(spot_light_specular) * spec * frag_color;
//var_spherical_harmonic_lighting
ambient = ambient * attenuation * intensity;
diffuse = diffuse * attenuation * intensity;
specular = specular * attenuation * intensity;
return (ambient + diffuse + specular);
}
void main()
{
vec3 view_dir = normalize(vec3(view_pos - var_frag_pos));
vec3 frag_color = vec3(Albedo(var_texcoord0.xy));
vec3 albedo = pow(frag_color, vec3(2.2));
float metallic = texture2D(metallic_map, var_texcoord0.xy).r;
float ao = texture2D(ao_map, var_texcoord0.xy).r;
vec3 N = get_normal_from_map();
vec3 V = normalize(vec3(view_pos - var_frag_pos));
vec3 F0 = vec3(0.04);
F0 = mix(F0, albedo, metallic);
vec3 Lo = vec3(0.0);
vec3 L = normalize(vec3(spot_light_pos - var_frag_pos));
vec3 H = normalize(V + L);
float distance = length(spot_light_pos - var_frag_pos);
float attenuation = 1.0 / (distance * distance);
float theta = dot(L, normalize(vec3(spot_light_pos)));
float cut_off = spot_light_inout_cut_off.x;
float outter_cut_off = spot_light_inout_cut_off.y;
float epsilon = cut_off - outter_cut_off;
float intensity = clamp((theta - outter_cut_off) / epsilon, 0.0, 1.0);
vec3 radiance = vec3(spot_light_diffuse * attenuation * intensity);
float NDF = distribution_GGX(N, H, roughness.x);
float G = geometry_smith(N, V, L, roughness.x);
vec3 F = fresnel_schlick(max(dot(H, V), 0.0), F0);
vec3 nominator = NDF * G * F;
float denominator = 4.0 * max(dot(N, V), 0.0) * max(dot(N, L), 0.0) + 0.001;
vec3 specular = nominator / denominator;
vec3 kS = F;
vec3 kD = vec3(1.0) - kS;
kD *= 1.0 - metallic;
float NdotL = max(dot(N, L), 0.0);
Lo += (kD * albedo / PI + specular) * radiance * NdotL;
vec3 ambient = vec3(0.05) * albedo * ao;
vec3 color = ambient + Lo;
color += calculate_spot_light(vec3(spot_light_pos), N, vec3(var_frag_pos), view_dir, frag_color);
color = color / (color + vec3(1.0));
color = pow(color, vec3(1.0 / 2.2));
gl_FragColor = vec4(color, 1.0) * tint;
}