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