MetaCoreEngineV2/rpcore/shader/includes/lights.inc.glsl
2026-01-13 17:06:06 +08:00

152 lines
5.9 KiB
GLSL

/**
*
* RenderPipeline
*
* Copyright (c) 2014-2016 tobspr <tobias.springer1@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*
*/
#pragma once
#pragma include "includes/material.inc.glsl"
#pragma include "includes/brdf.inc.glsl"
#pragma include "includes/ies_lighting.inc.glsl"
// Computes the quadratic attenuation curve
float attenuation_curve(float dist_square, float radius) {
#if 0
return step(dist_square, radius * radius);
#endif
#if 1
float factor = dist_square / (radius * radius);
float smooth_factor = saturate(1.0 - factor * factor);
return smooth_factor * smooth_factor / max(0.01 * 0.01, dist_square);
#endif
}
// Computes the attenuation for a spot light
float get_spotlight_attenuation(vec3 l, vec3 light_dir, float fov, float radius,
float dist_sq, int ies_profile) {
float dist_attenuation = attenuation_curve(dist_sq, radius);
float cos_angle = dot(l, -light_dir);
// Rescale angle to fit the full range of the IES profile. We only do this
// for spot lights, for point lights we use the actual angle.
// This is NOT physically correct for spotlights without a FoV of 180deg.
// However, IES profiles might look quite boring when not getting rescaled,
// so the rescaling is performed.
float linear_angle = (cos_angle - fov) / (1 - fov);
float angle_att = saturate(linear_angle);
float ies_factor = get_ies_factor(ies_profile, linear_angle, 0);
return ies_factor * angle_att * angle_att * dist_attenuation;
}
// Closest point on spherical area light, also returns energy factor
vec3 get_spherical_area_light_vector(vec3 n, vec3 l_unscaled, vec3 v, float radius) {
vec3 r = reflect(-v, n);
vec3 center_to_ray = dot(l_unscaled, r) * r - l_unscaled;
vec3 closest_point = l_unscaled + center_to_ray *
saturate(radius / max(1e-3, length(center_to_ray)));
return closest_point;
}
vec3 get_spherical_area_light_horizon(vec3 l_unscaled, vec3 n, float radius) {
return normalize(l_unscaled + n * (0.5 * radius));
}
float get_spherical_area_light_energy(float alpha, float radius, float dist_sq) {
return max(0.000005, alpha * alpha) / max(0.01, radius * radius) * 4 * M_PI;
}
// Computes a lights influence
// TODO: Make this method faster
vec3 apply_light(Material m, vec3 v, vec3 l, vec3 light_color, float attenuation, float shadow,
vec3 transmittance, float energy, float clearcoat_energy, vec3 l_diffuse) {
// Debugging: Fast rendering path
#if 0
return light_color * attenuation;
#endif
float NxL = saturate(dot(m.normal, l_diffuse));
if (m.shading_model == SHADING_MODEL_FOLIAGE) {
transmittance = transmittance.xxx;
} else if (m.shading_model == SHADING_MODEL_SKIN) {
NxL = saturate(0.3 + dot(m.normal, l_diffuse));
} else {
transmittance = vec3(1);
}
// Compute the dot products
vec3 h = normalize(l + v);
float NxV = max(1e-5, dot(m.normal, v));
float NxH = max(1e-5, dot(m.normal, h));
float VxH = clamp(dot(v, h), 1e-5, 1.0);
float LxH = max(0, dot(l, h));
vec3 f0 = get_material_f0(m);
// Diffuse contribution
vec3 shading_result = brdf_diffuse(NxV, NxL, LxH, VxH, m.roughness)
* m.basecolor * (1 - m.metallic);
// Specular contribution:
// We add some roughness for clearcoat - this is due to the reason that
// light gets scattered and thus a wider highlight is shown.
// This approximates the reference in mitsuba very well.
// float distribution = brdf_distribution(NxH, m.roughness);
float distribution = brdf_distribution(NxH, m.roughness); // xxx
float visibility = brdf_visibility(NxL, NxV, NxH, VxH, m.roughness);
vec3 fresnel = brdf_schlick_fresnel(f0, LxH);
// The division by 4 * NxV * NxL is done in the geometric (visibility) term
// already, so to evaluate the complete brdf we just do a multiply
shading_result += (distribution * visibility) * fresnel / (4.0 * NxV * NxL) * energy;
if (m.shading_model == SHADING_MODEL_CLEARCOAT) {
float distribution_coat = brdf_distribution(NxH, CLEARCOAT_ROUGHNESS);
float visibility_coat = brdf_visibility(NxL, NxV, NxH, VxH, CLEARCOAT_ROUGHNESS);
vec3 fresnel_coat = brdf_schlick_fresnel(vec3(CLEARCOAT_SPECULAR), LxH);
// Approximation to match reference
shading_result *= (1 - fresnel_coat.x);
shading_result *= 0.4 + 3.0 * m.linear_roughness;
shading_result *= 0.5 + 0.5 * m.basecolor;
vec3 coat_spec = (distribution_coat * visibility_coat * clearcoat_energy) * fresnel_coat;
shading_result += coat_spec;
}
return max(vec3(0),
(shading_result * light_color) * (attenuation * shadow * NxL) * transmittance);
}
vec3 apply_light(Material m, vec3 v, vec3 l, vec3 light_color, float attenuation,
float shadow, vec3 transmittance) {
return apply_light(
m, v, l, light_color, attenuation, shadow, transmittance, 1.0, 1.0, l);
}