Files
test2/source/blender/draw/engines/eevee/shaders/eevee_nodetree_lib.glsl

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

810 lines
23 KiB
Plaintext
Raw Normal View History

/* 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"
EEVEE: Reduce necessary includes This saves a few milisecond of compile time per shader. This removes the need for occlusion lib when not using occlusion node. To improve detection of uneeded includes, we add a new logging system which output can be fed to mermaid to inspect dependencies. The new dependencies can be inspected using `--log "gpu.shader_dependencies"` Example pasted here: ```mermaid flowchart LR draw_curves_lib.glsl_7298 --> gpu_shader_math_constants_lib.glsl_600 style gpu_shader_math_constants_lib.glsl_600 fill:#0f0 gpu_shader_math_matrix_conversion_lib.glsl_1032 --> gpu_shader_math_base_lib.glsl_1406 style gpu_shader_math_base_lib.glsl_1406 fill:#1e0 gpu_shader_math_matrix_conversion_lib.glsl_1032 --> gpu_shader_math_euler_lib.glsl_461 style gpu_shader_math_euler_lib.glsl_461 fill:#0f0 gpu_shader_math_matrix_compare_lib.glsl_2964 --> gpu_shader_math_vector_compare_lib.glsl_2489 style gpu_shader_math_vector_compare_lib.glsl_2489 fill:#2d0 gpu_shader_math_matrix_conversion_lib.glsl_1032 --> gpu_shader_math_matrix_compare_lib.glsl_2964 style gpu_shader_math_matrix_compare_lib.glsl_2964 fill:#2d0 gpu_shader_math_matrix_conversion_lib.glsl_1032 --> gpu_shader_math_quaternion_lib.glsl_395 style gpu_shader_math_quaternion_lib.glsl_395 fill:#0f0 gpu_shader_math_matrix_conversion_lib.glsl_1032 --> gpu_shader_utildefines_lib.glsl_3112 style gpu_shader_utildefines_lib.glsl_3112 fill:#3c0 draw_curves_lib.glsl_7298 --> gpu_shader_math_matrix_conversion_lib.glsl_1032 style gpu_shader_math_matrix_conversion_lib.glsl_1032 fill:#1e0 draw_curves_lib.glsl_7298 --> gpu_shader_math_matrix_transform_lib.glsl_706 style gpu_shader_math_matrix_transform_lib.glsl_706 fill:#0f0 eevee_surf_deferred_frag.glsl_4531 --> draw_curves_lib.glsl_7298 style draw_curves_lib.glsl_7298 fill:#780 eevee_surf_deferred_frag.glsl_4531 --> draw_view_lib.glsl_3551 style draw_view_lib.glsl_3551 fill:#3c0 eevee_gbuffer_lib.glsl_14598 --> gpu_shader_math_vector_reduce_lib.glsl_1383 style gpu_shader_math_vector_reduce_lib.glsl_1383 fill:#1e0 eevee_gbuffer_lib.glsl_14598 --> gpu_shader_codegen_lib.glsl_6143 style gpu_shader_codegen_lib.glsl_6143 fill:#690 eevee_gbuffer_lib.glsl_14598 --> gpu_shader_math_vector_lib.glsl_5038 style gpu_shader_math_vector_lib.glsl_5038 fill:#5a0 eevee_gbuffer_lib.glsl_14598 --> gpu_shader_utildefines_lib.glsl_3112 style gpu_shader_utildefines_lib.glsl_3112 fill:#3c0 eevee_gbuffer_write_lib.glsl_7324 --> eevee_gbuffer_lib.glsl_14598 style eevee_gbuffer_lib.glsl_14598 fill:#e10 eevee_surf_deferred_frag.glsl_4531 --> eevee_gbuffer_write_lib.glsl_7324 style eevee_gbuffer_write_lib.glsl_7324 fill:#780 eevee_ambient_occlusion_lib.glsl_10738 --> draw_view_lib.glsl_3551 style draw_view_lib.glsl_3551 fill:#3c0 draw_math_geom_lib.glsl_5172 --> gpu_shader_math_vector_lib.glsl_5038 style gpu_shader_math_vector_lib.glsl_5038 fill:#5a0 draw_math_geom_lib.glsl_5172 --> gpu_shader_math_vector_reduce_lib.glsl_1383 style gpu_shader_math_vector_reduce_lib.glsl_1383 fill:#1e0 eevee_ray_types_lib.glsl_2390 --> draw_math_geom_lib.glsl_5172 style draw_math_geom_lib.glsl_5172 fill:#5a0 eevee_ray_types_lib.glsl_2390 --> draw_view_lib.glsl_3551 style draw_view_lib.glsl_3551 fill:#3c0 eevee_ray_types_lib.glsl_2390 --> gpu_shader_math_matrix_transform_lib.glsl_706 style gpu_shader_math_matrix_transform_lib.glsl_706 fill:#0f0 gpu_shader_math_safe_lib.glsl_1235 --> gpu_shader_math_constants_lib.glsl_600 style gpu_shader_math_constants_lib.glsl_600 fill:#0f0 eevee_ray_types_lib.glsl_2390 --> gpu_shader_math_safe_lib.glsl_1235 style gpu_shader_math_safe_lib.glsl_1235 fill:#1e0 eevee_ray_types_lib.glsl_2390 --> gpu_shader_ray_lib.glsl_137 style gpu_shader_ray_lib.glsl_137 fill:#0f0 eevee_ambient_occlusion_lib.glsl_10738 --> eevee_ray_types_lib.glsl_2390 style eevee_ray_types_lib.glsl_2390 fill:#2d0 eevee_sampling_lib.glsl_4291 --> gpu_shader_math_base_lib.glsl_1406 style gpu_shader_math_base_lib.glsl_1406 fill:#1e0 eevee_sampling_lib.glsl_4291 --> gpu_shader_math_constants_lib.glsl_600 style gpu_shader_math_constants_lib.glsl_600 fill:#0f0 eevee_sampling_lib.glsl_4291 --> gpu_shader_math_safe_lib.glsl_1235 style gpu_shader_math_safe_lib.glsl_1235 fill:#1e0 eevee_ambient_occlusion_lib.glsl_10738 --> eevee_sampling_lib.glsl_4291 style eevee_sampling_lib.glsl_4291 fill:#4b0 eevee_ambient_occlusion_lib.glsl_10738 --> eevee_utility_tx_lib.glsl_1225 style eevee_utility_tx_lib.glsl_1225 fill:#1e0 eevee_ambient_occlusion_lib.glsl_10738 --> gpu_shader_math_base_lib.glsl_1406 style gpu_shader_math_base_lib.glsl_1406 fill:#1e0 gpu_shader_math_fast_lib.glsl_921 --> gpu_shader_math_constants_lib.glsl_600 style gpu_shader_math_constants_lib.glsl_600 fill:#0f0 eevee_ambient_occlusion_lib.glsl_10738 --> gpu_shader_math_fast_lib.glsl_921 style gpu_shader_math_fast_lib.glsl_921 fill:#0f0 gpu_shader_math_vector_safe_lib.glsl_5847 --> gpu_shader_math_safe_lib.glsl_1235 style gpu_shader_math_safe_lib.glsl_1235 fill:#1e0 eevee_ambient_occlusion_lib.glsl_10738 --> gpu_shader_math_vector_safe_lib.glsl_5847 style gpu_shader_math_vector_safe_lib.glsl_5847 fill:#5a0 eevee_ambient_occlusion_lib.glsl_10738 --> gpu_shader_utildefines_lib.glsl_3112 style gpu_shader_utildefines_lib.glsl_3112 fill:#3c0 eevee_nodetree_frag_lib.glsl_395 --> eevee_ambient_occlusion_lib.glsl_10738 style eevee_ambient_occlusion_lib.glsl_10738 fill:#a50 eevee_nodetree_frag_lib.glsl_395 --> eevee_geom_types_lib.glsl_682 style eevee_geom_types_lib.glsl_682 fill:#0f0 draw_model_lib.glsl_2563 --> draw_view_lib.glsl_3551 style draw_view_lib.glsl_3551 fill:#3c0 eevee_nodetree_lib.glsl_16051 --> draw_model_lib.glsl_2563 style draw_model_lib.glsl_2563 fill:#2d0 draw_object_infos_lib.glsl_1114 --> draw_model_lib.glsl_2563 style draw_model_lib.glsl_2563 fill:#2d0 eevee_nodetree_lib.glsl_16051 --> draw_object_infos_lib.glsl_1114 style draw_object_infos_lib.glsl_1114 fill:#1e0 eevee_nodetree_lib.glsl_16051 --> draw_view_lib.glsl_3551 style draw_view_lib.glsl_3551 fill:#3c0 eevee_nodetree_lib.glsl_16051 --> eevee_renderpass_lib.glsl_1793 style eevee_renderpass_lib.glsl_1793 fill:#1e0 eevee_nodetree_lib.glsl_16051 --> eevee_utility_tx_lib.glsl_1225 style eevee_utility_tx_lib.glsl_1225 fill:#1e0 eevee_nodetree_lib.glsl_16051 --> gpu_shader_codegen_lib.glsl_6143 style gpu_shader_codegen_lib.glsl_6143 fill:#690 eevee_nodetree_lib.glsl_16051 --> gpu_shader_math_base_lib.glsl_1406 style gpu_shader_math_base_lib.glsl_1406 fill:#1e0 eevee_nodetree_lib.glsl_16051 --> gpu_shader_math_safe_lib.glsl_1235 style gpu_shader_math_safe_lib.glsl_1235 fill:#1e0 eevee_nodetree_lib.glsl_16051 --> gpu_shader_math_vector_reduce_lib.glsl_1383 style gpu_shader_math_vector_reduce_lib.glsl_1383 fill:#1e0 eevee_nodetree_lib.glsl_16051 --> gpu_shader_utildefines_lib.glsl_3112 style gpu_shader_utildefines_lib.glsl_3112 fill:#3c0 eevee_nodetree_frag_lib.glsl_395 --> eevee_nodetree_lib.glsl_16051 style eevee_nodetree_lib.glsl_16051 fill:#f00 gpu_shader_material_ambient_occlusion.glsl_558 --> gpu_shader_math_vector_safe_lib.glsl_5847 style gpu_shader_math_vector_safe_lib.glsl_5847 fill:#5a0 eevee_nodetree_frag_lib.glsl_395 --> gpu_shader_material_ambient_occlusion.glsl_558 style gpu_shader_material_ambient_occlusion.glsl_558 fill:#0f0 eevee_nodetree_frag_lib.glsl_395 --> gpu_shader_material_emission.glsl_380 style gpu_shader_material_emission.glsl_380 fill:#0f0 gpu_shader_material_output_material.glsl_850 --> gpu_shader_material_transform_utils.glsl_2136 style gpu_shader_material_transform_utils.glsl_2136 fill:#2d0 eevee_nodetree_frag_lib.glsl_395 --> gpu_shader_material_output_material.glsl_850 style gpu_shader_material_output_material.glsl_850 fill:#0f0 eevee_nodetree_frag_lib.glsl_395 --> gpu_shader_material_world_normals.glsl_128 style gpu_shader_material_world_normals.glsl_128 fill:#0f0 eevee_surf_deferred_frag.glsl_4531 --> eevee_nodetree_frag_lib.glsl_395 style eevee_nodetree_frag_lib.glsl_395 fill:#0f0 eevee_surf_deferred_frag.glsl_4531 --> eevee_sampling_lib.glsl_4291 style eevee_sampling_lib.glsl_4291 fill:#4b0 eevee_surf_lib.glsl_3650 --> draw_view_lib.glsl_3551 style draw_view_lib.glsl_3551 fill:#3c0 eevee_surf_lib.glsl_3650 --> gpu_shader_codegen_lib.glsl_6143 style gpu_shader_codegen_lib.glsl_6143 fill:#690 eevee_surf_lib.glsl_3650 --> gpu_shader_math_base_lib.glsl_1406 style gpu_shader_math_base_lib.glsl_1406 fill:#1e0 eevee_surf_lib.glsl_3650 --> gpu_shader_math_vector_safe_lib.glsl_5847 style gpu_shader_math_vector_safe_lib.glsl_5847 fill:#5a0 eevee_surf_deferred_frag.glsl_4531 --> eevee_surf_lib.glsl_3650 style eevee_surf_lib.glsl_3650 fill:#3c0 ``` Pull Request: https://projects.blender.org/blender/blender/pulls/146580
2025-09-23 17:21:56 +02:00
#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);
EEVEE-Next: Add consistent support for thickness output This add support for the thickness output in a consistent manner across all BSDF. If thickness is above zero, for any ray going below the surface, the object is modeled as a sphere tangent to the shading point and of diameter equal to the given thickness. The ray is then intersected with that sphere and transmitted out. This model perfectly matches a raytracer behavior for a sphere model of diameter equal to thickness. This replaces the old refraction depth model which was modelling a infinite slab of material. For simplicity, we do not do 2 microfacet transmission events. We simply use the main lobe direction for the first interface. This still matches the raytracer behavior for smooth surfaces. Weirdly enough the apparent roughness doesn't need to be amended. To get shadowing and translucency approximation to work, this splits the transmission BSDF evaluation to its own light loop. This simplifies a lot of logic and assumes only one transmission BSDF is ever sampled in a nodetree. This changes the behavior of the thickness output with regard to the thickness from shadow. We now take the min instead of the max between both. This break a lot of file since the default thickness is set to `0.1`. This patch doesn't change the default thickness output behavior but it will be changed in another PR. Note this might change the shadow sampling pattern since translucent and non-translucent are now sampling them separately. Pull Request: https://projects.blender.org/blender/blender/pulls/120329
2024-04-12 22:06:25 +02:00
#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);
EEVEE-Next: Add consistent support for thickness output This add support for the thickness output in a consistent manner across all BSDF. If thickness is above zero, for any ray going below the surface, the object is modeled as a sphere tangent to the shading point and of diameter equal to the given thickness. The ray is then intersected with that sphere and transmitted out. This model perfectly matches a raytracer behavior for a sphere model of diameter equal to thickness. This replaces the old refraction depth model which was modelling a infinite slab of material. For simplicity, we do not do 2 microfacet transmission events. We simply use the main lobe direction for the first interface. This still matches the raytracer behavior for smooth surfaces. Weirdly enough the apparent roughness doesn't need to be amended. To get shadowing and translucency approximation to work, this splits the transmission BSDF evaluation to its own light loop. This simplifies a lot of logic and assumes only one transmission BSDF is ever sampled in a nodetree. This changes the behavior of the thickness output with regard to the thickness from shadow. We now take the min instead of the max between both. This break a lot of file since the default thickness is set to `0.1`. This patch doesn't change the default thickness output behavior but it will be changed in another PR. Note this might change the shadow sampling pattern since translucent and non-translucent are now sampling them separately. Pull Request: https://projects.blender.org/blender/blender/pulls/120329
2024-04-12 22:06:25 +02:00
#endif
return Closure(0);
}
Closure closure_eval(ClosureSubsurface diffuse)
{
ClosureUndetermined cl;
closure_base_copy(cl, diffuse);
cl.data.rgb = diffuse.sss_radius;
EEVEE-Next: Add consistent support for thickness output This add support for the thickness output in a consistent manner across all BSDF. If thickness is above zero, for any ray going below the surface, the object is modeled as a sphere tangent to the shading point and of diameter equal to the given thickness. The ray is then intersected with that sphere and transmitted out. This model perfectly matches a raytracer behavior for a sphere model of diameter equal to thickness. This replaces the old refraction depth model which was modelling a infinite slab of material. For simplicity, we do not do 2 microfacet transmission events. We simply use the main lobe direction for the first interface. This still matches the raytracer behavior for smooth surfaces. Weirdly enough the apparent roughness doesn't need to be amended. To get shadowing and translucency approximation to work, this splits the transmission BSDF evaluation to its own light loop. This simplifies a lot of logic and assumes only one transmission BSDF is ever sampled in a nodetree. This changes the behavior of the thickness output with regard to the thickness from shadow. We now take the min instead of the max between both. This break a lot of file since the default thickness is set to `0.1`. This patch doesn't change the default thickness output behavior but it will be changed in another PR. Note this might change the shadow sampling pattern since translucent and non-translucent are now sampling them separately. Pull Request: https://projects.blender.org/blender/blender/pulls/120329
2024-04-12 22:06:25 +02:00
/* 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);
EEVEE-Next: Add consistent support for thickness output This add support for the thickness output in a consistent manner across all BSDF. If thickness is above zero, for any ray going below the surface, the object is modeled as a sphere tangent to the shading point and of diameter equal to the given thickness. The ray is then intersected with that sphere and transmitted out. This model perfectly matches a raytracer behavior for a sphere model of diameter equal to thickness. This replaces the old refraction depth model which was modelling a infinite slab of material. For simplicity, we do not do 2 microfacet transmission events. We simply use the main lobe direction for the first interface. This still matches the raytracer behavior for smooth surfaces. Weirdly enough the apparent roughness doesn't need to be amended. To get shadowing and translucency approximation to work, this splits the transmission BSDF evaluation to its own light loop. This simplifies a lot of logic and assumes only one transmission BSDF is ever sampled in a nodetree. This changes the behavior of the thickness output with regard to the thickness from shadow. We now take the min instead of the max between both. This break a lot of file since the default thickness is set to `0.1`. This patch doesn't change the default thickness output behavior but it will be changed in another PR. Note this might change the shadow sampling pattern since translucent and non-translucent are now sampling them separately. Pull Request: https://projects.blender.org/blender/blender/pulls/120329
2024-04-12 22:06:25 +02:00
/* 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
2024-02-26 10:23:12 +11:00
* 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;
EEVEE-Next: Add consistent support for thickness output This add support for the thickness output in a consistent manner across all BSDF. If thickness is above zero, for any ray going below the surface, the object is modeled as a sphere tangent to the shading point and of diameter equal to the given thickness. The ray is then intersected with that sphere and transmitted out. This model perfectly matches a raytracer behavior for a sphere model of diameter equal to thickness. This replaces the old refraction depth model which was modelling a infinite slab of material. For simplicity, we do not do 2 microfacet transmission events. We simply use the main lobe direction for the first interface. This still matches the raytracer behavior for smooth surfaces. Weirdly enough the apparent roughness doesn't need to be amended. To get shadowing and translucency approximation to work, this splits the transmission BSDF evaluation to its own light loop. This simplifies a lot of logic and assumes only one transmission BSDF is ever sampled in a nodetree. This changes the behavior of the thickness output with regard to the thickness from shadow. We now take the min instead of the max between both. This break a lot of file since the default thickness is set to `0.1`. This patch doesn't change the default thickness output behavior but it will be changed in another PR. Note this might change the shadow sampling pattern since translucent and non-translucent are now sampling them separately. Pull Request: https://projects.blender.org/blender/blender/pulls/120329
2024-04-12 22:06:25 +02:00
#ifdef MAT_CLEARCOAT
# if CLOSURE_BIN_COUNT == 2
/* Multiple reflection closures. */
CHOOSE_MIN_WEIGHT_CLOSURE_BIN(0, 1);
EEVEE-Next: Add consistent support for thickness output This add support for the thickness output in a consistent manner across all BSDF. If thickness is above zero, for any ray going below the surface, the object is modeled as a sphere tangent to the shading point and of diameter equal to the given thickness. The ray is then intersected with that sphere and transmitted out. This model perfectly matches a raytracer behavior for a sphere model of diameter equal to thickness. This replaces the old refraction depth model which was modelling a infinite slab of material. For simplicity, we do not do 2 microfacet transmission events. We simply use the main lobe direction for the first interface. This still matches the raytracer behavior for smooth surfaces. Weirdly enough the apparent roughness doesn't need to be amended. To get shadowing and translucency approximation to work, this splits the transmission BSDF evaluation to its own light loop. This simplifies a lot of logic and assumes only one transmission BSDF is ever sampled in a nodetree. This changes the behavior of the thickness output with regard to the thickness from shadow. We now take the min instead of the max between both. This break a lot of file since the default thickness is set to `0.1`. This patch doesn't change the default thickness output behavior but it will be changed in another PR. Note this might change the shadow sampling pattern since translucent and non-translucent are now sampling them separately. Pull Request: https://projects.blender.org/blender/blender/pulls/120329
2024-04-12 22:06:25 +02:00
# elif CLOSURE_BIN_COUNT == 3
/* Multiple reflection closures and one other closure. */
CHOOSE_MIN_WEIGHT_CLOSURE_BIN(1, 2);
EEVEE-Next: Add consistent support for thickness output This add support for the thickness output in a consistent manner across all BSDF. If thickness is above zero, for any ray going below the surface, the object is modeled as a sphere tangent to the shading point and of diameter equal to the given thickness. The ray is then intersected with that sphere and transmitted out. This model perfectly matches a raytracer behavior for a sphere model of diameter equal to thickness. This replaces the old refraction depth model which was modelling a infinite slab of material. For simplicity, we do not do 2 microfacet transmission events. We simply use the main lobe direction for the first interface. This still matches the raytracer behavior for smooth surfaces. Weirdly enough the apparent roughness doesn't need to be amended. To get shadowing and translucency approximation to work, this splits the transmission BSDF evaluation to its own light loop. This simplifies a lot of logic and assumes only one transmission BSDF is ever sampled in a nodetree. This changes the behavior of the thickness output with regard to the thickness from shadow. We now take the min instead of the max between both. This break a lot of file since the default thickness is set to `0.1`. This patch doesn't change the default thickness output behavior but it will be changed in another PR. Note this might change the shadow sampling pattern since translucent and non-translucent are now sampling them separately. Pull Request: https://projects.blender.org/blender/blender/pulls/120329
2024-04-12 22:06:25 +02:00
# 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;
EEVEE-Next: Add consistent support for thickness output This add support for the thickness output in a consistent manner across all BSDF. If thickness is above zero, for any ray going below the surface, the object is modeled as a sphere tangent to the shading point and of diameter equal to the given thickness. The ray is then intersected with that sphere and transmitted out. This model perfectly matches a raytracer behavior for a sphere model of diameter equal to thickness. This replaces the old refraction depth model which was modelling a infinite slab of material. For simplicity, we do not do 2 microfacet transmission events. We simply use the main lobe direction for the first interface. This still matches the raytracer behavior for smooth surfaces. Weirdly enough the apparent roughness doesn't need to be amended. To get shadowing and translucency approximation to work, this splits the transmission BSDF evaluation to its own light loop. This simplifies a lot of logic and assumes only one transmission BSDF is ever sampled in a nodetree. This changes the behavior of the thickness output with regard to the thickness from shadow. We now take the min instead of the max between both. This break a lot of file since the default thickness is set to `0.1`. This patch doesn't change the default thickness output behavior but it will be changed in another PR. Note this might change the shadow sampling pattern since translucent and non-translucent are now sampling them separately. Pull Request: https://projects.blender.org/blender/blender/pulls/120329
2024-04-12 22:06:25 +02:00
/* 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);
}
2023-09-05 10:49:20 +10:00
/* 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)
{
2023-08-25 08:56:58 +10:00
/* 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);
EEVEE-Next: Volume: Fragment shader voxelization This replaces the compute shader pass for volume material properties voxelization by a fragment shader that is run only once per pixel. The fragment shader then execute the nodetree in a loop for each individual froxel. The motivations are: - faster evaluation of homogenous materials: can evaluate nodetree once and fast write the properties for all froxel in a loop. This matches cycles homogenous material optimization (except that it only considers the first hit). - no invocations for empty froxels: not restricted to box dispach. - support for more than one material: invocations are per pixel. - cleaner implementation (no compute shader specific paths). Implementation wise, this is done by adding a stencil texture when rendering volumetric objects. It is populated during the occupancy phase but it is not directly used (the stencil test is enabled but since we use `imageAtomic` to set the occupancy bits, the fragment shader is forced to be run). The early depth-test is then turned on for the material properties pass, allowing only one fragment to be invoked. This fragment runs the nodetree at the desired frequency: once per direction (homogenous), or once per froxel (heterogenous). Note that I tried to use the frontmost fragment using a depth equal test but it was failing for some reason on Apple silicon producing flickering artifacts. We might reconsider this frontmost fragment approach later since the result is now face order dependant when an object has multiple materials. Pull Request: https://projects.blender.org/blender/blender/pulls/119439
2024-04-05 16:33:58 +02:00
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
*/
2023-07-14 19:03:00 +02:00
float F0_from_ior(float eta)
{
return square(f0_from_ior(eta));
}
float F0_from_f0(float f0)
{
return square(f0);
}
2023-07-14 19:03:00 +02:00
/* 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;
}
2023-07-14 19:03:00 +02:00
/* 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);
2023-07-14 19:03:00 +02:00
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
2023-10-03 11:22:43 +11:00
* "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);
}
2023-07-14 19:03:00 +02:00
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;
2023-07-14 19:03:00 +02:00
}
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.
2023-09-26 19:50:04 +10:00
* 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)
2023-07-14 19:03:00 +02:00
{
auto &utility_tx = sampler_get(eevee_utility_texture, utility_tx);
if (ior == 1.0f) {
reflectance = float3(0.0f);
transmittance = transmission_tint;
return;
2023-07-14 19:03:00 +02:00
}
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;
2023-07-14 19:03:00 +02:00
}
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.
*
* \{ */
EEVEE-Next: Volume: Fragment shader voxelization This replaces the compute shader pass for volume material properties voxelization by a fragment shader that is run only once per pixel. The fragment shader then execute the nodetree in a loop for each individual froxel. The motivations are: - faster evaluation of homogenous materials: can evaluate nodetree once and fast write the properties for all froxel in a loop. This matches cycles homogenous material optimization (except that it only considers the first hit). - no invocations for empty froxels: not restricted to box dispach. - support for more than one material: invocations are per pixel. - cleaner implementation (no compute shader specific paths). Implementation wise, this is done by adding a stencil texture when rendering volumetric objects. It is populated during the occupancy phase but it is not directly used (the stencil test is enabled but since we use `imageAtomic` to set the occupancy bits, the fragment shader is forced to be run). The early depth-test is then turned on for the material properties pass, allowing only one fragment to be invoked. This fragment runs the nodetree at the desired frequency: once per direction (homogenous), or once per froxel (heterogenous). Note that I tried to use the frontmost fragment using a depth equal test but it was failing for some reason on Apple silicon producing flickering artifacts. We might reconsider this frontmost fragment approach later since the result is now face order dependant when an object has multiple materials. Pull Request: https://projects.blender.org/blender/blender/pulls/119439
2024-04-05 16:33:58 +02:00
/* 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)
EEVEE-Next: Volume: Fragment shader voxelization This replaces the compute shader pass for volume material properties voxelization by a fragment shader that is run only once per pixel. The fragment shader then execute the nodetree in a loop for each individual froxel. The motivations are: - faster evaluation of homogenous materials: can evaluate nodetree once and fast write the properties for all froxel in a loop. This matches cycles homogenous material optimization (except that it only considers the first hit). - no invocations for empty froxels: not restricted to box dispach. - support for more than one material: invocations are per pixel. - cleaner implementation (no compute shader specific paths). Implementation wise, this is done by adding a stencil texture when rendering volumetric objects. It is populated during the occupancy phase but it is not directly used (the stencil test is enabled but since we use `imageAtomic` to set the occupancy bits, the fragment shader is forced to be run). The early depth-test is then turned on for the material properties pass, allowing only one fragment to be invoked. This fragment runs the nodetree at the desired frequency: once per direction (homogenous), or once per froxel (heterogenous). Note that I tried to use the frontmost fragment using a depth equal test but it was failing for some reason on Apple silicon producing flickering artifacts. We might reconsider this frontmost fragment approach later since the result is now face order dependant when an object has multiple materials. Pull Request: https://projects.blender.org/blender/blender/pulls/119439
2024-04-05 16:33:58 +02:00
/* 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)
{
EEVEE-Next: Volume: Fragment shader voxelization This replaces the compute shader pass for volume material properties voxelization by a fragment shader that is run only once per pixel. The fragment shader then execute the nodetree in a loop for each individual froxel. The motivations are: - faster evaluation of homogenous materials: can evaluate nodetree once and fast write the properties for all froxel in a loop. This matches cycles homogenous material optimization (except that it only considers the first hit). - no invocations for empty froxels: not restricted to box dispach. - support for more than one material: invocations are per pixel. - cleaner implementation (no compute shader specific paths). Implementation wise, this is done by adding a stencil texture when rendering volumetric objects. It is populated during the occupancy phase but it is not directly used (the stencil test is enabled but since we use `imageAtomic` to set the occupancy bits, the fragment shader is forced to be run). The early depth-test is then turned on for the material properties pass, allowing only one fragment to be invoked. This fragment runs the nodetree at the desired frequency: once per direction (homogenous), or once per froxel (heterogenous). Note that I tried to use the frontmost fragment using a depth equal test but it was failing for some reason on Apple silicon producing flickering artifacts. We might reconsider this frontmost fragment approach later since the result is now face order dependant when an object has multiple materials. Pull Request: https://projects.blender.org/blender/blender/pulls/119439
2024-04-05 16:33:58 +02:00
#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;
EEVEE-Next: Volume: Fragment shader voxelization This replaces the compute shader pass for volume material properties voxelization by a fragment shader that is run only once per pixel. The fragment shader then execute the nodetree in a loop for each individual froxel. The motivations are: - faster evaluation of homogenous materials: can evaluate nodetree once and fast write the properties for all froxel in a loop. This matches cycles homogenous material optimization (except that it only considers the first hit). - no invocations for empty froxels: not restricted to box dispach. - support for more than one material: invocations are per pixel. - cleaner implementation (no compute shader specific paths). Implementation wise, this is done by adding a stencil texture when rendering volumetric objects. It is populated during the occupancy phase but it is not directly used (the stencil test is enabled but since we use `imageAtomic` to set the occupancy bits, the fragment shader is forced to be run). The early depth-test is then turned on for the material properties pass, allowing only one fragment to be invoked. This fragment runs the nodetree at the desired frequency: once per direction (homogenous), or once per froxel (heterogenous). Note that I tried to use the frontmost fragment using a depth equal test but it was failing for some reason on Apple silicon producing flickering artifacts. We might reconsider this frontmost fragment approach later since the result is now face order dependant when an object has multiple materials. Pull Request: https://projects.blender.org/blender/blender/pulls/119439
2024-04-05 16:33:58 +02:00
#endif
return attr;
}
float4 attr_load_color_post(float4 attr)
{
EEVEE-Next: Volume: Fragment shader voxelization This replaces the compute shader pass for volume material properties voxelization by a fragment shader that is run only once per pixel. The fragment shader then execute the nodetree in a loop for each individual froxel. The motivations are: - faster evaluation of homogenous materials: can evaluate nodetree once and fast write the properties for all froxel in a loop. This matches cycles homogenous material optimization (except that it only considers the first hit). - no invocations for empty froxels: not restricted to box dispach. - support for more than one material: invocations are per pixel. - cleaner implementation (no compute shader specific paths). Implementation wise, this is done by adding a stencil texture when rendering volumetric objects. It is populated during the occupancy phase but it is not directly used (the stencil test is enabled but since we use `imageAtomic` to set the occupancy bits, the fragment shader is forced to be run). The early depth-test is then turned on for the material properties pass, allowing only one fragment to be invoked. This fragment runs the nodetree at the desired frequency: once per direction (homogenous), or once per froxel (heterogenous). Note that I tried to use the frontmost fragment using a depth equal test but it was failing for some reason on Apple silicon producing flickering artifacts. We might reconsider this frontmost fragment approach later since the result is now face order dependant when an object has multiple materials. Pull Request: https://projects.blender.org/blender/blender/pulls/119439
2024-04-05 16:33:58 +02:00
#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;
EEVEE-Next: Volume: Fragment shader voxelization This replaces the compute shader pass for volume material properties voxelization by a fragment shader that is run only once per pixel. The fragment shader then execute the nodetree in a loop for each individual froxel. The motivations are: - faster evaluation of homogenous materials: can evaluate nodetree once and fast write the properties for all froxel in a loop. This matches cycles homogenous material optimization (except that it only considers the first hit). - no invocations for empty froxels: not restricted to box dispach. - support for more than one material: invocations are per pixel. - cleaner implementation (no compute shader specific paths). Implementation wise, this is done by adding a stencil texture when rendering volumetric objects. It is populated during the occupancy phase but it is not directly used (the stencil test is enabled but since we use `imageAtomic` to set the occupancy bits, the fragment shader is forced to be run). The early depth-test is then turned on for the material properties pass, allowing only one fragment to be invoked. This fragment runs the nodetree at the desired frequency: once per direction (homogenous), or once per froxel (heterogenous). Note that I tried to use the frontmost fragment using a depth equal test but it was failing for some reason on Apple silicon producing flickering artifacts. We might reconsider this frontmost fragment approach later since the result is now face order dependant when an object has multiple materials. Pull Request: https://projects.blender.org/blender/blender/pulls/119439
2024-04-05 16:33:58 +02:00
#endif
return attr;
}
EEVEE-Next: Volume: Fragment shader voxelization This replaces the compute shader pass for volume material properties voxelization by a fragment shader that is run only once per pixel. The fragment shader then execute the nodetree in a loop for each individual froxel. The motivations are: - faster evaluation of homogenous materials: can evaluate nodetree once and fast write the properties for all froxel in a loop. This matches cycles homogenous material optimization (except that it only considers the first hit). - no invocations for empty froxels: not restricted to box dispach. - support for more than one material: invocations are per pixel. - cleaner implementation (no compute shader specific paths). Implementation wise, this is done by adding a stencil texture when rendering volumetric objects. It is populated during the occupancy phase but it is not directly used (the stencil test is enabled but since we use `imageAtomic` to set the occupancy bits, the fragment shader is forced to be run). The early depth-test is then turned on for the material properties pass, allowing only one fragment to be invoked. This fragment runs the nodetree at the desired frequency: once per direction (homogenous), or once per froxel (heterogenous). Note that I tried to use the frontmost fragment using a depth equal test but it was failing for some reason on Apple silicon producing flickering artifacts. We might reconsider this frontmost fragment approach later since the result is now face order dependant when an object has multiple materials. Pull Request: https://projects.blender.org/blender/blender/pulls/119439
2024-04-05 16:33:58 +02:00
#undef GRID_ATTRIBUTES_LOAD_POST
/** \} */
/* -------------------------------------------------------------------- */
2022-09-02 20:56:55 +02:00
/** \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);
}
/** \} */