Fix: EEVEE: Object holdout not working

This implement the holdout flag by switching to
the holdout case in the shader. This has a few benefits:
- Doesn't recompile the shaders.
- Makes the object infos mandatory (already the case in
  practice)
- Handle transparent materials properly, keeping the
  transparency working.

Fix #123284

Pull Request: https://projects.blender.org/blender/blender/pulls/123315
This commit is contained in:
Clément Foucault
2024-06-17 19:08:37 +02:00
committed by Clément Foucault
parent df98aa61bb
commit b5a9a67dcf
12 changed files with 65 additions and 36 deletions

View File

@@ -421,9 +421,6 @@ Material &MaterialModule::material_sync(Object *ob,
::Material *MaterialModule::material_from_slot(Object *ob, int slot)
{
if (ob->base_flag & BASE_HOLDOUT) {
return BKE_material_default_holdout();
}
::Material *ma = BKE_object_material_get(ob, slot + 1);
if (ma == nullptr) {
if (ob->type == OB_VOLUME) {

View File

@@ -404,12 +404,6 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
GPUCodegenOutput &codegen = *codegen_;
ShaderCreateInfo &info = *reinterpret_cast<ShaderCreateInfo *>(codegen.create_info);
/* WORKAROUND: Replace by new ob info. */
int64_t ob_info_index = info.additional_infos_.first_index_of_try("draw_object_infos");
if (ob_info_index != -1) {
info.additional_infos_[ob_info_index] = "draw_object_infos_new";
}
/* WORKAROUND: Add new ob attr buffer. */
if (GPU_material_uniform_attributes(gpumat) != nullptr) {
info.additional_info("draw_object_attribute_new");
@@ -586,9 +580,6 @@ void ShaderModule::material_create_info_ammend(GPUMaterial *gpumat, GPUCodegenOu
info.vertex_inputs_.clear();
/* Volume materials require these for loading the grid attributes from smoke sims. */
info.additional_info("draw_volume_infos");
if (ob_info_index == -1) {
info.additional_info("draw_object_infos_new");
}
}
break;
case MAT_GEOM_POINT_CLOUD:

View File

@@ -22,7 +22,7 @@ int g_attr_id = 0;
/* Point clouds and curves are not compatible with volume grids.
* They will fallback to their own attributes loading. */
#if defined(MAT_VOLUME) && !defined(MAT_GEOM_CURVES) && !defined(MAT_GEOM_POINT_CLOUD)
# if defined(OBINFO_LIB) && !defined(MAT_GEOM_WORLD)
# if defined(VOLUME_INFO_LIB) && !defined(MAT_GEOM_WORLD)
# define GRID_ATTRIBUTES
# endif

View File

@@ -731,7 +731,7 @@ float film_scaling_factor_get()
/* Point clouds and curves are not compatible with volume grids.
* They will fallback to their own attributes loading. */
#if defined(MAT_VOLUME) && !defined(MAT_GEOM_CURVES) && !defined(MAT_GEOM_POINT_CLOUD)
# if defined(OBINFO_LIB) && !defined(MAT_GEOM_WORLD)
# 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
@@ -740,7 +740,7 @@ float film_scaling_factor_get()
float attr_load_temperature_post(float attr)
{
#ifdef GRID_ATTRIBUTES_LOAD_POST
/* Bring the into standard range without having to modify the grid values */
/* Bring the value into standard range without having to modify the grid values */
attr = (attr > 0.01) ? (attr * drw_volume.temperature_mul + drw_volume.temperature_bias) : 0.0;
#endif
return attr;

View File

@@ -50,9 +50,20 @@ void main()
float thickness = nodetree_thickness() * thickness_mode;
/** Transparency weight is already applied through dithering, remove it from other closures. */
float transparency = 1.0 - average(g_transmittance);
float transparency_rcp = safe_rcp(transparency);
g_emission *= transparency_rcp;
float alpha = 1.0 - average(g_transmittance);
float alpha_rcp = safe_rcp(alpha);
/* Object holdout. */
eObjectInfoFlag ob_flag = eObjectInfoFlag(floatBitsToUint(drw_infos[resource_id].infos.w));
if (flag_test(ob_flag, OBJECT_HOLDOUT)) {
/* alpha is set from rejected pixels / dithering. */
g_holdout = 1.0;
/* Set alpha to 0.0 so that lighting is not computed. */
alpha_rcp = 0.0;
}
g_emission *= alpha_rcp;
ivec2 out_texel = ivec2(gl_FragCoord.xy);
@@ -72,12 +83,12 @@ void main()
/* ----- GBuffer output ----- */
GBufferData gbuf_data;
gbuf_data.closure[0] = g_closure_get_resolved(0, transparency_rcp);
gbuf_data.closure[0] = g_closure_get_resolved(0, alpha_rcp);
#if CLOSURE_BIN_COUNT > 1
gbuf_data.closure[1] = g_closure_get_resolved(1, transparency_rcp);
gbuf_data.closure[1] = g_closure_get_resolved(1, alpha_rcp);
#endif
#if CLOSURE_BIN_COUNT > 2
gbuf_data.closure[2] = g_closure_get_resolved(2, transparency_rcp);
gbuf_data.closure[2] = g_closure_get_resolved(2, alpha_rcp);
#endif
gbuf_data.surface_N = g_data.N;
gbuf_data.thickness = thickness;

View File

@@ -49,6 +49,13 @@ void main()
nodetree_surface(closure_rand);
eObjectInfoFlag ob_flag = eObjectInfoFlag(floatBitsToUint(drw_infos[resource_id].infos.w));
if (flag_test(ob_flag, OBJECT_HOLDOUT)) {
g_holdout = 1.0;
}
g_holdout = saturate(g_holdout);
vec3 radiance, transmittance;
forward_lighting_eval(g_thickness, radiance, transmittance);
@@ -64,6 +71,6 @@ void main()
radiance *= 1.0 - saturate(g_holdout);
out_radiance = vec4(radiance, 0.0);
out_radiance = vec4(radiance, g_holdout);
out_transmittance = vec4(transmittance, saturate(average(transmittance)));
}

View File

@@ -53,9 +53,20 @@ void main()
g_holdout = saturate(g_holdout);
/** Transparency weight is already applied through dithering, remove it from other closures. */
float transparency = 1.0 - average(g_transmittance);
float transparency_rcp = safe_rcp(transparency);
g_emission *= transparency_rcp;
float alpha = 1.0 - average(g_transmittance);
float alpha_rcp = safe_rcp(alpha);
/* Object holdout. */
eObjectInfoFlag ob_flag = eObjectInfoFlag(floatBitsToUint(drw_infos[resource_id].infos.w));
if (flag_test(ob_flag, OBJECT_HOLDOUT)) {
/* alpha is set from rejected pixels / dithering. */
g_holdout = 1.0;
/* Set alpha to 0.0 so that lighting is not computed. */
alpha_rcp = 0.0;
}
g_emission *= alpha_rcp;
ivec2 out_texel = ivec2(gl_FragCoord.xy);
@@ -75,12 +86,12 @@ void main()
/* ----- GBuffer output ----- */
GBufferData gbuf_data;
gbuf_data.closure[0] = g_closure_get_resolved(0, transparency_rcp);
gbuf_data.closure[0] = g_closure_get_resolved(0, alpha_rcp);
#if CLOSURE_BIN_COUNT > 1
gbuf_data.closure[1] = g_closure_get_resolved(1, transparency_rcp);
gbuf_data.closure[1] = g_closure_get_resolved(1, alpha_rcp);
#endif
#if CLOSURE_BIN_COUNT > 2
gbuf_data.closure[2] = g_closure_get_resolved(2, transparency_rcp);
gbuf_data.closure[2] = g_closure_get_resolved(2, alpha_rcp);
#endif
gbuf_data.surface_N = g_data.N;
gbuf_data.thickness = g_thickness;

View File

@@ -54,7 +54,10 @@ GPU_SHADER_CREATE_INFO(eevee_geom_mesh)
.vertex_in(1, Type::VEC3, "nor")
.vertex_source("eevee_geom_mesh_vert.glsl")
.vertex_out(eevee_surf_iface)
.additional_info("draw_modelmat_new", "draw_resource_id_varying", "draw_view");
.additional_info("draw_modelmat_new",
"draw_object_infos_new",
"draw_resource_id_varying",
"draw_view");
GPU_SHADER_INTERFACE_INFO(eevee_surf_point_cloud_iface, "point_cloud_interp")
.smooth(Type::FLOAT, "radius")
@@ -71,6 +74,7 @@ GPU_SHADER_CREATE_INFO(eevee_geom_point_cloud)
.vertex_out(eevee_surf_point_cloud_flat_iface)
.additional_info("draw_pointcloud_new",
"draw_modelmat_new",
"draw_object_infos_new",
"draw_resource_id_varying",
"draw_view");
@@ -91,7 +95,10 @@ GPU_SHADER_CREATE_INFO(eevee_geom_gpencil)
.define("MAT_GEOM_GPENCIL")
.vertex_source("eevee_geom_gpencil_vert.glsl")
.vertex_out(eevee_surf_iface)
.additional_info("draw_gpencil_new", "draw_resource_id_varying", "draw_resource_id_new");
.additional_info("draw_gpencil_new",
"draw_object_infos_new",
"draw_resource_id_varying",
"draw_resource_id_new");
GPU_SHADER_INTERFACE_INFO(eevee_surf_curve_iface, "curve_interp")
.smooth(Type::VEC2, "barycentric_coords")
@@ -111,6 +118,7 @@ GPU_SHADER_CREATE_INFO(eevee_geom_curves)
.vertex_out(eevee_surf_curve_iface)
.vertex_out(eevee_surf_curve_flat_iface)
.additional_info("draw_modelmat_new",
"draw_object_infos_new",
"draw_resource_id_varying",
"draw_view",
"draw_hair_new",
@@ -122,7 +130,10 @@ GPU_SHADER_CREATE_INFO(eevee_geom_world)
.builtins(BuiltinBits::VERTEX_ID)
.vertex_source("eevee_geom_world_vert.glsl")
.vertex_out(eevee_surf_iface)
.additional_info("draw_modelmat_new", "draw_resource_id_varying", "draw_view");
.additional_info("draw_modelmat_new",
"draw_object_infos_new", /* Unused, but allow debug compilation. */
"draw_resource_id_varying",
"draw_view");
/** \} */

View File

@@ -73,6 +73,8 @@ inline void ObjectInfos::sync(const blender::draw::ObjectRef ref, bool is_active
{
object_attrs_len = 0;
object_attrs_offset = 0;
bool is_holdout = (ref.object->base_flag & BASE_HOLDOUT) ||
(ref.object->visibility_flag & OB_HOLDOUT);
ob_color = ref.object->color;
index = ref.object->index;
@@ -85,6 +87,7 @@ inline void ObjectInfos::sync(const blender::draw::ObjectRef ref, bool is_active
flag, ref.object->base_flag & BASE_FROM_SET, eObjectInfoFlag::OBJECT_FROM_SET);
SET_FLAG_FROM_TEST(
flag, ref.object->transflag & OB_NEG_SCALE, eObjectInfoFlag::OBJECT_NEGATIVE_SCALE);
SET_FLAG_FROM_TEST(flag, is_holdout, eObjectInfoFlag::OBJECT_HOLDOUT);
if (ref.dupli_object == nullptr) {
/* TODO(fclem): this is rather costly to do at draw time. Maybe we can

View File

@@ -153,8 +153,9 @@ enum eObjectInfoFlag : uint32_t {
OBJECT_FROM_SET = (1u << 2u),
OBJECT_ACTIVE = (1u << 3u),
OBJECT_NEGATIVE_SCALE = (1u << 4u),
OBJECT_HOLDOUT = (1u << 5u),
/* Avoid skipped info to change culling. */
OBJECT_NO_INFO = ~OBJECT_NEGATIVE_SCALE
OBJECT_NO_INFO = ~OBJECT_HOLDOUT
};
struct ObjectInfos {

View File

@@ -18,6 +18,7 @@ GPU_SHADER_CREATE_INFO(draw_object_infos)
GPU_SHADER_CREATE_INFO(draw_volume_infos)
.typedef_source("draw_shader_shared.hh")
.define("VOLUME_INFO_LIB")
.uniform_buf(DRW_OBJ_DATA_INFO_UBO_SLOT, "VolumeInfos", "drw_volume", Frequency::BATCH);
GPU_SHADER_CREATE_INFO(draw_curves_infos)

View File

@@ -282,10 +282,6 @@ class GPUCodegen {
create_info = new GPUCodegenCreateInfo("codegen");
output.create_info = reinterpret_cast<GPUShaderCreateInfo *>(
static_cast<ShaderCreateInfo *>(create_info));
if (GPU_material_flag_get(mat_, GPU_MATFLAG_OBJECT_INFO)) {
create_info->additional_info("draw_object_infos");
}
}
~GPUCodegen()