for u in material.uniforms:
ss.sub.append(Statement("uniform", u.name, *u.values[:u.size]))
st.sub.append(ss)
- elif material.receive_shadows:
- st.sub.append(Statement("receive_shadows", True))
+ else:
+ if material.receive_shadows:
+ st.sub.append(Statement("receive_shadows", True))
+ if material.image_based_lighting:
+ st.sub.append(Statement("image_based_lighting", True))
tech_res.statements.append(st)
self.shader = material.shader
self.receive_shadows = material.receive_shadows
self.cast_shadows = (material.shadow_method!='NONE')
+ self.image_based_lighting = material.image_based_lighting
if self.render_mode=='EXTERNAL' and not self.technique:
raise Exception("Invalid configuration on material {}: No technique for external rendering".format(self.name))
self.layout.prop(mat, "shader")
elif mat.render_mode=='EXTERNAL':
self.layout.prop(mat, "technique")
- self.layout.prop(mat, "array_atlas")
if mat.render_mode=='BUILTIN':
self.layout.prop(mat, "receive_shadows")
+ self.layout.prop(mat, "image_based_lighting")
+ self.layout.prop(mat, "array_atlas")
if mat.array_atlas:
self.layout.prop(mat, "array_layer")
if mat.render_mode!='EXTERNAL':
bpy.types.Material.technique = bpy.props.StringProperty(name="Custom technique", description="Name of an external technique to use for rendering")
bpy.types.Material.shader = bpy.props.StringProperty(name="Custom shader", description="Name of an external technique to use for rendering")
bpy.types.Material.receive_shadows = bpy.props.BoolProperty(name="Receive shadows", description="Receive shadows from a shadow map", default=True)
+ bpy.types.Material.image_based_lighting = bpy.props.BoolProperty(name="Image based lighting", description="Use an environment map for ambient lighting", default=False)
bpy.types.Material.array_atlas = bpy.props.BoolProperty(name="Texture array atlas", description="The material is stored in a texture array")
bpy.types.Material.array_layer = bpy.props.IntProperty("Texture array layer", description="Layer of the texture array atlas to use")
bpy.types.Material.material_atlas = bpy.props.BoolProperty(name="Material atlas", description="Make this material part of a material atlas")
--- /dev/null
+import cubemap_effect;
+import _pbr_prefilter;
+
+#pragma MSP stage(fragment)
+layout(location=0) out vec3 frag_color;
+void main()
+{
+ vec3 normal = normalize(texcoord);
+ vec3 tangent = normalize(abs(normal.x)>abs(normal.y) ? vec3(-normal.z, 0.0, normal.x) : vec3(0.0, -normal.z, normal.y));
+ mat3 orientation = mat3(tangent, cross(normal, tangent), normal);
+
+ vec3 sum = vec3(0.0);
+ for(int i=0; i<n_samples; ++i)
+ {
+ vec2 uv = hammersley(i, n_samples);
+ vec3 dir = orientation*uv_to_hemisphere(uv.x, sqrt(1.0-uv.y));
+ sum += textureLod(environment_map, dir, 0).rgb;
+ }
+
+ frag_color = sum/n_samples;
+}
--- /dev/null
+import cubemap_effect;
+import _pbr_prefilter;
+
+#pragma MSP stage(fragment)
+layout(location=0) out vec3 frag_color;
+void main()
+{
+ vec3 normal = normalize(texcoord);
+ vec3 tangent = normalize(abs(normal.x)>abs(normal.y) ? vec3(-normal.z, 0.0, normal.x) : vec3(0.0, -normal.z, normal.y));
+ mat3 orientation = mat3(tangent, cross(normal, tangent), normal);
+
+ vec3 sum = vec3(0.0);
+ float weight = 0.0;
+ for(int i=0; i<n_samples; ++i)
+ {
+ vec3 halfway = orientation*ndist_ggxtr_importance_sample(hammersley(i, n_samples), roughness);
+ vec3 light_dir = reflect(-normal, halfway);
+
+ float n_dot_light = dot(normal, light_dir);
+
+ if(n_dot_light>0)
+ {
+ sum += textureLod(environment_map, light_dir, 0).rgb*n_dot_light;
+ weight += n_dot_light;
+ }
+ }
+
+ frag_color = sum/weight;
+}
--- /dev/null
+filter LINEAR_MIPMAP_LINEAR;
+wrap CLAMP_TO_EDGE;
uniform PrecalcParams
{
int n_samples;
+ float roughness;
};
+uniform samplerCube environment_map;
+
const float PI = 3.1415926535;
vec2 hammersley(int i, int count)
uniform sampler2D normal_map;
uniform samplerCube environment_map;
+uniform samplerCube irradiance_map;
layout(constant_id=auto) const bool use_normal_map = false;
return normalize(world_normal);
}
-virtual vec3 get_environment_sample(vec3 direction)
+virtual vec3 get_environment_sample(vec3 direction, float roughness)
{
- return texture(environment_map, env_world_matrix*direction).rgb;
+ float lod = (2-roughness)*roughness*(textureQueryLevels(environment_map)-1);
+ return textureLod(environment_map, env_world_matrix*direction, lod).rgb;
}
virtual vec3 get_reflection(vec3 normal, vec3 look)
{
vec3 reflect_dir = reflect(look, normal);
- return get_environment_sample(reflect_dir);
+ return get_environment_sample(reflect_dir, 0.0);
+}
+
+virtual vec3 get_irradiance_sample(vec3 normal)
+{
+ return texture(irradiance_map, env_world_matrix*normal).rgb;
}
vec3 apply_fog(vec3 color)
layout(constant_id=auto) const bool use_occlusion_map = false;
layout(constant_id=auto) const bool use_emission = false;
layout(constant_id=auto) const bool use_emission_map = false;
+layout(constant_id=auto) const bool use_image_based_lighting = false;
const float PI = 3.1415926535;
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;
+ if(use_image_based_lighting)
+ {
+ vec3 irradiance = get_irradiance_sample(normal);
+ vec3 reflection = get_environment_sample(reflect(look, normal), roughness).rgb;
+
+ return k_diff*irradiance*base_color+k_spec*reflection;
+ }
+ else
+ return (k_diff*base_color+k_spec)*ambient_color.rgb;
}
vec3 cooktorrance_lighting(vec3 normal, vec3 look, vec3 base_color, float metalness, float roughness)
--- /dev/null
+uniform CubeParams
+{
+ mat3 faces[6];
+};
+
+#pragma MSP stage(vertex)
+layout(location=0) in vec4 vertex;
+void main()
+{
+ gl_Position = vertex;
+}
+
+#pragma MSP stage(geometry)
+layout(triangles) in;
+layout(triangle_strip, max_vertices=18) out;
+void main()
+{
+ for(int i=0; i<6; ++i)
+ {
+ gl_Layer = i;
+ for(int j=0; j<3; ++j)
+ {
+ gl_Position = gl_in[j].gl_Position;
+ out vec3 texcoord = faces[i]*vec3(gl_in[j].gl_Position.xy, 1.0);
+ EmitVertex();
+ }
+ EndPrimitive();
+ }
+}
#include <algorithm>
#include <cmath>
#include "environmentmap.h"
+#include "mesh.h"
#include "renderer.h"
#include "resources.h"
#include "texunit.h"
EnvironmentMap::EnvironmentMap(unsigned s, Renderable &r, Renderable &e):
Effect(r),
environment(e),
- sampler(Resources::get_global().get<Sampler>("_linear_clamp.samp"))
+ irradiance_shprog(Resources::get_global().get<Program>("_envmap_irradiance.glsl.shader")),
+ specular_shprog(Resources::get_global().get<Program>("_envmap_specular.glsl.shader")),
+ fullscreen_mesh(Resources::get_global().get<Mesh>("_fullscreen_quad.mesh")),
+ sampler(Resources::get_global().get<Sampler>("_linear_clamp.samp")),
+ mip_sampler(Resources::get_global().get<Sampler>("_mip_clamp.samp"))
{
- init(s, RGB8);
+ init(s, RGB8, 1);
}
EnvironmentMap::EnvironmentMap(unsigned s, PixelFormat f, Renderable &r, Renderable &e):
Effect(r),
environment(e),
- sampler(Resources::get_global().get<Sampler>("_linear_clamp.samp"))
+ irradiance_shprog(Resources::get_global().get<Program>("_envmap_irradiance.glsl.shader")),
+ specular_shprog(Resources::get_global().get<Program>("_envmap_specular.glsl.shader")),
+ fullscreen_mesh(Resources::get_global().get<Mesh>("_fullscreen_quad.mesh")),
+ sampler(Resources::get_global().get<Sampler>("_linear_clamp.samp")),
+ mip_sampler(Resources::get_global().get<Sampler>("_mip_clamp.samp"))
{
- init(s, f);
+ init(s, f, 1);
}
-void EnvironmentMap::init(unsigned s, PixelFormat f)
+EnvironmentMap::EnvironmentMap(unsigned s, PixelFormat f, unsigned l, Renderable &r, Renderable &e):
+ Effect(r),
+ environment(e),
+ irradiance_shprog(Resources::get_global().get<Program>("_envmap_irradiance.glsl.shader")),
+ specular_shprog(Resources::get_global().get<Program>("_envmap_specular.glsl.shader")),
+ fullscreen_mesh(Resources::get_global().get<Mesh>("_fullscreen_quad.mesh")),
+ sampler(Resources::get_global().get<Sampler>("_linear_clamp.samp")),
+ mip_sampler(Resources::get_global().get<Sampler>("_mip_clamp.samp"))
+{
+ init(s, f, l);
+}
+
+void EnvironmentMap::init(unsigned s, PixelFormat f, unsigned l)
{
+ if(!l || (1U<<(l-1))>=s)
+ throw invalid_argument("EnvironmentMap::EnvironmentMap");
+
size = s;
rendered = false;
update_interval = 1;
update_delay = 0;
- env_tex.storage(f, size);
+ env_tex.storage(f, size, l);
depth_buf.storage(DEPTH_COMPONENT32F, size, size);
for(unsigned i=0; i<6; ++i)
{
faces[i].camera.set_depth_clip(0.1, 100);
}
+ irradiance.storage(f, size/4, 1);
+ irradiance_fbo.attach_layered(COLOR_ATTACHMENT0, irradiance);
+
+ if(l>1)
+ {
+ specular_fbos.resize(l-1);
+ for(unsigned i=1; i<l; ++i)
+ specular_fbos[i-1].attach_layered(COLOR_ATTACHMENT0, env_tex, i);
+
+ LinAl::Matrix<float, 3, 3> face_matrices[6];
+ for(unsigned i=0; i<6; ++i)
+ {
+ GL::TextureCubeFace face = GL::TextureCube::enumerate_faces(i);
+ GL::Vector3 columns[3];
+ columns[0] = GL::TextureCube::get_s_direction(face);
+ columns[1] = GL::TextureCube::get_t_direction(face);
+ columns[2] = GL::TextureCube::get_face_direction(face);
+ face_matrices[i] = LinAl::Matrix<float, 3, 3>::from_columns(columns);
+ }
+
+ prefilter_shdata.uniform_array("faces", 6, &face_matrices[0]);
+ prefilter_shdata.uniform("n_samples", 128);
+ prefilter_shdata.uniform("roughness", 1.0f);
+ }
+
shdata.uniform("env_world_matrix", LinAl::SquareMatrix<float, 3>::identity());
}
renderer.set_camera(faces[i].camera);
renderer.render(environment);
}
+
+ irradiance_fbo.bind();
+ renderer.set_shader_program(&irradiance_shprog, &prefilter_shdata);
+ renderer.set_texture("environment_map", &env_tex, &sampler);
+ fullscreen_mesh.draw(renderer);
+
+ renderer.set_shader_program(&specular_shprog);
+ for(unsigned i=0; i<specular_fbos.size(); ++i)
+ {
+ prefilter_shdata.uniform("roughness", 1.0f-sqrt(1.0f-static_cast<float>(i+1)/specular_fbos.size()));
+ specular_fbos[i].bind();
+ fullscreen_mesh.draw(renderer);
+ }
}
void EnvironmentMap::finish_frame()
Renderer::Push _push_rend(renderer);
- renderer.set_texture("environment_map", &env_tex, &sampler);
+ renderer.set_texture("environment_map", &env_tex, &mip_sampler);
+ renderer.set_texture("irradiance_map", &irradiance, &sampler);
renderer.add_shader_data(shdata);
renderer.render(renderable, tag);
}
namespace Msp {
namespace GL {
+class Mesh;
+
/**
Creates a cube map texture of the surroundings of the renderable. This texture
can then be used to implement effects such as reflections or refractions.
TextureCube env_tex;
Renderbuffer depth_buf;
Face faces[6];
+
+ TextureCube irradiance;
+ const Program &irradiance_shprog;
+ Framebuffer irradiance_fbo;
+ const Program &specular_shprog;
+ std::vector<Framebuffer> specular_fbos;
+ ProgramData prefilter_shdata;
+ const Mesh &fullscreen_mesh;
+
const Sampler &sampler;
+ const Sampler &mip_sampler;
ProgramData shdata;
bool rendered;
unsigned update_interval;
public:
EnvironmentMap(unsigned size, Renderable &rend, Renderable &env);
EnvironmentMap(unsigned size, PixelFormat, Renderable &rend, Renderable &env);
+ EnvironmentMap(unsigned size, PixelFormat, unsigned, Renderable &rend, Renderable &env);
private:
- void init(unsigned, PixelFormat);
+ void init(unsigned, PixelFormat, unsigned);
public:
void set_depth_clip(float, float);
const Program &shprog = resources.get<Program>("_pbr_fresnel_lookup.glsl.shader");
ProgramData shdata;
shdata.uniform("n_samples", 1024);
+ // Not actually used here, but put it in to satisfy the interface
+ shdata.uniform("roughness", 0.0f);
const Mesh &mesh = resources.get<Mesh>("_fullscreen_quad.mesh");
Framebuffer fresnel_lookup_fbo;
shdata(0),
material(0),
back_faces(false),
- receive_shadows(false)
+ receive_shadows(false),
+ image_based_lighting(false)
{ }
void RenderPass::set_material_textures()
map<string, int> extra_spec;
if(receive_shadows)
extra_spec["use_shadow_map"] = true;
+ if(image_based_lighting)
+ extra_spec["use_image_based_lighting"] = true;
shprog = material->create_compatible_shader(extra_spec);
shprog.keep();
void RenderPass::Loader::init_actions()
{
add("shader", &Loader::shader);
+ add("image_based_lighting", &RenderPass::image_based_lighting);
add("material", &Loader::material_inline);
add("material", &Loader::material);
add("material_slot", &RenderPass::material_slot);
std::vector<TextureSlot> textures;
bool back_faces;
bool receive_shadows;
+ bool image_based_lighting;
public:
RenderPass();
bool get_back_faces() const { return back_faces; }
void set_receive_shadows(bool);
bool get_receive_shadows() const { return receive_shadows; }
+ void set_image_based_lighting(bool);
+ bool get_image_based_lighting() const { return image_based_lighting; }
void apply(Renderer &) const;