MetaCore/third_party/simplepbr-shaders/shaders/simplepbr.frag

310 lines
9.6 KiB
GLSL

// Based on code from https://github.com/KhronosGroup/glTF-Sample-Viewer
#version 120
#ifndef MAX_LIGHTS
#define MAX_LIGHTS 8
#endif
#ifndef USE_NORMAL_MAP
#define USE_NORMAL_MAP
#endif
#ifndef USE_EMISSION_MAP
#define USE_EMISSION_MAP
#endif
#ifndef USE_OCCLUSION_MAP
#define USE_OCCLUSION_MAP
#endif
#ifdef USE_330
#define texture2D texture
#define textureCube texture
#define textureCubeLod textureLod
#else
#extension GL_ARB_shader_texture_lod : require
#endif
uniform struct p3d_MaterialParameters {
vec4 baseColor;
vec4 emission;
float roughness;
float metallic;
} p3d_Material;
uniform struct p3d_LightSourceParameters {
vec4 position;
vec4 diffuse;
vec4 specular;
vec3 attenuation;
vec3 spotDirection;
float spotCosCutoff;
#ifdef ENABLE_SHADOWS
sampler2DShadow shadowMap;
mat4 shadowViewMatrix;
#endif
} p3d_LightSource[MAX_LIGHTS];
uniform struct p3d_LightModelParameters {
vec4 ambient;
} p3d_LightModel;
#ifdef ENABLE_FOG
uniform struct p3d_FogParameters {
vec4 color;
float density;
} p3d_Fog;
#endif
uniform vec4 p3d_ColorScale;
uniform vec4 p3d_TexAlphaOnly;
uniform vec3 sh_coeffs[9];
uniform vec3 camera_world_position;
struct FunctionParamters {
float n_dot_l;
float n_dot_v;
float n_dot_h;
float l_dot_h;
float v_dot_h;
float roughness;
float metallic;
vec3 reflection0;
vec3 diffuse_color;
vec3 specular_color;
};
uniform sampler2D p3d_TextureBaseColor;
uniform sampler2D p3d_TextureMetalRoughness;
uniform sampler2D p3d_TextureNormal;
uniform sampler2D p3d_TextureEmission;
uniform sampler2D p3d_TextureOcclusion;
uniform sampler2D brdf_lut;
uniform samplerCube filtered_env_map;
uniform float max_reflection_lod;
uniform float metacore_AlphaCutoff;
uniform float metacore_AlphaMode;
#ifdef ENABLE_SHADOWS
uniform float global_shadow_bias;
#endif
const vec3 F0 = vec3(0.04);
const float PI = 3.141592653589793;
const float SPOTSMOOTH = 0.001;
const float LIGHT_CUTOFF = 0.001;
varying vec3 v_view_position;
varying vec3 v_world_position;
varying vec4 v_color;
varying vec2 v_texcoord;
varying mat3 v_view_tbn;
varying mat3 v_world_tbn;
#ifdef ENABLE_SHADOWS
varying vec4 v_shadow_pos[MAX_LIGHTS];
#endif
#ifdef USE_330
out vec4 o_color;
#endif
// Schlick's Fresnel approximation with Spherical Gaussian approximation to replace the power
vec3 specular_reflection(FunctionParamters func_params) {
vec3 f0 = func_params.reflection0;
float v_dot_h= func_params.v_dot_h;
return f0 + (vec3(1.0) - f0) * pow(2.0, (-5.55473 * v_dot_h - 6.98316) * v_dot_h);
}
vec3 fresnelSchlickRoughness(float u, vec3 f0, float roughness) {
return f0 + (max(vec3(1.0 - roughness), f0) - f0) * pow(clamp(1.0 - u, 0.0, 1.0), 5.0);
}
// Smith GGX with optional fast sqrt approximation (see https://google.github.io/filament/Filament.md.html#materialsystem/specularbrdf/geometricshadowing(specularg))
float visibility_occlusion(FunctionParamters func_params) {
float r = func_params.roughness;
float n_dot_l = func_params.n_dot_l;
float n_dot_v = func_params.n_dot_v;
#ifdef SMITH_SQRT_APPROX
float ggxv = n_dot_l * (n_dot_v * (1.0 - r) + r);
float ggxl = n_dot_v * (n_dot_l * (1.0 - r) + r);
#else
float r2 = r * r;
float ggxv = n_dot_l * sqrt(n_dot_v * n_dot_v * (1.0 - r2) + r2);
float ggxl = n_dot_v * sqrt(n_dot_l * n_dot_l * (1.0 - r2) + r2);
#endif
float ggx = ggxv + ggxl;
if (ggx > 0.0) {
return 0.5 / ggx;
}
return 0.0;
}
// GGX/Trowbridge-Reitz
float microfacet_distribution(FunctionParamters func_params) {
float roughness2 = func_params.roughness * func_params.roughness;
float f = (func_params.n_dot_h * func_params.n_dot_h) * (roughness2 - 1.0) + 1.0;
return roughness2 / (PI * f * f);
}
// Lambert
float diffuse_function() {
return 1.0 / PI;
}
#ifdef ENABLE_SHADOWS
float shadow_caster_contrib(sampler2DShadow shadowmap, vec4 shadowpos) {
vec3 light_space_coords = shadowpos.xyz / shadowpos.w;
light_space_coords.z -= global_shadow_bias;
#ifdef USE_330
float shadow = texture(shadowmap, light_space_coords);
#else
float shadow = shadow2D(shadowmap, light_space_coords).r;
#endif
return shadow;
}
#endif
vec3 get_normalmap_data() {
#ifdef CALC_NORMAL_Z
vec2 normalXY = 2.0 * texture2D(p3d_TextureNormal, v_texcoord).rg - 1.0;
float normalZ = sqrt(clamp(1.0 - dot(normalXY, normalXY), 0.0, 1.0));
return vec3(
normalXY,
normalZ
);
#else
return 2.0 * texture2D(p3d_TextureNormal, v_texcoord).rgb - 1.0;
#endif
}
vec3 irradiance_from_sh(vec3 normal) {
return
+ sh_coeffs[0] * 0.282095
+ sh_coeffs[1] * 0.488603 * normal.x
+ sh_coeffs[2] * 0.488603 * normal.z
+ sh_coeffs[3] * 0.488603 * normal.y
+ sh_coeffs[4] * 1.092548 * normal.x * normal.z
+ sh_coeffs[5] * 1.092548 * normal.y * normal.z
+ sh_coeffs[6] * 1.092548 * normal.y * normal.x
+ sh_coeffs[7] * (0.946176 * normal.z * normal.z - 0.315392)
+ sh_coeffs[8] * 0.546274 * (normal.x * normal.x - normal.y * normal.y);
}
void main() {
vec4 metal_rough = texture2D(p3d_TextureMetalRoughness, v_texcoord);
float metallic = clamp(p3d_Material.metallic * metal_rough.b, 0.0, 1.0);
float perceptual_roughness = clamp(p3d_Material.roughness * metal_rough.g, 0.0, 1.0);
float alpha_roughness = perceptual_roughness * perceptual_roughness;
vec4 base_color = p3d_Material.baseColor * v_color * p3d_ColorScale * (texture2D(p3d_TextureBaseColor, v_texcoord) + p3d_TexAlphaOnly);
vec3 diffuse_color = (base_color.rgb * (vec3(1.0) - F0)) * (1.0 - metallic);
vec3 spec_color = mix(F0, base_color.rgb, metallic);
#ifdef USE_NORMAL_MAP
vec3 normalmap = get_normalmap_data();
#else
vec3 normalmap = vec3(0, 0, 1);
#endif
vec3 n = normalize(v_view_tbn * normalmap);
vec3 world_normal = normalize(v_world_tbn * normalmap);
vec3 v = normalize(-v_view_position);
#ifdef USE_OCCLUSION_MAP
float ambient_occlusion = texture2D(p3d_TextureOcclusion, v_texcoord).r;
#else
float ambient_occlusion = 1.0;
#endif
#ifdef USE_EMISSION_MAP
vec3 emission = p3d_Material.emission.rgb * texture2D(p3d_TextureEmission, v_texcoord).rgb;
#else
vec3 emission = vec3(0.0);
#endif
vec4 color = vec4(vec3(0.0), base_color.a);
if (metacore_AlphaMode > 0.5 && metacore_AlphaMode < 1.5 && base_color.a < metacore_AlphaCutoff) {
discard;
}
float n_dot_v = clamp(abs(dot(n, v)), 0.0, 1.0);
for (int i = 0; i < p3d_LightSource.length(); ++i) {
vec3 lightcol = p3d_LightSource[i].diffuse.rgb;
if (dot(lightcol, lightcol) < LIGHT_CUTOFF) {
continue;
}
vec3 light_pos = p3d_LightSource[i].position.xyz - v_view_position * p3d_LightSource[i].position.w;
vec3 l = normalize(light_pos);
vec3 h = normalize(l + v);
float dist = length(light_pos);
vec3 att_const = p3d_LightSource[i].attenuation;
float attenuation_factor = 1.0 / (att_const.x + att_const.y * dist + att_const.z * dist * dist);
float spotcos = dot(normalize(p3d_LightSource[i].spotDirection), -l);
float spotcutoff = p3d_LightSource[i].spotCosCutoff;
float shadowSpot = (spotcutoff > SPOTSMOOTH) ? smoothstep(spotcutoff-SPOTSMOOTH, spotcutoff+SPOTSMOOTH, spotcos) : 1.0;
#ifdef ENABLE_SHADOWS
float shadow_caster = shadow_caster_contrib(p3d_LightSource[i].shadowMap, v_shadow_pos[i]);
#else
float shadow_caster = 1.0;
#endif
float shadow = shadowSpot * shadow_caster * attenuation_factor;
FunctionParamters func_params;
func_params.n_dot_l = clamp(dot(n, l), 0.0, 1.0);
func_params.n_dot_v = n_dot_v;
func_params.n_dot_h = clamp(dot(n, h), 0.0, 1.0);
func_params.l_dot_h = clamp(dot(l, h), 0.0, 1.0);
func_params.v_dot_h = clamp(dot(v, h), 0.0, 1.0);
func_params.roughness = alpha_roughness;
func_params.metallic = metallic;
func_params.reflection0 = spec_color;
func_params.diffuse_color = diffuse_color;
func_params.specular_color = spec_color;
vec3 F = specular_reflection(func_params);
float V = visibility_occlusion(func_params); // V = G / (4 * n_dot_l * n_dot_v)
float D = microfacet_distribution(func_params);
vec3 diffuse_contrib = diffuse_color * diffuse_function();
vec3 spec_contrib = vec3(F * V * D);
color.rgb += func_params.n_dot_l * lightcol * (diffuse_contrib + spec_contrib) * shadow;
}
// Indirect diffuse + specular (IBL)
vec3 ibl_f = fresnelSchlickRoughness(n_dot_v, spec_color, perceptual_roughness);
vec3 ibl_kd = (1.0 - ibl_f) * (1.0 - metallic);
vec3 ibl_diff = base_color.rgb * max(irradiance_from_sh(world_normal), 0.0) * diffuse_function();
vec3 world_view = normalize(camera_world_position - v_world_position);
vec3 ibl_r = reflect(-world_view, world_normal);
vec2 env_brdf = texture2D(brdf_lut, vec2(n_dot_v, perceptual_roughness)).rg;
vec3 ibl_spec_color = textureCubeLod(filtered_env_map, ibl_r, perceptual_roughness * max_reflection_lod).rgb;
vec3 ibl_spec = ibl_spec_color * (ibl_f * env_brdf.x + env_brdf.y);
color.rgb += (ibl_kd * ibl_diff + ibl_spec) * ambient_occlusion;
// Indirect diffuse (ambient light)
color.rgb += (diffuse_color + spec_color) * p3d_LightModel.ambient.rgb * ambient_occlusion;
// Emission
color.rgb += emission;
#ifdef ENABLE_FOG
// Exponential fog
float fog_distance = length(v_view_position);
float fog_factor = clamp(1.0 / exp(fog_distance * p3d_Fog.density), 0.0, 1.0);
color = mix(p3d_Fog.color, color, fog_factor);
#endif
#ifdef USE_330
o_color = color;
#else
gl_FragColor = color;
#endif
}