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:
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
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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 */
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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));
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue