Commit a60c43e1 authored by Johannes Braun's avatar Johannes Braun
Browse files

Improved Light structs and fixed advanced phong shading

parent 0d85c7ca
......@@ -4,7 +4,7 @@
<item name="window_size_x" value="1440"/>
<item name="window_size_y" value="900"/>
<item name="vsync" value="0"/>
<item name="msaa" value="2"/>
<item name="msaa" value="4"/>
</group>
<item name="splash_screen" value="/preferences/splash_01.xml"/>
......
......@@ -146,7 +146,7 @@ BSDFResult computeBSDF(const in Material material, const in vec2 random, const i
// Compute the microsurface normal probability and with that the final probability density function
// Clamp up denominator to not divide by zero.
float jacobian_reflect = 1 / clamp(4 * m_dot_out, 0.00001f, 1);
float jacobian_reflect = 1 / clamp(4 * m_dot_out, 0.0001f, 1);
float probability_m = hemisphere_sample.distribution * cos_theta;
float pdf = probability_m * jacobian_reflect;
......@@ -154,14 +154,14 @@ BSDFResult computeBSDF(const in Material material, const in vec2 random, const i
float ior_out_2 = ior_out * ior_out;
float jacobian_refract_denominator = ior_in * m_dot_in + ior_out * m_dot_out_refract;
jacobian_refract_denominator *= jacobian_refract_denominator;
jacobian_refract_denominator = clamp(jacobian_refract_denominator, 0.00001f, 1.f);
jacobian_refract_denominator = clamp(jacobian_refract_denominator, 0.0001f, 1.f);
//Take the absolute value as m_dot_out_refract is negative
float jacobian_refract = abs(ior_out_2 * m_dot_out_refract / jacobian_refract_denominator);
float pdf_refract = probability_m * jacobian_refract;
// Same here... clamp up denominator to not divide by zero
float brdf_denominator = clamp((4 * n_dot_in * n_dot_out), 0.0000001f, 1.f);
float brdf_denominator = clamp((4 * n_dot_in * n_dot_out), 0.0001f, 1.f);
float btdf_denominator = jacobian_refract_denominator;
// Finally, build the brdf. As we do russian roulette, we can just tint the brdf instead of multiplying with the fresnel value.
......
......@@ -87,7 +87,7 @@ vec4 loadColorStore(int id)
bool sampleLight(const in Light light, const in vec3 position, out vec3 point_on_light, out vec3 color, const in vec2 random_sample)
{
return u_light_sample[light.data.light_type % 4](light, position, point_on_light, color, random_sample);
return u_light_sample[light.type % 4](light, position, point_on_light, color, random_sample);
}
//////////////////////////////////////////////////////////////////////////
......@@ -145,15 +145,9 @@ bool shade(int id, inout vec3 radiance, uint bounce, out uint bsdf_id)
&& traces_data[id].properties.travelled_distance < u_linespace_properties.distance_threshold;
if (lights_data.length() > 0) {
//Select random light... well... at least if there are any.
//Select random light.
Light light = lights_data[int(floor(random_sample.x * lights_data.length()))];
//Shadow test
//float light_distance;
// vec3 light_color;
// float light_distance;
// bool success = sampleLight(light, vertex, random_sample, shadow_test, light_distance, light_color);
vec3 point_on_light;
vec3 light_color;
bool success = sampleLight(light, vertex.position.xyz, point_on_light, light_color, random_sample);
......
......@@ -3,6 +3,7 @@
#include <util/scene/light.glsl>
#include <util/scene/material.glsl>
#include <util/math/ggx.glsl>
layout(std430) restrict readonly buffer lightsBuffer
{
......@@ -13,7 +14,7 @@ subroutine uniform lightSample u_light_sample[4];
bool sampleLight(const in Light light, const in vec3 position, out vec3 point_on_light, out vec3 color, const in vec2 random_sample)
{
return u_light_sample[light.data.light_type % 4](light, position, point_on_light, color, random_sample);
return u_light_sample[light.type % 4](light, position, point_on_light, color, random_sample);
}
vec4 phong(const in Material material, const in LightShadow light_and_map, samplerCube environment, const in vec3 position, const in vec3 normal, const in vec3 camera)
......@@ -21,58 +22,66 @@ vec4 phong(const in Material material, const in LightShadow light_and_map, sampl
vec3 color = vec3(0);
vec3 point_on_light;
vec3 phong_diffuse = vec3(0);
vec3 phong_specular = vec3(0);
vec3 eye = normalize(camera-position.xyz);
vec3 reflection_vector = reflect(-eye, normal.xyz);
vec3 environment_reflect = textureLod(environment, reflection_vector, material.roughness.value * 8).rgb;
float cos_phi_n_reflect = 1;//pow(max(dot(reflection_vector, eye), 0.0f), phong_shininess);//20+1-(1+99*material.roughness.value));
vec3 environment_refract = vec3(0);
//Tinting.
vec3 reflective_tint = mix(vec3(1), material.base.value, material.metallic.value);
environment_reflect *= reflective_tint * cos_phi_n_reflect;
vec3 refraction = vec3(0);
if(material.transmission.value > 0)
{
vec3 refraction_vector = refract(-eye, normal.xyz, 1 / material.ior);
environment_refract = material.base.value * mix(vec3(0),
textureLod(environment, refraction_vector, material.roughness.value * 8).rgb,
material.transmission.value);
}
float shadowing = 1;
if(light_and_map.light.sampleLight(position, point_on_light, color.rgb, vec2(-1))) {
vec3 point_to_light = normalize(point_on_light - position);
float shadowing = 1.f;//sampleShadowMap(light_and_map, position, point_to_light, normal, camera);
shadowing = sampleShadowMap(light_and_map, position, point_to_light, normal, camera);
if(shadowing <= 0)
{
return vec4(0, 0, 0, 1);
}
vec3 eye = normalize(camera-position.xyz);
vec3 half_vector = normalize(eye + point_to_light);
//DIFFUSE
float cos_phi = max(dot(normalize(normal.xyz), normalize(point_to_light)), 0.0f);
vec3 light_reflection_vector = reflect(-point_to_light, normal.xyz);
vec3 light_reflection_vector = normalize(reflect(-point_to_light, normal.xyz));
float phong_shininess = 20+100-(1+99*material.roughness.value);
//SPECULAR
float cos_phi_n = pow(max(dot(light_reflection_vector, eye), 0.0f), 1-(1+99*material.roughness.value));
vec3 reflection_vector = reflect(-eye, normal.xyz);
vec3 environment_reflect = textureLod(environment, reflection_vector, material.roughness.value * 8).rgb;
vec3 environment_refract = vec3(0);
//Tinting.
vec3 reflective_tint = mix(vec3(1), material.base.value, material.metallic.value);
environment_reflect *= reflective_tint;
float cos_phi_n = pow(max(dot(light_reflection_vector, eye), 0.0f), phong_shininess);
vec3 refraction = vec3(0);
if(material.transmission.value > 0)
{
vec3 refraction_vector = refract(-eye, normal.xyz, 1 / material.ior);
environment_refract = material.base.value * mix(vec3(0),
textureLod(environment, refraction_vector, material.roughness.value * 8).rgb,
max(material.transmission.value, material.metallic.value));
}
float k_s = material.roughness.value;
float k_rest = 1-k_s;
float k_t = k_rest * material.transmission.value;
float k_d = 1 - k_s - k_t;
vec3 phong_diffuse = material.base.value * cos_phi * color;
vec3 phong_specular = (reflective_tint * cos_phi_n * color);
return vec4(shadowing * (phong_diffuse + phong_specular), 1);
phong_diffuse = shadowing * material.base.value * color.rgb * cos_phi;
phong_specular = shadowing * reflective_tint * cos_phi_n * color;//(environment_reflect + reflective_tint * cos_phi_n * color) / 2;
}
else
{
//color may still be set e.g. by ambient
//if this else is triggered by a low attenuation (example), it shall be assured that the color will be set to a near-zero value.
return vec4(color, 1);
phong_diffuse = color;
}
// called F0 in several papers, it helps computing the fresnel via Schlick's approximation
float fresnel = fresnelSchlick(max(0, dot(eye, normal)), normalResponse(material.ior, material.metallic.value));
float k_s = fresnel;
float k_t = (1-k_s) * material.transmission.value;
float k_d = 1 - k_s - k_t;
return vec4(k_d * (phong_diffuse + material.base.value * textureLod(environment, normal, 8).rgb) + k_t * environment_refract + k_s * (environment_reflect + phong_specular), 1);
}
#endif // !__GBUFFER_LIGHTS_GLH
......@@ -48,23 +48,18 @@ void main()
parallax_texcoord = out_texcoord + (to_eye.xy * v);
}
output_base_ior.rgb = u_material.getBase(parallax_texcoord);
output_base_ior.a = u_material.ior;
output_modelview_position = out_view_position;
output_world_position = out_world_position;
output_rough_metallic_transmit_emit.b = u_material.getTransmission(parallax_texcoord);
// discard fragments via the transparency map.
if(output_rough_metallic_transmit_emit.b > 0.2){
discard;
}
output_rough_metallic_transmit_emit.r = u_material.getRoughness(parallax_texcoord);
output_rough_metallic_transmit_emit.g = u_material.getMetallic(parallax_texcoord);
output_rough_metallic_transmit_emit.a = u_material.getEmission(parallax_texcoord);
output_base_ior.rgb = u_material.getBase(parallax_texcoord);
output_base_ior.a = u_material.ior;
output_normal = u_material.has_normal_map ?
vec4(out_tangent_space * normalize((texture(u_material.normal_map, parallax_texcoord).xyz)*2.0 - 1.0), 0) :
vec4(out_normal.rgb, 0);
......
......@@ -3,7 +3,7 @@
#include <util/math/constants.glsl>
const float RANDOM_SAMPLING_FACTOR = 1000000.0;
const float RANDOM_SAMPLING_FACTOR = 100000000.0;
const float INV_RANDOM_SAMPLING_FACTOR = 1.0/RANDOM_SAMPLING_FACTOR;
//Method to generate a pseudo-random seed.
......
......@@ -30,13 +30,13 @@ struct Camera
void depthOfField(const in Camera camera, inout Ray ray, const in vec2 random)
{
// Sample a point on a unit disk
float r = sqrt(random.x);
float r = random.x;
float theta = 2 * PI * random.y;
float x = r * cos(theta);
float y = r * sin(theta);
// multiply radius
vec3 offset = camera.dof_size * (x * camera.axis_x_scaled + y * camera.axis_y_scaled);
vec3 offset = camera.dof_size * (x * camera.axis_x_scaled + y * camera.axis_y_scaled);
vec3 focus = ray.origin + camera.focus_distance * ray.direction;
ray.origin += offset;
ray.direction = normalize(focus - ray.origin);
......@@ -44,17 +44,22 @@ void depthOfField(const in Camera camera, inout Ray ray, const in vec2 random)
Ray getRayFromPixel(const in Camera camera, const in vec2 pixel, const in vec2 sub_pixel)
{
// Correct subpixel offset for an overall smoother image.
const float sub_pixel_correction = sqrt(2);
const vec2 sub_pixel_offset = 2*sub_pixel - 1;
Ray ray;
ray.origin = camera.position;
ray.direction = camera.bottom_left +
(pixel.x + sub_pixel.x) * camera.axis_x_scaled +
(pixel.y + sub_pixel.y) * camera.axis_y_scaled -
(pixel.x + sub_pixel_offset.x) * camera.axis_x_scaled +
(pixel.y + sub_pixel_offset.y) * camera.axis_y_scaled -
camera.position;
vec2 final_pixel = pixel + sub_pixel;
ray.px = final_pixel.x;
ray.py = final_pixel.y;
camera.depthOfField(ray, sub_pixel);
camera.depthOfField(ray, sub_pixel_offset);
return ray;
}
......
......@@ -4,7 +4,7 @@
#include <util/math/random.glsl>
const float DIRECTIONAL_DISTANCE = 1000000.f;
const float DIRECTIONAL_SMOOTH_DISTANCE = 10.f;
const float DIRECTIONAL_SMOOTH_DISTANCE = 1.f;
////////////////////////////////////////////////////////////////////
////
......@@ -17,23 +17,63 @@ struct Attenuation
float constant;
float linear;
float quadratic;
int p;
};
struct LightParameters
const uint ePoint = 0;
const uint eSpot = 1;
const uint eDirectional = 2;
const uint eAmbient = 3;
const uint eQuad = 4;
struct PointLight
{
int light_type;
float params[2];
int p;
vec3 position;
float radius;
vec3 color;
float pd_01;
Attenuation attenuation;
};
struct Light
struct DirectionalLight
{
vec3 direction;
float radius;
vec3 color;
};
struct SpotLight
{
vec4 position;
vec4 direction;
vec4 color;
vec3 position;
float radius;
vec3 direction;
float angle;
vec3 color;
float falloff;
Attenuation attenuation;
LightParameters data;
};
struct AmbientLight
{
vec3 color;
};
struct QuadLight
{
vec3 position;
float scale_x;
vec3 direction;
float scale_y;
vec3 color;
float pd_01;
Attenuation attenuation;
};
struct Light
{
vec4 dataset_01[3];
vec3 dataset_02;
uint type;
};
struct ShadowMap
......@@ -77,9 +117,74 @@ const vec2 poissonDisk[16] = vec2[](
subroutine bool lightSample(const in Light light, const in vec3 position, out vec3 point_on_light, out vec3 color, const in vec2 random_sample);
float attenuation(const in Light light, float distance)
PointLight asPoint(const in Light light)
{
PointLight point_light;
point_light.position = light.dataset_01[0].xyz;
point_light.radius = light.dataset_01[0].w;
point_light.color = light.dataset_01[1].rgb;
point_light.attenuation.constant = light.dataset_01[2].x;
point_light.attenuation.linear = light.dataset_01[2].y;
point_light.attenuation.quadratic = light.dataset_01[2].z;
return point_light;
}
DirectionalLight asDirectional(const in Light light)
{
return 1 / (light.attenuation.constant + distance * light.attenuation.linear + pow(distance, 2) * light.attenuation.quadratic);
DirectionalLight directional_light;
directional_light.direction = light.dataset_01[0].xyz;
directional_light.radius = light.dataset_01[0].w;
directional_light.color = light.dataset_01[1].rgb;
return directional_light;
}
SpotLight asSpot(const in Light light)
{
SpotLight spot_light;
spot_light.position = light.dataset_01[0].xyz;
spot_light.radius = light.dataset_01[0].w;
spot_light.direction = light.dataset_01[1].xyz;
spot_light.angle = light.dataset_01[1].w;
spot_light.color = light.dataset_01[2].rgb;
spot_light.falloff = light.dataset_01[2].w;
spot_light.attenuation.constant = light.dataset_02.x;
spot_light.attenuation.linear = light.dataset_02.y;
spot_light.attenuation.quadratic = light.dataset_02.z;
return spot_light;
}
AmbientLight asAmbient(const in Light light)
{
AmbientLight ambient_light;
ambient_light.color = light.dataset_01[0].rgb;
return ambient_light;
}
QuadLight asQuad(const in Light light)
{
QuadLight quad_light;
quad_light.position = light.dataset_01[0].xyz;
quad_light.scale_x = light.dataset_01[0].w;
quad_light.direction = light.dataset_01[1].xyz;
quad_light.scale_y = light.dataset_01[1].w;
quad_light.color = light.dataset_01[2].rgb;
quad_light.attenuation.constant = light.dataset_02.x;
quad_light.attenuation.linear = light.dataset_02.y;
quad_light.attenuation.quadratic = light.dataset_02.z;
return quad_light;
}
float attenuate(const in Attenuation attenuation, float distance)
{
return 1 / (attenuation.constant + distance * attenuation.linear + pow(distance, 2) * attenuation.quadratic);
}
float sampleShadowMap(const in LightShadow light, const in vec3 position, const in vec3 to_light, const in vec3 normal, const in vec3 camera) {
......@@ -89,21 +194,20 @@ float sampleShadowMap(const in LightShadow light, const in vec3 position, const
vec4 shadow_coord = light.shadow_map.matrix * vec4(position.xyz, 1); // clip coordinates, light space
vec3 shadow_coord_normalized = shadow_coord.xyz / shadow_coord.w; // normalized device coordinates, light space
//shadow_coord_normalized.xy = clamp(shadow_coord_normalized.xy, 0, 1);
float dst = pow(clamp(distance(camera, position.xyz) / 175.f, 0, 1), 3);
float cos_theta = dot(normal.xyz, to_light);
if (dst==0 || !(shadow_coord_normalized.x <= 1.f && shadow_coord_normalized.x > 0.f && shadow_coord_normalized.y <= 1.f && shadow_coord_normalized.y > 0.f)) {
return 1;
}
float bias = 0.005 * tan(acos(cos_theta));
bias = clamp(bias, 0, 0.001f);
float bias = 0.0005f * tan(acos(cos_theta));
bias = clamp(bias, 0, 0.0001f);
const vec2 size = vec2(light.shadow_map.map.textureSize(0));
const int num_samples = 16;
float shadow_value = 0.f;
for (int i = 0; i<num_samples; i++) {
shadow_value += texture(light.shadow_map.map, vec3(shadow_coord_normalized.xy + (3*poissonDisk[i] / size.x), shadow_coord_normalized.z - bias));
shadow_value += texture(light.shadow_map.map, vec3(shadow_coord_normalized.xy + (2*poissonDisk[i] / size.x), shadow_coord_normalized.z - bias));
}
darkening = mix(shadow_value / num_samples, 1, dst);
......@@ -114,22 +218,24 @@ float sampleShadowMap(const in LightShadow light, const in vec3 position, const
bool isColorSignificant(const in vec3 color)
{
return length(color.rgb) > 0.0001f;
return length(color) > 0.0001f;
}
subroutine(lightSample)
bool samplePoint(const in Light light, const in vec3 position, out vec3 point_on_light, out vec3 color, const in vec2 random_sample)
{
PointLight point_light = light.asPoint();
if(random_sample == vec2(-1))
{
point_on_light = light.position.xyz;
point_on_light = point_light.position;
}
else
{
point_on_light = (light.position.xyz + randUniformSphere(random_sample)*light.data.params[0]);
point_on_light = (point_light.position + randUniformSphere(random_sample)*point_light.radius);
}
color = light.color.rgb * light.attenuation(distance(position, point_on_light));
color = point_light.color * point_light.attenuation.attenuate(distance(position, point_on_light));
return isColorSignificant(color);
}
......@@ -137,38 +243,41 @@ bool samplePoint(const in Light light, const in vec3 position, out vec3 point_on
subroutine(lightSample)
bool sampleSpot(const in Light light, const in vec3 position, out vec3 point_on_light, out vec3 color, const in vec2 random_sample)
{
SpotLight spot_light = light.asSpot();
if(random_sample == vec2(-1))
{
point_on_light = light.position.xyz;
point_on_light = spot_light.position;
}
else
{
//TODO: Add proper spot light source radius
point_on_light = (light.position.xyz + randUniformSphere(random_sample)*0.1f);
point_on_light = (spot_light.position + randUniformSphere(random_sample)*spot_light.radius);
}
float angle_dot = max(dot(normalize(position-point_on_light), normalize(light.direction.xyz)), 0.0);
float att = (1-smoothstep(cos(light.data.params[0]), cos(light.data.params[1]), angle_dot)) * light.attenuation(distance(position, point_on_light));
color = att * light.color.rgb;
float angle_dot = max(dot(normalize(position-point_on_light), normalize(spot_light.direction.xyz)), 0.0);
float att = (smoothstep(cos(spot_light.angle * (1-spot_light.falloff)), cos(spot_light.angle), angle_dot)) * spot_light.attenuation.attenuate(distance(position, point_on_light));
color = att * spot_light.color;
return isColorSignificant(color);
}
subroutine(lightSample)
bool sampleDirectional(const in Light light, const in vec3 position, out vec3 point_on_light, out vec3 color, const in vec2 random_sample)
{
DirectionalLight directional_light = light.asDirectional();
if(random_sample == vec2(-1))
{
point_on_light = position - DIRECTIONAL_DISTANCE * light.direction.xyz;
point_on_light = position - DIRECTIONAL_DISTANCE * directional_light.direction;
}
else
{
//TODO: Add proper directional light source radius
point_on_light = position - DIRECTIONAL_SMOOTH_DISTANCE * light.direction.xyz; //(light.position.xyz + randUniformSphere(random_sample)*0.1f);
point_on_light += randUniformSphere(random_sample)*0.1f;
point_on_light = position - DIRECTIONAL_SMOOTH_DISTANCE * directional_light.direction;
point_on_light += randUniformSphere(random_sample)*directional_light.radius;
point_on_light = position - DIRECTIONAL_DISTANCE * normalize(position - point_on_light);
}
color = light.color.rgb;
color = directional_light.color;
//Sadly the color still has to be evaluated because there might be a directional light with black color.
return isColorSignificant(color);
......@@ -177,6 +286,8 @@ bool sampleDirectional(const in Light light, const in vec3 position, out vec3 po
subroutine(lightSample)
bool sampleAmbient(const in Light light, const in vec3 position, out vec3 point_on_light, out vec3 color, const in vec2 random_sample)
{
AmbientLight ambient_light = light.asAmbient();
if(random_sample == vec2(-1))
{
// Just to not have to bother with accidental normalization NaNs
......@@ -189,7 +300,7 @@ bool sampleAmbient(const in Light light, const in vec3 position, out vec3 point_
point_on_light = position + randUniformSphere(random_sample);
}
color = light.color.rgb;
color = ambient_light.color;
// If needed, ambient color can be calculated in, but in general, better leave it out.
return false;
......
......@@ -28,7 +28,7 @@ const fs::path engine_settings_path = files::asset("/preferences/default.xml");
const fs::path pathtracer_settings_path = files::asset("/preferences/pathtracer_default.xml");
const fs::path skybox_files_path = files::asset("/textures/ryfjallet/");
const fs::path env_audio_path = files::asset("/audio/env.wav");
const fs::path startup_scene_path = files::asset("/meshes/scenery/glassplane.dae");
const fs::path startup_scene_path = files::asset("/meshes/scenery/floating_island_lowpoly.dae");
// My little all-enclosing pathtracer pointer
std::unique_ptr<raytrace::Pathtracer> pathtracer = nullptr;
......
......@@ -7,7 +7,7 @@ namespace glare
{
namespace core
{
LightComponent::LightComponent(color::rgba32f color, Attenuation attenuation, LightParameters data): m_color(color), m_attenuation(attenuation), m_data(data)
LightComponent::LightComponent(color::rgba32f color, LightParameters data): m_color(color), m_data(data)
{
}
......@@ -31,8 +31,74 @@ namespace glare
void LightComponent::update()
{
if (!m_shadow_map_renderer && getOwner() && m_data.light_type == LightType::eDirectional) {
m_shadow_map_renderer = std::make_unique<ShadowMap>(4096, 350, getOwner());
m_shadow_map_renderer = std::make_unique<ShadowMap>(4096, 8, getOwner());
}
}
Light LightComponent::buildLight() const
{
const auto transform = getOwner()->absoluteTransform();
Light light;
switch (m_data.light_type)
{
default:
case LightType::ePoint:
{
light.point.attenuation = m_data.data.point.attenuation;
light.point.color = m_color.rgb;
light.point.radius = m_data.data.point.radius;
light.point.position = glm::vec3(transform * glm::vec4(0, 0, 0, 1));
light.point.type = LightType::ePoint;
} break;
case LightType::eDirectional:
{
light.directional.direction = glm::vec3(transform * glm::vec4(0, 0, -1, 0));
light.directional.color = m_color.rgb;
light.directional.radius = m_data.data.directional.radius;
light.point.type = LightType::eDirectional;
} break;