Blender V4.3
eevee_lookdev.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
9#include "BKE_image.hh"
10#include "BKE_lib_id.hh"
11#include "BKE_node.hh"
12#include "BKE_studiolight.h"
13
14#include "NOD_shader.h"
15
16#include "GPU_material.hh"
17
18#include "eevee_instance.hh"
19
20#include "draw_debug.hh"
21
22namespace blender::eevee {
23
24/* -------------------------------------------------------------------- */
29{
31 nullptr, "Lookdev World Nodetree", ntreeType_Shader->idname);
32
33 bNode *coordinate = bke::node_add_static_node(nullptr, ntree, SH_NODE_TEX_COORD);
34 bNodeSocket *coordinate_out = bke::node_find_socket(coordinate, SOCK_OUT, "Generated");
35
36 bNode *rotate = bke::node_add_static_node(nullptr, ntree, SH_NODE_VECTOR_ROTATE);
37 rotate->custom1 = NODE_VECTOR_ROTATE_TYPE_AXIS_Z;
38 bNodeSocket *rotate_vector_in = bke::node_find_socket(rotate, SOCK_IN, "Vector");
39 angle_socket_ = static_cast<bNodeSocketValueFloat *>(
40 static_cast<void *>(bke::node_find_socket(rotate, SOCK_IN, "Angle")->default_value));
41 bNodeSocket *rotate_out = bke::node_find_socket(rotate, SOCK_OUT, "Vector");
42
43 bNode *environment = bke::node_add_static_node(nullptr, ntree, SH_NODE_TEX_ENVIRONMENT);
44 environment_node_ = environment;
45 NodeTexImage *environment_storage = static_cast<NodeTexImage *>(environment->storage);
46 bNodeSocket *environment_vector_in = bke::node_find_socket(environment, SOCK_IN, "Vector");
47 bNodeSocket *environment_out = bke::node_find_socket(environment, SOCK_OUT, "Color");
48
49 bNode *background = bke::node_add_static_node(nullptr, ntree, SH_NODE_BACKGROUND);
50 bNodeSocket *background_out = bke::node_find_socket(background, SOCK_OUT, "Background");
51 bNodeSocket *background_color_in = bke::node_find_socket(background, SOCK_IN, "Color");
52 intensity_socket_ = static_cast<bNodeSocketValueFloat *>(
53 static_cast<void *>(bke::node_find_socket(background, SOCK_IN, "Strength")->default_value));
54
55 bNode *output = bke::node_add_static_node(nullptr, ntree, SH_NODE_OUTPUT_WORLD);
56 bNodeSocket *output_in = bke::node_find_socket(output, SOCK_IN, "Surface");
57
58 bke::node_add_link(ntree, coordinate, coordinate_out, rotate, rotate_vector_in);
59 bke::node_add_link(ntree, rotate, rotate_out, environment, environment_vector_in);
60 bke::node_add_link(ntree, environment, environment_out, background, background_color_in);
61 bke::node_add_link(ntree, background, background_out, output, output_in);
62 bke::node_set_active(ntree, output);
63
64 /* Create a dummy image data block to hold GPU textures generated by studio-lights. */
65 STRNCPY(image.id.name, "IMLookdev");
66 BKE_libblock_init_empty(&image.id);
67 image.type = IMA_TYPE_IMAGE;
68 image.source = IMA_SRC_GENERATED;
69 ImageTile *base_tile = BKE_image_get_tile(&image, 0);
70 base_tile->gen_x = 1;
71 base_tile->gen_y = 1;
72 base_tile->gen_type = IMA_GENTYPE_BLANK;
73 copy_v4_fl(base_tile->gen_color, 0.0f);
74 /* TODO: This works around the issue that the first time the texture is accessed the image would
75 * overwrite the set GPU texture. A better solution would be to use image data-blocks as part of
76 * the studio-lights, but that requires a larger refactoring. */
77 BKE_image_get_gpu_texture(&image, &environment_storage->iuser);
78
79 /* Create a dummy image data block to hold GPU textures generated by studio-lights. */
80 STRNCPY(world.id.name, "WOLookdev");
81 BKE_libblock_init_empty(&world.id);
82 world.use_nodes = true;
83 world.nodetree = ntree;
84}
85
91
92bool LookdevWorld::sync(const LookdevParameters &new_parameters)
93{
94 const bool parameters_changed = assign_if_different(parameters_, new_parameters);
95
96 if (parameters_changed) {
97 intensity_socket_->value = parameters_.intensity;
98 angle_socket_->value = parameters_.rot_z;
99
100 GPU_TEXTURE_FREE_SAFE(image.gputexture[TEXTARGET_2D][0]);
101 environment_node_->id = nullptr;
102
103 StudioLight *sl = BKE_studiolight_find(parameters_.hdri.c_str(),
105 if (sl) {
107 GPUTexture *texture = sl->equirect_radiance_gputexture;
108 if (texture != nullptr) {
109 GPU_texture_ref(texture);
110 image.gputexture[TEXTARGET_2D][0] = texture;
111 environment_node_->id = &image.id;
112 }
113 }
114
115 GPU_material_free(&world.gpumaterial);
116 }
117 return parameters_changed;
118}
119
122/* -------------------------------------------------------------------- */
128
130
131void LookdevModule::init(const rcti *visible_rect)
132{
133 visible_rect_ = *visible_rect;
134 enabled_ = inst_.is_viewport() && inst_.overlays_enabled() && inst_.use_lookdev_overlay();
135
136 if (enabled_) {
137 const int2 extent_dummy(1);
140 dummy_cryptomatte_tx_.ensure_2d(GPU_RGBA32F, extent_dummy, usage);
141 dummy_aov_color_tx_.ensure_2d_array(GPU_RGBA16F, extent_dummy, 1, usage);
142 dummy_aov_value_tx_.ensure_2d_array(GPU_R16F, extent_dummy, 1, usage);
143 }
144}
145
146float LookdevModule::calc_viewport_scale()
147{
148 const float viewport_scale = clamp_f(
149 BLI_rcti_size_x(&visible_rect_) / (2000.0f * UI_SCALE_FAC), 0.5f, 1.0f);
150 return viewport_scale;
151}
152
153static eDRWLevelOfDetail calc_level_of_detail(const float viewport_scale)
154{
155 float res_scale = clamp_f(
156 (U.lookdev_sphere_size / 400.0f) * viewport_scale * UI_SCALE_FAC, 0.1f, 1.0f);
157
158 if (res_scale > 0.7f) {
159 return DRW_LOD_HIGH;
160 }
161 else if (res_scale > 0.25f) {
162 return DRW_LOD_MEDIUM;
163 }
164 return DRW_LOD_LOW;
165}
166
167static int calc_sphere_extent(const float viewport_scale)
168{
169 const int sphere_radius = U.lookdev_sphere_size * UI_SCALE_FAC * viewport_scale;
170 return sphere_radius * 2;
171}
172
174{
175 if (!enabled_) {
176 return;
177 }
178 const float viewport_scale = calc_viewport_scale();
179 const int2 extent = int2(calc_sphere_extent(viewport_scale));
180
181 const eGPUTextureFormat color_format = GPU_RGBA16F;
182
183 for (int index : IndexRange(num_spheres)) {
184 if (spheres_[index].color_tx_.ensure_2d(color_format, extent)) {
185 /* Request redraw if the light-probe were off and the sampling was already finished. */
186 if (inst_.sampling.finished_viewport()) {
187 inst_.sampling.reset();
188 }
189 }
190
191 spheres_[index].framebuffer.ensure(GPU_ATTACHMENT_NONE,
192 GPU_ATTACHMENT_TEXTURE(spheres_[index].color_tx_));
193 }
194
195 const Camera &cam = inst_.camera;
196 float sphere_distance = cam.data_get().clip_near;
197 int2 display_extent = inst_.film.display_extent_get();
198 float pixel_radius = inst_.shadows.screen_pixel_radius(
199 cam.data_get().wininv, cam.is_perspective(), display_extent);
200
201 if (cam.is_perspective()) {
202 pixel_radius *= sphere_distance;
203 }
204
205 this->sphere_radius_ = (extent.x / 2) * pixel_radius;
206 this->sphere_position_ = cam.position() -
207 cam.forward() * (sphere_distance + this->sphere_radius_);
208
209 float4x4 model_m4 = float4x4(float3x3(cam.data_get().viewmat));
210 model_m4.location() = this->sphere_position_;
211 model_m4 = math::scale(model_m4, float3(this->sphere_radius_));
212
213 ResourceHandle handle = inst_.manager->resource_handle(model_m4);
214 gpu::Batch *geom = DRW_cache_sphere_get(calc_level_of_detail(viewport_scale));
215
216 sync_pass(spheres_[0].pass, geom, inst_.materials.metallic_mat, handle);
217 sync_pass(spheres_[1].pass, geom, inst_.materials.diffuse_mat, handle);
218 sync_display();
219}
220
221void LookdevModule::sync_pass(PassSimple &pass,
222 gpu::Batch *geom,
223 ::Material *mat,
224 ResourceHandle res_handle)
225{
226 pass.init();
227 pass.clear_depth(1.0f);
228 pass.clear_color(float4(0.0, 0.0, 0.0, 1.0));
229
231
234 pass.state_set(state);
235 pass.material_set(*inst_.manager, gpumat);
236 pass.bind_texture(RBUFS_UTILITY_TEX_SLOT, inst_.pipelines.utility_tx);
237 pass.bind_resources(inst_.uniform_data);
238 pass.bind_resources(inst_.lights);
239 pass.bind_resources(inst_.shadows);
240 pass.bind_resources(inst_.volume.result);
241 pass.bind_resources(inst_.sampling);
242 pass.bind_resources(inst_.hiz_buffer.front);
243 pass.bind_resources(inst_.volume_probes);
244 pass.bind_resources(inst_.sphere_probes);
245 pass.draw(geom, res_handle, 0);
246}
247
248void LookdevModule::sync_display()
249{
250 PassSimple &pass = display_ps_;
251
254 pass.init();
255 pass.state_set(state);
256 pass.shader_set(inst_.shaders.static_shader_get(LOOKDEV_DISPLAY));
257 pass.push_constant("viewportSize", float2(DRW_viewport_size_get()));
258 pass.push_constant("invertedViewportSize", float2(DRW_viewport_invert_size_get()));
259 pass.push_constant("anchor", int2(visible_rect_.xmax, visible_rect_.ymin));
260 pass.bind_texture("metallic_tx", &spheres_[0].color_tx_);
261 pass.bind_texture("diffuse_tx", &spheres_[1].color_tx_);
262
263 pass.draw_procedural(GPU_PRIM_TRIS, 2, 6);
264}
265
267{
268 if (!enabled_) {
269 return;
270 }
271 for (Sphere &sphere : spheres_) {
272 sphere.framebuffer.bind();
273 inst_.manager->submit(sphere.pass, view);
274 }
275}
276
278{
279 if (!enabled_) {
280 return;
281 }
282
283 BLI_assert(inst_.is_viewport());
284
286 /* The viewport of the framebuffer can be modified when border rendering is enabled. */
289 inst_.manager->submit(display_ps_);
290}
291
294/* -------------------------------------------------------------------- */
299
301{
302 if (v3d == nullptr) {
303 return;
304 }
305
306 const ::View3DShading &shading = v3d->shading;
307 show_scene_world = shading.type == OB_RENDER ? shading.flag & V3D_SHADING_SCENE_WORLD_RENDER :
308 shading.flag & V3D_SHADING_SCENE_WORLD;
309 if (!show_scene_world) {
310 rot_z = shading.studiolight_rot_z;
311 background_opacity = shading.studiolight_background;
312 blur = shading.studiolight_blur;
313 intensity = shading.studiolight_intensity;
314 hdri = StringRefNull(shading.lookdev_light);
315 }
316}
317
319{
320 return hdri == other.hdri && rot_z == other.rot_z &&
321 background_opacity == other.background_opacity && blur == other.blur &&
322 intensity == other.intensity && show_scene_world == other.show_scene_world;
323}
324
326{
327 return !(*this == other);
328}
329
332} // namespace blender::eevee
GPUTexture * BKE_image_get_gpu_texture(Image *image, ImageUser *iuser)
Definition image_gpu.cc:476
void BKE_libblock_free_datablock(ID *id, int flag) ATTR_NONNULL()
void BKE_libblock_init_empty(ID *id) ATTR_NONNULL(1)
Definition lib_id.cc:1420
#define SH_NODE_OUTPUT_WORLD
Definition BKE_node.hh:914
#define SH_NODE_VECTOR_ROTATE
Definition BKE_node.hh:991
#define SH_NODE_TEX_COORD
Definition BKE_node.hh:938
#define SH_NODE_TEX_ENVIRONMENT
Definition BKE_node.hh:940
@ STUDIOLIGHT_EQUIRECT_RADIANCE_GPUTEXTURE
struct StudioLight * BKE_studiolight_find(const char *name, int flag)
void BKE_studiolight_ensure_flag(StudioLight *sl, int flag)
#define STUDIOLIGHT_ORIENTATIONS_MATERIAL_MODE
#define BLI_assert(a)
Definition BLI_assert.h:50
MINLINE float clamp_f(float value, float min, float max)
MINLINE void copy_v4_fl(float r[4], float f)
BLI_INLINE int BLI_rcti_size_x(const struct rcti *rct)
Definition BLI_rect.h:189
#define STRNCPY(dst, src)
Definition BLI_string.h:593
@ IMA_SRC_GENERATED
@ IMA_TYPE_IMAGE
@ IMA_GENTYPE_BLANK
@ TEXTARGET_2D
@ NODE_VECTOR_ROTATE_TYPE_AXIS_Z
@ SOCK_OUT
@ SOCK_IN
@ OB_RENDER
#define UI_SCALE_FAC
@ V3D_SHADING_SCENE_WORLD_RENDER
@ V3D_SHADING_SCENE_WORLD
#define GPU_ATTACHMENT_TEXTURE(_texture)
#define GPU_ATTACHMENT_NONE
void GPU_framebuffer_viewport_reset(GPUFrameBuffer *framebuffer)
void GPU_framebuffer_bind(GPUFrameBuffer *framebuffer)
void GPU_material_free(ListBase *gpumaterial)
@ GPU_PRIM_TRIS
void GPU_texture_ref(GPUTexture *texture)
eGPUTextureUsage
@ GPU_TEXTURE_USAGE_SHADER_READ
@ GPU_TEXTURE_USAGE_SHADER_WRITE
#define GPU_TEXTURE_FREE_SAFE(texture)
eGPUTextureFormat
struct blender::bke::bNodeTreeType * ntreeType_Shader
unsigned int U
Definition btGjkEpa3.h:78
void ensure(GPUAttachment depth=GPU_ATTACHMENT_NONE, GPUAttachment color1=GPU_ATTACHMENT_NONE, GPUAttachment color2=GPU_ATTACHMENT_NONE, GPUAttachment color3=GPU_ATTACHMENT_NONE, GPUAttachment color4=GPU_ATTACHMENT_NONE, GPUAttachment color5=GPU_ATTACHMENT_NONE, GPUAttachment color6=GPU_ATTACHMENT_NONE, GPUAttachment color7=GPU_ATTACHMENT_NONE, GPUAttachment color8=GPU_ATTACHMENT_NONE)
void submit(PassSimple &pass, View &view)
ResourceHandle resource_handle(const ObjectRef &ref, float inflate_bounds=0.0f)
bool ensure_2d_array(eGPUTextureFormat format, int2 extent, int layers, eGPUTextureUsage usage=GPU_TEXTURE_USAGE_GENERAL, const float *data=nullptr, int mip_len=1)
bool ensure_2d(eGPUTextureFormat format, int2 extent, eGPUTextureUsage usage=GPU_TEXTURE_USAGE_GENERAL, const float *data=nullptr, int mip_len=1)
const float3 & forward() const
const float3 & position() const
const CameraData & data_get() const
bool is_perspective() const
int2 display_extent_get() const
struct blender::eevee::HiZBuffer::@195 front
A running instance of the engine.
VolumeProbeModule volume_probes
SphereProbeModule sphere_probes
UniformDataModule uniform_data
void init(const rcti *visible_rect)
bool sync(const LookdevParameters &new_parameters)
GPUMaterial * material_shader_get(::Material *blender_mat, bNodeTree *nodetree, eMaterialPipeline pipeline_type, eMaterialGeometry geometry_type, bool deferred_compilation)
GPUShader * static_shader_get(eShaderType shader_type)
static float screen_pixel_radius(const float4x4 &wininv, bool is_perspective, const int2 &extent)
struct blender::eevee::VolumeModule::@196 result
additional_info("compositor_sum_float_shared") .push_constant(Type additional_info("compositor_sum_float_shared") .push_constant(Type GPU_RGBA32F
local_group_size(16, 16) .push_constant(Type texture
blender::gpu::Batch * DRW_cache_sphere_get(const eDRWLevelOfDetail level_of_detail)
eDRWLevelOfDetail
Definition draw_cache.hh:32
@ DRW_LOD_MEDIUM
Definition draw_cache.hh:34
@ DRW_LOD_LOW
Definition draw_cache.hh:33
@ DRW_LOD_HIGH
Definition draw_cache.hh:35
Simple API to draw debug shapes and log in the viewport.
const float * DRW_viewport_invert_size_get()
DefaultFramebufferList * DRW_viewport_framebuffer_list_get()
const float * DRW_viewport_size_get()
DRWState
Definition draw_state.hh:25
@ DRW_STATE_BLEND_ALPHA
Definition draw_state.hh:55
@ DRW_STATE_WRITE_DEPTH
Definition draw_state.hh:29
@ DRW_STATE_WRITE_COLOR
Definition draw_state.hh:30
@ DRW_STATE_CULL_BACK
Definition draw_state.hh:43
@ DRW_STATE_DEPTH_ALWAYS
Definition draw_state.hh:36
#define RBUFS_UTILITY_TEX_SLOT
static ulong state[N]
void node_set_active(bNodeTree *ntree, bNode *node)
Definition node.cc:3896
bNode * node_add_static_node(const bContext *C, bNodeTree *ntree, int type)
Definition node.cc:2642
bNodeLink * node_add_link(bNodeTree *ntree, bNode *fromnode, bNodeSocket *fromsock, bNode *tonode, bNodeSocket *tosock)
Definition node.cc:2912
bNodeTree * node_tree_add_tree(Main *bmain, const char *name, const char *idname)
Definition node.cc:3226
bNodeSocket * node_find_socket(bNode *node, eNodeSocketInOut in_out, StringRef identifier)
Definition node.cc:1829
detail::Pass< command::DrawCommandBuf > PassSimple
static int calc_sphere_extent(const float viewport_scale)
static eDRWLevelOfDetail calc_level_of_detail(const float viewport_scale)
MatBase< T, NumCol, NumRow > scale(const MatBase< T, NumCol, NumRow > &mat, const VectorT &scale)
bool assign_if_different(T &old_value, T new_value)
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
VecBase< int32_t, 2 > int2
VecBase< float, 2 > float2
MatBase< float, 3, 3 > float3x3
VecBase< float, 3 > float3
GPUFrameBuffer * default_fb
float gen_color[4]
struct bNodeTree * nodetree
struct GPUTexture * equirect_radiance_gputexture
void * default_value
struct ID * id
void * storage
bool operator==(const LookdevParameters &other) const
bool operator!=(const LookdevParameters &other) const
int ymin
int xmax
void * BKE_image_get_tile
Definition stubs.c:36