From e05c77d2e3582a6962f42dcec0fc5f7845ff448d Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Wed, 5 May 2021 12:10:13 +0300 Subject: [PATCH] Implement ambient lighting in the Cook-Torrance shader The fresnel lookup texture used here is the same that will be needed for image-based lighting. --- builtin_data/_pbr_fresnel_lookup.glsl | 43 +++++++++++++++++++++++++++ builtin_data/_pbr_prefilter.glsl | 27 +++++++++++++++++ shaderlib/cooktorrance.glsl | 13 ++++++++ source/materials/pbrmaterial.cpp | 39 +++++++++++++++++++++++- source/materials/pbrmaterial.h | 6 ++++ 5 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 builtin_data/_pbr_fresnel_lookup.glsl create mode 100644 builtin_data/_pbr_prefilter.glsl diff --git a/builtin_data/_pbr_fresnel_lookup.glsl b/builtin_data/_pbr_fresnel_lookup.glsl new file mode 100644 index 00000000..0ba97072 --- /dev/null +++ b/builtin_data/_pbr_fresnel_lookup.glsl @@ -0,0 +1,43 @@ +import _pbr_prefilter; + +#pragma MSP stage(vertex) +layout(location=0) in vec4 vertex; +void main() +{ + gl_Position = vertex; + out vec2 texcoord = vertex.xy*0.5+0.5; +} + +#pragma MSP stage(fragment) +layout(location=0) out vec2 frag_out; +void main() +{ + vec3 normal = vec3(0.0, 0.0, 1.0); + vec3 look_dir = vec3(sqrt(1.0-texcoord.y*texcoord.y), 0.0, -texcoord.y); + float roughness = texcoord.x; + + float geom_k = roughness*roughness/2.0; + float geom_look = -look_dir.z/(geom_k-look_dir.z*(1.0-geom_k)); + + vec2 sum = vec2(0.0); + for(int i=0; i0) + { + float geom_light = light_dir.z/(geom_k+light_dir.z*(1.0-geom_k)); + float geom = geom_look*geom_light; + + // Look_dir points towards the surface, so the dot product is negated + float half_dot_look = dot(halfway, look_dir); + float ng = geom*half_dot_look/(halfway.z*look_dir.z); + float a = pow(max(1+half_dot_look, 0.0), 5.0); + + sum += vec2(ng*(1-a), ng*a); + } + } + + frag_out = sum/n_samples; +} diff --git a/builtin_data/_pbr_prefilter.glsl b/builtin_data/_pbr_prefilter.glsl new file mode 100644 index 00000000..5f446891 --- /dev/null +++ b/builtin_data/_pbr_prefilter.glsl @@ -0,0 +1,27 @@ +uniform PrecalcParams +{ + int n_samples; +}; + +const float PI = 3.1415926535; + +vec2 hammersley(int i, int count) +{ + float y = bitfieldReverse(uint(i))*2.3283064e-10; + return vec2(float(i)/count, y); +} + +vec3 uv_to_hemisphere(float u, float v) +{ + float phi = u*2.0*PI; + float r = sqrt(1.0-v*v); + return vec3(cos(phi)*r, sin(phi)*r, v); +} + +vec3 ndist_ggxtr_importance_sample(vec2 uv, float roughness) +{ + float rough_q = roughness*roughness; + rough_q *= rough_q; + float v = sqrt((1.0-uv.y)/(1.0+(rough_q-1.0)*uv.y)); + return uv_to_hemisphere(uv.x, v); +} diff --git a/shaderlib/cooktorrance.glsl b/shaderlib/cooktorrance.glsl index 8e95752f..f8aed826 100644 --- a/shaderlib/cooktorrance.glsl +++ b/shaderlib/cooktorrance.glsl @@ -20,6 +20,7 @@ uniform sampler2D metalness_map; uniform sampler2D roughness_map; uniform sampler2D occlusion_map; uniform sampler2D emission_map; +uniform sampler2D fresnel_lookup; layout(constant_id=auto) const bool use_base_color_map = false; layout(constant_id=auto) const bool use_metalness_map = false; @@ -131,6 +132,16 @@ vec3 cooktorrance_one_light_direct(vec3 normal, vec3 look, vec3 light, vec3 ligh return max(dot(normal, light), 0.0)*light_color*(k_diff*lambert_diffuse(base_color)+k_spec*ndist*geom/spec_denom); } +vec3 cooktorrance_environment(vec3 normal, vec3 look, vec3 base_color, float metalness, float roughness) +{ + vec3 f0 = mix(vec3(0.04), base_color, metalness); + vec2 scale_bias = texture(fresnel_lookup, vec2(roughness, max(dot(normal, -look), 0.0))).rg; + vec3 k_spec = f0*scale_bias.x+scale_bias.y; + vec3 k_diff = (1.0-k_spec)*(1.0-metalness); + + return (k_diff*base_color+k_spec)*ambient_color.rgb; +} + vec3 cooktorrance_lighting(vec3 normal, vec3 look, vec3 base_color, float metalness, float roughness) { vec3 light = normalize(world_light_dir); @@ -138,6 +149,8 @@ vec3 cooktorrance_lighting(vec3 normal, vec3 look, vec3 base_color, float metaln float shadow = get_shadow_factor(0); vec3 color = cooktorrance_one_light_direct(normal, look, light, light_sources[0].color, base_color, metalness, roughness)*shadow; + color += cooktorrance_environment(normal, look, base_color, metalness, roughness); + color *= get_occlusion_value(); if(use_emission) diff --git a/source/materials/pbrmaterial.cpp b/source/materials/pbrmaterial.cpp index 7c73996f..783a8bed 100644 --- a/source/materials/pbrmaterial.cpp +++ b/source/materials/pbrmaterial.cpp @@ -1,4 +1,9 @@ +#include "framebuffer.h" +#include "mesh.h" #include "pbrmaterial.h" +#include "renderer.h" +#include "resources.h" +#include "texture2d.h" using namespace std; @@ -13,10 +18,12 @@ const Tag PbrMaterial::texture_tags[] = Tag("roughness_map"), Tag("occlusion_map"), Tag("emission_map"), + Tag("fresnel_lookup"), Tag() }; -PbrMaterial::PbrMaterial() +PbrMaterial::PbrMaterial(): + fresnel_lookup(get_or_create_fresnel_lookup()) { set_base_color(0.8f); set_metalness(0.0f); @@ -24,6 +31,34 @@ PbrMaterial::PbrMaterial() set_emission(0.0f); } +const Texture2D &PbrMaterial::get_or_create_fresnel_lookup() +{ + Resources &resources = Resources::get_global(); + + static const string name = "_pbr_env_fresnel_lookup.tex2d"; + Texture2D *fresnel_lookup = resources.find(name); + if(fresnel_lookup) + return *fresnel_lookup; + + fresnel_lookup = new Texture2D; + fresnel_lookup->storage(RG8, 128, 128, 1); + resources.add(name, fresnel_lookup); + + const Program &shprog = resources.get("_pbr_fresnel_lookup.glsl.shader"); + ProgramData shdata; + shdata.uniform("n_samples", 1024); + + const Mesh &mesh = resources.get("_fullscreen_quad.mesh"); + Framebuffer fresnel_lookup_fbo; + fresnel_lookup_fbo.attach(COLOR_ATTACHMENT0, *fresnel_lookup); + Bind bind_fbo(fresnel_lookup_fbo); + Renderer renderer; + renderer.set_shader_program(&shprog, &shdata); + mesh.draw(renderer); + + return *fresnel_lookup; +} + void PbrMaterial::fill_program_info(string &module_name, map &spec_values) const { module_name = "cooktorrance.glsl"; @@ -64,6 +99,8 @@ const Texture *PbrMaterial::get_texture(Tag tag) const return occlusion.texture; else if(tag==texture_tags[5]) return emission.texture; + else if(tag==texture_tags[6]) + return &fresnel_lookup; else return 0; } diff --git a/source/materials/pbrmaterial.h b/source/materials/pbrmaterial.h index 3e9b26ad..7c0d5459 100644 --- a/source/materials/pbrmaterial.h +++ b/source/materials/pbrmaterial.h @@ -6,6 +6,8 @@ namespace Msp { namespace GL { +class Texture2D; + class PbrMaterial: public Material { public: @@ -29,12 +31,16 @@ private: Property roughness; Property occlusion; Property emission; + const Texture2D &fresnel_lookup; static const Tag texture_tags[]; public: PbrMaterial(); +private: + static const Texture2D &get_or_create_fresnel_lookup(); + protected: virtual void fill_program_info(std::string &, std::map &) const; -- 2.43.0