Eevee: Use manual derivatives for better quality bump.

We basically duplicate the height map branch plugged into the bump node,
and tag each node in each branch as dx/dy/ref using `branch_tag`.

Then we add a one pixel offset on the texture coordinates if the node is
tagged as dx or dy.

The dx/dy branches are plugged into (new) hidden sockets on the bump node.

This match cycles bump better but have a performance impact. Also, complex
nodetrees can now become instruction limited and not compile anymore.

Reviewers: brecht

Differential Revision: https://developer.blender.org/D5531
This commit is contained in:
Clément Foucault 2019-08-19 20:33:17 +02:00
parent 83a7d98a32
commit ffd5e1e6ac
Notes: blender-bot 2023-02-14 06:55:40 +01:00
Referenced by commit fbd01624e3, Shader Nodes: Convert bump node to use new socket builder
Referenced by issue #83624, Bump map node is doing something strange which is causing unusable viewport performance.
Referenced by issue #79638, Bump node in Eevee. Huge performance drop.
Referenced by issue #71201, eevee/blender freezing  for a few seconds when renaming a few materials via python
Referenced by issue #70405, Bump Node not generating normals of textures using Geometry Position
Referenced by issue #69776, Eevee noise texture bug when there are 4 or more other noise textures being used in the bump node
Referenced by issue #68476, High Quality Bump
17 changed files with 159 additions and 90 deletions

View File

