From 1ce28896082f15058630a7f2c044b6cf68eeef4f Mon Sep 17 00:00:00 2001 From: Sergiusz Bazanski Date: Sun, 26 Jul 2020 15:26:40 +0200 Subject: [PATCH] engine/shaders: add camera post-processing, split up We now also use shaderc instead of glslandValidator, which has #include. --- engine/shaders/BUILD | 3 +- engine/shaders/forward.frag | 189 +++++------------------------ engine/shaders/forward_brdf.frag | 166 +++++++++++++++++++++++++ engine/shaders/forward_defs.frag | 44 +++++++ engine/src/render/light.rs | 2 +- third_party/shaderc/BUILD.external | 1 + tools/bzl/glsl.bzl | 6 +- 7 files changed, 247 insertions(+), 164 deletions(-) create mode 100644 engine/shaders/forward_brdf.frag create mode 100644 engine/shaders/forward_defs.frag diff --git a/engine/shaders/BUILD b/engine/shaders/BUILD index 34dd84b..d4c8de2 100644 --- a/engine/shaders/BUILD +++ b/engine/shaders/BUILD @@ -12,8 +12,9 @@ glsl_binary( name = "forward_frag", srcs = [ "forward.frag", + "forward_brdf.frag", - #"forward_camera.frag", + "forward_defs.frag", ], visibility = ["//engine:__pkg__"], ) diff --git a/engine/shaders/forward.frag b/engine/shaders/forward.frag index 86b0573..3e62b79 100644 --- a/engine/shaders/forward.frag +++ b/engine/shaders/forward.frag @@ -17,126 +17,38 @@ // Abrasion. If not, see . // // 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), 7–24. -// doi: 10.1145/357290.357293 -// -// [Sch94] -// Christophe Schlick. 1994. "An Inexpensive BRDF Model for Physically-based Rendering" -// Computer Graphics Forum, 13(3), 233–246. -// 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 +#include "forward_defs.frag" +#include "forward_brdf.frag" -struct OmniLight { - vec4 pos; - vec4 color; -}; +/// Camera settings. +// Aperture (in f-stops) +const float CAMERA_APERTURE = 4.0; // f/8 and be there +// Shutter speed (in seconds) +const float CAMERA_SHUTTER = 1.0 / 60; // 180° shutter at 30FPS. +// Film sensitivity ('ISO') +const float CAMERA_SENSITIVITY = 3200.0; -layout(binding = 0) uniform FragmentUniformBufferObject { - vec4 cameraPos; - OmniLight omniLights[4]; -} ubo; -layout(binding = 1) uniform sampler2D texSamplerDiffuse; -layout(binding = 2) uniform sampler2D texSamplerRoughness; +// Exposure Value at ISO 100, per [Ray00] equation (12). +const float CAMERA_EV_100 = log2((CAMERA_APERTURE * CAMERA_APERTURE)/CAMERA_SHUTTER); +// Exposure value at CAMERA_SENSITIVITY +const float CAMERA_EV = CAMERA_EV_100 - log2(CAMERA_SENSITIVITY / 100); +const float CAMERA_EXPOSURE = 1.0 / (pow(2.0, CAMERA_EV) * 1.2); -layout(location = 0) in vec2 fragTexCoord; -layout(location = 1) in vec3 fragWorldPos; -layout(location = 2) in vec3 fragNormal; +const mat3 XYZ_TO_SRGB = mat3( + 3.2406, -0.9689, 0.0557, + -1.5372, 1.8758, -0.2040, + -0.4986, 0.0415, 1.0570 +); -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; +float GammaCorrect(float v) { + if (v <= 0.0031308) { + return 12.92 * v; + } + return 1.055 * pow(v, (1.0/2.4)) - 0.055; } @@ -162,52 +74,9 @@ void main() { 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 Lo = BRDFIlluminance(N, V, F0, albedo, dielectric, roughness); vec3 ambient = vec3(0.00) * albedo; - vec3 color = ambient + Lo; - outColor = vec4(color, 1.0); + vec3 color = XYZ_TO_SRGB * ((ambient + Lo) * CAMERA_EXPOSURE); + + outColor = vec4(GammaCorrect(color.x), GammaCorrect(color.y), GammaCorrect(color.z), 1.0); } diff --git a/engine/shaders/forward_brdf.frag b/engine/shaders/forward_brdf.frag new file mode 100644 index 0000000..0560596 --- /dev/null +++ b/engine/shaders/forward_brdf.frag @@ -0,0 +1,166 @@ +// Copyright 2020 Sergiusz 'q3k' Bazanski +// +// 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 . +// +// vim: set ft=glsl: + +// 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), 7–24. +// doi: 10.1145/357290.357293 +// +// [Sch94] +// Christophe Schlick. 1994. "An Inexpensive BRDF Model for Physically-based Rendering" +// Computer Graphics Forum, 13(3), 233–246. +// 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 + +#include "forward_defs.frag" + +// [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; +} + +vec3 BRDFIlluminance(vec3 N, vec3 V, vec3 F0, vec3 albedo, float dielectric, float roughness) { + // 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 < 4; ++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; + } + return Lo; +} diff --git a/engine/shaders/forward_defs.frag b/engine/shaders/forward_defs.frag new file mode 100644 index 0000000..9c5783b --- /dev/null +++ b/engine/shaders/forward_defs.frag @@ -0,0 +1,44 @@ +// Forward rendering fragment shader. +// +// Copyright 2020 Sergiusz 'q3k' Bazanski +// +// 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 . +// +// vim: set ft=glsl: + +#ifndef _FORWARD_DEFS_FRAG_ +#define _FORWARD_DEFS_FRAG_ + +const float PI = 3.14159; + +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; + +#endif diff --git a/engine/src/render/light.rs b/engine/src/render/light.rs index a613586..5ec19e0 100644 --- a/engine/src/render/light.rs +++ b/engine/src/render/light.rs @@ -43,7 +43,7 @@ impl Omni { pub fn test(position: cgm::Vector3) -> Self { Self { position, - color: color::XYZ::new(234.7, 214.1, 207.9), + color: color::XYZ::new(234.7*10.0, 214.1*10.0, 207.9*10.0), // TODO: use a better method id: time::SystemTime::now().duration_since(time::UNIX_EPOCH).unwrap().as_nanos() as u64, } diff --git a/third_party/shaderc/BUILD.external b/third_party/shaderc/BUILD.external index 537b1f9..01919f9 100644 --- a/third_party/shaderc/BUILD.external +++ b/third_party/shaderc/BUILD.external @@ -88,4 +88,5 @@ cc_binary( ":libshaderc", ":libshaderc_util", ], + visibility = ["//visibility:public"], ) diff --git a/tools/bzl/glsl.bzl b/tools/bzl/glsl.bzl index 22f27e3..0123a22 100644 --- a/tools/bzl/glsl.bzl +++ b/tools/bzl/glsl.bzl @@ -3,7 +3,9 @@ def _glsl_binary(ctx): binary = ctx.outputs.binary compiler = ctx.executable._compiler - args = ["-V", "-o", binary.path] + [s.path for s in srcs] + main = srcs[0].path + + args = [main, "-o", binary.path] ctx.actions.run( inputs=srcs, @@ -21,7 +23,7 @@ glsl_binary = rule( allow_files=True, ), "_compiler": attr.label( - default=Label("@glslang//:glslangValidator"), + default=Label("@shaderc//:glslc"), allow_single_file=True, executable=True, cfg="host",