diff --git a/scripts/startup/bl_ui/space_view3d.py b/scripts/startup/bl_ui/space_view3d.py index fa4bd8df7b2..9a33176534f 100644 --- a/scripts/startup/bl_ui/space_view3d.py +++ b/scripts/startup/bl_ui/space_view3d.py @@ -6145,14 +6145,17 @@ class VIEW3D_PT_shading_lighting(Panel): split = layout.split(factor=0.9) col = split.column() + engine = context.scene.render.engine row = col.row() - row.prop(shading, "use_studiolight_view_rotation", text="", icon='WORLD', toggle=True) - row = row.row() + if engine != 'BLENDER_EEVEE_NEXT': + row.prop(shading, "use_studiolight_view_rotation", text="", icon='WORLD', toggle=True) + row = row.row() row.prop(shading, "studiolight_rotate_z", text="Rotation") col.prop(shading, "studiolight_intensity") col.prop(shading, "studiolight_background_alpha") - col.prop(shading, "studiolight_background_blur") + if engine != 'BLENDER_EEVEE_NEXT': + col.prop(shading, "studiolight_background_blur") col = split.column() # to align properly with above elif shading.type == 'RENDERED': @@ -6176,7 +6179,9 @@ class VIEW3D_PT_shading_lighting(Panel): col.prop(shading, "studiolight_rotate_z", text="Rotation") col.prop(shading, "studiolight_intensity") col.prop(shading, "studiolight_background_alpha") - col.prop(shading, "studiolight_background_blur") + engine = context.scene.render.engine + if engine != 'BLENDER_EEVEE_NEXT': + col.prop(shading, "studiolight_background_blur") col = split.column() # to align properly with above diff --git a/source/blender/draw/CMakeLists.txt b/source/blender/draw/CMakeLists.txt index b7cbfb9ad96..738d579acd1 100644 --- a/source/blender/draw/CMakeLists.txt +++ b/source/blender/draw/CMakeLists.txt @@ -152,6 +152,7 @@ set(SRC engines/eevee_next/eevee_light.cc engines/eevee_next/eevee_lightcache.cc engines/eevee_next/eevee_lightprobe.cc + engines/eevee_next/eevee_lookdev.cc engines/eevee_next/eevee_material.cc engines/eevee_next/eevee_motion_blur.cc engines/eevee_next/eevee_pipeline.cc @@ -297,6 +298,7 @@ set(SRC engines/eevee_next/eevee_light.hh engines/eevee_next/eevee_lightcache.hh engines/eevee_next/eevee_lightprobe.hh + engines/eevee_next/eevee_lookdev.hh engines/eevee_next/eevee_material.hh engines/eevee_next/eevee_motion_blur.hh engines/eevee_next/eevee_pipeline.hh diff --git a/source/blender/draw/engines/eevee_next/eevee_instance.hh b/source/blender/draw/engines/eevee_next/eevee_instance.hh index c4712150f36..c2b1746b3d5 100644 --- a/source/blender/draw/engines/eevee_next/eevee_instance.hh +++ b/source/blender/draw/engines/eevee_next/eevee_instance.hh @@ -25,6 +25,7 @@ #include "eevee_irradiance_cache.hh" #include "eevee_light.hh" #include "eevee_lightprobe.hh" +#include "eevee_lookdev.hh" #include "eevee_material.hh" #include "eevee_motion_blur.hh" #include "eevee_pipeline.hh" @@ -71,6 +72,7 @@ class Instance { MainView main_view; CaptureView capture_view; World world; + LookdevModule lookdev; LightProbeModule light_probes; IrradianceCache irradiance_cache; @@ -124,6 +126,7 @@ class Instance { main_view(*this), capture_view(*this), world(*this), + lookdev(*this), light_probes(*this), irradiance_cache(*this){}; ~Instance(){}; diff --git a/source/blender/draw/engines/eevee_next/eevee_lookdev.cc b/source/blender/draw/engines/eevee_next/eevee_lookdev.cc new file mode 100644 index 00000000000..1a9d40e6203 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_lookdev.cc @@ -0,0 +1,199 @@ +/* SPDX-FileCopyrightText: 2023 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup eevee + */ + +#include "BKE_image.h" +#include "BKE_lib_id.h" +#include "BKE_node.hh" +#include "BKE_studiolight.h" + +#include "NOD_shader.h" + +#include "eevee_instance.hh" + +namespace blender::eevee { + +/* -------------------------------------------------------------------- */ +/** \name Viewport Override Node-Tree + * \{ */ + +LookdevWorldNodeTree::LookdevWorldNodeTree() +{ + bNodeTree *ntree = ntreeAddTree(nullptr, "Lookdev World Nodetree", ntreeType_Shader->idname); + ntree_ = ntree; + + bNode *coordinate = nodeAddStaticNode(nullptr, ntree, SH_NODE_TEX_COORD); + bNodeSocket *coordinate_out = nodeFindSocket(coordinate, SOCK_OUT, "Generated"); + + bNode *rotate = nodeAddStaticNode(nullptr, ntree, SH_NODE_VECTOR_ROTATE); + rotate->custom1 = NODE_VECTOR_ROTATE_TYPE_AXIS_Z; + bNodeSocket *rotate_vector_in = nodeFindSocket(rotate, SOCK_IN, "Vector"); + angle_socket_ = static_cast( + static_cast(nodeFindSocket(rotate, SOCK_IN, "Angle")->default_value)); + bNodeSocket *rotate_out = nodeFindSocket(rotate, SOCK_OUT, "Vector"); + + bNode *environment = nodeAddStaticNode(nullptr, ntree, SH_NODE_TEX_ENVIRONMENT); + environment_node_ = environment; + NodeTexImage *environment_storage = static_cast(environment->storage); + bNodeSocket *environment_vector_in = nodeFindSocket(environment, SOCK_IN, "Vector"); + bNodeSocket *environment_out = nodeFindSocket(environment, SOCK_OUT, "Color"); + + bNode *background = nodeAddStaticNode(nullptr, ntree, SH_NODE_BACKGROUND); + bNodeSocket *background_out = nodeFindSocket(background, SOCK_OUT, "Background"); + bNodeSocket *background_color_in = nodeFindSocket(background, SOCK_IN, "Color"); + intensity_socket_ = static_cast( + static_cast(nodeFindSocket(background, SOCK_IN, "Strength")->default_value)); + + bNode *output = nodeAddStaticNode(nullptr, ntree, SH_NODE_OUTPUT_WORLD); + bNodeSocket *output_in = nodeFindSocket(output, SOCK_IN, "Surface"); + + nodeAddLink(ntree, coordinate, coordinate_out, rotate, rotate_vector_in); + nodeAddLink(ntree, rotate, rotate_out, environment, environment_vector_in); + nodeAddLink(ntree, environment, environment_out, background, background_color_in); + nodeAddLink(ntree, background, background_out, output, output_in); + nodeSetActive(ntree, output); + + /* Create a dummy image data block to hold GPU textures generated by studiolights. */ + BLI_strncpy(image.id.name, "IMLookdev", sizeof(image.id.name)); + BKE_libblock_init_empty(&image.id); + image.type = IMA_TYPE_IMAGE; + image.source = IMA_SRC_GENERATED; + ImageTile *base_tile = BKE_image_get_tile(&image, 0); + base_tile->gen_x = 1; + base_tile->gen_y = 1; + base_tile->gen_type = IMA_GENTYPE_BLANK; + copy_v4_fl(base_tile->gen_color, 0.0f); + /* + * TODO: This works around the issue that the first time the texture is accessed the image would + * overwrite the set GPU texture. A better solution would be to use image data-blocks as part of + * the studiolights, but that requires a larger refactoring. + */ + BKE_image_get_gpu_texture(&image, &environment_storage->iuser, nullptr); +} + +LookdevWorldNodeTree::~LookdevWorldNodeTree() +{ + ntreeFreeEmbeddedTree(ntree_); + MEM_SAFE_FREE(ntree_); + BKE_libblock_free_datablock(&image.id, 0); +} + +bNodeTree *LookdevWorldNodeTree::nodetree_get(const LookdevParameters ¶meters) +{ + intensity_socket_->value = parameters.intensity; + angle_socket_->value = parameters.rot_z; + + GPU_TEXTURE_FREE_SAFE(image.gputexture[TEXTARGET_2D][0]); + environment_node_->id = nullptr; + + StudioLight *sl = BKE_studiolight_find(parameters.hdri.c_str(), + STUDIOLIGHT_ORIENTATIONS_MATERIAL_MODE); + if (sl) { + BKE_studiolight_ensure_flag(sl, STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE); + GPUTexture *texture = sl->equirect_radiance_gputexture; + if (texture != nullptr) { + GPU_texture_ref(texture); + image.gputexture[TEXTARGET_2D][0] = texture; + environment_node_->id = &image.id; + } + } + + return ntree_; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Lookdev + * + * \{ */ + +LookdevModule::~LookdevModule() +{ + GPU_material_free(&gpu_materials_); + gpu_material_ = nullptr; +} + +bool LookdevModule::sync_world() +{ + /* Check based on the v3d if the world is overridden. */ + LookdevParameters new_parameters(inst_.v3d); + if (parameters_ != new_parameters) { + if (parameters_.gpu_parameters_changed(new_parameters)) { + GPU_material_free(&gpu_materials_); + gpu_material_ = nullptr; + } + + parameters_ = new_parameters; + inst_.reflection_probes.do_world_update_set(true); + inst_.sampling.reset(); + } + + if (parameters_.show_scene_world) { + return false; + } + + ::bNodeTree *node_tree = world_override_tree.nodetree_get(parameters_); + gpu_material_ = inst_.shaders.material_shader_get("EEVEE Lookdev Background", + gpu_materials_, + node_tree, + MAT_PIPE_DEFERRED, + MAT_GEOM_WORLD, + true); + inst_.pipelines.world.sync(gpu_material_); + inst_.pipelines.background.sync(gpu_material_, parameters_.background_opacity); + + return true; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Parameters + * \{ */ + +LookdevParameters::LookdevParameters() {} + +LookdevParameters::LookdevParameters(const ::View3D *v3d) +{ + if (v3d == nullptr) { + return; + } + + const ::View3DShading &shading = v3d->shading; + show_scene_world = shading.type == OB_RENDER ? shading.flag & V3D_SHADING_SCENE_WORLD_RENDER : + shading.flag & V3D_SHADING_SCENE_WORLD; + if (!show_scene_world) { + rot_z = shading.studiolight_rot_z; + background_opacity = shading.studiolight_background; + blur = shading.studiolight_blur; + intensity = shading.studiolight_intensity; + hdri = StringRefNull(shading.lookdev_light); + } +} + +bool LookdevParameters::operator==(const LookdevParameters &other) const +{ + return hdri == other.hdri && rot_z == other.rot_z && + background_opacity == other.background_opacity && blur == other.blur && + intensity == other.intensity && show_scene_world == other.show_scene_world; +} + +bool LookdevParameters::gpu_parameters_changed(const LookdevParameters &other) const +{ + return !(hdri == other.hdri && rot_z == other.rot_z && blur == other.blur && + intensity == other.intensity); +} + +bool LookdevParameters::operator!=(const LookdevParameters &other) const +{ + return !(*this == other); +} + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_lookdev.hh b/source/blender/draw/engines/eevee_next/eevee_lookdev.hh new file mode 100644 index 00000000000..0c2b7e11df1 --- /dev/null +++ b/source/blender/draw/engines/eevee_next/eevee_lookdev.hh @@ -0,0 +1,96 @@ +/* SPDX-FileCopyrightText: 2023 Blender Foundation + * + * SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup eevee + * + * World rendering with material handling. Also take care of lookdev + * HDRI and default material. + */ + +#pragma once + +#include "DNA_world_types.h" + +namespace blender::eevee { + +class Instance; + +/* -------------------------------------------------------------------- */ +/** \name Parameters + * + * Parameters used to check changes and to configure the world shader node tree. + * + * \{ */ +struct LookdevParameters { + std::string hdri; + float rot_z = 0.0f; + float background_opacity = 0.0f; + float intensity = 1.0f; + float blur = 0.0f; + bool show_scene_world = true; + + LookdevParameters(); + LookdevParameters(const ::View3D *v3d); + bool operator==(const LookdevParameters &other) const; + bool operator!=(const LookdevParameters &other) const; + bool gpu_parameters_changed(const LookdevParameters &other) const; +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Viewport Override Node-Tree + * + * In a viewport the world can be overridden by a custom HDRI and some settings. + * \{ */ + +class LookdevWorldNodeTree { + private: + bNodeTree *ntree_ = nullptr; + bNode *environment_node_ = nullptr; + bNodeSocketValueFloat *intensity_socket_ = nullptr; + bNodeSocketValueFloat *angle_socket_ = nullptr; + ::Image image = {}; + + public: + LookdevWorldNodeTree(); + ~LookdevWorldNodeTree(); + + bNodeTree *nodetree_get(const LookdevParameters ¶meters); +}; + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Lookdev + * + * Look Development can override the world. + * + * \{ */ + +class LookdevModule { + public: + private: + Instance &inst_; + + LookdevWorldNodeTree world_override_tree; + + LookdevParameters parameters_; + ListBase gpu_materials_ = {nullptr, nullptr}; + GPUMaterial *gpu_material_ = nullptr; + + public: + LookdevModule(Instance &inst) : inst_(inst){}; + ~LookdevModule(); + + bool sync_world(); + + private: + ::World *get_world(::bNodeTree *node_tree); +}; + +/** \} */ + +} // namespace blender::eevee diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc index 9d63e67a311..017392942a8 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.cc +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.cc @@ -22,7 +22,7 @@ namespace blender::eevee { * Used to draw background. * \{ */ -void BackgroundPipeline::sync(GPUMaterial *gpumat) +void BackgroundPipeline::sync(GPUMaterial *gpumat, const float background_opacity) { Manager &manager = *inst_.manager; RenderBuffers &rbufs = inst_.render_buffers; @@ -32,7 +32,7 @@ void BackgroundPipeline::sync(GPUMaterial *gpumat) world_ps_.init(); world_ps_.state_set(DRW_STATE_WRITE_COLOR); world_ps_.material_set(manager, gpumat); - world_ps_.push_constant("world_opacity_fade", inst_.film.background_opacity_get()); + world_ps_.push_constant("world_opacity_fade", background_opacity); world_ps_.bind_texture("utility_tx", inst_.pipelines.utility_tx); /* RenderPasses & AOVs. Cleared by background (even if bad practice). */ world_ps_.bind_image("rp_color_img", &rbufs.rp_color_tx); diff --git a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh index f8f7b53cb15..47cc44bbfe4 100644 --- a/source/blender/draw/engines/eevee_next/eevee_pipeline.hh +++ b/source/blender/draw/engines/eevee_next/eevee_pipeline.hh @@ -37,7 +37,7 @@ class BackgroundPipeline { public: BackgroundPipeline(Instance &inst) : inst_(inst){}; - void sync(GPUMaterial *gpumat); + void sync(GPUMaterial *gpumat, float background_opacity); void render(View &view); }; diff --git a/source/blender/draw/engines/eevee_next/eevee_world.cc b/source/blender/draw/engines/eevee_next/eevee_world.cc index 908b6491210..7a97234ef35 100644 --- a/source/blender/draw/engines/eevee_next/eevee_world.cc +++ b/source/blender/draw/engines/eevee_next/eevee_world.cc @@ -78,9 +78,9 @@ World::~World() void World::sync() { - // if (inst_.lookdev.sync_world()) { - // return; - // } + if (inst_.lookdev.sync_world()) { + return; + } ::World *bl_world = inst_.scene->world; if (bl_world == nullptr) { @@ -108,7 +108,7 @@ void World::sync() inst_.manager->register_layer_attributes(gpumat); - inst_.pipelines.background.sync(gpumat); + inst_.pipelines.background.sync(gpumat, inst_.film.background_opacity_get()); inst_.pipelines.world.sync(gpumat); }