]> git.tdb.fi Git - libs/gl.git/commitdiff
Implement ambient lighting in the Cook-Torrance shader
authorMikko Rasa <tdb@tdb.fi>
Wed, 5 May 2021 09:10:13 +0000 (12:10 +0300)
committerMikko Rasa <tdb@tdb.fi>
Wed, 5 May 2021 11:36:06 +0000 (14:36 +0300)
The fresnel lookup texture used here is the same that will be needed for
image-based lighting.

builtin_data/_pbr_fresnel_lookup.glsl [new file with mode: 0644]
builtin_data/_pbr_prefilter.glsl [new file with mode: 0644]
shaderlib/cooktorrance.glsl
source/materials/pbrmaterial.cpp
source/materials/pbrmaterial.h

diff --git a/builtin_data/_pbr_fresnel_lookup.glsl b/builtin_data/_pbr_fresnel_lookup.glsl
new file mode 100644 (file)
index 0000000..0ba9707
--- /dev/null
@@ -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; i<n_samples; ++i)
+       {
+               vec3 halfway = ndist_ggxtr_importance_sample(hammersley(i, n_samples), roughness);
+               vec3 light_dir = reflect(look_dir, halfway);
+
+               if(light_dir.z>0)
+               {
+                       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 (file)
index 0000000..5f44689
--- /dev/null
@@ -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);
+}
index 8e95752f7d6dc71b6e8e8fcc942ff2b3da518743..f8aed8267273fcb4f1ac97c929e3823ba29dbcb0 100644 (file)
@@ -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)
index 7c73996fd2f7b8e91af2ab4eb27d08fd5592de70..783a8bed2c37eab456e2283e5274d97394fc2075 100644 (file)
@@ -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<Texture2D>(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<Program>("_pbr_fresnel_lookup.glsl.shader");
+       ProgramData shdata;
+       shdata.uniform("n_samples", 1024);
+
+       const Mesh &mesh = resources.get<Mesh>("_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<string, int> &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;
 }
index 3e9b26ade8d525aff7b08c4d399d34f36e5d8f0c..7c0d545936db21b5464c20bf60512f3566b94ae2 100644 (file)
@@ -6,6 +6,8 @@
 namespace Msp {
 namespace GL {
 
+class Texture2D;
+
 class PbrMaterial: public Material
 {
 public:
@@ -29,12 +31,16 @@ private:
        Property<float> roughness;
        Property<float> occlusion;
        Property<Color> 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<std::string, int> &) const;