Eevee-next: Lookdev HDRI Switcher #109548

Merged
Jeroen Bakker merged 4 commits from Jeroen-Bakker/blender:eevee-next-lookdev-hdri-switcher into main 2023-07-03 15:34:18 +02:00
8 changed files with 316 additions and 11 deletions

View File

@ -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

View File

@ -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

View File

@ -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(){};

View File

@ -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<bNodeSocketValueFloat *>(
static_cast<void *>(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<NodeTexImage *>(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<bNodeSocketValueFloat *>(
static_cast<void *>(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 &parameters)
{
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

View File

@ -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 &parameters);
};
/** \} */
/* -------------------------------------------------------------------- */
/** \name Lookdev
*
* Look Development can override the world.
Jeroen-Bakker marked this conversation as resolved Outdated

Do not add this in the future comment. If you want to add information about future features, add some comment inside the structure with some explicit members.

Do not add this `in the future` comment. If you want to add information about future features, add some comment inside the structure with some explicit members.
*
* \{ */
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

View File

@ -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);

View File

@ -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);
};

View File

@ -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);
}