Blender V5.0
shader_graph.cpp
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2011-2022 Blender Foundation
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
6#include "scene/attribute.h"
8#include "scene/scene.h"
9#include "scene/shader.h"
10#include "scene/shader_nodes.h"
11
12#include "util/algorithm.h"
13
14#include "util/log.h"
15#include "util/md5.h"
16#include "util/queue.h"
17
19
20namespace {
21
23{
24 for (const ShaderInput *in : node->inputs) {
25 if (in->link) {
26 return true;
27 }
28 }
29 return false;
30}
31
33{
34 for (const ShaderInput *in : node->inputs) {
35 if (in->link) {
36 if (done.find(in->link->parent) == done.end()) {
37 return false;
38 }
39 }
40 }
41 return true;
42}
43
44} /* namespace */
45
46/* Sockets */
47
49{
50 if (link) {
51 link->links.erase(remove(link->links.begin(), link->links.end(), this), link->links.end());
52 }
53 link = nullptr;
54}
55
57{
58 for (ShaderInput *sock : links) {
59 sock->link = nullptr;
60 }
61
62 links.clear();
63}
64
65/* Node */
66
71
73 : Node(other.type), bump(other.bump), special_type(other.special_type)
74{
75 /* Inputs and outputs are recreated, no links to other nodes will remain. */
76 name = other.name;
78}
79
81{
82 for (const SocketType &socket : type->inputs) {
83 if (socket.flags & SocketType::LINKABLE) {
84 inputs.push_back(make_unique<ShaderInput>(socket, this));
85 }
86 }
87
88 for (const SocketType &socket : type->outputs) {
89 outputs.push_back(make_unique<ShaderOutput>(socket, this));
90 }
91}
92
94{
95 for (ShaderInput *socket : inputs) {
96 if (socket->name() == name) {
97 return socket;
98 }
99 }
100
101 return nullptr;
102}
103
105{
106 for (ShaderOutput *socket : outputs) {
107 if (socket->name() == name) {
108 return socket;
109 }
110 }
111
112 return nullptr;
113}
114
116{
117 for (ShaderInput *socket : inputs) {
118 if (socket->name() == name) {
119 return socket;
120 }
121 }
122
123 return nullptr;
124}
125
127{
128 for (ShaderOutput *socket : outputs) {
129 if (socket->name() == name) {
130 return socket;
131 }
132 }
133
134 return nullptr;
135}
136
138{
139 ShaderInput *socket = input(name);
140 if (socket && socket->link) {
141 socket->disconnect();
142 }
143}
144
146{
147 assert(input->link == nullptr);
148 inputs.erase(input);
149}
150
152{
153 for (ShaderInput *input : inputs) {
154 if (!input->link) {
156 if (shader->has_surface_link()) {
158 }
159 if (shader->has_volume) {
161 }
162 }
163 else if (input->flags() & SocketType::LINK_TEXTURE_UV) {
164 if (shader->has_surface_link()) {
166 }
167 }
168 }
169 }
170}
171
173{
174 if (type != other.type || bump != other.bump) {
175 return false;
176 }
177
178 assert(inputs.size() == other.inputs.size());
179
180 /* Compare unlinkable sockets */
181 for (const SocketType &socket : type->inputs) {
182 if (!(socket.flags & SocketType::LINKABLE)) {
183 if (!Node::equals_value(other, socket)) {
184 return false;
185 }
186 }
187 }
188
189 /* Compare linkable input sockets */
190 for (int i = 0; i < inputs.size(); ++i) {
191 ShaderInput *input_a = inputs[i];
192 ShaderInput *input_b = other.inputs[i];
193 if (input_a->link == nullptr && input_b->link == nullptr) {
194 /* Unconnected inputs are expected to have the same value. */
195 if (!Node::equals_value(other, input_a->socket_type)) {
196 return false;
197 }
198 }
199 else if (input_a->link != nullptr && input_b->link != nullptr) {
200 /* Expect links are to come from the same exact socket. */
201 if (input_a->link != input_b->link) {
202 return false;
203 }
204 }
205 else {
206 /* One socket has a link and another has not, inputs can't be
207 * considered equal.
208 */
209 return false;
210 }
211 }
212
213 return true;
214}
215
216/* Graph */
217
219{
220 finalized = false;
221 simplified = false;
222 num_node_ids = 0;
224}
225
230
232{
234 simplified = false;
235
236 node->set_owner(this);
237 node->id = num_node_ids++;
238 nodes.push_back(std::move(node));
239}
240
242{
243 return static_cast<OutputNode *>(nodes[0]);
244}
245
247{
249 assert(from && to);
250
251 if (to->link) {
252 LOG_WARNING << "Graph connect: input already connected.";
253 return;
254 }
255
256 if (from->type() != to->type()) {
257 /* can't do automatic conversion from closure */
258 if (from->type() == SocketType::CLOSURE) {
259 LOG_WARNING << "Shader graph connect: can only connect closure to closure ("
260 << from->parent->name.c_str() << "." << from->name().c_str() << " to "
261 << to->parent->name.c_str() << "." << to->name().c_str() << ")";
262 return;
263 }
264
265 /* add automatic conversion node in case of type mismatch */
266 ShaderNode *convert;
267 ShaderInput *convert_in;
268
269 if (to->type() == SocketType::CLOSURE) {
271 emission->from_auto_conversion = true;
272 emission->set_color(one_float3());
273 emission->set_strength(1.0f);
274 convert = emission;
275 /* Connect float inputs to Strength to save an additional Value->Color conversion. */
276 if (from->type() == SocketType::FLOAT) {
277 convert_in = convert->input("Strength");
278 }
279 else {
280 convert_in = convert->input("Color");
281 }
282 }
283 else {
284 convert = create_node<ConvertNode>(from->type(), to->type(), true);
285 convert_in = convert->inputs[0];
286 }
287
288 connect(from, convert_in);
289 connect(convert->outputs[0], to);
290 }
291 else {
292 /* types match, just connect */
293 to->link = from;
294 from->links.push_back(to);
295 }
296}
297
299{
301 simplified = false;
302
303 from->disconnect();
304}
305
307{
309 assert(to->link);
310 simplified = false;
311
312 to->disconnect();
313}
314
316{
317 ShaderOutput *out = from->link;
318 if (out) {
319 disconnect(from);
320 connect(out, to);
321 }
322 to->parent->copy_value(to->socket_type, *(from->parent), from->socket_type);
323}
324
326{
327 /* Copy because disconnect modifies this list. */
328 const vector<ShaderInput *> outputs = from->links;
329
330 for (ShaderInput *sock : outputs) {
331 disconnect(sock);
332 if (to) {
333 connect(to, sock);
334 }
335 }
336}
337
339{
340 simplified = false;
341
342 /* Copy because disconnect modifies this list */
343 const vector<ShaderInput *> outputs = from->links;
344
345 /* Bypass node by moving all links from "from" to "to" */
346 for (ShaderInput *sock : node->inputs) {
347 if (sock->link) {
348 disconnect(sock);
349 }
350 }
351
352 for (ShaderInput *sock : outputs) {
353 disconnect(sock);
354 if (to) {
355 connect(to, sock);
356 }
357 }
358}
359
361{
362 if (!simplified) {
363 expand();
364 default_inputs(scene->shader_manager->use_osl());
365 clean(scene);
367
368 simplified = true;
369 }
370}
371
372void ShaderGraph::finalize(Scene *scene, bool do_bump, bool bump_in_object_space)
373{
374 /* before compiling, the shader graph may undergo a number of modifications.
375 * currently we set default geometry shader inputs, and create automatic bump
376 * from displacement. a graph can be finalized only once, and should not be
377 * modified afterwards. */
378
379 if (!finalized) {
380 simplify(scene);
381
382 if (do_bump) {
383 bump_from_displacement(bump_in_object_space);
384 }
385
386 ShaderInput *surface_in = output()->input("Surface");
387 ShaderInput *volume_in = output()->input("Volume");
388
389 /* todo: make this work when surface and volume closures are tangled up */
390
391 if (surface_in->link) {
392 transform_multi_closure(surface_in->link->parent, nullptr, false);
393 }
394 if (volume_in->link) {
395 transform_multi_closure(volume_in->link->parent, nullptr, true);
396 }
397
398 finalized = true;
399 }
400}
401
403{
404 /* find all nodes that this input depends on directly and indirectly */
405 ShaderNode *node = (input->link) ? input->link->parent : nullptr;
406
407 if (node != nullptr && dependencies.find(node) == dependencies.end()) {
408 for (ShaderInput *in : node->inputs) {
409 find_dependencies(dependencies, in);
410 }
411
412 dependencies.insert(node);
413 }
414}
415
417{
418 nodes.clear();
419}
420
422{
423 /* copy a set of nodes, and the links between them. the assumption is
424 * made that all nodes that inputs are linked to are in the set too. */
425
426 /* copy nodes */
427 for (ShaderNode *node : nodes) {
428 ShaderNode *nnode = node->clone(this);
429 nnodemap[node] = nnode;
430 }
431
432 /* recreate links */
433 for (ShaderNode *node : nodes) {
434 for (ShaderInput *input : node->inputs) {
435 if (input->link) {
436 /* find new input and output */
437 ShaderNode *nfrom = nnodemap[input->link->parent];
438 ShaderNode *nto = nnodemap[input->parent];
439 ShaderOutput *noutput = nfrom->output(input->link->name());
440 ShaderInput *ninput = nto->input(input->name());
441
442 /* connect */
443 connect(noutput, ninput);
444 }
445 }
446 }
447}
448
449/* Graph simplification */
450/* ******************** */
451
452/* Remove proxy nodes.
453 *
454 * These only exists temporarily when exporting groups, and we must remove them
455 * early so that node->attributes() and default links do not see them.
456 */
458{
459 vector<bool> removed(num_node_ids, false);
460 bool any_node_removed = false;
461
462 for (ShaderNode *node : nodes) {
463 if (node->special_type == SHADER_SPECIAL_TYPE_PROXY) {
464 ConvertNode *proxy = static_cast<ConvertNode *>(node);
465 ShaderInput *input = proxy->inputs[0];
466 ShaderOutput *output = proxy->outputs[0];
467
468 /* bypass the proxy node */
469 if (input->link) {
470 relink(proxy, output, input->link);
471 }
472 else {
473 /* Copy because disconnect modifies this list */
474 const vector<ShaderInput *> links(output->links);
475
476 for (ShaderInput *to : links) {
477 /* Remove any auto-convert nodes too if they lead to
478 * sockets with an automatically set default value. */
479 ShaderNode *tonode = to->parent;
480
482 bool all_links_removed = true;
483 const vector<ShaderInput *> links = tonode->outputs[0]->links;
484
485 for (ShaderInput *autoin : links) {
486 if (autoin->flags() & SocketType::DEFAULT_LINK_MASK) {
487 disconnect(autoin);
488 }
489 else {
490 all_links_removed = false;
491 }
492 }
493
494 if (all_links_removed) {
495 removed[tonode->id] = true;
496 }
497 }
498
499 disconnect(to);
500
501 /* transfer the default input value to the target socket */
502 tonode->copy_value(to->socket_type, *proxy, input->socket_type);
503 }
504 }
505
506 removed[proxy->id] = true;
507 any_node_removed = true;
508 }
509 }
510
511 /* remove nodes */
512 if (any_node_removed) {
514
515 for (size_t i = 0; i < nodes.size(); i++) {
516 unique_ptr<ShaderNode> node = nodes.steal(i);
517 if (!removed[node->id]) {
518 newnodes.push_back(std::move(node));
519 }
520 }
521
522 nodes = std::move(newnodes);
523 }
524}
525
526/* Constant folding.
527 *
528 * Try to constant fold some nodes, and pipe result directly to
529 * the input socket of connected nodes.
530 */
532{
533 ShaderNodeSet done;
534 ShaderNodeSet scheduled;
535 queue<ShaderNode *> traverse_queue;
536
537 const bool has_displacement = (output()->input("Displacement")->link != nullptr);
538
539 /* Schedule nodes which doesn't have any dependencies. */
540 for (ShaderNode *node : nodes) {
541 if (!check_node_inputs_has_links(node)) {
542 traverse_queue.push(node);
543 scheduled.insert(node);
544 }
545 }
546
547 while (!traverse_queue.empty()) {
548 ShaderNode *node = traverse_queue.front();
549 traverse_queue.pop();
550 done.insert(node);
551 for (ShaderOutput *output : node->outputs) {
552 if (output->links.empty()) {
553 continue;
554 }
555 /* Schedule node which was depending on the value,
556 * when possible. Do it before disconnect.
557 */
558 for (ShaderInput *input : output->links) {
559 if (scheduled.find(input->parent) != scheduled.end()) {
560 /* Node might not be optimized yet but scheduled already
561 * by other dependencies. No need to re-schedule it.
562 */
563 continue;
564 }
565 /* Schedule node if its inputs are fully done. */
566 if (check_node_inputs_traversed(input->parent, done)) {
567 traverse_queue.push(input->parent);
568 scheduled.insert(input->parent);
569 }
570 }
571 /* Optimize current node. */
572 const ConstantFolder folder(this, node, output, scene);
573 node->constant_fold(folder);
574 }
575 }
576
577 /* Folding might have removed all nodes connected to the displacement output
578 * even tho there is displacement to be applied, so add in a value node if
579 * that happens to ensure there is still a valid graph for displacement.
580 */
581 if (has_displacement && !output()->input("Displacement")->link) {
583 value->set_value(output()->get_displacement());
584
585 connect(value->output("Color"), output()->input("Displacement"));
586 }
587}
588
589/* Simplification. */
591{
592 for (ShaderNode *node : nodes) {
593 node->simplify_settings(scene);
594 }
595}
596
597/* Deduplicate nodes with same settings. */
599{
600 /* NOTES:
601 * - Deduplication happens for nodes which has same exact settings and same
602 * exact input links configuration (either connected to same output or has
603 * the same exact default value).
604 * - Deduplication happens in the bottom-top manner, so we know for fact that
605 * all traversed nodes are either can not be deduplicated at all or were
606 * already deduplicated.
607 */
608
609 ShaderNodeSet scheduled;
610 ShaderNodeSet done;
611 map<ustring, ShaderNodeSet> candidates;
612 queue<ShaderNode *> traverse_queue;
613 int num_deduplicated = 0;
614
615 /* Schedule nodes which doesn't have any dependencies. */
616 for (ShaderNode *node : nodes) {
617 if (!check_node_inputs_has_links(node)) {
618 traverse_queue.push(node);
619 scheduled.insert(node);
620 }
621 }
622
623 while (!traverse_queue.empty()) {
624 ShaderNode *node = traverse_queue.front();
625 traverse_queue.pop();
626 done.insert(node);
627 /* Schedule the nodes which were depending on the current node. */
628 bool has_output_links = false;
629 for (ShaderOutput *output : node->outputs) {
630 for (ShaderInput *input : output->links) {
631 has_output_links = true;
632 if (scheduled.find(input->parent) != scheduled.end()) {
633 /* Node might not be optimized yet but scheduled already
634 * by other dependencies. No need to re-schedule it.
635 */
636 continue;
637 }
638 /* Schedule node if its inputs are fully done. */
639 if (check_node_inputs_traversed(input->parent, done)) {
640 traverse_queue.push(input->parent);
641 scheduled.insert(input->parent);
642 }
643 }
644 }
645 /* Only need to care about nodes that are actually used */
646 if (!has_output_links) {
647 continue;
648 }
649 /* Try to merge this node with another one. */
650 ShaderNode *merge_with = nullptr;
651 for (ShaderNode *other_node : candidates[node->type->name]) {
652 if (node != other_node && node->equals(*other_node)) {
653 merge_with = other_node;
654 break;
655 }
656 }
657 /* If found an equivalent, merge; otherwise keep node for later merges */
658 if (merge_with != nullptr) {
659 for (int i = 0; i < node->outputs.size(); ++i) {
660 relink(node, node->outputs[i], merge_with->outputs[i]);
661 }
662 num_deduplicated++;
663 }
664 else {
665 candidates[node->type->name].insert(node);
666 }
667 }
668
669 if (num_deduplicated > 0) {
670 LOG_TRACE << "Deduplicated " << num_deduplicated << " nodes.";
671 }
672}
673
674/* Does two optimizations:
675 * - Check whether volume output has meaningful nodes, otherwise disconnect the output.
676 * - Tag volume attribute nodes as supporting stochastic sampling. */
678{
679 ShaderInput *volume_in = output()->input("Volume");
680 if (volume_in->link == nullptr) {
681 return;
682 }
683
684 bool has_valid_volume = false;
685
686 using ShaderNodeAndNonLinear = std::pair<ShaderNode *, bool>;
687 set<ShaderNodeAndNonLinear, ShaderNodeIDAndBoolComparator> scheduled;
688 queue<ShaderNodeAndNonLinear> traverse_queue;
689
690 /* Schedule volume output. */
691 traverse_queue.emplace(volume_in->link->parent, false);
692 scheduled.insert({volume_in->link->parent, false});
693
694 /* Traverse down the tree. */
695 while (!traverse_queue.empty()) {
696 auto [node, nonlinear] = traverse_queue.front();
697 traverse_queue.pop();
698
699 /* Disable stochastic sampling on node if its contribution is nonlinear.
700 * This defaults to true in the class, so we only need to disable it. */
701 if (nonlinear && node->type == AttributeNode::get_node_type()) {
702 static_cast<AttributeNode *>(node)->stochastic_sample = false;
703 }
704 nonlinear = nonlinear || !node->is_linear_operation();
705
706 /* Node is fully valid for volume, won't be able to optimize it out. */
707 if (node->has_volume_support()) {
708 has_valid_volume = true;
709 }
710
711 for (ShaderInput *input : node->inputs) {
712 if (input->link == nullptr) {
713 continue;
714 }
715 ShaderNode *input_node = input->link->parent;
716 if (scheduled.find({input_node, nonlinear}) != scheduled.end()) {
717 continue;
718 }
719 traverse_queue.emplace(input_node, nonlinear);
720 scheduled.insert({input_node, nonlinear});
721 }
722 }
723
725 for (ShaderNode *node : nodes) {
726 if (node->type == AttributeNode::get_node_type() &&
727 static_cast<AttributeNode *>(node)->stochastic_sample)
728 {
729 LOG_TRACE << "Volume attribute node " << node->name << " uses stochastic sampling";
730 }
731 }
732 }
733
734 if (!has_valid_volume) {
735 /* We can remove the entire volume shader. */
736 LOG_TRACE << "Disconnect meaningless volume output.";
737 disconnect(volume_in->link);
738 }
739}
740
742{
743 visited[node->id] = true;
744 on_stack[node->id] = true;
745
746 for (ShaderInput *input : node->inputs) {
747 if (input->link) {
748 ShaderNode *depnode = input->link->parent;
749
750 if (on_stack[depnode->id]) {
751 /* break cycle */
753 LOG_WARNING << "Shader graph: detected cycle in graph, connection removed.";
754 }
755 else if (!visited[depnode->id]) {
756 /* visit dependencies */
757 break_cycles(depnode, visited, on_stack);
758 }
759 }
760 }
761
762 on_stack[node->id] = false;
763}
764
766{
767 /* Compute hash of all nodes linked to displacement, to detect if we need
768 * to recompute displacement when shader nodes change. */
769 ShaderInput *displacement_in = output()->input("Displacement");
770
771 if (!displacement_in->link) {
773 return;
774 }
775
776 ShaderNodeSet nodes_displace;
777 find_dependencies(nodes_displace, displacement_in);
778
779 MD5Hash md5;
780 for (ShaderNode *node : nodes_displace) {
781 node->hash(md5);
782 for (ShaderInput *input : node->inputs) {
783 int link_id = (input->link) ? input->link->parent->id : 0;
784 md5.append((uint8_t *)&link_id, sizeof(link_id));
785 md5.append((input->link) ? input->link->name().c_str() : "");
786 }
787
788 if (node->special_type == SHADER_SPECIAL_TYPE_OSL) {
789 /* Hash takes into account socket values, to detect changes
790 * in the code of the node we need an exception. */
791 OSLNode *oslnode = static_cast<OSLNode *>(node);
792 md5.append(oslnode->bytecode_hash);
793 }
794 }
795
797}
798
800{
801 /* Graph simplification */
802
803 /* NOTE: Remove proxy nodes was already done. */
804 constant_fold(scene);
805 simplify_settings(scene);
808
809 /* we do two things here: find cycles and break them, and remove unused
810 * nodes that don't feed into the output. how cycles are broken is
811 * undefined, they are invalid input, the important thing is to not crash */
812
813 vector<bool> visited(num_node_ids, false);
814 vector<bool> on_stack(num_node_ids, false);
815
816 /* break cycles */
817 break_cycles(output(), visited, on_stack);
818 for (ShaderNode *node : nodes) {
819 if (node->special_type == SHADER_SPECIAL_TYPE_OUTPUT_AOV) {
820 break_cycles(node, visited, on_stack);
821 }
822 }
823
824 /* disconnect unused nodes */
825 for (ShaderNode *node : nodes) {
826 if (!visited[node->id]) {
827 for (ShaderInput *to : node->inputs) {
828 ShaderOutput *from = to->link;
829
830 if (from) {
831 to->link = nullptr;
832 from->links.erase(remove(from->links.begin(), from->links.end(), to), from->links.end());
833 }
834 }
835 }
836 }
837
838 /* remove unused nodes */
840
841 for (size_t i = 0; i < nodes.size(); i++) {
842 unique_ptr<ShaderNode> node = nodes.steal(i);
843 if (visited[node->id]) {
844 newnodes.push_back(std::move(node));
845 }
846 }
847
848 nodes = std::move(newnodes);
849}
850
852{
853 /* Call expand on all nodes, to generate additional nodes.
854 * No range based for loop because we modify the vector, and want to expand
855 * newly generated nodes too. */
856 for (size_t i = 0; i < nodes.size(); i++) {
857 ShaderNode *node = nodes[i];
858 node->expand(this);
859 }
860}
861
863{
864 /* nodes can specify default texture coordinates, for now we give
865 * everything the position by default, except for the sky texture */
866
867 GeometryNode *geom = nullptr;
868 TextureCoordinateNode *texco = nullptr;
869 VectorTransformNode *normal_transform = nullptr;
870
871 for (size_t i = 0; i < nodes.size(); i++) {
872 ShaderNode *node = nodes[i];
873
874 for (ShaderInput *input : node->inputs) {
875 if (!input->link && (!(input->flags() & SocketType::OSL_INTERNAL) || do_osl)) {
877 if (!texco) {
879 }
880
881 connect(texco->output("Generated"), input);
882 }
883 if (input->flags() & SocketType::LINK_TEXTURE_NORMAL) {
884 if (!texco) {
886 }
887
888 connect(texco->output("Normal"), input);
889 }
890 else if (input->flags() & SocketType::LINK_TEXTURE_UV) {
891 if (!texco) {
893 }
894
895 connect(texco->output("UV"), input);
896 }
897 else if (input->flags() & SocketType::LINK_TEXTURE_INCOMING) {
898 if (!geom) {
900 }
901 if (!normal_transform) {
902 normal_transform = create_node<VectorTransformNode>();
903 normal_transform->set_transform_type(NODE_VECTOR_TRANSFORM_TYPE_NORMAL);
904 normal_transform->set_convert_from(NODE_VECTOR_TRANSFORM_CONVERT_SPACE_WORLD);
905 normal_transform->set_convert_to(NODE_VECTOR_TRANSFORM_CONVERT_SPACE_OBJECT);
906 connect(geom->output("Incoming"), normal_transform->input("Vector"));
907 }
908
909 connect(normal_transform->output("Vector"), input);
910 }
911 else if (input->flags() & SocketType::LINK_INCOMING) {
912 if (!geom) {
914 }
915
916 connect(geom->output("Incoming"), input);
917 }
918 else if (input->flags() & SocketType::LINK_NORMAL) {
919 if (!geom) {
921 }
922
923 connect(geom->output("Normal"), input);
924 }
925 else if (input->flags() & SocketType::LINK_POSITION) {
926 if (!geom) {
928 }
929
930 connect(geom->output("Position"), input);
931 }
932 else if (input->flags() & SocketType::LINK_TANGENT) {
933 if (!geom) {
935 }
936
937 connect(geom->output("Tangent"), input);
938 }
939 }
940 }
941 }
942}
943
945{
946 /* We transverse the node graph looking for bump nodes, when we find them,
947 * like in bump_from_displacement(), we copy the sub-graph defined from "bump"
948 * input to the inputs "center","dx" and "dy" What is in "bump" input is moved
949 * to "center" input. */
950
951 /* No range based for loop because we modify the vector. */
952 for (int i = 0; i < nodes.size(); i++) {
953 ShaderNode *node = nodes[i];
954
955 if (node->special_type == SHADER_SPECIAL_TYPE_BUMP && node->input("Height")->link) {
956 BumpNode *bump = static_cast<BumpNode *>(node);
957 ShaderInput *bump_input = node->input("Height");
958 ShaderNodeSet nodes_bump;
959
960 /* Make 2 extra copies of the subgraph defined in Bump input. */
961 ShaderNodeMap nodes_dx;
962 ShaderNodeMap nodes_dy;
963
964 /* Find dependencies for the given input. */
965 find_dependencies(nodes_bump, bump_input);
966
967 copy_nodes(nodes_bump, nodes_dx);
968 copy_nodes(nodes_bump, nodes_dy);
969
970 /* Mark nodes to indicate they are use for bump computation, so
971 * that any texture coordinates are shifted by dx/dy when sampling. */
972 for (ShaderNode *node : nodes_bump) {
973 node->bump = SHADER_BUMP_CENTER;
974 node->bump_filter_width = bump->get_filter_width();
975 }
976 for (const NodePair &pair : nodes_dx) {
977 pair.second->bump = SHADER_BUMP_DX;
978 pair.second->bump_filter_width = bump->get_filter_width();
979 }
980 for (const NodePair &pair : nodes_dy) {
981 pair.second->bump = SHADER_BUMP_DY;
982 pair.second->bump_filter_width = bump->get_filter_width();
983 }
984
985 ShaderOutput *out = bump_input->link;
986 ShaderOutput *out_dx = nodes_dx[out->parent]->output(out->name());
987 ShaderOutput *out_dy = nodes_dy[out->parent]->output(out->name());
988
989 connect(out_dx, node->input("SampleX"));
990 connect(out_dy, node->input("SampleY"));
991
992 /* Connect what is connected is bump to sample-center input. */
993 connect(out, node->input("SampleCenter"));
994
995 /* Bump input is just for connectivity purpose for the graph input,
996 * we re-connected this input to sample-center, so lets disconnect it
997 * from bump input. */
998 disconnect(bump_input);
999 }
1000 }
1001}
1002
1003void ShaderGraph::bump_from_displacement(bool use_object_space)
1004{
1005 /* generate bump mapping automatically from displacement. bump mapping is
1006 * done using a 3-tap filter, computing the displacement at the center,
1007 * and two other positions shifted by ray differentials.
1008 *
1009 * since the input to displacement is a node graph, we need to ensure that
1010 * all texture coordinates use are shift by the ray differentials. for this
1011 * reason we make 3 copies of the node subgraph defining the displacement,
1012 * with each different geometry and texture coordinate nodes that generate
1013 * different shifted coordinates.
1014 *
1015 * these 3 displacement values are then fed into the bump node, which will
1016 * output the perturbed normal. */
1017
1018 ShaderInput *displacement_in = output()->input("Displacement");
1019
1020 if (!displacement_in->link) {
1021 return;
1022 }
1023
1024 /* find dependencies for the given input */
1025 ShaderNodeSet nodes_displace;
1026 find_dependencies(nodes_displace, displacement_in);
1027
1028 /* Add bump node. */
1030 bump->set_use_object_space(use_object_space);
1031 bump->set_distance(1.0f);
1032
1033 /* copy nodes for 3 bump samples */
1034 ShaderNodeMap nodes_center;
1035 ShaderNodeMap nodes_dx;
1036 ShaderNodeMap nodes_dy;
1037
1038 copy_nodes(nodes_displace, nodes_center);
1039 copy_nodes(nodes_displace, nodes_dx);
1040 copy_nodes(nodes_displace, nodes_dy);
1041
1042 /* mark nodes to indicate they are use for bump computation, so
1043 * that any texture coordinates are shifted by dx/dy when sampling */
1044 for (const NodePair &pair : nodes_center) {
1045 pair.second->bump = SHADER_BUMP_CENTER;
1046 pair.second->bump_filter_width = bump->get_filter_width();
1047 }
1048 for (const NodePair &pair : nodes_dx) {
1049 pair.second->bump = SHADER_BUMP_DX;
1050 pair.second->bump_filter_width = bump->get_filter_width();
1051 }
1052 for (const NodePair &pair : nodes_dy) {
1053 pair.second->bump = SHADER_BUMP_DY;
1054 pair.second->bump_filter_width = bump->get_filter_width();
1055 }
1056
1057 /* add set normal node and connect the bump normal output to the set normal
1058 * output, so it can finally set the shader normal, note we are only doing
1059 * this for bump from displacement, this will be the only bump allowed to
1060 * overwrite the shader normal */
1061 ShaderNode *set_normal = create_node<SetNormalNode>();
1062
1063 /* Connect copied graphs to bump node. */
1064 ShaderOutput *out = displacement_in->link;
1065 ShaderOutput *out_center = nodes_center[out->parent]->output(out->name());
1066 ShaderOutput *out_dx = nodes_dx[out->parent]->output(out->name());
1067 ShaderOutput *out_dy = nodes_dy[out->parent]->output(out->name());
1068
1069 /* convert displacement vector to height */
1073
1074 dot_center->set_math_type(NODE_VECTOR_MATH_DOT_PRODUCT);
1075 dot_dx->set_math_type(NODE_VECTOR_MATH_DOT_PRODUCT);
1076 dot_dy->set_math_type(NODE_VECTOR_MATH_DOT_PRODUCT);
1077
1079 connect(geom->output("Normal"), bump->input("Normal"));
1080 connect(geom->output("Normal"), dot_center->input("Vector2"));
1081 connect(geom->output("Normal"), dot_dx->input("Vector2"));
1082 connect(geom->output("Normal"), dot_dy->input("Vector2"));
1083
1084 connect(out_center, dot_center->input("Vector1"));
1085 connect(out_dx, dot_dx->input("Vector1"));
1086 connect(out_dy, dot_dy->input("Vector1"));
1087
1088 connect(dot_center->output("Value"), bump->input("SampleCenter"));
1089 connect(dot_dx->output("Value"), bump->input("SampleX"));
1090 connect(dot_dy->output("Value"), bump->input("SampleY"));
1091
1092 /* connect the bump out to the set normal in: */
1093 connect(bump->output("Normal"), set_normal->input("Direction"));
1094
1095 /* connect to output node */
1096 connect(set_normal->output("Normal"), output()->input("Normal"));
1097}
1098
1100{
1101 /* for SVM in multi closure mode, this transforms the shader mix/add part of
1102 * the graph into nodes that feed weights into closure nodes. this is too
1103 * avoid building a closure tree and then flattening it, and instead write it
1104 * directly to an array */
1105
1107 ShaderInput *fin = node->input("Fac");
1108 ShaderInput *cl1in = node->input("Closure1");
1109 ShaderInput *cl2in = node->input("Closure2");
1110 ShaderOutput *weight1_out;
1111 ShaderOutput *weight2_out;
1112
1113 if (fin) {
1114 /* mix closure: add node to mix closure weights */
1116 ShaderInput *fac_in = mix_node->input("Fac");
1117 ShaderInput *weight_in = mix_node->input("Weight");
1118
1119 if (fin->link) {
1120 connect(fin->link, fac_in);
1121 }
1122 else {
1123 mix_node->set_fac(node->get_float(fin->socket_type));
1124 }
1125
1126 if (weight_out) {
1127 connect(weight_out, weight_in);
1128 }
1129
1130 weight1_out = mix_node->output("Weight1");
1131 weight2_out = mix_node->output("Weight2");
1132 }
1133 else {
1134 /* add closure: just pass on any weights */
1135 weight1_out = weight_out;
1136 weight2_out = weight_out;
1137 }
1138
1139 if (cl1in->link) {
1140 transform_multi_closure(cl1in->link->parent, weight1_out, volume);
1141 }
1142 if (cl2in->link) {
1143 transform_multi_closure(cl2in->link->parent, weight2_out, volume);
1144 }
1145 }
1146 else {
1147 ShaderInput *weight_in = node->input((volume) ? "VolumeMixWeight" : "SurfaceMixWeight");
1148
1149 /* not a closure node? */
1150 if (!weight_in) {
1151 return;
1152 }
1153
1154 /* already has a weight connected to it? add weights */
1155 const float weight_value = node->get_float(weight_in->socket_type);
1156 if (weight_in->link || weight_value != 0.0f) {
1157 MathNode *math_node = create_node<MathNode>();
1158
1159 if (weight_in->link) {
1160 connect(weight_in->link, math_node->input("Value1"));
1161 }
1162 else {
1163 math_node->set_value1(weight_value);
1164 }
1165
1166 if (weight_out) {
1167 connect(weight_out, math_node->input("Value2"));
1168 }
1169 else {
1170 math_node->set_value2(1.0f);
1171 }
1172
1173 weight_out = math_node->output("Value");
1174 if (weight_in->link) {
1175 disconnect(weight_in);
1176 }
1177 }
1178
1179 /* connected to closure mix weight */
1180 if (weight_out) {
1181 connect(weight_out, weight_in);
1182 }
1183 else {
1184 node->set(weight_in->socket_type, weight_value + 1.0f);
1185 }
1186 }
1187}
1188
1190{
1191 int num_closures = 0;
1192 for (ShaderNode *node : nodes) {
1193 const ClosureType closure_type = node->get_closure_type();
1194 if (closure_type == CLOSURE_NONE_ID) {
1195 continue;
1196 }
1197 if (CLOSURE_IS_BSSRDF(closure_type)) {
1198 num_closures += 3;
1199 }
1200 else if (CLOSURE_IS_BSDF_MULTISCATTER(closure_type)) {
1201 num_closures += 2;
1202 }
1203 else if (CLOSURE_IS_PRINCIPLED(closure_type)) {
1204 num_closures += 12;
1205 }
1206 else if (CLOSURE_IS_VOLUME(closure_type)) {
1207 /* TODO(sergey): Verify this is still needed, since we have special minimized volume
1208 * storage for the volume steps. */
1209 num_closures += MAX_VOLUME_STACK_SIZE;
1210 }
1211 else if (closure_type == CLOSURE_BSDF_PHYSICAL_CONDUCTOR ||
1212 closure_type == CLOSURE_BSDF_F82_CONDUCTOR ||
1214 closure_type == CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID ||
1215 closure_type == CLOSURE_BSDF_HAIR_CHIANG_ID ||
1216 closure_type == CLOSURE_BSDF_HAIR_HUANG_ID)
1217 {
1218 num_closures += 2;
1219 }
1220 else {
1221 ++num_closures;
1222 }
1223 }
1224 return num_closures;
1225}
1226
1227void ShaderGraph::dump_graph(const char *filename)
1228{
1229 FILE *fd = fopen(filename, "w");
1230
1231 if (fd == nullptr) {
1232 LOG_ERROR << "Error opening file for dumping the graph: " << filename;
1233 return;
1234 }
1235
1236 fprintf(fd, "digraph shader_graph {\n");
1237 fprintf(fd, "ranksep=1.5\n");
1238 fprintf(fd, "rankdir=LR\n");
1239 fprintf(fd, "splines=false\n");
1240
1241 for (ShaderNode *node : nodes) {
1242 fprintf(fd, "// NODE: %p\n", node);
1243 fprintf(fd, "\"%p\" [shape=record,label=\"{", node);
1244 if (!node->inputs.empty()) {
1245 fprintf(fd, "{");
1246 for (ShaderInput *socket : node->inputs) {
1247 if (socket != node->inputs[0]) {
1248 fprintf(fd, "|");
1249 }
1250 fprintf(fd, "<IN_%p>%s", socket, socket->name().c_str());
1251 }
1252 fprintf(fd, "}|");
1253 }
1254 fprintf(fd, "%s", node->name.c_str());
1255 if (node->bump == SHADER_BUMP_CENTER) {
1256 fprintf(fd, " (bump:center)");
1257 }
1258 else if (node->bump == SHADER_BUMP_DX) {
1259 fprintf(fd, " (bump:dx)");
1260 }
1261 else if (node->bump == SHADER_BUMP_DY) {
1262 fprintf(fd, " (bump:dy)");
1263 }
1264 if (!node->outputs.empty()) {
1265 fprintf(fd, "|{");
1266 for (ShaderOutput *socket : node->outputs) {
1267 if (socket != node->outputs[0]) {
1268 fprintf(fd, "|");
1269 }
1270 fprintf(fd, "<OUT_%p>%s", socket, socket->name().c_str());
1271 }
1272 fprintf(fd, "}");
1273 }
1274 fprintf(fd, "}\"]");
1275 }
1276
1277 for (ShaderNode *node : nodes) {
1278 for (ShaderOutput *output : node->outputs) {
1279 for (ShaderInput *input : output->links) {
1280 fprintf(fd,
1281 "// CONNECTION: OUT_%p->IN_%p (%s:%s)\n",
1282 output,
1283 input,
1284 output->name().c_str(),
1285 input->name().c_str());
1286 fprintf(fd,
1287 "\"%p\":\"OUT_%p\":e -> \"%p\":\"IN_%p\":w [label=\"\"]\n",
1288 output->parent,
1289 output,
1290 input->parent,
1291 input);
1292 }
1293 }
1294 }
1295
1296 fprintf(fd, "}\n");
1297 fclose(fd);
1298}
1299
@ NODE_VECTOR_MATH_DOT_PRODUCT
bool from_auto_conversion
Definition md5.h:19
void append(const uint8_t *data, const int nbytes)
Definition md5.cpp:268
string get_hex()
Definition md5.cpp:367
string bytecode_hash
void find_dependencies(ShaderNodeSet &dependencies, ShaderInput *input)
string displacement_hash
OutputNode * output()
unique_ptr_vector< ShaderNode > nodes
void simplify(Scene *scene)
T * create_node(Args &&...args)
void add_node(unique_ptr< ShaderNode > &&node)
void disconnect(ShaderOutput *from)
size_t num_node_ids
void break_cycles(ShaderNode *node, vector< bool > &visited, vector< bool > &on_stack)
void copy_nodes(ShaderNodeSet &nodes, ShaderNodeMap &nnodemap)
void clean(Scene *scene)
void finalize(Scene *scene, bool do_bump=false, bool bump_in_object_space=false)
void compute_displacement_hash()
void default_inputs(bool do_osl)
void connect(ShaderOutput *from, ShaderInput *to)
void relink(ShaderInput *from, ShaderInput *to)
void optimize_volume_output()
void constant_fold(Scene *scene)
void transform_multi_closure(ShaderNode *node, ShaderOutput *weight_out, bool volume)
void deduplicate_nodes()
void remove_proxy_nodes()
void simplify_settings(Scene *scene)
void bump_from_displacement(bool use_object_space)
~ShaderGraph() override
pair< ShaderNode *const, ShaderNode * > NodePair
void refine_bump_nodes()
void dump_graph(const char *filename)
ustring name() const
SocketType::Type type() const
ShaderOutput * link
void disconnect()
ShaderNode * parent
const SocketType & socket_type
ShaderInput * input(const char *name)
void remove_input(ShaderInput *input)
ShaderNodeSpecialType special_type
virtual ShaderNode * clone(ShaderGraph *graph) const =0
virtual bool equals(const ShaderNode &other)
float bump_filter_width
void disconnect_unused_input(const char *name)
virtual void constant_fold(const ConstantFolder &)
virtual void expand(ShaderGraph *)
ShaderNode(const NodeType *type)
void create_inputs_outputs(const NodeType *type)
unique_ptr_vector< ShaderInput > inputs
ShaderBump bump
ShaderOutput * output(const char *name)
virtual void attributes(Shader *shader, AttributeRequestSet *attributes)
unique_ptr_vector< ShaderOutput > outputs
vector< ShaderInput * > links
ustring name() const
ShaderNode * parent
SocketType::Type type() const
bool has_surface_link() const
bool has_volume
void push_back(unique_ptr< T > &&value)
size_t size() const
#define CLOSURE_IS_VOLUME(type)
#define MAX_VOLUME_STACK_SIZE
#define CLOSURE_IS_PRINCIPLED(type)
#define CLOSURE_IS_BSSRDF(type)
#define CLOSURE_IS_BSDF_MULTISCATTER(type)
#define CCL_NAMESPACE_END
#define input
#define assert(assertion)
#define in
#define out
@ NODE_VECTOR_TRANSFORM_CONVERT_SPACE_OBJECT
@ NODE_VECTOR_TRANSFORM_CONVERT_SPACE_WORLD
ClosureType
@ CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID
@ CLOSURE_NONE_ID
@ CLOSURE_BSDF_MICROFACET_BECKMANN_GLASS_ID
@ CLOSURE_BSDF_PHYSICAL_CONDUCTOR
@ CLOSURE_BSDF_HAIR_HUANG_ID
@ CLOSURE_BSDF_F82_CONDUCTOR
@ CLOSURE_BSDF_HAIR_CHIANG_ID
@ NODE_VECTOR_TRANSFORM_TYPE_NORMAL
@ ATTR_STD_GENERATED_TRANSFORM
@ ATTR_STD_UV
@ ATTR_STD_GENERATED
#define LOG_IS_ON(level)
Definition log.h:113
@ LOG_LEVEL_TRACE
Definition log.h:27
#define LOG_ERROR
Definition log.h:101
#define LOG_WARNING
Definition log.h:103
#define LOG_TRACE
Definition log.h:108
ccl_device_inline float3 one_float3()
Definition math_float3.h:26
bool check_node_inputs_traversed(const ShaderNode *node, const ShaderNodeSet &done)
bool check_node_inputs_has_links(const ShaderNode *node)
static blender::bke::bNodeSocketTemplate outputs[]
@ SHADER_SPECIAL_TYPE_PROXY
@ SHADER_SPECIAL_TYPE_OUTPUT_AOV
@ SHADER_SPECIAL_TYPE_COMBINE_CLOSURE
@ SHADER_SPECIAL_TYPE_BUMP
@ SHADER_SPECIAL_TYPE_AUTOCONVERT
@ SHADER_SPECIAL_TYPE_OSL
set< ShaderNode *, ShaderNodeIDComparator > ShaderNodeSet
map< ShaderNode *, ShaderNode *, ShaderNodeIDComparator > ShaderNodeMap
@ SHADER_BUMP_CENTER
@ SHADER_BUMP_DX
@ SHADER_BUMP_DY
ustring name
Definition node_type.h:125
float get_float(const SocketType &input) const
void set(const SocketType &input, bool value)
const NodeType * type
Definition graph/node.h:178
void set_value(const SocketType &socket, const Node &other, const SocketType &other_socket)
ustring name
Definition graph/node.h:177
void copy_value(const SocketType &socket, const Node &other, const SocketType &other_socket)
bool equals_value(const Node &other, const SocketType &socket) const
Node(const NodeType *type, ustring name=ustring())
unique_ptr< ShaderManager > shader_manager
Definition scene.h:148
@ LINK_TEXTURE_UV
Definition node_type.h:70
@ LINK_TEXTURE_GENERATED
Definition node_type.h:68
@ LINK_INCOMING
Definition node_type.h:72
@ LINK_TEXTURE_INCOMING
Definition node_type.h:71
@ LINK_TEXTURE_NORMAL
Definition node_type.h:69
@ DEFAULT_LINK_MASK
Definition node_type.h:77
@ LINK_POSITION
Definition node_type.h:74
i
Definition text_draw.cc:230