[Bf-blender-cvs] [ccef6d3] compositor-2016: Cycles: Support bump mapping in GLSL viewport

Sergey Sharybin noreply at git.blender.org
Wed Jun 8 21:48:59 CEST 2016


Commit: ccef6d37eeb3bade127ca22b7e6bbceef27e419b
Author: Sergey Sharybin
Date:   Fri May 20 14:16:54 2016 +0200
Branches: compositor-2016
https://developer.blender.org/rBccef6d37eeb3bade127ca22b7e6bbceef27e419b

Cycles: Support bump mapping in GLSL viewport

This commit implements Bump node in GLSL, making it possible to
see previews of bump mapping in viewport without need to render.
Nothing really fancy going on here, just uses internal dFdx/dFdy
functions to get derivatives of the surface and map itself.
Quite basic but seems to behave correct-ish.

This commit also makes Displacement material output to affect
viewport shading by re-linking unconnected Normal input to a
node which was used for displacement output (via Bump node).

Intention of all this is to make it really easy to do bump map
painting with Cycles as an active render engine.

Reviewers: campbellbarton, mont29, brecht, psy-fi

Reviewed By: brecht

Subscribers: Blendify, eyecandy

Differential Revision: https://developer.blender.org/D2014

===================================================================

M	source/blender/gpu/shaders/gpu_shader_material.glsl
M	source/blender/nodes/shader/node_shader_tree.c
M	source/blender/nodes/shader/nodes/node_shader_bump.c

===================================================================

diff --git a/source/blender/gpu/shaders/gpu_shader_material.glsl b/source/blender/gpu/shaders/gpu_shader_material.glsl
index dea5b99..28bf99b 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 c4ec55c..29b1e5b 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 22027d5..285dede 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 */




More information about the Bf-blender-cvs mailing list