vumichien's picture
First commit
b4c8bc3
#version 330 core
///////////////////////////////////////////////////////////////////////////////
// Structs
///////////////////////////////////////////////////////////////////////////////
struct SpotLight {
vec3 color;
float intensity;
float range;
vec3 position;
vec3 direction;
float light_angle_scale;
float light_angle_offset;
#ifdef SPOT_LIGHT_SHADOWS
sampler2D shadow_map;
mat4 light_matrix;
#endif
};
struct DirectionalLight {
vec3 color;
float intensity;
vec3 direction;
#ifdef DIRECTIONAL_LIGHT_SHADOWS
sampler2D shadow_map;
mat4 light_matrix;
#endif
};
struct PointLight {
vec3 color;
float intensity;
float range;
vec3 position;
#ifdef POINT_LIGHT_SHADOWS
samplerCube shadow_map;
#endif
};
struct Material {
vec3 emissive_factor;
#ifdef USE_METALLIC_MATERIAL
vec4 base_color_factor;
float metallic_factor;
float roughness_factor;
#endif
#ifdef USE_GLOSSY_MATERIAL
vec4 diffuse_factor;
vec3 specular_factor;
float glossiness_factor;
#endif
#ifdef HAS_NORMAL_TEX
sampler2D normal_texture;
#endif
#ifdef HAS_OCCLUSION_TEX
sampler2D occlusion_texture;
#endif
#ifdef HAS_EMISSIVE_TEX
sampler2D emissive_texture;
#endif
#ifdef HAS_BASE_COLOR_TEX
sampler2D base_color_texture;
#endif
#ifdef HAS_METALLIC_ROUGHNESS_TEX
sampler2D metallic_roughness_texture;
#endif
#ifdef HAS_DIFFUSE_TEX
sampler2D diffuse_texture;
#endif
#ifdef HAS_SPECULAR_GLOSSINESS_TEX
sampler2D specular_glossiness;
#endif
};
struct PBRInfo {
float nl;
float nv;
float nh;
float lh;
float vh;
float roughness;
float metallic;
vec3 f0;
vec3 c_diff;
};
///////////////////////////////////////////////////////////////////////////////
// Uniforms
///////////////////////////////////////////////////////////////////////////////
uniform Material material;
uniform PointLight point_lights[MAX_POINT_LIGHTS];
uniform int n_point_lights;
uniform DirectionalLight directional_lights[MAX_DIRECTIONAL_LIGHTS];
uniform int n_directional_lights;
uniform SpotLight spot_lights[MAX_SPOT_LIGHTS];
uniform int n_spot_lights;
uniform vec3 cam_pos;
uniform vec3 ambient_light;
#ifdef USE_IBL
uniform samplerCube diffuse_env;
uniform samplerCube specular_env;
#endif
///////////////////////////////////////////////////////////////////////////////
// Inputs
///////////////////////////////////////////////////////////////////////////////
in vec3 frag_position;
#ifdef NORMAL_LOC
in vec3 frag_normal;
#endif
#ifdef HAS_NORMAL_TEX
#ifdef TANGENT_LOC
#ifdef NORMAL_LOC
in mat3 tbn;
#endif
#endif
#endif
#ifdef TEXCOORD_0_LOC
in vec2 uv_0;
#endif
#ifdef TEXCOORD_1_LOC
in vec2 uv_1;
#endif
#ifdef COLOR_0_LOC
in vec4 color_multiplier;
#endif
///////////////////////////////////////////////////////////////////////////////
// OUTPUTS
///////////////////////////////////////////////////////////////////////////////
out vec4 frag_color;
///////////////////////////////////////////////////////////////////////////////
// Constants
///////////////////////////////////////////////////////////////////////////////
const float PI = 3.141592653589793;
const float min_roughness = 0.04;
///////////////////////////////////////////////////////////////////////////////
// Utility Functions
///////////////////////////////////////////////////////////////////////////////
vec4 srgb_to_linear(vec4 srgb)
{
#ifndef SRGB_CORRECTED
// Fast Approximation
//vec3 linOut = pow(srgbIn.xyz,vec3(2.2));
//
vec3 b_less = step(vec3(0.04045),srgb.xyz);
vec3 lin_out = mix( srgb.xyz/vec3(12.92), pow((srgb.xyz+vec3(0.055))/vec3(1.055),vec3(2.4)), b_less );
return vec4(lin_out, srgb.w);
#else
return srgb;
#endif
}
// Normal computation
vec3 get_normal()
{
#ifdef HAS_NORMAL_TEX
#ifndef HAS_TANGENTS
vec3 pos_dx = dFdx(frag_position);
vec3 pos_dy = dFdy(frag_position);
vec3 tex_dx = dFdx(vec3(uv_0, 0.0));
vec3 tex_dy = dFdy(vec3(uv_0, 0.0));
vec3 t = (tex_dy.t * pos_dx - tex_dx.t * pos_dy) / (tex_dx.s * tex_dy.t - tex_dy.s * tex_dx.t);
#ifdef NORMAL_LOC
vec3 ng = normalize(frag_normal);
#else
vec3 = cross(pos_dx, pos_dy);
#endif
t = normalize(t - ng * dot(ng, t));
vec3 b = normalize(cross(ng, t));
mat3 tbn_n = mat3(t, b, ng);
#else
mat3 tbn_n = tbn;
#endif
vec3 n = texture(material.normal_texture, uv_0).rgb;
n = normalize(tbn_n * ((2.0 * n - 1.0) * vec3(1.0, 1.0, 1.0)));
return n; // TODO NORMAL MAPPING
#else
#ifdef NORMAL_LOC
return frag_normal;
#else
return normalize(cam_pos - frag_position);
#endif
#endif
}
// Fresnel
vec3 specular_reflection(PBRInfo info)
{
vec3 res = info.f0 + (1.0 - info.f0) * pow(clamp(1.0 - info.vh, 0.0, 1.0), 5.0);
return res;
}
// Smith
float geometric_occlusion(PBRInfo info)
{
float r = info.roughness + 1.0;
float k = r * r / 8.0;
float g1 = info.nv / (info.nv * (1.0 - k) + k);
float g2 = info.nl / (info.nl * (1.0 - k) + k);
//float k = info.roughness * sqrt(2.0 / PI);
//float g1 = info.lh / (info.lh * (1.0 - k) + k);
//float g2 = info.nh / (info.nh * (1.0 - k) + k);
return g1 * g2;
}
float microfacet_distribution(PBRInfo info)
{
float a = info.roughness * info.roughness;
float a2 = a * a;
float nh2 = info.nh * info.nh;
float denom = (nh2 * (a2 - 1.0) + 1.0);
return a2 / (PI * denom * denom);
}
vec3 compute_brdf(vec3 n, vec3 v, vec3 l,
float roughness, float metalness,
vec3 f0, vec3 c_diff, vec3 albedo,
vec3 radiance)
{
vec3 h = normalize(l+v);
float nl = clamp(dot(n, l), 0.001, 1.0);
float nv = clamp(abs(dot(n, v)), 0.001, 1.0);
float nh = clamp(dot(n, h), 0.0, 1.0);
float lh = clamp(dot(l, h), 0.0, 1.0);
float vh = clamp(dot(v, h), 0.0, 1.0);
PBRInfo info = PBRInfo(nl, nv, nh, lh, vh, roughness, metalness, f0, c_diff);
// Compute PBR terms
vec3 F = specular_reflection(info);
float G = geometric_occlusion(info);
float D = microfacet_distribution(info);
// Compute BRDF
vec3 diffuse_contrib = (1.0 - F) * c_diff / PI;
vec3 spec_contrib = F * G * D / (4.0 * nl * nv + 0.001);
vec3 color = nl * radiance * (diffuse_contrib + spec_contrib);
return color;
}
float texture2DCompare(sampler2D depths, vec2 uv, float compare) {
return compare > texture(depths, uv.xy).r ? 1.0 : 0.0;
}
float texture2DShadowLerp(sampler2D depths, vec2 size, vec2 uv, float compare) {
vec2 texelSize = vec2(1.0)/size;
vec2 f = fract(uv*size+0.5);
vec2 centroidUV = floor(uv*size+0.5)/size;
float lb = texture2DCompare(depths, centroidUV+texelSize*vec2(0.0, 0.0), compare);
float lt = texture2DCompare(depths, centroidUV+texelSize*vec2(0.0, 1.0), compare);
float rb = texture2DCompare(depths, centroidUV+texelSize*vec2(1.0, 0.0), compare);
float rt = texture2DCompare(depths, centroidUV+texelSize*vec2(1.0, 1.0), compare);
float a = mix(lb, lt, f.y);
float b = mix(rb, rt, f.y);
float c = mix(a, b, f.x);
return c;
}
float PCF(sampler2D depths, vec2 size, vec2 uv, float compare){
float result = 0.0;
for(int x=-1; x<=1; x++){
for(int y=-1; y<=1; y++){
vec2 off = vec2(x,y)/size;
result += texture2DShadowLerp(depths, size, uv+off, compare);
}
}
return result/9.0;
}
float shadow_calc(mat4 light_matrix, sampler2D shadow_map, float nl)
{
// Compute light texture UV coords
vec4 proj_coords = vec4(light_matrix * vec4(frag_position.xyz, 1.0));
vec3 light_coords = proj_coords.xyz / proj_coords.w;
light_coords = light_coords * 0.5 + 0.5;
float current_depth = light_coords.z;
float bias = max(0.001 * (1.0 - nl), 0.0001) / proj_coords.w;
float compare = (current_depth - bias);
float shadow = PCF(shadow_map, textureSize(shadow_map, 0), light_coords.xy, compare);
if (light_coords.z > 1.0) {
shadow = 0.0;
}
return shadow;
}
///////////////////////////////////////////////////////////////////////////////
// MAIN
///////////////////////////////////////////////////////////////////////////////
void main()
{
vec4 color = vec4(vec3(0.0), 1.0);
///////////////////////////////////////////////////////////////////////////////
// Handle Metallic Materials
///////////////////////////////////////////////////////////////////////////////
#ifdef USE_METALLIC_MATERIAL
// Compute metallic/roughness factors
float roughness = material.roughness_factor;
float metallic = material.metallic_factor;
#ifdef HAS_METALLIC_ROUGHNESS_TEX
vec2 mr = texture(material.metallic_roughness_texture, uv_0).rg;
roughness = roughness * mr.r;
metallic = metallic * mr.g;
#endif
roughness = clamp(roughness, min_roughness, 1.0);
metallic = clamp(metallic, 0.0, 1.0);
// In convention, material roughness is perceputal roughness ^ 2
float alpha_roughness = roughness * roughness;
// Compute albedo
vec4 base_color = material.base_color_factor;
#ifdef HAS_BASE_COLOR_TEX
base_color = base_color * srgb_to_linear(texture(material.base_color_texture, uv_0));
#endif
// Compute specular and diffuse colors
vec3 dialectric_spec = vec3(min_roughness);
vec3 c_diff = mix(vec3(0.0), base_color.rgb * (1 - min_roughness), 1.0 - metallic);
vec3 f0 = mix(dialectric_spec, base_color.rgb, metallic);
// Compute normal
vec3 n = normalize(get_normal());
// Loop over lights
for (int i = 0; i < n_directional_lights; i++) {
vec3 direction = directional_lights[i].direction;
vec3 v = normalize(cam_pos - frag_position); // Vector towards camera
vec3 l = normalize(-1.0 * direction); // Vector towards light
// Compute attenuation and radiance
float attenuation = directional_lights[i].intensity;
vec3 radiance = attenuation * directional_lights[i].color;
// Compute outbound color
vec3 res = compute_brdf(n, v, l, roughness, metallic,
f0, c_diff, base_color.rgb, radiance);
// Compute shadow
#ifdef DIRECTIONAL_LIGHT_SHADOWS
float nl = clamp(dot(n,l), 0.0, 1.0);
float shadow = shadow_calc(
directional_lights[i].light_matrix,
directional_lights[i].shadow_map,
nl
);
res = res * (1.0 - shadow);
#endif
color.xyz += res;
}
for (int i = 0; i < n_point_lights; i++) {
vec3 position = point_lights[i].position;
vec3 v = normalize(cam_pos - frag_position); // Vector towards camera
vec3 l = normalize(position - frag_position); // Vector towards light
// Compute attenuation and radiance
float dist = length(position - frag_position);
float attenuation = point_lights[i].intensity / (dist * dist);
vec3 radiance = attenuation * point_lights[i].color;
// Compute outbound color
vec3 res = compute_brdf(n, v, l, roughness, metallic,
f0, c_diff, base_color.rgb, radiance);
color.xyz += res;
}
for (int i = 0; i < n_spot_lights; i++) {
vec3 position = spot_lights[i].position;
vec3 v = normalize(cam_pos - frag_position); // Vector towards camera
vec3 l = normalize(position - frag_position); // Vector towards light
// Compute attenuation and radiance
vec3 direction = spot_lights[i].direction;
float las = spot_lights[i].light_angle_scale;
float lao = spot_lights[i].light_angle_offset;
float dist = length(position - frag_position);
float cd = clamp(dot(direction, -l), 0.0, 1.0);
float attenuation = clamp(cd * las + lao, 0.0, 1.0);
attenuation = attenuation * attenuation * spot_lights[i].intensity;
attenuation = attenuation / (dist * dist);
vec3 radiance = attenuation * spot_lights[i].color;
// Compute outbound color
vec3 res = compute_brdf(n, v, l, roughness, metallic,
f0, c_diff, base_color.rgb, radiance);
#ifdef SPOT_LIGHT_SHADOWS
float nl = clamp(dot(n,l), 0.0, 1.0);
float shadow = shadow_calc(
spot_lights[i].light_matrix,
spot_lights[i].shadow_map,
nl
);
res = res * (1.0 - shadow);
#endif
color.xyz += res;
}
color.xyz += base_color.xyz * ambient_light;
// Calculate lighting from environment
#ifdef USE_IBL
// TODO
#endif
// Apply occlusion
#ifdef HAS_OCCLUSION_TEX
float ao = texture(material.occlusion_texture, uv_0).r;
color.xyz *= ao;
#endif
// Apply emissive map
vec3 emissive = material.emissive_factor;
#ifdef HAS_EMISSIVE_TEX
emissive *= srgb_to_linear(texture(material.emissive_texture, uv_0)).rgb;
#endif
color.xyz += emissive * material.emissive_factor;
#ifdef COLOR_0_LOC
color *= color_multiplier;
#endif
frag_color = clamp(vec4(pow(color.xyz, vec3(1.0/2.2)), color.a * base_color.a), 0.0, 1.0);
#else
// TODO GLOSSY MATERIAL BRDF
#endif
///////////////////////////////////////////////////////////////////////////////
// Handle Glossy Materials
///////////////////////////////////////////////////////////////////////////////
}