#version 430 core const float PI = 3.141592653589793238462643383; const float TAU = 6.283185307179586476925286766; const float EPSILON = 0.000001; in vec3 frag_position; in vec3 frag_normal; in vec3 frag_tangent; in vec3 frag_bitangent; in vec2 frag_texture_coord; in vec4 view_position; out vec4 FragColor; uniform int has_albedo_texture; uniform sampler2D albedo_texture; uniform vec4 albedo_factor; uniform int has_metallic_texture; uniform sampler2D metallic_texture; uniform float metallic_factor; uniform int has_roughness_texture; uniform sampler2D roughness_texture; uniform float roughness_factor; uniform int has_normal_texture; uniform sampler2D normal_texture; uniform int has_emissive_texture; uniform sampler2D emissive_texture; uniform vec4 emissive_factor; uniform mat4 view_matrix; uniform mat4 view_matrix_inverse; uniform mat4 model_matrix; uniform int has_shadow_map; uniform sampler2DShadow shadow_map; uniform mat4 shadow_matrix; uniform int has_environment_map; uniform samplerCube environment_map; struct SunLight { vec3 direction; float _padding0; vec3 color; float intensity; }; struct PointLight { vec3 position; float _padding0; vec3 color; float intensity; }; struct SpotLight { vec3 position; float inner_radius; vec3 color; float intensity; vec3 direction; float outer_radius; }; #define MAX_SUN_LIGHTS 4 #define MAX_POINT_LIGHTS 128 #define MAX_SPOT_LIGHTS 128 layout (std140) uniform lights { uint sun_light_count; uint point_light_count; uint spot_light_count; float ambient_light; SunLight sun_lights[MAX_SUN_LIGHTS]; PointLight point_lights[MAX_POINT_LIGHTS]; SpotLight spot_lights[MAX_SPOT_LIGHTS]; }; struct MaterialInfo { vec4 Albedo; float Metallic; float Roughness; vec4 Emissive; }; uniform float time; uniform float width; uniform float height; float clamped_dot(vec3 v, vec3 w) { return max( dot(v, w) , 0.0); } vec3 DiffuseBRDF(MaterialInfo material, vec3 L, vec3 V) { // Lambertian return material.Albedo.xyz / PI; } float SpecularNDF(MaterialInfo material, vec3 N, vec3 H) { // GGX float a = material.Roughness * material.Roughness; float a2 = a * a; float n_dot_h = clamped_dot(N, H); float n_dot_h2 = n_dot_h * n_dot_h; float d = n_dot_h2 * (a2 - 1.0) + 1.0; return a2 / (PI * d*d + EPSILON); } float SpecularGeometricAttenuation(MaterialInfo material, vec3 N, vec3 L, vec3 V, vec3 H) { // Schlick float r = material.Roughness + 1; float k = r*r / 8.0; float n_dot_v = clamped_dot(N, V); float n_dot_l = clamped_dot(N, L); float denom_v = n_dot_v * (1.0 - k) + k; float denom_l = n_dot_l * (1.0 - k) + k; return (n_dot_v / denom_v) * (n_dot_l / denom_l); } vec3 SpecularFresnel(vec3 F0, vec3 V, vec3 H) { // Schlick, Epic paper float v_dot_h = clamped_dot(V, H); float exp = (-5.55473 * v_dot_h - 6.98316) * v_dot_h; return F0 + (1.0 - F0) * pow(2, exp); } vec3 BRDF(MaterialInfo material, vec3 radiance, vec3 N, vec3 L, vec3 V, vec3 H) { // Cook-Torrance vec3 F0 = mix(vec3(0.04), material.Albedo.xyz, material.Metallic); float D = SpecularNDF(material, N, H); vec3 F = SpecularFresnel(F0, N, H); float G = SpecularGeometricAttenuation(material, N, L, V, H); float n_dot_v = clamped_dot(N, V); float n_dot_l = clamped_dot(N, L); float specular_denom = (4.0 * n_dot_l * n_dot_v) + EPSILON; vec3 specular = D * F * G / specular_denom; vec3 diffuse = (1.0 - F) * DiffuseBRDF(material, L, V); return (diffuse + specular) * radiance * n_dot_l; } void main() { vec4 final_color = vec4(0,0,0,1); // PBR Parameters MaterialInfo material; material.Albedo = albedo_factor; if(has_albedo_texture != 0) material.Albedo = albedo_factor * texture(albedo_texture, frag_texture_coord); material.Metallic = metallic_factor; if(has_metallic_texture != 0) material.Metallic = metallic_factor * texture(metallic_texture, frag_texture_coord).b; material.Roughness = roughness_factor; if(has_roughness_texture != 0) material.Roughness = roughness_factor * texture(roughness_texture, frag_texture_coord).g; material.Emissive = emissive_factor; if(has_emissive_texture != 0) material.Emissive = emissive_factor * texture(emissive_texture, frag_texture_coord); vec3 Normal = normalize(frag_normal); if(has_normal_texture != 0) { vec3 normal_map = normalize(texture(normal_texture, frag_texture_coord).rgb * 2.0 - 1.0); mat3 TBN = mat3(normalize(frag_tangent), normalize(frag_bitangent), normalize(frag_normal)); Normal = normalize(TBN * normal_map); } vec4 camera_position_4 = view_matrix_inverse * vec4(view_position.xy / view_position.w, 0, 1); vec3 camera_position = camera_position_4.xyz / camera_position_4.w; // vec4 pixel_position_4 = view_matrix_inverse * view_position; // vec3 pixel_position = pixel_position_4.xyz / pixel_position_4.w; // vec3 view_direction = normalize(pixel_position - camera_position); vec3 view_direction = normalize(frag_position - camera_position); vec3 V = -view_direction; vec3 N = Normal; // Sun lights for(uint i = 0; i < sun_light_count; i++) { SunLight light = sun_lights[i]; float shadow = 1.0; if(has_shadow_map != 0) { shadow = 0; vec2 texel_size = 1.0 / textureSize(shadow_map, 0); float bias = 0.0001; vec4 from_light_view = shadow_matrix * vec4(frag_position, 1.0); from_light_view /= from_light_view.w; from_light_view = from_light_view * 0.5 + 0.5; // [-1,1] => [0,1] uv coords from_light_view.z = from_light_view.z - bias; // Bias to attenuate z fighting for(int x = -1; x <= 1; x++) for(int y = -1; y <= 1; y++) { float s = texture(shadow_map, from_light_view.xyz + vec3(x * texel_size.x, y * texel_size.y, 0)); s = clamp(s, 0.0, 1.0); shadow += s; } shadow /= 9; } vec3 L = -light.direction; vec3 H = normalize(V + L); vec3 radiance = light.color * light.intensity * shadow; vec3 color = BRDF(material, radiance, N, L, V, H); final_color.xyz += color; } // Point lights for(uint i = 0; i < point_light_count; i++) { PointLight light = point_lights[i]; vec3 diff = light.position - frag_position; float dist2 = abs(dot(diff, diff)); float attenuation = 1.0 / (dist2 + EPSILON); float intensity = light.intensity * attenuation; vec3 L = normalize(light.position - frag_position); vec3 H = normalize(V + L); vec3 radiance = light.color * intensity; vec3 color = BRDF(material, radiance, N, L, V, H); final_color.xyz += color; } // Spot lights for(uint i = 0; i < spot_light_count; i++) { SpotLight light = spot_lights[i]; vec3 light_direction = normalize(frag_position - light.position); float dist2 = abs(dot(light.position, frag_position)); float attenuation = 1.0 / (dist2 + EPSILON); float intensity = light.intensity * attenuation; float angle = acos(dot(light.direction, light_direction)); float spot_factor = (angle - light.outer_radius) / (light.inner_radius - light.outer_radius); spot_factor = clamp(spot_factor, 0.0, 1.0); intensity *= spot_factor; vec3 L = normalize(light.position - frag_position); vec3 H = normalize(V + L); vec3 radiance = light.color * intensity; vec3 color = BRDF(material, radiance, N, L, V, H); final_color.xyz += color; } // Environment map light if(has_environment_map != 0) { vec3 L = normalize(reflect(-V, N)); vec3 H = N;//normalize(V + L); float levels = textureQueryLevels(environment_map); vec3 radiance = textureLod(environment_map, L.xzy, material.Roughness * levels).rgb; //vec3 color = BRDF(material, radiance, N, L, V, H); vec3 F0 = mix(vec3(0.04), material.Albedo.xyz, material.Metallic); float D = SpecularNDF(material, N, H); vec3 F = SpecularFresnel(F0, N, H); float G = SpecularGeometricAttenuation(material, N, L, V, H); float n_dot_v = clamped_dot(N, V); float n_dot_l = clamped_dot(N, L); float specular_denom = (4.0 * n_dot_l * n_dot_v) + EPSILON; vec3 specular = D * F * G / specular_denom; vec3 diffuse = (1.0 - F) * DiffuseBRDF(material, L, V); vec3 color = (diffuse /*+ specular*/) * radiance * n_dot_l; final_color.xyz += color; } // Ambient light final_color.xyz += ambient_light * material.Albedo.xyz; final_color.a = material.Albedo.a; // Emissive color final_color.xyz += material.Emissive.xyz * material.Emissive.a; FragColor = final_color; //FragColor.xyz = Normal; }