This is the first step of moving the create infos back inside shader sources. All info files are now treated as source files. However, they are not considered in the include tree yet. This will come in another following PR. Each shader source file now generate a `.info` file containing only the create info declarations. This renames all info files so that they do not conflict with their previous versions that were copied (non-generated). Pull Request: https://projects.blender.org/blender/blender/pulls/146676
810 lines
23 KiB
GLSL
810 lines
23 KiB
GLSL
/* SPDX-FileCopyrightText: 2022-2023 Blender Authors
|
|
*
|
|
* SPDX-License-Identifier: GPL-2.0-or-later */
|
|
|
|
#pragma once
|
|
|
|
#include "infos/eevee_common_infos.hh"
|
|
|
|
SHADER_LIBRARY_CREATE_INFO(eevee_global_ubo)
|
|
SHADER_LIBRARY_CREATE_INFO(eevee_utility_texture)
|
|
|
|
#include "draw_model_lib.glsl"
|
|
#include "draw_object_infos_lib.glsl"
|
|
#include "draw_view_lib.glsl"
|
|
#include "eevee_renderpass_lib.glsl"
|
|
#include "eevee_utility_tx_lib.glsl"
|
|
#include "gpu_shader_codegen_lib.glsl"
|
|
#include "gpu_shader_math_base_lib.glsl"
|
|
#include "gpu_shader_math_safe_lib.glsl"
|
|
#include "gpu_shader_math_vector_reduce_lib.glsl"
|
|
#include "gpu_shader_utildefines_lib.glsl"
|
|
|
|
packed_float3 g_emission;
|
|
packed_float3 g_transmittance;
|
|
float g_holdout;
|
|
|
|
packed_float3 g_volume_scattering;
|
|
float g_volume_anisotropy;
|
|
packed_float3 g_volume_absorption;
|
|
|
|
/* The Closure type is never used. Use float as dummy type. */
|
|
#define Closure float
|
|
#define CLOSURE_DEFAULT 0.0f
|
|
|
|
/* Maximum number of picked closure. */
|
|
#ifndef CLOSURE_BIN_COUNT
|
|
# define CLOSURE_BIN_COUNT 1
|
|
#endif
|
|
/* Sampled closure parameters. */
|
|
ClosureUndetermined g_closure_bins[CLOSURE_BIN_COUNT];
|
|
/* Random number per sampled closure type. */
|
|
float g_closure_rand[CLOSURE_BIN_COUNT];
|
|
|
|
ClosureUndetermined g_closure_get(uchar i)
|
|
{
|
|
switch (i) {
|
|
case 0:
|
|
return g_closure_bins[0];
|
|
#if CLOSURE_BIN_COUNT > 1
|
|
case 1:
|
|
return g_closure_bins[1];
|
|
#endif
|
|
#if CLOSURE_BIN_COUNT > 2
|
|
case 2:
|
|
return g_closure_bins[2];
|
|
#endif
|
|
}
|
|
/* Unreachable. */
|
|
assert(false);
|
|
return g_closure_bins[0];
|
|
}
|
|
|
|
ClosureUndetermined g_closure_get_resolved(uchar i, float weight_fac)
|
|
{
|
|
ClosureUndetermined cl = g_closure_get(i);
|
|
cl.color *= cl.weight * weight_fac;
|
|
return cl;
|
|
}
|
|
|
|
ClosureType closure_type_get(ClosureDiffuse cl)
|
|
{
|
|
return CLOSURE_BSDF_DIFFUSE_ID;
|
|
}
|
|
|
|
ClosureType closure_type_get(ClosureTranslucent cl)
|
|
{
|
|
return CLOSURE_BSDF_TRANSLUCENT_ID;
|
|
}
|
|
|
|
ClosureType closure_type_get(ClosureReflection cl)
|
|
{
|
|
return CLOSURE_BSDF_MICROFACET_GGX_REFLECTION_ID;
|
|
}
|
|
|
|
ClosureType closure_type_get(ClosureRefraction cl)
|
|
{
|
|
return CLOSURE_BSDF_MICROFACET_GGX_REFRACTION_ID;
|
|
}
|
|
|
|
ClosureType closure_type_get(ClosureSubsurface cl)
|
|
{
|
|
return CLOSURE_BSSRDF_BURLEY_ID;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the closure is to be selected based on the input weight.
|
|
*/
|
|
bool closure_select_check(float weight, inout float total_weight, inout float r)
|
|
{
|
|
if (weight < 1e-5f) {
|
|
return false;
|
|
}
|
|
total_weight += weight;
|
|
float x = weight / total_weight;
|
|
bool chosen = (r < x);
|
|
/* Assuming that if r is in the interval [0,x] or [x,1], it's still uniformly distributed within
|
|
* that interval, so remapping to [0,1] again to explore this space of probability. */
|
|
r = (chosen) ? (r / x) : ((r - x) / (1.0f - x));
|
|
return chosen;
|
|
}
|
|
|
|
/**
|
|
* Assign `candidate` to `destination` based on a random value and the respective weights.
|
|
*/
|
|
void closure_select(inout ClosureUndetermined destination,
|
|
inout float random,
|
|
ClosureUndetermined candidate)
|
|
{
|
|
float candidate_color_weight = average(abs(candidate.color));
|
|
if (closure_select_check(candidate.weight * candidate_color_weight, destination.weight, random))
|
|
{
|
|
float total_weight = destination.weight;
|
|
destination = candidate;
|
|
destination.color /= candidate_color_weight;
|
|
destination.weight = total_weight;
|
|
}
|
|
}
|
|
|
|
void closure_weights_reset(float closure_rand)
|
|
{
|
|
g_closure_rand[0] = closure_rand;
|
|
g_closure_bins[0].weight = 0.0f;
|
|
#if CLOSURE_BIN_COUNT > 1
|
|
g_closure_rand[1] = closure_rand;
|
|
g_closure_bins[1].weight = 0.0f;
|
|
#endif
|
|
#if CLOSURE_BIN_COUNT > 2
|
|
g_closure_rand[2] = closure_rand;
|
|
g_closure_bins[2].weight = 0.0f;
|
|
#endif
|
|
|
|
g_volume_scattering = float3(0.0f);
|
|
g_volume_anisotropy = 0.0f;
|
|
g_volume_absorption = float3(0.0f);
|
|
|
|
g_emission = float3(0.0f);
|
|
g_transmittance = float3(0.0f);
|
|
g_volume_scattering = float3(0.0f);
|
|
g_volume_absorption = float3(0.0f);
|
|
g_holdout = 0.0f;
|
|
}
|
|
|
|
#define closure_base_copy(cl, in_cl) \
|
|
cl.weight = in_cl.weight; \
|
|
cl.color = in_cl.color; \
|
|
cl.N = in_cl.N; \
|
|
cl.type = closure_type_get(in_cl);
|
|
|
|
/* Single BSDFs. */
|
|
Closure closure_eval(ClosureDiffuse diffuse)
|
|
{
|
|
ClosureUndetermined cl;
|
|
closure_base_copy(cl, diffuse);
|
|
#if (CLOSURE_BIN_COUNT > 1) && defined(MAT_TRANSLUCENT) && !defined(MAT_CLEARCOAT)
|
|
/* Use second slot so we can have diffuse + translucent without noise. */
|
|
closure_select(g_closure_bins[1], g_closure_rand[1], cl);
|
|
#else
|
|
/* Either is single closure or use same bin as transmission bin. */
|
|
closure_select(g_closure_bins[0], g_closure_rand[0], cl);
|
|
#endif
|
|
return Closure(0);
|
|
}
|
|
|
|
Closure closure_eval(ClosureSubsurface diffuse)
|
|
{
|
|
ClosureUndetermined cl;
|
|
closure_base_copy(cl, diffuse);
|
|
cl.data.rgb = diffuse.sss_radius;
|
|
/* Transmission Closures are always in first bin. */
|
|
closure_select(g_closure_bins[0], g_closure_rand[0], cl);
|
|
return Closure(0);
|
|
}
|
|
|
|
Closure closure_eval(ClosureTranslucent translucent)
|
|
{
|
|
ClosureUndetermined cl;
|
|
closure_base_copy(cl, translucent);
|
|
/* Transmission Closures are always in first bin. */
|
|
closure_select(g_closure_bins[0], g_closure_rand[0], cl);
|
|
return Closure(0);
|
|
}
|
|
|
|
/* Alternate between two bins on a per closure basis.
|
|
* Allow clearcoat layer without noise.
|
|
* Choosing the bin with the least weight can choose a
|
|
* different bin for the same closure and
|
|
* produce issue with ray-tracing denoiser.
|
|
* Always start with the second bin, this one doesn't
|
|
* overlap with other closure. */
|
|
bool g_closure_reflection_bin = true;
|
|
#define CHOOSE_MIN_WEIGHT_CLOSURE_BIN(a, b) \
|
|
if (g_closure_reflection_bin) { \
|
|
closure_select(g_closure_bins[b], g_closure_rand[b], cl); \
|
|
} \
|
|
else { \
|
|
closure_select(g_closure_bins[a], g_closure_rand[a], cl); \
|
|
} \
|
|
g_closure_reflection_bin = !g_closure_reflection_bin;
|
|
|
|
Closure closure_eval(ClosureReflection reflection)
|
|
{
|
|
ClosureUndetermined cl;
|
|
closure_base_copy(cl, reflection);
|
|
cl.data.r = reflection.roughness;
|
|
|
|
#ifdef MAT_CLEARCOAT
|
|
# if CLOSURE_BIN_COUNT == 2
|
|
/* Multiple reflection closures. */
|
|
CHOOSE_MIN_WEIGHT_CLOSURE_BIN(0, 1);
|
|
# elif CLOSURE_BIN_COUNT == 3
|
|
/* Multiple reflection closures and one other closure. */
|
|
CHOOSE_MIN_WEIGHT_CLOSURE_BIN(1, 2);
|
|
# else
|
|
# error Clearcoat should always have at least 2 bins
|
|
# endif
|
|
#else
|
|
# if CLOSURE_BIN_COUNT == 1
|
|
/* Only one reflection closure is present in the whole tree. */
|
|
closure_select(g_closure_bins[0], g_closure_rand[0], cl);
|
|
# elif CLOSURE_BIN_COUNT == 2
|
|
/* Only one reflection and one other closure. */
|
|
closure_select(g_closure_bins[1], g_closure_rand[1], cl);
|
|
# elif CLOSURE_BIN_COUNT == 3
|
|
/* Only one reflection and two other closures. */
|
|
closure_select(g_closure_bins[2], g_closure_rand[2], cl);
|
|
# endif
|
|
#endif
|
|
|
|
#undef CHOOSE_MIN_WEIGHT_CLOSURE_BIN
|
|
|
|
return Closure(0);
|
|
}
|
|
|
|
Closure closure_eval(ClosureRefraction refraction)
|
|
{
|
|
ClosureUndetermined cl;
|
|
closure_base_copy(cl, refraction);
|
|
cl.data.r = refraction.roughness;
|
|
cl.data.g = refraction.ior;
|
|
/* Transmission Closures are always in first bin. */
|
|
closure_select(g_closure_bins[0], g_closure_rand[0], cl);
|
|
return Closure(0);
|
|
}
|
|
|
|
Closure closure_eval(ClosureEmission emission)
|
|
{
|
|
g_emission += emission.emission * emission.weight;
|
|
return Closure(0);
|
|
}
|
|
|
|
Closure closure_eval(ClosureTransparency transparency)
|
|
{
|
|
g_transmittance += transparency.transmittance * transparency.weight;
|
|
g_holdout += transparency.holdout * transparency.weight;
|
|
return Closure(0);
|
|
}
|
|
|
|
Closure closure_eval(ClosureVolumeScatter volume_scatter)
|
|
{
|
|
g_volume_scattering += volume_scatter.scattering * volume_scatter.weight;
|
|
g_volume_anisotropy += volume_scatter.anisotropy * volume_scatter.weight;
|
|
return Closure(0);
|
|
}
|
|
|
|
Closure closure_eval(ClosureVolumeAbsorption volume_absorption)
|
|
{
|
|
g_volume_absorption += volume_absorption.absorption * volume_absorption.weight;
|
|
return Closure(0);
|
|
}
|
|
|
|
Closure closure_eval(ClosureHair hair)
|
|
{
|
|
/* TODO */
|
|
return Closure(0);
|
|
}
|
|
|
|
/* Glass BSDF. */
|
|
Closure closure_eval(ClosureReflection reflection, ClosureRefraction refraction)
|
|
{
|
|
closure_eval(reflection);
|
|
closure_eval(refraction);
|
|
return Closure(0);
|
|
}
|
|
|
|
/* Dielectric BSDF. */
|
|
Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection reflection)
|
|
{
|
|
closure_eval(diffuse);
|
|
closure_eval(reflection);
|
|
return Closure(0);
|
|
}
|
|
|
|
/* Coat BSDF. */
|
|
Closure closure_eval(ClosureReflection reflection, ClosureReflection coat)
|
|
{
|
|
closure_eval(reflection);
|
|
closure_eval(coat);
|
|
return Closure(0);
|
|
}
|
|
|
|
/* Volume BSDF. */
|
|
Closure closure_eval(ClosureVolumeScatter volume_scatter,
|
|
ClosureVolumeAbsorption volume_absorption,
|
|
ClosureEmission emission)
|
|
{
|
|
closure_eval(volume_scatter);
|
|
closure_eval(volume_absorption);
|
|
closure_eval(emission);
|
|
return Closure(0);
|
|
}
|
|
|
|
/* Specular BSDF. */
|
|
Closure closure_eval(ClosureDiffuse diffuse, ClosureReflection reflection, ClosureReflection coat)
|
|
{
|
|
closure_eval(diffuse);
|
|
closure_eval(reflection);
|
|
closure_eval(coat);
|
|
return Closure(0);
|
|
}
|
|
|
|
/* Principled BSDF. */
|
|
Closure closure_eval(ClosureDiffuse diffuse,
|
|
ClosureReflection reflection,
|
|
ClosureReflection coat,
|
|
ClosureRefraction refraction)
|
|
{
|
|
closure_eval(diffuse);
|
|
closure_eval(reflection);
|
|
closure_eval(coat);
|
|
closure_eval(refraction);
|
|
return Closure(0);
|
|
}
|
|
|
|
/* NOP since we are sampling closures. */
|
|
Closure closure_add(Closure cl1, Closure cl2)
|
|
{
|
|
return Closure(0);
|
|
}
|
|
Closure closure_mix(Closure cl1, Closure cl2, float fac)
|
|
{
|
|
return Closure(0);
|
|
}
|
|
|
|
float ambient_occlusion_eval(float3 normal,
|
|
float max_distance,
|
|
const float inverted,
|
|
const float sample_count)
|
|
{
|
|
/* Avoid multi-line pre-processor conditionals.
|
|
* Some drivers don't handle them correctly. */
|
|
// clang-format off
|
|
#if defined(GPU_FRAGMENT_SHADER) && defined(MAT_AMBIENT_OCCLUSION) && !defined(MAT_DEPTH) && !defined(MAT_SHADOW)
|
|
// clang-format on
|
|
# if 0 /* TODO(fclem): Finish inverted horizon scan. */
|
|
/* TODO(fclem): Replace eevee_ambient_occlusion_lib by eevee_horizon_scan_eval_lib when this is
|
|
* finished. */
|
|
float3 vP = drw_point_world_to_view(g_data.P);
|
|
float3 vN = drw_normal_world_to_view(normal);
|
|
|
|
int2 texel = int2(gl_FragCoord.xy);
|
|
float2 noise;
|
|
noise.x = interleaved_gradient_noise(float2(texel), 3.0f, 0.0f);
|
|
noise.y = utility_tx_fetch(utility_tx, float2(texel), UTIL_BLUE_NOISE_LAYER).r;
|
|
noise = fract(noise + sampling_rng_2D_get(SAMPLING_AO_U));
|
|
|
|
ClosureOcclusion occlusion;
|
|
occlusion.N = (inverted != 0.0f) ? -vN : vN;
|
|
|
|
HorizonScanContext ctx;
|
|
ctx.occlusion = occlusion;
|
|
|
|
horizon_scan_eval(vP,
|
|
ctx,
|
|
noise,
|
|
uniform_buf.ao.pixel_size,
|
|
max_distance,
|
|
uniform_buf.ao.thickness_near,
|
|
uniform_buf.ao.thickness_far,
|
|
uniform_buf.ao.angle_bias,
|
|
2,
|
|
10,
|
|
inverted != 0.0f,
|
|
true);
|
|
|
|
return saturate(ctx.occlusion_result.r);
|
|
# else
|
|
float3 vP = drw_point_world_to_view(g_data.P);
|
|
int2 texel = int2(gl_FragCoord.xy);
|
|
OcclusionData data = ambient_occlusion_search(
|
|
vP, hiz_tx, texel, max_distance, inverted, sample_count);
|
|
|
|
float3 V = drw_world_incident_vector(g_data.P);
|
|
float3 N = normal;
|
|
float3 Ng = g_data.Ng;
|
|
|
|
float unused_error, visibility;
|
|
float3 unused;
|
|
ambient_occlusion_eval(data, texel, V, N, Ng, inverted, visibility, unused_error, unused);
|
|
return visibility;
|
|
# endif
|
|
#else
|
|
return 1.0f;
|
|
#endif
|
|
}
|
|
|
|
#ifndef GPU_METAL
|
|
Closure nodetree_surface(float closure_rand);
|
|
Closure nodetree_volume();
|
|
float3 nodetree_displacement();
|
|
float nodetree_thickness();
|
|
float4 closure_to_rgba(Closure cl);
|
|
#endif
|
|
|
|
/**
|
|
* Used for packing.
|
|
* This is the reflection coefficient also denoted r.
|
|
* https://en.wikipedia.org/wiki/Fresnel_equations#Complex_amplitude_reflection_and_transmission_coefficients
|
|
*/
|
|
float f0_from_ior(float eta)
|
|
{
|
|
return (eta - 1.0f) / (eta + 1.0f);
|
|
}
|
|
|
|
/**
|
|
* Simplified form of F_eta(eta, 1.0).
|
|
* This is the power reflection coefficient also denoted R.
|
|
* https://en.wikipedia.org/wiki/Fresnel_equations#Complex_amplitude_reflection_and_transmission_coefficients
|
|
*/
|
|
float F0_from_ior(float eta)
|
|
{
|
|
return square(f0_from_ior(eta));
|
|
}
|
|
float F0_from_f0(float f0)
|
|
{
|
|
return square(f0);
|
|
}
|
|
|
|
/* Return the fresnel color from a precomputed LUT value (from brdf_lut). */
|
|
float3 F_brdf_single_scatter(float3 f0, float3 f90, float2 lut)
|
|
{
|
|
return f0 * lut.x + f90 * lut.y;
|
|
}
|
|
|
|
/* Multi-scattering brdf approximation from
|
|
* "A Multiple-Scattering Microfacet Model for Real-Time Image-based Lighting"
|
|
* https://jcgt.org/published/0008/01/03/paper.pdf by Carmelo J. Fdez-Agüera. */
|
|
float3 F_brdf_multi_scatter(float3 f0, float3 f90, float2 lut)
|
|
{
|
|
float3 FssEss = F_brdf_single_scatter(f0, f90, lut);
|
|
|
|
float Ess = lut.x + lut.y;
|
|
float Ems = 1.0f - Ess;
|
|
float3 Favg = f0 + (f90 - f0) / 21.0f;
|
|
|
|
/* The original paper uses `FssEss * radiance + Fms*Ems * irradiance`, but
|
|
* "A Journey Through Implementing Multi-scattering BRDFs and Area Lights" by Steve McAuley
|
|
* suggests to use `FssEss * radiance + Fms*Ems * radiance` which results in comparable quality.
|
|
* We handle `radiance` outside of this function, so the result simplifies to:
|
|
* `FssEss + Fms*Ems = FssEss * (1 + Ems*Favg / (1 - Ems*Favg)) = FssEss / (1 - Ems*Favg)`.
|
|
* This is a simple albedo scaling very similar to the approach used by Cycles:
|
|
* "Practical multiple scattering compensation for microfacet model". */
|
|
return FssEss / (1.0f - Ems * Favg);
|
|
}
|
|
|
|
float2 brdf_lut(float cos_theta, float roughness)
|
|
{
|
|
auto &utility_tx = sampler_get(eevee_utility_texture, utility_tx);
|
|
return utility_tx_sample_lut(utility_tx, cos_theta, roughness, UTIL_BSDF_LAYER).rg;
|
|
}
|
|
|
|
void brdf_f82_tint_lut(float3 F0,
|
|
float3 F82,
|
|
float cos_theta,
|
|
float roughness,
|
|
bool do_multiscatter,
|
|
out float3 reflectance)
|
|
{
|
|
auto &utility_tx = sampler_get(eevee_utility_texture, utility_tx);
|
|
float3 split_sum = utility_tx_sample_lut(utility_tx, cos_theta, roughness, UTIL_BSDF_LAYER).rgb;
|
|
|
|
reflectance = do_multiscatter ? F_brdf_multi_scatter(F0, float3(1.0f), split_sum.xy) :
|
|
F_brdf_single_scatter(F0, float3(1.0f), split_sum.xy);
|
|
|
|
/* Precompute the F82 term factor for the Fresnel model.
|
|
* In the classic F82 model, the F82 input directly determines the value of the Fresnel
|
|
* model at ~82°, similar to F0 and F90.
|
|
* With F82-Tint, on the other hand, the value at 82° is the value of the classic Schlick
|
|
* model multiplied by the tint input.
|
|
* Therefore, the factor follows by setting `F82Tint(cosI) = FSchlick(cosI) - b*cosI*(1-cosI)^6`
|
|
* and `F82Tint(acos(1/7)) = FSchlick(acos(1/7)) * f82_tint` and solving for `b`. */
|
|
constexpr float f = 6.0f / 7.0f;
|
|
constexpr float f5 = (f * f) * (f * f) * f;
|
|
constexpr float f6 = (f * f) * (f * f) * (f * f);
|
|
float3 F_schlick = mix(F0, float3(1.0f), f5);
|
|
float3 b = F_schlick * (7.0f / f6) * (1.0f - F82);
|
|
reflectance -= b * split_sum.z;
|
|
}
|
|
|
|
/* Return texture coordinates to sample BSDF LUT. */
|
|
float3 lut_coords_bsdf(float cos_theta, float roughness, float ior)
|
|
{
|
|
/* IOR is the sine of the critical angle. */
|
|
float critical_cos = sqrt(1.0f - ior * ior);
|
|
|
|
float3 coords;
|
|
coords.x = square(ior);
|
|
coords.y = cos_theta;
|
|
coords.y -= critical_cos;
|
|
coords.y /= (coords.y > 0.0f) ? (1.0f - critical_cos) : critical_cos;
|
|
coords.y = coords.y * 0.5f + 0.5f;
|
|
coords.z = roughness;
|
|
|
|
return saturate(coords);
|
|
}
|
|
|
|
/* Return texture coordinates to sample Surface LUT. */
|
|
float3 lut_coords_btdf(float cos_theta, float roughness, float f0)
|
|
{
|
|
return float3(sqrt(f0), sqrt(1.0f - cos_theta), roughness);
|
|
}
|
|
|
|
/* Computes the reflectance and transmittance based on the tint (`f0`, `f90`, `transmission_tint`)
|
|
* and the BSDF LUT. */
|
|
void bsdf_lut(float3 F0,
|
|
float3 F90,
|
|
float3 transmission_tint,
|
|
float cos_theta,
|
|
float roughness,
|
|
float ior,
|
|
bool do_multiscatter,
|
|
out float3 reflectance,
|
|
out float3 transmittance)
|
|
{
|
|
auto &utility_tx = sampler_get(eevee_utility_texture, utility_tx);
|
|
if (ior == 1.0f) {
|
|
reflectance = float3(0.0f);
|
|
transmittance = transmission_tint;
|
|
return;
|
|
}
|
|
|
|
float2 split_sum;
|
|
float transmission_factor;
|
|
|
|
const float f0 = f0_from_ior(ior);
|
|
|
|
if (ior > 1.0f) {
|
|
/* Gradually increase `f90` from 0 to 1 when IOR is in the range of [1.0f, 1.33f], to avoid
|
|
* harsh transition at `IOR == 1`. */
|
|
if (all(equal(F90, float3(1.0f)))) {
|
|
F90 = float3(saturate(2.33f / 0.33f * f0));
|
|
}
|
|
const float3 coords = lut_coords_btdf(cos_theta, roughness, f0);
|
|
const float4 bsdf = utility_tx_sample_bsdf_lut(utility_tx, coords.xy, coords.z);
|
|
split_sum = brdf_lut(cos_theta, roughness);
|
|
transmission_factor = bsdf.a;
|
|
}
|
|
else {
|
|
const float3 coords = lut_coords_bsdf(cos_theta, roughness, ior);
|
|
const float3 bsdf = utility_tx_sample_bsdf_lut(utility_tx, coords.xy, coords.z).rgb;
|
|
split_sum = bsdf.rg;
|
|
transmission_factor = bsdf.b;
|
|
}
|
|
|
|
reflectance = F_brdf_single_scatter(F0, F90, split_sum);
|
|
transmittance = (float3(1.0f) - F0) * transmission_factor * transmission_tint;
|
|
|
|
if (do_multiscatter) {
|
|
const float real_F0 = F0_from_f0(f0);
|
|
const float Ess = real_F0 * split_sum.x + split_sum.y + (1.0f - real_F0) * transmission_factor;
|
|
const float Ems = 1.0f - Ess;
|
|
/* Assume that the transmissive tint makes up most of the overall color if it's not zero. */
|
|
const float3 Favg = all(equal(transmission_tint, float3(0.0f))) ? F0 + (F90 - F0) / 21.0f :
|
|
transmission_tint;
|
|
|
|
float3 scale = 1.0f / (1.0f - Ems * Favg);
|
|
reflectance *= scale;
|
|
transmittance *= scale;
|
|
}
|
|
}
|
|
|
|
/* Computes the reflectance and transmittance based on the BSDF LUT. */
|
|
float2 bsdf_lut(float cos_theta, float roughness, float ior, bool do_multiscatter)
|
|
{
|
|
float F0 = F0_from_ior(ior);
|
|
float3 color = float3(1.0f);
|
|
float3 reflectance, transmittance;
|
|
bsdf_lut(float3(F0),
|
|
color,
|
|
color,
|
|
cos_theta,
|
|
roughness,
|
|
ior,
|
|
do_multiscatter,
|
|
reflectance,
|
|
transmittance);
|
|
return float2(reflectance.r, transmittance.r);
|
|
}
|
|
|
|
#ifdef GPU_VERTEX_SHADER
|
|
# define closure_to_rgba(a) float4(0.0f)
|
|
#endif
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Fragment Displacement
|
|
*
|
|
* Displacement happening in the fragment shader.
|
|
* Can be used in conjunction with a per vertex displacement.
|
|
*
|
|
* \{ */
|
|
|
|
#ifndef GPU_METAL
|
|
/* Prototype. */
|
|
float derivative_scale_get();
|
|
#endif
|
|
|
|
#ifdef MAT_DISPLACEMENT_BUMP
|
|
/* Return new shading normal. */
|
|
float3 displacement_bump()
|
|
{
|
|
# if !defined(MAT_GEOM_CURVES)
|
|
/* This is the filter width for automatic displacement + bump mapping, which is fixed.
|
|
* NOTE: keep the same as default bump node filter width. */
|
|
constexpr float bump_filter_width = 0.1f;
|
|
|
|
float2 dHd;
|
|
dF_branch(dot(nodetree_displacement(), g_data.N + dF_impl(g_data.N)), bump_filter_width, dHd);
|
|
|
|
float3 dPdx = gpu_dfdx(g_data.P) * derivative_scale_get();
|
|
float3 dPdy = gpu_dfdy(g_data.P) * derivative_scale_get();
|
|
|
|
/* Get surface tangents from normal. */
|
|
float3 Rx = cross(dPdy, g_data.N);
|
|
float3 Ry = cross(g_data.N, dPdx);
|
|
|
|
/* Compute surface gradient and determinant. */
|
|
float det = dot(dPdx, Rx);
|
|
|
|
float3 surfgrad = dHd.x * Rx + dHd.y * Ry;
|
|
|
|
float facing = FrontFacing ? 1.0f : -1.0f;
|
|
return normalize(bump_filter_width * abs(det) * g_data.N - facing * sign(det) * surfgrad);
|
|
# else
|
|
return g_data.N;
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
void fragment_displacement()
|
|
{
|
|
#ifdef MAT_DISPLACEMENT_BUMP
|
|
g_data.N = g_data.Ni = displacement_bump();
|
|
#endif
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Coordinate implementations
|
|
*
|
|
* Callbacks for the texture coordinate node.
|
|
*
|
|
* \{ */
|
|
|
|
float3 coordinate_camera(float3 P)
|
|
{
|
|
float3 vP;
|
|
if (false /* Probe. */) {
|
|
/* Unsupported. It would make the probe camera-dependent. */
|
|
vP = P;
|
|
}
|
|
else {
|
|
#ifdef MAT_GEOM_WORLD
|
|
vP = drw_normal_world_to_view(P);
|
|
#else
|
|
vP = drw_point_world_to_view(P);
|
|
#endif
|
|
}
|
|
vP.z = -vP.z;
|
|
return vP;
|
|
}
|
|
|
|
float3 coordinate_screen(float3 P)
|
|
{
|
|
float3 window = float3(0.0f);
|
|
if (false /* Probe. */) {
|
|
/* Unsupported. It would make the probe camera-dependent. */
|
|
window.xy = float2(0.5f);
|
|
}
|
|
else {
|
|
#ifdef MAT_GEOM_WORLD
|
|
window.xy = drw_point_view_to_screen(interp.P).xy;
|
|
#else
|
|
/* TODO(fclem): Actual camera transform. */
|
|
window.xy = drw_point_world_to_screen(P).xy;
|
|
#endif
|
|
window.xy = window.xy * uniform_buf.camera.uv_scale + uniform_buf.camera.uv_bias;
|
|
}
|
|
return window;
|
|
}
|
|
|
|
float3 coordinate_reflect(float3 P, float3 N)
|
|
{
|
|
#ifdef MAT_GEOM_WORLD
|
|
return N;
|
|
#else
|
|
return -reflect(drw_world_incident_vector(P), N);
|
|
#endif
|
|
}
|
|
|
|
float3 coordinate_incoming(float3 P)
|
|
{
|
|
#ifdef MAT_GEOM_WORLD
|
|
return -P;
|
|
#else
|
|
return drw_world_incident_vector(P);
|
|
#endif
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Mixed render resolution
|
|
*
|
|
* Callbacks image texture sampling.
|
|
*
|
|
* \{ */
|
|
|
|
float texture_lod_bias_get()
|
|
{
|
|
return uniform_buf.film.texture_lod_bias;
|
|
}
|
|
|
|
/**
|
|
* Scale hardware derivatives depending on render resolution.
|
|
* This is because the distance between pixels increases as we lower the resolution. The hardware
|
|
* uses neighboring pixels to compute derivatives and thus the value increases as we lower the
|
|
* resolution. So we compensate by scaling them back to the expected amplitude at full resolution.
|
|
*/
|
|
float derivative_scale_get()
|
|
{
|
|
return 1.0 / float(uniform_buf.film.scaling_factor);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Volume Attribute post
|
|
*
|
|
* TODO(@fclem): These implementation details should concern the DRWContext and not be a fix on
|
|
* the engine side. But as of now, the engines are responsible for loading the attributes.
|
|
*
|
|
* \{ */
|
|
|
|
/* Point clouds and curves are not compatible with volume grids.
|
|
* They will fall back to their own attributes loading. */
|
|
#if defined(MAT_VOLUME) && !defined(MAT_GEOM_CURVES) && !defined(MAT_GEOM_POINTCLOUD)
|
|
# if defined(VOLUME_INFO_LIB) && !defined(MAT_GEOM_WORLD)
|
|
/* We could just check for GRID_ATTRIBUTES but this avoids for header dependency. */
|
|
# define GRID_ATTRIBUTES_LOAD_POST
|
|
# endif
|
|
#endif
|
|
|
|
float attr_load_temperature_post(float attr)
|
|
{
|
|
#ifdef GRID_ATTRIBUTES_LOAD_POST
|
|
/* Bring the value into standard range without having to modify the grid values */
|
|
attr = (attr > 0.01f) ? (attr * drw_volume.temperature_mul + drw_volume.temperature_bias) : 0.0f;
|
|
#endif
|
|
return attr;
|
|
}
|
|
float4 attr_load_color_post(float4 attr)
|
|
{
|
|
#ifdef GRID_ATTRIBUTES_LOAD_POST
|
|
/* Density is premultiplied for interpolation, divide it out here. */
|
|
attr.rgb *= safe_rcp(attr.a);
|
|
attr.rgb *= drw_volume.color_mul.rgb;
|
|
attr.a = 1.0f;
|
|
#endif
|
|
return attr;
|
|
}
|
|
|
|
#undef GRID_ATTRIBUTES_LOAD_POST
|
|
|
|
/** \} */
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Uniform Attributes
|
|
*
|
|
* TODO(@fclem): These implementation details should concern the DRWContext and not be a fix on
|
|
* the engine side. But as of now, the engines are responsible for loading the attributes.
|
|
*
|
|
* \{ */
|
|
|
|
float4 attr_load_uniform(float4 attr, const uint attr_hash)
|
|
{
|
|
return drw_object_attribute(attr_hash);
|
|
}
|
|
|
|
/** \} */
|