blob: 5719688c17811b628f98b61eeef2a496fce1481b [file] [log] [blame]
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
uniform FragInfo {
float exposure;
vec3 camera_position;
}
frag_info;
uniform sampler2D base_color_texture;
uniform sampler2D normal_texture;
uniform sampler2D occlusion_roughness_metallic_texture;
in vec3 v_position;
in mat3 v_tangent_space;
in vec2 v_texture_coords;
out vec4 frag_color;
#include <impeller/constants.glsl>
const float kPi = 3.14159265358979323846;
const vec3 kLightNormal = normalize(vec3(2.0, 5.0, -5.0));
const vec3 kLightColor = vec3(1.0, 0.8, 0.8) * 3.0;
const vec3 kHighlightNormal = normalize(vec3(-2.0, -3.0, 0.0));
const vec3 kHighlightColor = vec3(1.0, 0.1, 0.0) * 1.5;
const float kGamma = 2.2;
// Convert from sRGB to linear space.
// This can be removed once Impeller supports sRGB texture inputs.
vec3 SampleSRGB(sampler2D tex, vec2 uv) {
vec3 color = texture(tex, uv).rgb;
return pow(color, vec3(kGamma));
}
//------------------------------------------------------------------------------
/// Lighting equation.
/// See also: https://learnopengl.com/PBR/Lighting
///
vec3 FresnelSchlick(float cos_theta, vec3 reflectance) {
return reflectance +
(1.0 - reflectance) * pow(clamp(1.0 - cos_theta, 0.0, 1.0), 5.0);
}
float DistributionGGX(vec3 normal, vec3 half_vector, float roughness) {
float a = roughness * roughness;
float a2 = a * a;
float NdotH = max(dot(normal, half_vector), 0.0);
float NdotH2 = NdotH * NdotH;
float num = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = kPi * denom * denom;
return num / denom;
}
float GeometrySchlickGGX(float NdotV, float roughness) {
float r = (roughness + 1.0);
float k = (r * r) / 8.0;
float num = NdotV;
float denom = NdotV * (1.0 - k) + k;
return num / denom;
}
float GeometrySmith(vec3 normal,
vec3 camera_normal,
vec3 light_normal,
float roughness) {
float camera_ggx =
GeometrySchlickGGX(max(dot(normal, camera_normal), 0.0), roughness);
float light_ggx =
GeometrySchlickGGX(max(dot(normal, light_normal), 0.0), roughness);
return camera_ggx * light_ggx;
}
vec3 LightFormula(vec3 light_color,
vec3 camera_normal,
vec3 light_normal,
vec3 albedo,
vec3 normal,
float metallic,
float roughness,
vec3 reflectance) {
vec3 half_vector = normalize(camera_normal + light_normal);
// Cook-Torrance BRDF.
float distribution = DistributionGGX(normal, half_vector, roughness);
float geometry =
GeometrySmith(normal, camera_normal, light_normal, roughness);
vec3 fresnel =
FresnelSchlick(max(dot(half_vector, camera_normal), 0.0), reflectance);
vec3 kS = fresnel;
vec3 kD = vec3(1.0) - kS;
kD *= 1.0 - metallic;
vec3 numerator = distribution * geometry * fresnel;
float denominator = 4.0 * max(dot(normal, camera_normal), 0.0) *
max(dot(normal, light_normal), 0.0) +
0.0001;
vec3 specular = numerator / denominator;
float NdotL = max(dot(normal, light_normal), 0.0);
return (kD * albedo / kPi + specular) * light_color * NdotL;
}
void main() {
vec3 albedo = SampleSRGB(base_color_texture, v_texture_coords);
vec3 normal =
normalize(v_tangent_space *
(texture(normal_texture, v_texture_coords).rgb * 2.0 - 1.0));
vec3 orm =
texture(occlusion_roughness_metallic_texture, v_texture_coords).rgb;
float occlusion = orm.r;
float roughness = orm.g;
float metallic = orm.b;
vec3 camera_normal = normalize(frag_info.camera_position - v_position);
vec3 reflectance = mix(vec3(0.04), albedo, metallic);
vec3 out_radiance =
LightFormula(kLightColor, camera_normal, kLightNormal, albedo, normal,
metallic, roughness, reflectance);
out_radiance +=
LightFormula(kHighlightColor, camera_normal, kHighlightNormal, albedo,
normal, metallic, roughness, reflectance);
vec3 ambient = vec3(0.03) * albedo * occlusion;
vec3 out_color = ambient + out_radiance;
// Tone mapping.
out_color = vec3(1.0) - exp(-out_color * frag_info.exposure);
#ifndef IMPELLER_TARGET_METAL
out_color = pow(out_color, vec3(1.0 / kGamma));
#endif
frag_color = vec4(out_color, 1.0);
}