@ -1,5 +1,22 @@
void node_bump(
float strength, float dist, float height, vec3 N, vec3 surf_pos, float invert, out vec3 result)
void dfdx_v3(vec3 v, out vec3 dy)
{
dy = v + abs(dFdx(v));
}
void dfdy_v3(vec3 v, out vec3 dy)
{
dy = v + abs(dFdy(v));
}
void node_bump(float strength,
float dist,
float height,
float height_dx,
float height_dy,
vec3 N,
vec3 surf_pos,
float invert,
out vec3 result)
{
N = mat3(ViewMatrix) * normalize(N);
dist *= gl_FrontFacing ? invert : -invert;
@ -14,8 +31,8 @@ void node_bump(
/* Compute surface gradient and determinant. */
float det = dot(dPdx, Rx);
float dHdx = dFdx(height);
float dHdy = dFdy(height);
float dHdx = height_dx - height;
float dHdy = height_dy - height;
vec3 surfgrad = dHdx * Rx + dHdy * Ry;
strength = max(strength, 0.0);

View File

@ -276,7 +276,8 @@ typedef struct bNode {
short preview_xsize, preview_ysize;
/** Used at runtime when going through the tree. Initialize before use. */
short tmp_flag;
char _pad2[2];
/** Used at runtime to tag derivatives branches. EEVEE only. */
short branch_tag;
/** Runtime during drawing. */
struct uiBlock *block;

View File

@ -713,19 +713,22 @@ static bool ntree_branch_count_and_tag_nodes(bNode *fromnode,
return true;
}
static void ntree_shader_copy_branch_displacement(bNodeTree *ntree,
bNode *displacement_node,
bNodeSocket *displacement_socket,
bNodeLink *displacement_link)
/* Create a copy of a branch starting from a given node.
* callback is executed once for every copied node.
* Returns input node copy. */
static bNode *ntree_shader_copy_branch(bNodeTree *ntree,
bNode *start_node,
void (*callback)(bNode *node, int user_data),
int user_data)
{
/* Init tmp flag. */
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
node->tmp_flag = -1;
}
/* Count and tag all nodes inside the displacement branch of the tree. */
displacement_node->tmp_flag = 0;
start_node->tmp_flag = 0;
int node_count = 1;
nodeChainIter(ntree, displacement_node, ntree_branch_count_and_tag_nodes, &node_count, true);
nodeChainIter(ntree, start_node, ntree_branch_count_and_tag_nodes, &node_count, true);
/* Make a full copy of the branch */
bNode **nodes_copy = MEM_mallocN(sizeof(bNode *) * node_count, __func__);
LISTBASE_FOREACH (bNode *, node, &ntree->nodes) {
@ -755,15 +758,30 @@ static void ntree_shader_copy_branch_displacement(bNodeTree *ntree,
nodeAddLink(ntree, fromnode, fromsock, tonode, tosock);
}
}
/* Per node callback. */
if (callback) {
for (int i = 0; i < node_count; i++) {
callback(nodes_copy[i], user_data);
}
}
bNode *start_node_copy = nodes_copy[start_node->tmp_flag];
MEM_freeN(nodes_copy);
return start_node_copy;
}
static void ntree_shader_copy_branch_displacement(bNodeTree *ntree,
bNode *displacement_node,
bNodeSocket *displacement_socket,
bNodeLink *displacement_link)
{
/* Replace displacement socket/node/link. */
bNode *tonode = displacement_link->tonode;
bNodeSocket *tosock = displacement_link->tosock;
displacement_node = nodes_copy[displacement_node->tmp_flag];
displacement_node = ntree_shader_copy_branch(ntree, displacement_node, NULL, 0);
displacement_socket = ntree_shader_node_find_output(displacement_node,
displacement_socket->identifier);
nodeRemLink(ntree, displacement_link);
nodeAddLink(ntree, displacement_node, displacement_socket, tonode, tosock);
MEM_freeN(nodes_copy);
ntreeUpdateTree(G.main, ntree);
}
@ -838,6 +856,47 @@ static void ntree_shader_relink_displacement(bNodeTree *ntree, bNode *output_nod
ntreeUpdateTree(G.main, ntree);
}
static void node_tag_branch_as_derivative(bNode *node, int dx)
{
if (dx) {
node->branch_tag = 1;
}
else {
node->branch_tag = 2;
}
}
static bool ntree_shader_bump_branches(bNode *UNUSED(fromnode),
bNode *tonode,
void *userdata,
const bool UNUSED(reversed))
{
bNodeTree *ntree = (bNodeTree *)userdata;
if (tonode->type == SH_NODE_BUMP) {
bNodeSocket *height_dx_sock, *height_dy_sock, *bump_socket, *bump_dx_socket, *bump_dy_socket;
bNode *bump = tonode;
bump_socket = ntree_shader_node_find_input(bump, "Height");
bump_dx_socket = ntree_shader_node_find_input(bump, "Height_dx");
bump_dy_socket = ntree_shader_node_find_input(bump, "Height_dy");
if (bump_dx_socket->link) {
/* Avoid reconnecting the same bump twice. */
}
else if (bump_socket && bump_socket->link) {
bNodeLink *link = bump_socket->link;
bNode *height = link->fromnode;
bNode *height_dx = ntree_shader_copy_branch(ntree, height, node_tag_branch_as_derivative, 1);
bNode *height_dy = ntree_shader_copy_branch(ntree, height, node_tag_branch_as_derivative, 0);
height_dx_sock = ntree_shader_node_find_output(height_dx, link->fromsock->identifier);
height_dy_sock = ntree_shader_node_find_output(height_dy, link->fromsock->identifier);
nodeAddLink(ntree, height_dx, height_dx_sock, bump, bump_dx_socket);
nodeAddLink(ntree, height_dy, height_dy_sock, bump, bump_dy_socket);
/* We could end iter here, but other bump node could be plugged into other input sockets. */
}
}
return true;
}
static bool ntree_tag_bsdf_cb(bNode *fromnode,
bNode *UNUSED(tonode),
void *userdata,
@ -925,6 +984,10 @@ void ntreeGPUMaterialNodes(bNodeTree *localtree,
*/
ntree_shader_relink_displacement(localtree, output);
/* Duplicate bump height branches for manual derivatives.
*/
nodeChainIter(localtree, output, ntree_shader_bump_branches, localtree, true);
/* TODO(fclem): consider moving this to the gpu shader tree evaluation. */
nTreeTags tags = {
.ssr_id = 1.0,

View File

@ -258,6 +258,30 @@ void ntreeExecGPUNodes(bNodeTreeExec *exec, GPUMaterial *mat, bNode *output_node
}
}
void node_shader_gpu_bump_tex_coord(GPUMaterial *mat, bNode *node, GPUNodeLink **link)
{
if (node->branch_tag == 1) {
/* Add one time the value fo derivative to the input vector. */
GPU_link(mat, "dfdx_v3", *link, link);
}
else if (node->branch_tag == 2) {
/* Add one time the value fo derivative to the input vector. */
GPU_link(mat, "dfdy_v3", *link, link);
}
else {
/* nothing to do, reference center value. */
}
}
void node_shader_gpu_default_tex_coord(GPUMaterial *mat, bNode *node, GPUNodeLink **link)
{
if (!*link) {
*link = GPU_attribute(CD_ORCO, "");
GPU_link(mat, "generated_texco", GPU_builtin(GPU_VIEW_POSITION), *link, link);
node_shader_gpu_bump_tex_coord(mat, node, link);
}
}
void node_shader_gpu_tex_mapping(GPUMaterial *mat,
bNode *node,
GPUNodeStack *in,

View File

@ -84,6 +84,12 @@ void nodestack_get_vec(float *in, short type_in, bNodeStack *ns);
void node_gpu_stack_from_data(struct GPUNodeStack *gs, int type, struct bNodeStack *ns);
void node_data_from_gpu_stack(struct bNodeStack *ns, struct GPUNodeStack *gs);
void node_shader_gpu_bump_tex_coord(struct GPUMaterial *mat,
struct bNode *node,
struct GPUNodeLink **link);
void node_shader_gpu_default_tex_coord(struct GPUMaterial *mat,
struct bNode *node,
struct GPUNodeLink **link);
void node_shader_gpu_tex_mapping(struct GPUMaterial *mat,
struct bNode *node,
struct GPUNodeStack *in,

View File

@ -24,32 +24,17 @@
#include "node_shader_util.h"
/* **************** BUMP ******************** */
/* clang-format off */
static bNodeSocketTemplate sh_node_bump_in[] = {
{SOCK_FLOAT, 1, N_("Strength"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1.0f, PROP_FACTOR},
{SOCK_FLOAT, 1, N_("Distance"), 1.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1000.0f},
{SOCK_FLOAT,
1,
N_("Height"),
1.0f,
1.0f,
1.0f,
1.0f,
-1000.0f,
1000.0f,
PROP_NONE,
SOCK_HIDE_VALUE},
{SOCK_VECTOR,
1,
N_("Normal"),
0.0f,
0.0f,
0.0f,
1.0f,
-1.0f,
1.0f,
PROP_NONE,
SOCK_HIDE_VALUE},
{-1, 0, ""}};
{SOCK_FLOAT, 1, N_("Height"), 1.0f, 1.0f, 1.0f, 1.0f, -1000.0f, 1000.0f, PROP_NONE, SOCK_HIDE_VALUE},
{SOCK_FLOAT, 1, N_("Height_dx"), 1.0f, 1.0f, 1.0f, 1.0f, -1000.0f, 1000.0f, PROP_NONE, SOCK_UNAVAIL},
{SOCK_FLOAT, 1, N_("Height_dy"), 1.0f, 1.0f, 1.0f, 1.0f, -1000.0f, 1000.0f, PROP_NONE, SOCK_UNAVAIL},
{SOCK_VECTOR, 1, N_("Normal"), 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 1.0f, PROP_NONE, SOCK_HIDE_VALUE},
{-1, 0, ""}
};
/* clang-format on */
static bNodeSocketTemplate sh_node_bump_out[] = {{SOCK_VECTOR, 0, "Normal"}, {-1, 0, ""}};
@ -59,8 +44,8 @@ static int gpu_shader_bump(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
if (!in[3].link) {
GPU_link(mat, "world_normals_get", &in[3].link);
if (!in[5].link) {
GPU_link(mat, "world_normals_get", &in[5].link);
}
float invert = (node->custom1) ? -1.0 : 1.0;

View File

@ -157,11 +157,7 @@ static int node_shader_gpu_tex_brick(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
if (!in[0].link) {
in[0].link = GPU_attribute(CD_ORCO, "");
GPU_link(mat, "generated_texco", GPU_builtin(GPU_VIEW_POSITION), in[0].link, &in[0].link);
}
node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
node_shader_gpu_tex_mapping(mat, node, in, out);
NodeTexBrick *tex = (NodeTexBrick *)node->storage;
float offset_freq = tex->offset_freq;
@ -171,9 +167,9 @@ static int node_shader_gpu_tex_brick(GPUMaterial *mat,
"node_tex_brick",
in,
out,
GPU_constant(&tex->offset),
GPU_uniform(&tex->offset),
GPU_constant(&offset_freq),
GPU_constant(&tex->squash),
GPU_uniform(&tex->squash),
GPU_constant(&squash_freq));
}

View File

@ -70,11 +70,7 @@ static int node_shader_gpu_tex_checker(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
if (!in[0].link) {
in[0].link = GPU_attribute(CD_ORCO, "");
GPU_link(mat, "generated_texco", GPU_builtin(GPU_VIEW_POSITION), in[0].link, &in[0].link);
}
node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
node_shader_gpu_tex_mapping(mat, node, in, out);
return GPU_stack_link(mat, node, "node_tex_checker", in, out);

View File

@ -45,22 +45,24 @@ static int node_shader_gpu_tex_coord(GPUMaterial *mat,
GPUNodeLink *inv_obmat = (ob != NULL) ? GPU_uniform(&ob->imat[0][0]) :
GPU_builtin(GPU_INVERSE_OBJECT_MATRIX);
/* TODO only request orco if needed. */
GPUNodeLink *orco = GPU_attribute(CD_ORCO, "");
GPUNodeLink *mtface = GPU_attribute(CD_MTFACE, "");
GPUNodeLink *viewpos = GPU_builtin(GPU_VIEW_POSITION);
GPUNodeLink *worldnor = GPU_builtin(GPU_WORLD_NORMAL);
GPUNodeLink *texcofacs = GPU_builtin(GPU_CAMERA_TEXCO_FACTORS);
GPU_link(mat, "generated_from_orco", orco, &orco);
return GPU_stack_link(mat,
node,
"node_tex_coord",
in,
out,
GPU_builtin(GPU_VIEW_POSITION),
GPU_builtin(GPU_WORLD_NORMAL),
inv_obmat,
GPU_builtin(GPU_CAMERA_TEXCO_FACTORS),
orco,
mtface);
GPU_stack_link(
mat, node, "node_tex_coord", in, out, viewpos, worldnor, inv_obmat, texcofacs, orco, mtface);
/* for each output. */
for (int i = 0; sh_node_tex_coord_out[i].type != -1; i++) {
node_shader_gpu_bump_tex_coord(mat, node, &out[i].link);
}
return 1;
}
/* node type definition */

View File

@ -75,6 +75,7 @@ static int node_shader_gpu_tex_environment(GPUMaterial *mat,
if (!in[0].link) {
GPU_link(mat, "node_tex_environment_texco", GPU_builtin(GPU_VIEW_POSITION), &in[0].link);
node_shader_gpu_bump_tex_coord(mat, node, &in[0].link);
}
node_shader_gpu_tex_mapping(mat, node, in, out);

View File

@ -68,11 +68,7 @@ static int node_shader_gpu_tex_gradient(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
if (!in[0].link) {
in[0].link = GPU_attribute(CD_ORCO, "");
GPU_link(mat, "generated_texco", GPU_builtin(GPU_VIEW_POSITION), in[0].link, &in[0].link);
}
node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
node_shader_gpu_tex_mapping(mat, node, in, out);
NodeTexGradient *tex = (NodeTexGradient *)node->storage;

View File

@ -118,6 +118,7 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat,
if (!*texco) {
*texco = GPU_attribute(CD_MTFACE, "");
node_shader_gpu_bump_tex_coord(mat, node, texco);
}
node_shader_gpu_tex_mapping(mat, node, in, out);

View File

@ -73,11 +73,7 @@ static int node_shader_gpu_tex_magic(GPUMaterial *mat,
NodeTexMagic *tex = (NodeTexMagic *)node->storage;
float depth = tex->depth;
if (!in[0].link) {
in[0].link = GPU_attribute(CD_ORCO, "");
GPU_link(mat, "generated_texco", GPU_builtin(GPU_VIEW_POSITION), in[0].link, &in[0].link);
}
node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
node_shader_gpu_tex_mapping(mat, node, in, out);
return GPU_stack_link(mat, node, "node_tex_magic", in, out, GPU_constant(&depth));

View File

@ -74,11 +74,7 @@ static int node_shader_gpu_tex_musgrave(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
if (!in[0].link) {
in[0].link = GPU_attribute(CD_ORCO, "");
GPU_link(mat, "generated_texco", GPU_builtin(GPU_VIEW_POSITION), in[0].link, &in[0].link);
}
node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
node_shader_gpu_tex_mapping(mat, node, in, out);
NodeTexMusgrave *tex = (NodeTexMusgrave *)node->storage;

View File

@ -72,10 +72,7 @@ static int node_shader_gpu_tex_noise(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
if (!in[0].link) {
in[0].link = GPU_attribute(CD_ORCO, "");
GPU_link(mat, "generated_texco", GPU_builtin(GPU_VIEW_POSITION), in[0].link, &in[0].link);
}
node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
node_shader_gpu_tex_mapping(mat, node, in, out);
NodeTexNoise *tex = (NodeTexNoise *)node->storage;

View File

@ -72,11 +72,7 @@ static int node_shader_gpu_tex_voronoi(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
if (!in[0].link) {
in[0].link = GPU_attribute(CD_ORCO, "");
GPU_link(mat, "generated_texco", GPU_builtin(GPU_VIEW_POSITION), in[0].link, &in[0].link);
}
node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
node_shader_gpu_tex_mapping(mat, node, in, out);
NodeTexVoronoi *tex = (NodeTexVoronoi *)node->storage;

View File

@ -72,11 +72,7 @@ static int node_shader_gpu_tex_wave(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
if (!in[0].link) {
in[0].link = GPU_attribute(CD_ORCO, "");
GPU_link(mat, "generated_texco", GPU_builtin(GPU_VIEW_POSITION), in[0].link, &in[0].link);
}
node_shader_gpu_default_tex_coord(mat, node, &in[0].link);
node_shader_gpu_tex_mapping(mat, node, in, out);
NodeTexWave *tex = (NodeTexWave *)node->storage;