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",