Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/gpu/shaders/gpu_shader_material.glsl22
-rw-r--r--source/blender/nodes/shader/node_shader_tree.c160
-rw-r--r--source/blender/nodes/shader/nodes/node_shader_bump.c10
3 files changed, 188 insertions, 4 deletions
diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl
index dea5b994e74..28bf99b83dc 100644
--- a/source/blender/gpu/shaders/gpu_shader_material.glsl
+++ b/source/blender/gpu/shaders/gpu_shader_material.glsl
@@ -3160,9 +3160,27 @@ void node_normal_map(vec4 tangent, vec3 normal, vec3 texnormal, out vec3 outnorm
outnormal = normalize(outnormal);
}
-void node_bump(float strength, float dist, float height, vec3 N, out vec3 result)
+void node_bump(float strength, float dist, float height, vec3 N, vec3 surf_pos, out vec3 result)
{
- result = N;
+ vec3 dPdx = dFdx(surf_pos);
+ vec3 dPdy = dFdy(surf_pos);
+
+ /* Get surface tangents from normal. */
+ vec3 Rx = cross(dPdy, N);
+ vec3 Ry = cross(N, dPdx);
+
+ /* Compute surface gradient and determinant. */
+ float det = dot(dPdx, Rx);
+ float absdet = abs(det);
+
+ float dHdx = dFdx(height);
+ float dHdy = dFdy(height);
+ vec3 surfgrad = dHdx*Rx + dHdy*Ry;
+
+ strength = max(strength, 0.0);
+
+ result = normalize(absdet*N - dist*sign(det)*surfgrad);
+ result = normalize(strength*result + (1.0 - strength)*N);
}
/* output */
diff --git a/source/blender/nodes/shader/node_shader_tree.c b/source/blender/nodes/shader/node_shader_tree.c
index c4ec55c8d06..29b1e5bc5c7 100644
--- a/source/blender/nodes/shader/node_shader_tree.c
+++ b/source/blender/nodes/shader/node_shader_tree.c
@@ -199,12 +199,172 @@ void register_node_tree_type_sh(void)
/* GPU material from shader nodes */
+/* Find an output node of the shader tree.
+ *
+ * NOTE: it will only return output which is NOT in the group, which isn't how
+ * render engines works but it's how the GPU shader compilation works. This we
+ * can change in the future and make it a generic function, but for now it stays
+ * private here.
+ */
+static bNode *ntree_shader_output_node(bNodeTree *ntree)
+{
+ /* Make sure we only have single node tagged as output. */
+ ntreeSetOutput(ntree);
+ for (bNode *node = ntree->nodes.first; node != NULL; node = node->next) {
+ if (node->flag & NODE_DO_OUTPUT) {
+ return node;
+ }
+ }
+ return NULL;
+}
+
+/* Find socket with a specified identifier. */
+static bNodeSocket *ntree_shader_node_find_socket(ListBase *sockets,
+ const char *identifier)
+{
+ for (bNodeSocket *sock = sockets->first; sock != NULL; sock = sock->next) {
+ if (STREQ(sock->identifier, identifier)) {
+ return sock;
+ }
+ }
+ return NULL;
+}
+
+/* Find input socket with a specified identifier. */
+static bNodeSocket *ntree_shader_node_find_input(bNode *node,
+ const char *identifier)
+{
+ return ntree_shader_node_find_socket(&node->inputs, identifier);
+}
+
+/* Find output socket with a specified identifier. */
+static bNodeSocket *ntree_shader_node_find_output(bNode *node,
+ const char *identifier)
+{
+ return ntree_shader_node_find_socket(&node->outputs, identifier);
+}
+
+/* Check whether shader has a displacement.
+ *
+ * Will also return a node and it's socket which is connected to a displacement
+ * output. Additionally, link which is attached to the displacement output is
+ * also returned.
+ */
+static bool ntree_shader_has_displacement(bNodeTree *ntree,
+ bNode **r_node,
+ bNodeSocket **r_socket,
+ bNodeLink **r_link)
+{
+ bNode *output_node = ntree_shader_output_node(ntree);
+ if (output_node == NULL) {
+ /* We can't have displacement without output node, apparently. */
+ return false;
+ }
+ /* Make sure sockets links pointers are correct. */
+ ntreeUpdateTree(G.main, ntree);
+ bNodeSocket *displacement = ntree_shader_node_find_input(output_node,
+ "Displacement");
+
+ if (displacement == NULL) {
+ /* Non-cycles node is used as an output. */
+ return false;
+ }
+ if (displacement->link != NULL) {
+ *r_node = displacement->link->fromnode;
+ *r_socket = displacement->link->fromsock;
+ *r_link = displacement->link;
+ }
+ return displacement->link != NULL;
+}
+
+/* Use specified node and socket as an input for unconnected normal sockets. */
+static void ntree_shader_link_builtin_normal(bNodeTree *ntree,
+ bNode *node_from,
+ bNodeSocket *socket_from)
+{
+ for (bNode *node = ntree->nodes.first; node != NULL; node = node->next) {
+ if (node == node_from) {
+ /* Don't connect node itself! */
+ continue;
+ }
+ bNodeSocket *sock = ntree_shader_node_find_input(node, "Normal");
+ /* TODO(sergey): Can we do something smarter here than just a name-based
+ * matching?
+ */
+ if (sock == NULL) {
+ /* There's no Normal input, nothing to link. */
+ continue;
+ }
+ if (sock->link != NULL) {
+ /* Something is linked to the normal input already. can't
+ * use other input for that.
+ */
+ continue;
+ }
+ /* Create connection between specified node and the normal input. */
+ nodeAddLink(ntree, node_from, socket_from, node, sock);
+ }
+}
+
+/* Re-link displacement output to unconnected normal sockets via bump node.
+ * This way material with have proper displacement in the viewport.
+ */
+static void ntree_shader_relink_displacement(bNodeTree *ntree,
+ short compatibility)
+{
+ if (compatibility != NODE_NEW_SHADING) {
+ /* We can only deal with new shading system here. */
+ return;
+ }
+ bNode *displacement_node;
+ bNodeSocket *displacement_socket;
+ bNodeLink *displacement_link;
+ if (!ntree_shader_has_displacement(ntree,
+ &displacement_node,
+ &displacement_socket,
+ &displacement_link))
+ {
+ /* There is no displacement output connected, nothing to re-link. */
+ return;
+ }
+ /* We have to disconnect displacement output socket, otherwise we'll have
+ * cycles in the Cycles material :)
+ */
+ nodeRemLink(ntree, displacement_link);
+ /* We can't connect displacement to normal directly, use bump node for that
+ * and hope that it gives good enough approximation.
+ */
+ bNode *bump_node = nodeAddStaticNode(NULL, ntree, SH_NODE_BUMP);
+ bNodeSocket *bump_input_socket = ntree_shader_node_find_input(bump_node, "Height");
+ bNodeSocket *bump_output_socket = ntree_shader_node_find_output(bump_node, "Normal");
+ BLI_assert(bump_input_socket != NULL);
+ BLI_assert(bump_output_socket != NULL);
+ /* Connect bump node to where displacement output was originally
+ * connected to.
+ */
+ nodeAddLink(ntree,
+ displacement_node, displacement_socket,
+ bump_node, bump_input_socket);
+ /* Connect all free-standing Normal inputs. */
+ ntree_shader_link_builtin_normal(ntree, bump_node, bump_output_socket);
+ /* TODO(sergey): Reconnect Geometry Info->Normal sockets to the new
+ * bump node.
+ */
+ /* We modified the tree, it needs to be updated now. */
+ ntreeUpdateTree(G.main, ntree);
+}
+
void ntreeGPUMaterialNodes(bNodeTree *ntree, GPUMaterial *mat, short compatibility)
{
/* localize tree to create links for reroute and mute */
bNodeTree *localtree = ntreeLocalize(ntree);
bNodeTreeExec *exec;
+ /* Perform all needed modifications on the tree in order to support
+ * displacement/bump mapping.
+ */
+ ntree_shader_relink_displacement(localtree, compatibility);
+
exec = ntreeShaderBeginExecTree(localtree);
ntreeExecGPUNodes(exec, mat, 1, compatibility);
ntreeShaderEndExecTree(exec);
diff --git a/source/blender/nodes/shader/nodes/node_shader_bump.c b/source/blender/nodes/shader/nodes/node_shader_bump.c
index 22027d58385..285dede71e6 100644
--- a/source/blender/nodes/shader/nodes/node_shader_bump.c
+++ b/source/blender/nodes/shader/nodes/node_shader_bump.c
@@ -51,8 +51,14 @@ static int gpu_shader_bump(GPUMaterial *mat, bNode *UNUSED(node), bNodeExecData
in[3].link = GPU_builtin(GPU_VIEW_NORMAL);
else
GPU_link(mat, "direction_transform_m4v3", in[3].link, GPU_builtin(GPU_VIEW_MATRIX), &in[3].link);
-
- return GPU_stack_link(mat, "node_bump", in, out);
+ GPU_stack_link(mat, "node_bump", in, out, GPU_builtin(GPU_VIEW_POSITION));
+ /* Other nodes are applying view matrix if the input Normal has a link.
+ * We don't want normal to have view matrix applied twice, so we cancel it here.
+ *
+ * TODO(sergey): This is an extra multiplication which cancels each other,
+ * better avoid this but that requires bigger refactor.
+ */
+ return GPU_link(mat, "direction_transform_m4v3", out[0].link, GPU_builtin(GPU_INVERSE_VIEW_MATRIX), &out[0].link);
}
/* node type definition */