Eevee-next: Lookdev HDRI Switcher #109548
@ -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
|
||||
|
||||
|
||||
|
@ -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
|
||||
|
@ -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(){};
|
||||
|
199
source/blender/draw/engines/eevee_next/eevee_lookdev.cc
Normal file
199
source/blender/draw/engines/eevee_next/eevee_lookdev.cc
Normal 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 ¶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
|
96
source/blender/draw/engines/eevee_next/eevee_lookdev.hh
Normal file
96
source/blender/draw/engines/eevee_next/eevee_lookdev.hh
Normal 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 ¶meters);
|
||||
};
|
||||
|
||||
/** \} */
|
||||
|
||||
/* -------------------------------------------------------------------- */
|
||||
/** \name Lookdev
|
||||
*
|
||||
* Look Development can override the world.
|
||||
Jeroen-Bakker marked this conversation as resolved
Outdated
|
||||
*
|
||||
* \{ */
|
||||
|
||||
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
|
@ -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);
|
||||
|
@ -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);
|
||||
};
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user
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.