Blender V4.3
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#include "util/foreach.h"
14#include "util/log.h"
15#include "util/md5.h"
16#include "util/queue.h"
17
19
20namespace {
21
23{
24 foreach (const ShaderInput *in, node->inputs) {
25 if (in->link) {
26 return true;
27 }
28 }
29 return false;
30}
31
33{
34 foreach (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 = NULL;
54}
55
57{
58 foreach (ShaderInput *sock, links) {
59 sock->link = NULL;
60 }
61
62 links.clear();
63}
64
65/* Node */
66
68{
69 name = type->name;
70 id = -1;
73
75}
76
78{
79 foreach (ShaderInput *socket, inputs)
80 delete socket;
81
82 foreach (ShaderOutput *socket, outputs)
83 delete socket;
84}
85
87{
88 foreach (const SocketType &socket, type->inputs) {
89 if (socket.flags & SocketType::LINKABLE) {
90 inputs.push_back(new ShaderInput(socket, this));
91 }
92 }
93
94 foreach (const SocketType &socket, type->outputs) {
95 outputs.push_back(new ShaderOutput(socket, this));
96 }
97}
98
100{
101 foreach (ShaderInput *socket, inputs) {
102 if (socket->name() == name) {
103 return socket;
104 }
105 }
106
107 return NULL;
108}
109
111{
112 foreach (ShaderOutput *socket, outputs)
113 if (socket->name() == name) {
114 return socket;
115 }
116
117 return NULL;
118}
119
121{
122 foreach (ShaderInput *socket, inputs) {
123 if (socket->name() == name) {
124 return socket;
125 }
126 }
127
128 return NULL;
129}
130
132{
133 foreach (ShaderOutput *socket, outputs)
134 if (socket->name() == name) {
135 return socket;
136 }
137
138 return NULL;
139}
140
142{
143 assert(input->link == NULL);
144 delete input;
145 inputs.erase(remove(inputs.begin(), inputs.end(), input), inputs.end());
146}
147
149{
150 foreach (ShaderInput *input, inputs) {
151 if (!input->link) {
152 if (input->flags() & SocketType::LINK_TEXTURE_GENERATED) {
153 if (shader->has_surface_link()) {
154 attributes->add(ATTR_STD_GENERATED);
155 }
156 if (shader->has_volume) {
157 attributes->add(ATTR_STD_GENERATED_TRANSFORM);
158 }
159 }
160 else if (input->flags() & SocketType::LINK_TEXTURE_UV) {
161 if (shader->has_surface_link()) {
162 attributes->add(ATTR_STD_UV);
163 }
164 }
165 }
166 }
167}
168
170{
171 if (type != other.type || bump != other.bump) {
172 return false;
173 }
174
175 assert(inputs.size() == other.inputs.size());
176
177 /* Compare unlinkable sockets */
178 foreach (const SocketType &socket, type->inputs) {
179 if (!(socket.flags & SocketType::LINKABLE)) {
180 if (!Node::equals_value(other, socket)) {
181 return false;
182 }
183 }
184 }
185
186 /* Compare linkable input sockets */
187 for (int i = 0; i < inputs.size(); ++i) {
188 ShaderInput *input_a = inputs[i], *input_b = other.inputs[i];
189 if (input_a->link == NULL && input_b->link == NULL) {
190 /* Unconnected inputs are expected to have the same value. */
191 if (!Node::equals_value(other, input_a->socket_type)) {
192 return false;
193 }
194 }
195 else if (input_a->link != NULL && input_b->link != NULL) {
196 /* Expect links are to come from the same exact socket. */
197 if (input_a->link != input_b->link) {
198 return false;
199 }
200 }
201 else {
202 /* One socket has a link and another has not, inputs can't be
203 * considered equal.
204 */
205 return false;
206 }
207 }
208
209 return true;
210}
211
212/* Graph */
213
215{
216 finalized = false;
217 simplified = false;
218 num_node_ids = 0;
220}
221
226
228{
229 assert(!finalized);
230 simplified = false;
231
232 node->id = num_node_ids++;
233 nodes.push_back(node);
234 return node;
235}
236
238{
239 return (OutputNode *)nodes.front();
240}
241
243{
244 assert(!finalized);
245 assert(from && to);
246
247 if (to->link) {
248 fprintf(stderr, "Cycles shader graph connect: input already connected.\n");
249 return;
250 }
251
252 if (from->type() != to->type()) {
253 /* can't do automatic conversion from closure */
254 if (from->type() == SocketType::CLOSURE) {
255 fprintf(stderr,
256 "Cycles shader graph connect: can only connect closure to closure "
257 "(%s.%s to %s.%s).\n",
258 from->parent->name.c_str(),
259 from->name().c_str(),
260 to->parent->name.c_str(),
261 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 = add(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 = add(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{
300 assert(!finalized);
301 simplified = false;
302
303 from->disconnect();
304}
305
307{
308 assert(!finalized);
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 vector<ShaderInput *> outputs = from->links;
329
330 foreach (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 vector<ShaderInput *> outputs = from->links;
344
345 /* Bypass node by moving all links from "from" to "to" */
346 foreach (ShaderInput *sock, node->inputs) {
347 if (sock->link) {
348 disconnect(sock);
349 }
350 }
351
352 foreach (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, NULL, false);
393 }
394 if (volume_in->link) {
395 transform_multi_closure(volume_in->link->parent, NULL, 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 : NULL;
406
407 if (node != NULL && dependencies.find(node) == dependencies.end()) {
408 foreach (ShaderInput *in, node->inputs)
409 find_dependencies(dependencies, in);
410
411 dependencies.insert(node);
412 }
413}
414
416{
417 foreach (ShaderNode *node, nodes) {
418 delete_node(node);
419 }
420 nodes.clear();
421}
422
424{
425 /* copy a set of nodes, and the links between them. the assumption is
426 * made that all nodes that inputs are linked to are in the set too. */
427
428 /* copy nodes */
429 foreach (ShaderNode *node, nodes) {
430 ShaderNode *nnode = node->clone(this);
431 nnodemap[node] = nnode;
432
433 /* create new inputs and outputs to recreate links and ensure
434 * that we still point to valid SocketType if the NodeType
435 * changed in cloning, as it does for OSL nodes */
436 nnode->inputs.clear();
437 nnode->outputs.clear();
438 nnode->create_inputs_outputs(nnode->type);
439 }
440
441 /* recreate links */
442 foreach (ShaderNode *node, nodes) {
443 foreach (ShaderInput *input, node->inputs) {
444 if (input->link) {
445 /* find new input and output */
446 ShaderNode *nfrom = nnodemap[input->link->parent];
447 ShaderNode *nto = nnodemap[input->parent];
448 ShaderOutput *noutput = nfrom->output(input->link->name());
449 ShaderInput *ninput = nto->input(input->name());
450
451 /* connect */
452 connect(noutput, ninput);
453 }
454 }
455 }
456}
457
458/* Graph simplification */
459/* ******************** */
460
461/* Remove proxy nodes.
462 *
463 * These only exists temporarily when exporting groups, and we must remove them
464 * early so that node->attributes() and default links do not see them.
465 */
467{
468 vector<bool> removed(num_node_ids, false);
469 bool any_node_removed = false;
470
471 foreach (ShaderNode *node, nodes) {
472 if (node->special_type == SHADER_SPECIAL_TYPE_PROXY) {
473 ConvertNode *proxy = static_cast<ConvertNode *>(node);
474 ShaderInput *input = proxy->inputs[0];
475 ShaderOutput *output = proxy->outputs[0];
476
477 /* bypass the proxy node */
478 if (input->link) {
479 relink(proxy, output, input->link);
480 }
481 else {
482 /* Copy because disconnect modifies this list */
483 vector<ShaderInput *> links(output->links);
484
485 foreach (ShaderInput *to, links) {
486 /* Remove any auto-convert nodes too if they lead to
487 * sockets with an automatically set default value. */
488 ShaderNode *tonode = to->parent;
489
491 bool all_links_removed = true;
492 vector<ShaderInput *> links = tonode->outputs[0]->links;
493
494 foreach (ShaderInput *autoin, links) {
495 if (autoin->flags() & SocketType::DEFAULT_LINK_MASK) {
496 disconnect(autoin);
497 }
498 else {
499 all_links_removed = false;
500 }
501 }
502
503 if (all_links_removed) {
504 removed[tonode->id] = true;
505 }
506 }
507
508 disconnect(to);
509
510 /* transfer the default input value to the target socket */
511 tonode->copy_value(to->socket_type, *proxy, input->socket_type);
512 }
513 }
514
515 removed[proxy->id] = true;
516 any_node_removed = true;
517 }
518 }
519
520 /* remove nodes */
521 if (any_node_removed) {
522 list<ShaderNode *> newnodes;
523
524 foreach (ShaderNode *node, nodes) {
525 if (!removed[node->id]) {
526 newnodes.push_back(node);
527 }
528 else {
529 delete_node(node);
530 }
531 }
532
533 nodes = newnodes;
534 }
535}
536
537/* Constant folding.
538 *
539 * Try to constant fold some nodes, and pipe result directly to
540 * the input socket of connected nodes.
541 */
543{
544 ShaderNodeSet done, scheduled;
545 queue<ShaderNode *> traverse_queue;
546
547 bool has_displacement = (output()->input("Displacement")->link != NULL);
548
549 /* Schedule nodes which doesn't have any dependencies. */
550 foreach (ShaderNode *node, nodes) {
551 if (!check_node_inputs_has_links(node)) {
552 traverse_queue.push(node);
553 scheduled.insert(node);
554 }
555 }
556
557 while (!traverse_queue.empty()) {
558 ShaderNode *node = traverse_queue.front();
559 traverse_queue.pop();
560 done.insert(node);
561 foreach (ShaderOutput *output, node->outputs) {
562 if (output->links.size() == 0) {
563 continue;
564 }
565 /* Schedule node which was depending on the value,
566 * when possible. Do it before disconnect.
567 */
568 foreach (ShaderInput *input, output->links) {
569 if (scheduled.find(input->parent) != scheduled.end()) {
570 /* Node might not be optimized yet but scheduled already
571 * by other dependencies. No need to re-schedule it.
572 */
573 continue;
574 }
575 /* Schedule node if its inputs are fully done. */
576 if (check_node_inputs_traversed(input->parent, done)) {
577 traverse_queue.push(input->parent);
578 scheduled.insert(input->parent);
579 }
580 }
581 /* Optimize current node. */
582 ConstantFolder folder(this, node, output, scene);
583 node->constant_fold(folder);
584 }
585 }
586
587 /* Folding might have removed all nodes connected to the displacement output
588 * even tho there is displacement to be applied, so add in a value node if
589 * that happens to ensure there is still a valid graph for displacement.
590 */
591 if (has_displacement && !output()->input("Displacement")->link) {
593 value->set_value(output()->get_displacement());
594
595 connect(value->output("Color"), output()->input("Displacement"));
596 }
597}
598
599/* Simplification. */
601{
602 foreach (ShaderNode *node, nodes) {
603 node->simplify_settings(scene);
604 }
605}
606
607/* Deduplicate nodes with same settings. */
609{
610 /* NOTES:
611 * - Deduplication happens for nodes which has same exact settings and same
612 * exact input links configuration (either connected to same output or has
613 * the same exact default value).
614 * - Deduplication happens in the bottom-top manner, so we know for fact that
615 * all traversed nodes are either can not be deduplicated at all or were
616 * already deduplicated.
617 */
618
619 ShaderNodeSet scheduled, done;
620 map<ustring, ShaderNodeSet> candidates;
621 queue<ShaderNode *> traverse_queue;
622 int num_deduplicated = 0;
623
624 /* Schedule nodes which doesn't have any dependencies. */
625 foreach (ShaderNode *node, nodes) {
626 if (!check_node_inputs_has_links(node)) {
627 traverse_queue.push(node);
628 scheduled.insert(node);
629 }
630 }
631
632 while (!traverse_queue.empty()) {
633 ShaderNode *node = traverse_queue.front();
634 traverse_queue.pop();
635 done.insert(node);
636 /* Schedule the nodes which were depending on the current node. */
637 bool has_output_links = false;
638 foreach (ShaderOutput *output, node->outputs) {
639 foreach (ShaderInput *input, output->links) {
640 has_output_links = true;
641 if (scheduled.find(input->parent) != scheduled.end()) {
642 /* Node might not be optimized yet but scheduled already
643 * by other dependencies. No need to re-schedule it.
644 */
645 continue;
646 }
647 /* Schedule node if its inputs are fully done. */
648 if (check_node_inputs_traversed(input->parent, done)) {
649 traverse_queue.push(input->parent);
650 scheduled.insert(input->parent);
651 }
652 }
653 }
654 /* Only need to care about nodes that are actually used */
655 if (!has_output_links) {
656 continue;
657 }
658 /* Try to merge this node with another one. */
659 ShaderNode *merge_with = NULL;
660 foreach (ShaderNode *other_node, candidates[node->type->name]) {
661 if (node != other_node && node->equals(*other_node)) {
662 merge_with = other_node;
663 break;
664 }
665 }
666 /* If found an equivalent, merge; otherwise keep node for later merges */
667 if (merge_with != NULL) {
668 for (int i = 0; i < node->outputs.size(); ++i) {
669 relink(node, node->outputs[i], merge_with->outputs[i]);
670 }
671 num_deduplicated++;
672 }
673 else {
674 candidates[node->type->name].insert(node);
675 }
676 }
677
678 if (num_deduplicated > 0) {
679 VLOG_DEBUG << "Deduplicated " << num_deduplicated << " nodes.";
680 }
681}
682
683/* Check whether volume output has meaningful nodes, otherwise
684 * disconnect the output.
685 */
687{
688 /* Check whether we can optimize the whole volume graph out. */
689 ShaderInput *volume_in = output()->input("Volume");
690 if (volume_in->link == NULL) {
691 return;
692 }
693 bool has_valid_volume = false;
694 ShaderNodeSet scheduled;
695 queue<ShaderNode *> traverse_queue;
696 /* Schedule volume output. */
697 traverse_queue.push(volume_in->link->parent);
698 scheduled.insert(volume_in->link->parent);
699 /* Traverse down the tree. */
700 while (!traverse_queue.empty()) {
701 ShaderNode *node = traverse_queue.front();
702 traverse_queue.pop();
703 /* Node is fully valid for volume, can't optimize anything out. */
704 if (node->has_volume_support()) {
705 has_valid_volume = true;
706 break;
707 }
708 foreach (ShaderInput *input, node->inputs) {
709 if (input->link == NULL) {
710 continue;
711 }
712 if (scheduled.find(input->link->parent) != scheduled.end()) {
713 continue;
714 }
715 traverse_queue.push(input->link->parent);
716 scheduled.insert(input->link->parent);
717 }
718 }
719 if (!has_valid_volume) {
720 VLOG_DEBUG << "Disconnect meaningless volume output.";
721 disconnect(volume_in->link);
722 }
723}
724
726{
727 visited[node->id] = true;
728 on_stack[node->id] = true;
729
730 foreach (ShaderInput *input, node->inputs) {
731 if (input->link) {
732 ShaderNode *depnode = input->link->parent;
733
734 if (on_stack[depnode->id]) {
735 /* break cycle */
736 disconnect(input);
737 fprintf(stderr, "Cycles shader graph: detected cycle in graph, connection removed.\n");
738 }
739 else if (!visited[depnode->id]) {
740 /* visit dependencies */
741 break_cycles(depnode, visited, on_stack);
742 }
743 }
744 }
745
746 on_stack[node->id] = false;
747}
748
750{
751 /* Compute hash of all nodes linked to displacement, to detect if we need
752 * to recompute displacement when shader nodes change. */
753 ShaderInput *displacement_in = output()->input("Displacement");
754
755 if (!displacement_in->link) {
757 return;
758 }
759
760 ShaderNodeSet nodes_displace;
761 find_dependencies(nodes_displace, displacement_in);
762
763 MD5Hash md5;
764 foreach (ShaderNode *node, nodes_displace) {
765 node->hash(md5);
766 foreach (ShaderInput *input, node->inputs) {
767 int link_id = (input->link) ? input->link->parent->id : 0;
768 md5.append((uint8_t *)&link_id, sizeof(link_id));
769 md5.append((input->link) ? input->link->name().c_str() : "");
770 }
771
772 if (node->special_type == SHADER_SPECIAL_TYPE_OSL) {
773 /* Hash takes into account socket values, to detect changes
774 * in the code of the node we need an exception. */
775 OSLNode *oslnode = static_cast<OSLNode *>(node);
776 md5.append(oslnode->bytecode_hash);
777 }
778 }
779
781}
782
784{
785 /* Graph simplification */
786
787 /* NOTE: Remove proxy nodes was already done. */
788 constant_fold(scene);
789 simplify_settings(scene);
792
793 /* we do two things here: find cycles and break them, and remove unused
794 * nodes that don't feed into the output. how cycles are broken is
795 * undefined, they are invalid input, the important thing is to not crash */
796
798 vector<bool> on_stack(num_node_ids, false);
799
800 /* break cycles */
801 break_cycles(output(), visited, on_stack);
802 foreach (ShaderNode *node, nodes) {
803 if (node->special_type == SHADER_SPECIAL_TYPE_OUTPUT_AOV) {
804 break_cycles(node, visited, on_stack);
805 }
806 }
807
808 /* disconnect unused nodes */
809 foreach (ShaderNode *node, nodes) {
810 if (!visited[node->id]) {
811 foreach (ShaderInput *to, node->inputs) {
812 ShaderOutput *from = to->link;
813
814 if (from) {
815 to->link = NULL;
816 from->links.erase(remove(from->links.begin(), from->links.end(), to), from->links.end());
817 }
818 }
819 }
820 }
821
822 /* remove unused nodes */
823 list<ShaderNode *> newnodes;
824
825 foreach (ShaderNode *node, nodes) {
826 if (visited[node->id]) {
827 newnodes.push_back(node);
828 }
829 else {
830 delete_node(node);
831 }
832 }
833
834 nodes = newnodes;
835}
836
838{
839 /* Call expand on all nodes, to generate additional nodes. */
840 foreach (ShaderNode *node, nodes) {
841 node->expand(this);
842 }
843}
844
846{
847 /* nodes can specify default texture coordinates, for now we give
848 * everything the position by default, except for the sky texture */
849
850 GeometryNode *geom = NULL;
852 VectorTransformNode *normal_transform = NULL;
853
854 foreach (ShaderNode *node, nodes) {
855 foreach (ShaderInput *input, node->inputs) {
856 if (!input->link && (!(input->flags() & SocketType::OSL_INTERNAL) || do_osl)) {
857 if (input->flags() & SocketType::LINK_TEXTURE_GENERATED) {
858 if (!texco) {
860 }
861
862 connect(texco->output("Generated"), input);
863 }
864 if (input->flags() & SocketType::LINK_TEXTURE_NORMAL) {
865 if (!texco) {
867 }
868
869 connect(texco->output("Normal"), input);
870 }
871 else if (input->flags() & SocketType::LINK_TEXTURE_UV) {
872 if (!texco) {
874 }
875
876 connect(texco->output("UV"), input);
877 }
878 else if (input->flags() & SocketType::LINK_TEXTURE_INCOMING) {
879 if (!geom) {
881 }
882 if (!normal_transform) {
883 normal_transform = create_node<VectorTransformNode>();
884 normal_transform->set_transform_type(NODE_VECTOR_TRANSFORM_TYPE_NORMAL);
885 normal_transform->set_convert_from(NODE_VECTOR_TRANSFORM_CONVERT_SPACE_WORLD);
886 normal_transform->set_convert_to(NODE_VECTOR_TRANSFORM_CONVERT_SPACE_OBJECT);
887 connect(geom->output("Incoming"), normal_transform->input("Vector"));
888 }
889
890 connect(normal_transform->output("Vector"), input);
891 }
892 else if (input->flags() & SocketType::LINK_INCOMING) {
893 if (!geom) {
895 }
896
897 connect(geom->output("Incoming"), input);
898 }
899 else if (input->flags() & SocketType::LINK_NORMAL) {
900 if (!geom) {
902 }
903
904 connect(geom->output("Normal"), input);
905 }
906 else if (input->flags() & SocketType::LINK_POSITION) {
907 if (!geom) {
909 }
910
911 connect(geom->output("Position"), input);
912 }
913 else if (input->flags() & SocketType::LINK_TANGENT) {
914 if (!geom) {
916 }
917
918 connect(geom->output("Tangent"), input);
919 }
920 }
921 }
922 }
923
924 if (geom) {
925 add(geom);
926 }
927 if (texco) {
928 add(texco);
929 }
930 if (normal_transform) {
931 add(normal_transform);
932 }
933}
934
936{
937 /* We transverse the node graph looking for bump nodes, when we find them,
938 * like in bump_from_displacement(), we copy the sub-graph defined from "bump"
939 * input to the inputs "center","dx" and "dy" What is in "bump" input is moved
940 * to "center" input. */
941
942 foreach (ShaderNode *node, nodes) {
943 if (node->special_type == SHADER_SPECIAL_TYPE_BUMP && node->input("Height")->link) {
944 ShaderInput *bump_input = node->input("Height");
945 ShaderNodeSet nodes_bump;
946
947 /* Make 2 extra copies of the subgraph defined in Bump input. */
948 ShaderNodeMap nodes_dx;
949 ShaderNodeMap nodes_dy;
950
951 /* Find dependencies for the given input. */
952 find_dependencies(nodes_bump, bump_input);
953
954 copy_nodes(nodes_bump, nodes_dx);
955 copy_nodes(nodes_bump, nodes_dy);
956
957 /* Mark nodes to indicate they are use for bump computation, so
958 * that any texture coordinates are shifted by dx/dy when sampling. */
959 foreach (ShaderNode *node, nodes_bump)
960 node->bump = SHADER_BUMP_CENTER;
961 foreach (NodePair &pair, nodes_dx)
962 pair.second->bump = SHADER_BUMP_DX;
963 foreach (NodePair &pair, nodes_dy)
964 pair.second->bump = SHADER_BUMP_DY;
965
966 ShaderOutput *out = bump_input->link;
967 ShaderOutput *out_dx = nodes_dx[out->parent]->output(out->name());
968 ShaderOutput *out_dy = nodes_dy[out->parent]->output(out->name());
969
970 connect(out_dx, node->input("SampleX"));
971 connect(out_dy, node->input("SampleY"));
972
973 /* Add generated nodes. */
974 foreach (NodePair &pair, nodes_dx)
975 add(pair.second);
976 foreach (NodePair &pair, nodes_dy)
977 add(pair.second);
978
979 /* Connect what is connected is bump to sample-center input. */
980 connect(out, node->input("SampleCenter"));
981
982 /* Bump input is just for connectivity purpose for the graph input,
983 * we re-connected this input to sample-center, so lets disconnect it
984 * from bump input. */
985 disconnect(bump_input);
986 }
987 }
988}
989
990void ShaderGraph::bump_from_displacement(bool use_object_space)
991{
992 /* generate bump mapping automatically from displacement. bump mapping is
993 * done using a 3-tap filter, computing the displacement at the center,
994 * and two other positions shifted by ray differentials.
995 *
996 * since the input to displacement is a node graph, we need to ensure that
997 * all texture coordinates use are shift by the ray differentials. for this
998 * reason we make 3 copies of the node subgraph defining the displacement,
999 * with each different geometry and texture coordinate nodes that generate
1000 * different shifted coordinates.
1001 *
1002 * these 3 displacement values are then fed into the bump node, which will
1003 * output the perturbed normal. */
1004
1005 ShaderInput *displacement_in = output()->input("Displacement");
1006
1007 if (!displacement_in->link) {
1008 return;
1009 }
1010
1011 /* find dependencies for the given input */
1012 ShaderNodeSet nodes_displace;
1013 find_dependencies(nodes_displace, displacement_in);
1014
1015 /* copy nodes for 3 bump samples */
1016 ShaderNodeMap nodes_center;
1017 ShaderNodeMap nodes_dx;
1018 ShaderNodeMap nodes_dy;
1019
1020 copy_nodes(nodes_displace, nodes_center);
1021 copy_nodes(nodes_displace, nodes_dx);
1022 copy_nodes(nodes_displace, nodes_dy);
1023
1024 /* mark nodes to indicate they are use for bump computation, so
1025 * that any texture coordinates are shifted by dx/dy when sampling */
1026 foreach (NodePair &pair, nodes_center)
1027 pair.second->bump = SHADER_BUMP_CENTER;
1028 foreach (NodePair &pair, nodes_dx)
1029 pair.second->bump = SHADER_BUMP_DX;
1030 foreach (NodePair &pair, nodes_dy)
1031 pair.second->bump = SHADER_BUMP_DY;
1032
1033 /* add set normal node and connect the bump normal output to the set normal
1034 * output, so it can finally set the shader normal, note we are only doing
1035 * this for bump from displacement, this will be the only bump allowed to
1036 * overwrite the shader normal */
1037 ShaderNode *set_normal = add(create_node<SetNormalNode>());
1038
1039 /* add bump node and connect copied graphs to it */
1041 bump->set_use_object_space(use_object_space);
1042 bump->set_distance(1.0f);
1043
1044 ShaderOutput *out = displacement_in->link;
1045 ShaderOutput *out_center = nodes_center[out->parent]->output(out->name());
1046 ShaderOutput *out_dx = nodes_dx[out->parent]->output(out->name());
1047 ShaderOutput *out_dy = nodes_dy[out->parent]->output(out->name());
1048
1049 /* convert displacement vector to height */
1053
1054 dot_center->set_math_type(NODE_VECTOR_MATH_DOT_PRODUCT);
1055 dot_dx->set_math_type(NODE_VECTOR_MATH_DOT_PRODUCT);
1056 dot_dy->set_math_type(NODE_VECTOR_MATH_DOT_PRODUCT);
1057
1059 connect(geom->output("Normal"), bump->input("Normal"));
1060 connect(geom->output("Normal"), dot_center->input("Vector2"));
1061 connect(geom->output("Normal"), dot_dx->input("Vector2"));
1062 connect(geom->output("Normal"), dot_dy->input("Vector2"));
1063
1064 connect(out_center, dot_center->input("Vector1"));
1065 connect(out_dx, dot_dx->input("Vector1"));
1066 connect(out_dy, dot_dy->input("Vector1"));
1067
1068 connect(dot_center->output("Value"), bump->input("SampleCenter"));
1069 connect(dot_dx->output("Value"), bump->input("SampleX"));
1070 connect(dot_dy->output("Value"), bump->input("SampleY"));
1071
1072 /* connect the bump out to the set normal in: */
1073 connect(bump->output("Normal"), set_normal->input("Direction"));
1074
1075 /* connect to output node */
1076 connect(set_normal->output("Normal"), output()->input("Normal"));
1077
1078 /* finally, add the copied nodes to the graph. we can't do this earlier
1079 * because we would create dependency cycles in the above loop */
1080 foreach (NodePair &pair, nodes_center)
1081 add(pair.second);
1082 foreach (NodePair &pair, nodes_dx)
1083 add(pair.second);
1084 foreach (NodePair &pair, nodes_dy)
1085 add(pair.second);
1086}
1087
1089{
1090 /* for SVM in multi closure mode, this transforms the shader mix/add part of
1091 * the graph into nodes that feed weights into closure nodes. this is too
1092 * avoid building a closure tree and then flattening it, and instead write it
1093 * directly to an array */
1094
1095 if (node->special_type == SHADER_SPECIAL_TYPE_COMBINE_CLOSURE) {
1096 ShaderInput *fin = node->input("Fac");
1097 ShaderInput *cl1in = node->input("Closure1");
1098 ShaderInput *cl2in = node->input("Closure2");
1099 ShaderOutput *weight1_out, *weight2_out;
1100
1101 if (fin) {
1102 /* mix closure: add node to mix closure weights */
1104 add(mix_node);
1105 ShaderInput *fac_in = mix_node->input("Fac");
1106 ShaderInput *weight_in = mix_node->input("Weight");
1107
1108 if (fin->link) {
1109 connect(fin->link, fac_in);
1110 }
1111 else {
1112 mix_node->set_fac(node->get_float(fin->socket_type));
1113 }
1114
1115 if (weight_out) {
1116 connect(weight_out, weight_in);
1117 }
1118
1119 weight1_out = mix_node->output("Weight1");
1120 weight2_out = mix_node->output("Weight2");
1121 }
1122 else {
1123 /* add closure: just pass on any weights */
1124 weight1_out = weight_out;
1125 weight2_out = weight_out;
1126 }
1127
1128 if (cl1in->link) {
1129 transform_multi_closure(cl1in->link->parent, weight1_out, volume);
1130 }
1131 if (cl2in->link) {
1132 transform_multi_closure(cl2in->link->parent, weight2_out, volume);
1133 }
1134 }
1135 else {
1136 ShaderInput *weight_in = node->input((volume) ? "VolumeMixWeight" : "SurfaceMixWeight");
1137
1138 /* not a closure node? */
1139 if (!weight_in) {
1140 return;
1141 }
1142
1143 /* already has a weight connected to it? add weights */
1144 float weight_value = node->get_float(weight_in->socket_type);
1145 if (weight_in->link || weight_value != 0.0f) {
1146 MathNode *math_node = create_node<MathNode>();
1147 add(math_node);
1148
1149 if (weight_in->link) {
1150 connect(weight_in->link, math_node->input("Value1"));
1151 }
1152 else {
1153 math_node->set_value1(weight_value);
1154 }
1155
1156 if (weight_out) {
1157 connect(weight_out, math_node->input("Value2"));
1158 }
1159 else {
1160 math_node->set_value2(1.0f);
1161 }
1162
1163 weight_out = math_node->output("Value");
1164 if (weight_in->link) {
1165 disconnect(weight_in);
1166 }
1167 }
1168
1169 /* connected to closure mix weight */
1170 if (weight_out) {
1171 connect(weight_out, weight_in);
1172 }
1173 else {
1174 node->set(weight_in->socket_type, weight_value + 1.0f);
1175 }
1176 }
1177}
1178
1180{
1181 int num_closures = 0;
1182 foreach (ShaderNode *node, nodes) {
1183 ClosureType closure_type = node->get_closure_type();
1184 if (closure_type == CLOSURE_NONE_ID) {
1185 continue;
1186 }
1187 else if (CLOSURE_IS_BSSRDF(closure_type)) {
1188 num_closures += 3;
1189 }
1190 else if (CLOSURE_IS_BSDF_MULTISCATTER(closure_type)) {
1191 num_closures += 2;
1192 }
1193 else if (CLOSURE_IS_PRINCIPLED(closure_type)) {
1194 num_closures += 12;
1195 }
1196 else if (CLOSURE_IS_VOLUME(closure_type)) {
1197 /* TODO(sergey): Verify this is still needed, since we have special minimized volume storage
1198 * for the volume steps. */
1199 num_closures += MAX_VOLUME_STACK_SIZE;
1200 }
1201 else if (closure_type == CLOSURE_BSDF_PHYSICAL_CONDUCTOR ||
1202 closure_type == CLOSURE_BSDF_F82_CONDUCTOR ||
1204 closure_type == CLOSURE_BSDF_MICROFACET_GGX_GLASS_ID ||
1205 closure_type == CLOSURE_BSDF_HAIR_CHIANG_ID ||
1206 closure_type == CLOSURE_BSDF_HAIR_HUANG_ID)
1207 {
1208 num_closures += 2;
1209 }
1210 else {
1211 ++num_closures;
1212 }
1213 }
1214 return num_closures;
1215}
1216
1217void ShaderGraph::dump_graph(const char *filename)
1218{
1219 FILE *fd = fopen(filename, "w");
1220
1221 if (fd == NULL) {
1222 printf("Error opening file for dumping the graph: %s\n", filename);
1223 return;
1224 }
1225
1226 fprintf(fd, "digraph shader_graph {\n");
1227 fprintf(fd, "ranksep=1.5\n");
1228 fprintf(fd, "rankdir=LR\n");
1229 fprintf(fd, "splines=false\n");
1230
1231 foreach (ShaderNode *node, nodes) {
1232 fprintf(fd, "// NODE: %p\n", node);
1233 fprintf(fd, "\"%p\" [shape=record,label=\"{", node);
1234 if (node->inputs.size()) {
1235 fprintf(fd, "{");
1236 foreach (ShaderInput *socket, node->inputs) {
1237 if (socket != node->inputs[0]) {
1238 fprintf(fd, "|");
1239 }
1240 fprintf(fd, "<IN_%p>%s", socket, socket->name().c_str());
1241 }
1242 fprintf(fd, "}|");
1243 }
1244 fprintf(fd, "%s", node->name.c_str());
1245 if (node->bump == SHADER_BUMP_CENTER) {
1246 fprintf(fd, " (bump:center)");
1247 }
1248 else if (node->bump == SHADER_BUMP_DX) {
1249 fprintf(fd, " (bump:dx)");
1250 }
1251 else if (node->bump == SHADER_BUMP_DY) {
1252 fprintf(fd, " (bump:dy)");
1253 }
1254 if (node->outputs.size()) {
1255 fprintf(fd, "|{");
1256 foreach (ShaderOutput *socket, node->outputs) {
1257 if (socket != node->outputs[0]) {
1258 fprintf(fd, "|");
1259 }
1260 fprintf(fd, "<OUT_%p>%s", socket, socket->name().c_str());
1261 }
1262 fprintf(fd, "}");
1263 }
1264 fprintf(fd, "}\"]");
1265 }
1266
1267 foreach (ShaderNode *node, nodes) {
1268 foreach (ShaderOutput *output, node->outputs) {
1269 foreach (ShaderInput *input, output->links) {
1270 fprintf(fd,
1271 "// CONNECTION: OUT_%p->IN_%p (%s:%s)\n",
1272 output,
1273 input,
1274 output->name().c_str(),
1275 input->name().c_str());
1276 fprintf(fd,
1277 "\"%p\":\"OUT_%p\":e -> \"%p\":\"IN_%p\":w [label=\"\"]\n",
1278 output->parent,
1279 output,
1280 input->parent,
1281 input);
1282 }
1283 }
1284 }
1285
1286 fprintf(fd, "}\n");
1287 fclose(fd);
1288}
1289
@ NODE_VECTOR_MATH_DOT_PRODUCT
Definition md5.h:21
string get_hex()
Definition md5.cpp:354
void append(const uint8_t *data, int size)
Definition md5.cpp:255
string bytecode_hash
void find_dependencies(ShaderNodeSet &dependencies, ShaderInput *input)
string displacement_hash
OutputNode * output()
void verify_volume_output()
void simplify(Scene *scene)
T * create_node(Args &&...args)
void disconnect(ShaderOutput *from)
void delete_node(T *node)
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)
pair< ShaderNode *const, ShaderNode * > NodePair
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 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)
ShaderNode * add(ShaderNode *node)
void refine_bump_nodes()
void dump_graph(const char *filename)
ustring name() const
ShaderOutput * link
void disconnect()
int flags() const
const SocketType & socket_type
ShaderInput * input(const char *name)
vector< ShaderOutput * > outputs
void remove_input(ShaderInput *input)
ShaderNodeSpecialType special_type
vector< ShaderInput * > inputs
virtual ~ShaderNode()
virtual ShaderNode * clone(ShaderGraph *graph) const =0
virtual bool equals(const ShaderNode &other)
ShaderNode(const NodeType *type)
void create_inputs_outputs(const NodeType *type)
ShaderBump bump
ShaderOutput * output(const char *name)
virtual void attributes(Shader *shader, AttributeRequestSet *attributes)
vector< ShaderInput * > links
ustring name() const
ShaderNode * parent
#define printf
OperationNode * node
StackEntry * from
Set< ComponentNode * > visited
#define CCL_NAMESPACE_END
#define NULL
#define CLOSURE_IS_VOLUME(type)
@ NODE_VECTOR_TRANSFORM_CONVERT_SPACE_OBJECT
@ NODE_VECTOR_TRANSFORM_CONVERT_SPACE_WORLD
#define CLOSURE_IS_PRINCIPLED(type)
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
#define CLOSURE_IS_BSSRDF(type)
@ NODE_VECTOR_TRANSFORM_TYPE_NORMAL
#define CLOSURE_IS_BSDF_MULTISCATTER(type)
#define MAX_VOLUME_STACK_SIZE
@ ATTR_STD_GENERATED_TRANSFORM
@ ATTR_STD_UV
@ ATTR_STD_GENERATED
#define VLOG_DEBUG
Definition log.h:81
ccl_device_inline float3 one_float3()
Definition math_float3.h:24
bool check_node_inputs_traversed(const ShaderNode *node, const ShaderNodeSet &done)
bool check_node_inputs_has_links(const ShaderNode *node)
@ 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_NONE
@ SHADER_SPECIAL_TYPE_OSL
set< ShaderNode *, ShaderNodeIDComparator > ShaderNodeSet
@ SHADER_BUMP_CENTER
@ SHADER_BUMP_DX
@ SHADER_BUMP_DY
@ SHADER_BUMP_NONE
map< ShaderNode *, ShaderNode *, ShaderNodeIDComparator > ShaderNodeMap
unsigned char uint8_t
Definition stdint.h:78
const NodeType * type
Definition graph/node.h:178
void set_value(const SocketType &input, const Node &other, const SocketType &other_input)
void copy_value(const SocketType &input, const Node &other, const SocketType &other_input)
ustring name
Definition graph/node.h:177
bool equals_value(const Node &other, const SocketType &input) const
@ LINK_TEXTURE_UV
Definition node_type.h:68
@ LINK_TEXTURE_GENERATED
Definition node_type.h:66
@ LINK_INCOMING
Definition node_type.h:70
@ LINK_TEXTURE_INCOMING
Definition node_type.h:69
@ LINK_TEXTURE_NORMAL
Definition node_type.h:67
@ DEFAULT_LINK_MASK
Definition node_type.h:75
@ LINK_POSITION
Definition node_type.h:72