abrasion/engine/shaders/forward.frag

214 lines
8.3 KiB
GLSL
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

// Forward rendering fragment shader.
//
// Copyright 2020 Sergiusz 'q3k' Bazanski <q3k@q3k.org>
//
// This file is part of Abrasion.
//
// Abrasion is free software: you can redistribute it and/or modify it under
// the terms of the GNU General Public License as published by the Free
// Software Foundation, version 3.
//
// Abrasion is distributed in the hope that it will be useful, but WITHOUT ANY
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
// FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
// details.
//
// You should have received a copy of the GNU General Public License along with
// Abrasion. If not, see <https://www.gnu.org/licenses/>.
//
// vim: set ft=glsl:
#version 450
#extension GL_ARB_separate_shader_objects : enable
// We implement a Lambertiand & Cook-Torrance BRDF-based lighting system.
// All of this is based on a number of scientific papers, meta-studies and modern sources. We do
// our best to cite as much as possible for future reference.
// Most of the maths is used straight from [Kar13].
//
// A good summary of different research is available this blog post by Brian Karis, that attempts
// to catalogue all existing BRDF-related functions:
// http://graphicrants.blogspot.com/2013/08/specular-brdf-reference.html
//
/// Bibliography:
//
// [Bec63]
// P. Beckmann & A. Spizzichino. 1963. "The Scattering of Electromagnetic Waves from Rough Surfaces"
// MacMillan, New York
//
// [Smi67]
// Bruce Smith. 1967. "Geometrical shadowing of a random rough surface."
// IEEE transactions on antennas and propagation 15.5 (1967): 668-671.
//
// [CT82]
// Robert L. Cook, Kenneth E. Torrance. 1982. "A Reflectance Model for Computer Graphics"
// ACM Transactions on Graphics, 1(1), 724.
// doi: 10.1145/357290.357293
//
// [Sch94]
// Christophe Schlick. 1994. "An Inexpensive BRDF Model for Physically-based Rendering"
// Computer Graphics Forum, 13(3), 233246.
// doi: 10.1111/1467-8659.1330233
//
// [Wa07]
// Bruce Walter et al. 2007. "Microfacet Models for Refraction through Rough Surfaces."
// Proceedings of the Eurographics Symposium on Rendering.
//
// [Bur12]
// Brent Burley. 2012. "Physically-Based Shading at Disney"
// URL: https://disney-animation.s3.amazonaws.com/library/s2012_pbs_disney_brdf_notes_v2.pdf
//
// [Kar13]
// Brian Karis. 2013. "Real Shading in Unreal Engine 4"
// URL: https://blog.selfshadow.com/publications/s2013-shading-course/karis/s2013_pbs_epic_notes_v2.pdf
//
// [Hei14]
// Eric Heitz. 2014. "Understanding the Masking-Shadowing Function in Microfacet-Based BRDFs"
// Journal of Computer Graphics Techniques, 3 (2).
//
// [GA19]
// Romain Guy, Mathias Agopian, "Physically Based Rendering in Filament"
// URL: https://google.github.io/filament/Filament.html
struct OmniLight {
vec4 pos;
vec4 color;
};
layout(binding = 0) uniform FragmentUniformBufferObject {
vec4 cameraPos;
OmniLight omniLights[4];
} ubo;
layout(binding = 1) uniform sampler2D texSamplerDiffuse;
layout(binding = 2) uniform sampler2D texSamplerRoughness;
layout(location = 0) in vec2 fragTexCoord;
layout(location = 1) in vec3 fragWorldPos;
layout(location = 2) in vec3 fragNormal;
layout(location = 0) out vec4 outColor;
const float PI = 3.14159;
// [Sch94] Fresnel approximation, used for F in Cook-Torrance BRDF.
vec3 FresnelSchlick(float HdotV, vec3 F0) {
return F0 + (1.0 - F0) * pow(1.0 - HdotV, 5.0);
}
// Microfacet Normal Distribution Function, used for D in Cook-Torrance BRDF.
float DistributionGGX(float NdotH, float roughness) {
// 'Roughness remapping' as per [Bur12]
float a = roughness * roughness;
// NDF from [Kar13], that cites [Bur12], which in turn cites [Wa07].
// However, I could not find the same equation form in [Bur12] or deduce it myself from [Wa07],
// and ended up taking the direct, untraceable form from [Kar13], so take this with a grain of salt.
float a2 = a * a;
float NdotH2 = NdotH * NdotH;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
return (a * a) / (PI * denom * denom);
}
float GeometrySchlickGGX(float NdotV, float roughness) {
// Remapping of K for analytical (non-IBL) lighting per [Kar13].
float r = (roughness + 1.0);
float k = (r * r) / 8.0;
// [Sch94] approximation of [Smi67] equation for [Bec63].
return (NdotV) / (NdotV * (1.0 - k) + k);
}
// Geometric shadowing function, used for G in Cook-Torrance BRDF.
float GeometrySmith(float NdotV, float NdotL, float roughness) {
// Smith geometric shadowing function.
// [GA19] cites [Hei14] as demonstrating [Smi97] to be correct.
float ggx2 = GeometrySchlickGGX(NdotV, roughness);
float ggx1 = GeometrySchlickGGX(NdotL, roughness);
return ggx1 * ggx2;
}
// Cook-Torrance [CT82] specular model.
vec3 SpecularCookTorrance(float NdotH, float NdotV, float NdotL, vec3 F, float roughness) {
float NDF = DistributionGGX(NdotH, roughness);
float G = GeometrySmith(NdotV, NdotL, roughness);
// F is taken in as a pre-computed argument for optimization purposes (it's reused for the
// lambertian component of the lighting model).
// Form from [Kar13], decuced from [CT82].
vec3 specular = (NDF * G * F) / max((4.0 * NdotV * NdotL), 0.0001);
return specular;
}
void main() {
vec3 cameraPos = ubo.cameraPos.xyz / ubo.cameraPos.w;
// Normal of this fragment.
vec3 N = normalize(fragNormal);
// Unit vector pointing at camera from this fragment.
vec3 V = normalize(cameraPos - fragWorldPos);
// Texture parameters for this fragment.
vec3 albedo = texture(texSamplerDiffuse, fragTexCoord).xyz;
float roughness = texture(texSamplerRoughness, fragTexCoord).x;
float metallic = 0.0;
float dielectric = 1.0 - metallic;
// Absolute Specular Reflectance at normal incidence. Ie., the base reflectivity of a
// material when looking straight at it.
// Trick from https://learnopengl.com/PBR/Theory : encode the reflectivity in the albedo for
// metallic materials (as they have no albedo). Otherwise, default to a typical reflectivity
// for non-metallic (dielectric) materials (0.04).
vec3 F0 = vec3(0.04);
F0 = mix(F0, albedo, metallic);
// Luminance of this fragment.
// Luminance is defined as the sum (integral) of all ilncoming illuminance over the half-sphere
// 'above' that point. As we currently only support analytic lighting (ie. omni lights), we
// integrate by iterating over all luminance sources, that currently are point lights.
vec3 Lo = vec3(0.0);
for (int i = 0; i < 3; ++i) {
vec3 lightPos = ubo.omniLights[i].pos.xyz;
vec3 lightColor = ubo.omniLights[i].color.xyz;
// Unit vector pointing at light from fragment.
vec3 L = normalize(lightPos - fragWorldPos);
// Half-vector between to-light and to-camera unit vectors.
vec3 H = normalize(V + L);
// Dot products re-used across further computation for this (fragment, light) pair.
float HdotV = max(dot(H, V), 0.0);
float NdotH = max(dot(N, H), 0.0);
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
// Translate luminous flux (lumen) into luminous intensity at this solid angle (candela).
// This follows the derivation in [GA19] (58).
float distance = length(lightPos - fragWorldPos);
vec3 intensity = (lightColor / (4 * PI * (distance * distance)));
// The Fresnel component from the Cook-Torrance specular BRDF is also used to calculate the
// lambertian diffuse weight kD. We calculate it outside of the function.
vec3 F = FresnelSchlick(HdotV, F0);
// Cook-Torrance specular value.
vec3 specular = SpecularCookTorrance(NdotH, NdotV, NdotL, F, roughness);
// Lambertian diffuse component, influenced by fresnel and dielectric/metalness.
vec3 kD = (vec3(1.0) - F) * dielectric;
// Lambertian diffuse value.
vec3 diffuse = albedo / PI;
// Illuminance for this point from this light is a result of scaling the luminous
// intensity of this light by the BRDL and by (N o L). This follows the definitions
// of illuminance and luminous intensity.
vec3 Li = (kD * diffuse + specular) * intensity * NdotL;
// Integration of luminance from illuminance.
Lo += Li;
}
vec3 ambient = vec3(0.00) * albedo;
vec3 color = ambient + Lo;
outColor = vec4(color, 1.0);
}