Blender V4.3
eevee_material.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2021 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
10
11#include "BKE_lib_id.hh"
12#include "BKE_material.h"
13#include "BKE_node.hh"
14#include "NOD_shader.h"
15
16#include "eevee_instance.hh"
17
18#include "eevee_material.hh"
19
20namespace blender::eevee {
21
22/* -------------------------------------------------------------------- */
28{
29 bNodeTree *ntree = bke::node_tree_add_tree(nullptr, "Shader Nodetree", ntreeType_Shader->idname);
30 bNode *bsdf = bke::node_add_static_node(nullptr, ntree, SH_NODE_BSDF_PRINCIPLED);
32 bNodeSocket *bsdf_out = bke::node_find_socket(bsdf, SOCK_OUT, "BSDF");
33 bNodeSocket *output_in = bke::node_find_socket(output, SOCK_IN, "Surface");
34 bke::node_add_link(ntree, bsdf, bsdf_out, output, output_in);
35 bke::node_set_active(ntree, output);
36
37 color_socket_ =
39 metallic_socket_ =
41 roughness_socket_ =
43 specular_socket_ = (bNodeSocketValueFloat *)bke::node_find_socket(
44 bsdf, SOCK_IN, "Specular IOR Level")
46 ntree_ = ntree;
47}
48
54
56{
57 /* WARNING: This function is not threadsafe. Which is not a problem for the moment. */
58 copy_v3_fl3(color_socket_->value, ma->r, ma->g, ma->b);
59 metallic_socket_->value = ma->metallic;
60 roughness_socket_->value = ma->roughness;
61 specular_socket_->value = ma->spec;
62
63 return ntree_;
64}
65
68/* -------------------------------------------------------------------- */
74{
75 {
76 diffuse_mat = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default diffuse");
78 nullptr, &diffuse_mat->id, "Shader Nodetree", ntreeType_Shader->idname);
79 diffuse_mat->use_nodes = true;
80 diffuse_mat->surface_render_method = MA_SURFACE_METHOD_FORWARD;
81
82 /* Use 0.18 as it is close to middle gray. Middle gray is typically defined as 18% reflectance
83 * of visible light and commonly used for VFX balls. */
84 bNode *bsdf = bke::node_add_static_node(nullptr, ntree, SH_NODE_BSDF_DIFFUSE);
85 bNodeSocket *base_color = bke::node_find_socket(bsdf, SOCK_IN, "Color");
86 copy_v3_fl(((bNodeSocketValueRGBA *)base_color->default_value)->value, 0.18f);
87
89
91 bsdf,
92 bke::node_find_socket(bsdf, SOCK_OUT, "BSDF"),
93 output,
94 bke::node_find_socket(output, SOCK_IN, "Surface"));
95
96 bke::node_set_active(ntree, output);
97 }
98 {
99 metallic_mat = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default metal");
101 nullptr, &metallic_mat->id, "Shader Nodetree", ntreeType_Shader->idname);
102 metallic_mat->use_nodes = true;
103 metallic_mat->surface_render_method = MA_SURFACE_METHOD_FORWARD;
104
105 bNode *bsdf = bke::node_add_static_node(nullptr, ntree, SH_NODE_BSDF_GLOSSY);
106 bNodeSocket *base_color = bke::node_find_socket(bsdf, SOCK_IN, "Color");
107 copy_v3_fl(((bNodeSocketValueRGBA *)base_color->default_value)->value, 1.0f);
108 bNodeSocket *roughness = bke::node_find_socket(bsdf, SOCK_IN, "Roughness");
109 ((bNodeSocketValueFloat *)roughness->default_value)->value = 0.0f;
110
112
113 bke::node_add_link(ntree,
114 bsdf,
115 bke::node_find_socket(bsdf, SOCK_OUT, "BSDF"),
116 output,
117 bke::node_find_socket(output, SOCK_IN, "Surface"));
118
119 bke::node_set_active(ntree, output);
120 }
121 {
122 error_mat_ = (::Material *)BKE_id_new_nomain(ID_MA, "EEVEE default error");
124 nullptr, &error_mat_->id, "Shader Nodetree", ntreeType_Shader->idname);
125 error_mat_->use_nodes = true;
126
127 /* Use emission and output material to be compatible with both World and Material. */
128 bNode *bsdf = bke::node_add_static_node(nullptr, ntree, SH_NODE_EMISSION);
129 bNodeSocket *color = bke::node_find_socket(bsdf, SOCK_IN, "Color");
130 copy_v3_fl3(((bNodeSocketValueRGBA *)color->default_value)->value, 1.0f, 0.0f, 1.0f);
131
133
134 bke::node_add_link(ntree,
135 bsdf,
136 bke::node_find_socket(bsdf, SOCK_OUT, "Emission"),
137 output,
138 bke::node_find_socket(output, SOCK_IN, "Surface"));
139
140 bke::node_set_active(ntree, output);
141 }
142}
143
145{
146 BKE_id_free(nullptr, metallic_mat);
147 BKE_id_free(nullptr, diffuse_mat);
148 BKE_id_free(nullptr, error_mat_);
149}
150
152{
155
156 material_map_.clear();
157 shader_map_.clear();
158}
159
160MaterialPass MaterialModule::material_pass_get(Object *ob,
161 ::Material *blender_mat,
162 eMaterialPipeline pipeline_type,
163 eMaterialGeometry geometry_type,
164 eMaterialProbe probe_capture)
165{
166 bNodeTree *ntree = (blender_mat->use_nodes && blender_mat->nodetree != nullptr) ?
167 blender_mat->nodetree :
168 default_surface_ntree_.nodetree_get(blender_mat);
169
170 bool use_deferred_compilation = inst_.is_viewport() || GPU_use_parallel_compilation();
171
172 MaterialPass matpass = MaterialPass();
173 matpass.gpumat = inst_.shaders.material_shader_get(
174 blender_mat, ntree, pipeline_type, geometry_type, use_deferred_compilation);
175
176 const bool is_volume = ELEM(pipeline_type, MAT_PIPE_VOLUME_OCCUPANCY, MAT_PIPE_VOLUME_MATERIAL);
177 const bool is_forward = ELEM(pipeline_type,
182
183 switch (GPU_material_status(matpass.gpumat)) {
184 case GPU_MAT_SUCCESS: {
185 /* Determine optimization status for remaining compilations counter. */
186 int optimization_status = GPU_material_optimization_status(matpass.gpumat);
187 if (optimization_status == GPU_MAT_OPTIMIZATION_QUEUED) {
189 }
190 break;
191 }
192 case GPU_MAT_QUEUED:
194 matpass.gpumat = inst_.shaders.material_default_shader_get(pipeline_type, geometry_type);
195 break;
196 case GPU_MAT_FAILED:
197 default:
198 matpass.gpumat = inst_.shaders.material_shader_get(
199 error_mat_, error_mat_->nodetree, pipeline_type, geometry_type, false);
200 break;
201 }
202 /* Returned material should be ready to be drawn. */
204
206
207 const bool is_transparent = GPU_material_flag_get(matpass.gpumat, GPU_MATFLAG_TRANSPARENT);
208
209 if (use_deferred_compilation && GPU_material_recalc_flag_get(matpass.gpumat)) {
210 /* TODO(Miguel Pozo): This is broken, it consumes the flag,
211 * but GPUMats can be shared across viewports. */
212 inst_.sampling.reset();
213
214 const bool has_displacement = GPU_material_has_displacement_output(matpass.gpumat) &&
216 const bool has_volume = GPU_material_has_volume_output(matpass.gpumat);
217
218 if (((pipeline_type == MAT_PIPE_SHADOW) && (is_transparent || has_displacement)) || has_volume)
219 {
220 /* WORKAROUND: This is to avoid lingering shadows from default material.
221 * Ideally, we should tag the caster object to update only the needed areas but that's a bit
222 * more involved. */
223 inst_.shadows.reset();
224 }
225 }
226
227 if (is_volume || (is_forward && is_transparent)) {
228 /* Sub pass is generated later. */
229 matpass.sub_pass = nullptr;
230 }
231 else {
232 ShaderKey shader_key(matpass.gpumat, blender_mat, probe_capture);
233
234 PassMain::Sub *shader_sub = shader_map_.lookup_or_add_cb(shader_key, [&]() {
235 /* First time encountering this shader. Create a sub that will contain materials using it. */
236 return inst_.pipelines.material_add(
237 ob, blender_mat, matpass.gpumat, pipeline_type, probe_capture);
238 });
239
240 if (shader_sub != nullptr) {
241 /* Create a sub for this material as `shader_sub` is for sharing shader between materials. */
242 matpass.sub_pass = &shader_sub->sub(GPU_material_get_name(matpass.gpumat));
243 matpass.sub_pass->material_set(*inst_.manager, matpass.gpumat);
244 }
245 else {
246 matpass.sub_pass = nullptr;
247 }
248 }
249
250 return matpass;
251}
252
253Material &MaterialModule::material_sync(Object *ob,
254 ::Material *blender_mat,
255 eMaterialGeometry geometry_type,
256 bool has_motion)
257{
258 bool hide_on_camera = ob->visibility_flag & OB_HIDE_CAMERA;
259
260 if (geometry_type == MAT_GEOM_VOLUME) {
261 MaterialKey material_key(
262 blender_mat, geometry_type, MAT_PIPE_VOLUME_MATERIAL, ob->visibility_flag);
263 Material &mat = material_map_.lookup_or_add_cb(material_key, [&]() {
264 Material mat = {};
265 mat.volume_occupancy = material_pass_get(
267 mat.volume_material = material_pass_get(
268 ob, blender_mat, MAT_PIPE_VOLUME_MATERIAL, MAT_GEOM_VOLUME);
269 return mat;
270 });
271
272 /* Volume needs to use one sub pass per object to support layering. */
273 VolumeLayer *layer = hide_on_camera ? nullptr :
275 if (layer) {
276 mat.volume_occupancy.sub_pass = layer->occupancy_add(
277 ob, blender_mat, mat.volume_occupancy.gpumat);
278 mat.volume_material.sub_pass = layer->material_add(
279 ob, blender_mat, mat.volume_material.gpumat);
280 }
281 else {
282 /* Culled volumes. */
283 mat.volume_occupancy.sub_pass = nullptr;
284 mat.volume_material.sub_pass = nullptr;
285 }
286 return mat;
287 }
288
289 const bool use_forward_pipeline = (blender_mat->surface_render_method ==
291 eMaterialPipeline surface_pipe, prepass_pipe;
292 if (use_forward_pipeline) {
293 surface_pipe = MAT_PIPE_FORWARD;
295 }
296 else {
297 surface_pipe = MAT_PIPE_DEFERRED;
299 }
300
301 MaterialKey material_key(blender_mat, geometry_type, surface_pipe, ob->visibility_flag);
302
303 Material &mat = material_map_.lookup_or_add_cb(material_key, [&]() {
304 Material mat;
305 if (inst_.is_baking()) {
306 if (ob->visibility_flag & OB_HIDE_PROBE_VOLUME) {
307 mat.capture = MaterialPass();
308 }
309 else {
310 mat.capture = material_pass_get(ob, blender_mat, MAT_PIPE_CAPTURE, geometry_type);
311 }
312 mat.prepass = MaterialPass();
313 /* TODO(fclem): Still need the shading pass for correct attribute extraction. Would be better
314 * to avoid this shader compilation in another context. */
315 mat.shading = material_pass_get(ob, blender_mat, surface_pipe, geometry_type);
316 mat.overlap_masking = MaterialPass();
317 mat.lightprobe_sphere_prepass = MaterialPass();
318 mat.lightprobe_sphere_shading = MaterialPass();
319 mat.planar_probe_prepass = MaterialPass();
320 mat.planar_probe_shading = MaterialPass();
321 mat.volume_occupancy = MaterialPass();
322 mat.volume_material = MaterialPass();
323 mat.has_volume = false; /* TODO */
324 mat.has_surface = GPU_material_has_surface_output(mat.shading.gpumat);
325 }
326 else {
327 /* Order is important for transparent. */
328 if (!hide_on_camera) {
329 mat.prepass = material_pass_get(ob, blender_mat, prepass_pipe, geometry_type);
330 }
331 else {
332 mat.prepass = MaterialPass();
333 }
334
335 mat.shading = material_pass_get(ob, blender_mat, surface_pipe, geometry_type);
336 if (hide_on_camera) {
337 /* Only null the sub_pass.
338 * `mat.shading.gpumat` is always needed for using the GPU_material API. */
339 mat.shading.sub_pass = nullptr;
340 }
341
342 mat.overlap_masking = MaterialPass();
343 mat.capture = MaterialPass();
344
346 {
347 mat.lightprobe_sphere_prepass = material_pass_get(
348 ob, blender_mat, MAT_PIPE_PREPASS_DEFERRED, geometry_type, MAT_PROBE_REFLECTION);
349 mat.lightprobe_sphere_shading = material_pass_get(
350 ob, blender_mat, MAT_PIPE_DEFERRED, geometry_type, MAT_PROBE_REFLECTION);
351 }
352 else {
353 mat.lightprobe_sphere_prepass = MaterialPass();
354 mat.lightprobe_sphere_shading = MaterialPass();
355 }
356
358 mat.planar_probe_prepass = material_pass_get(
359 ob, blender_mat, MAT_PIPE_PREPASS_PLANAR, geometry_type, MAT_PROBE_PLANAR);
360 mat.planar_probe_shading = material_pass_get(
361 ob, blender_mat, MAT_PIPE_DEFERRED, geometry_type, MAT_PROBE_PLANAR);
362 }
363 else {
364 mat.planar_probe_prepass = MaterialPass();
365 mat.planar_probe_shading = MaterialPass();
366 }
367
368 mat.has_surface = GPU_material_has_surface_output(mat.shading.gpumat);
369 mat.has_volume = GPU_material_has_volume_output(mat.shading.gpumat);
370 if (mat.has_volume && !hide_on_camera) {
371 mat.volume_occupancy = material_pass_get(
372 ob, blender_mat, MAT_PIPE_VOLUME_OCCUPANCY, geometry_type);
373 mat.volume_material = material_pass_get(
374 ob, blender_mat, MAT_PIPE_VOLUME_MATERIAL, geometry_type);
375 }
376 else {
377 mat.volume_occupancy = MaterialPass();
378 mat.volume_material = MaterialPass();
379 }
380 }
381
382 if (!(ob->visibility_flag & OB_HIDE_SHADOW)) {
383 mat.shadow = material_pass_get(ob, blender_mat, MAT_PIPE_SHADOW, geometry_type);
384 }
385 else {
386 mat.shadow = MaterialPass();
387 }
388
389 mat.is_alpha_blend_transparent = use_forward_pipeline &&
390 GPU_material_flag_get(mat.shading.gpumat,
392 mat.has_transparent_shadows = blender_mat->blend_flag & MA_BL_TRANSPARENT_SHADOW &&
393 GPU_material_flag_get(mat.shading.gpumat,
395
396 return mat;
397 });
398
399 if (mat.is_alpha_blend_transparent && !hide_on_camera) {
400 /* Transparent needs to use one sub pass per object to support reordering.
401 * NOTE: Pre-pass needs to be created first in order to be sorted first. */
402 mat.overlap_masking.sub_pass = inst_.pipelines.forward.prepass_transparent_add(
403 ob, blender_mat, mat.shading.gpumat);
404 mat.shading.sub_pass = inst_.pipelines.forward.material_transparent_add(
405 ob, blender_mat, mat.shading.gpumat);
406 }
407
408 if (mat.has_volume) {
409 /* Volume needs to use one sub pass per object to support layering. */
410 VolumeLayer *layer = hide_on_camera ? nullptr :
411 inst_.pipelines.volume.register_and_get_layer(ob);
412 if (layer) {
413 mat.volume_occupancy.sub_pass = layer->occupancy_add(
414 ob, blender_mat, mat.volume_occupancy.gpumat);
415 mat.volume_material.sub_pass = layer->material_add(
416 ob, blender_mat, mat.volume_material.gpumat);
417 }
418 else {
419 /* Culled volumes. */
420 mat.volume_occupancy.sub_pass = nullptr;
421 mat.volume_material.sub_pass = nullptr;
422 }
423 }
424 return mat;
425}
426
427::Material *MaterialModule::material_from_slot(Object *ob, int slot)
428{
429 ::Material *ma = BKE_object_material_get(ob, slot + 1);
430 if (ma == nullptr) {
431 if (ob->type == OB_VOLUME) {
433 }
435 }
436 return ma;
437}
438
439MaterialArray &MaterialModule::material_array_get(Object *ob, bool has_motion)
440{
441 material_array_.materials.clear();
442 material_array_.gpu_materials.clear();
443
444 const int materials_len = DRW_cache_object_material_count_get(ob);
445
446 for (auto i : IndexRange(materials_len)) {
447 ::Material *blender_mat = material_from_slot(ob, i);
448 Material &mat = material_sync(ob, blender_mat, to_material_geometry(ob), has_motion);
449 /* \note Perform a whole copy since next material_sync() can move the Material memory location
450 * (i.e: because of its container growing) */
451 material_array_.materials.append(mat);
452 material_array_.gpu_materials.append(mat.shading.gpumat);
453 }
454 return material_array_;
455}
456
457Material &MaterialModule::material_get(Object *ob,
458 bool has_motion,
459 int mat_nr,
460 eMaterialGeometry geometry_type)
461{
462 ::Material *blender_mat = material_from_slot(ob, mat_nr);
463 Material &mat = material_sync(ob, blender_mat, geometry_type, has_motion);
464 return mat;
465}
466
469} // namespace blender::eevee
void BKE_id_free(Main *bmain, void *idv)
void * BKE_id_new_nomain(short type, const char *name)
Definition lib_id.cc:1487
General operations, lookup, etc. for materials.
struct Material * BKE_object_material_get(struct Object *ob, short act)
struct Material * BKE_material_default_surface(void)
struct Material * BKE_material_default_volume(void)
#define SH_NODE_OUTPUT_MATERIAL
Definition BKE_node.hh:913
#define BLI_assert(a)
Definition BLI_assert.h:50
MINLINE void copy_v3_fl3(float v[3], float x, float y, float z)
MINLINE void copy_v3_fl(float r[3], float f)
#define ELEM(...)
@ ID_MA
@ MA_BL_TRANSPARENT_SHADOW
@ MA_SURFACE_METHOD_FORWARD
@ MA_DISPLACEMENT_BUMP
@ SOCK_OUT
@ SOCK_IN
@ OB_HIDE_CAMERA
@ OB_HIDE_PROBE_PLANAR
@ OB_HIDE_PROBE_CUBEMAP
@ OB_HIDE_SHADOW
@ OB_VOLUME
bool GPU_use_parallel_compilation()
bool GPU_material_has_surface_output(GPUMaterial *mat)
bool GPU_material_flag_get(const GPUMaterial *mat, eGPUMaterialFlag flag)
bool GPU_material_recalc_flag_get(GPUMaterial *mat)
eGPUMaterialStatus GPU_material_status(GPUMaterial *mat)
@ GPU_MATFLAG_TRANSPARENT
@ GPU_MAT_QUEUED
@ GPU_MAT_FAILED
@ GPU_MAT_SUCCESS
@ GPU_MAT_OPTIMIZATION_QUEUED
const char * GPU_material_get_name(GPUMaterial *material)
bool GPU_material_has_displacement_output(GPUMaterial *mat)
bool GPU_material_has_volume_output(GPUMaterial *mat)
eGPUMaterialOptimizationStatus GPU_material_optimization_status(GPUMaterial *mat)
#define MEM_SAFE_FREE(v)
struct blender::bke::bNodeTreeType * ntreeType_Shader
void register_layer_attributes(GPUMaterial *material)
PassBase< DrawCommandBufType > & sub(const char *name)
Definition draw_pass.hh:616
void material_set(Manager &manager, GPUMaterial *material)
detail::PassBase< command::DrawMultiBuf > Sub
Definition draw_pass.hh:462
bNodeTree * nodetree_get(::Material *ma)
A running instance of the engine.
bool needs_lightprobe_sphere_passes() const
bool needs_planar_probe_passes() const
PassMain::Sub * material_add(Object *, ::Material *blender_mat, GPUMaterial *gpumat, eMaterialPipeline pipeline_type, eMaterialProbe probe_capture)
GPUMaterial * material_shader_get(::Material *blender_mat, bNodeTree *nodetree, eMaterialPipeline pipeline_type, eMaterialGeometry geometry_type, bool deferred_compilation)
GPUMaterial * material_default_shader_get(eMaterialPipeline pipeline_type, eMaterialGeometry geometry_type)
VolumeLayer * register_and_get_layer(Object *ob)
int DRW_cache_object_material_count_get(const Object *ob)
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
bNodeTree * node_tree_add_tree_embedded(Main *bmain, ID *owner_id, const char *name, const char *idname)
Definition node.cc:3239
bNodeSocket * node_find_socket(bNode *node, eNodeSocketInOut in_out, StringRef identifier)
Definition node.cc:1829
void node_tree_free_embedded_tree(bNodeTree *ntree)
Definition node.cc:3632
@ MAT_PIPE_PREPASS_FORWARD_VELOCITY
@ MAT_PIPE_PREPASS_DEFERRED_VELOCITY
struct bNodeTree * nodetree
char surface_render_method
short visibility_flag
void * default_value