diff options
Diffstat (limited to 'source/blender/nodes/shader/nodes/node_shader_tex_image.cc')
-rw-r--r-- | source/blender/nodes/shader/nodes/node_shader_tex_image.cc | 191 |
1 files changed, 191 insertions, 0 deletions
diff --git a/source/blender/nodes/shader/nodes/node_shader_tex_image.cc b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc new file mode 100644 index 00000000000..df1051c07b4 --- /dev/null +++ b/source/blender/nodes/shader/nodes/node_shader_tex_image.cc @@ -0,0 +1,191 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2005 Blender Foundation. + * All rights reserved. + */ + +#include "../node_shader_util.h" + +namespace blender::nodes { + +static void sh_node_tex_image_declare(NodeDeclarationBuilder &b) +{ + b.is_function_node(); + b.add_input<decl::Vector>("Vector").implicit_field(); + b.add_output<decl::Color>("Color").no_muted_links(); + b.add_output<decl::Float>("Alpha").no_muted_links(); +}; + +}; // namespace blender::nodes + +static void node_shader_init_tex_image(bNodeTree *UNUSED(ntree), bNode *node) +{ + NodeTexImage *tex = (NodeTexImage *)MEM_callocN(sizeof(NodeTexImage), "NodeTexImage"); + BKE_texture_mapping_default(&tex->base.tex_mapping, TEXMAP_TYPE_POINT); + BKE_texture_colormapping_default(&tex->base.color_mapping); + BKE_imageuser_default(&tex->iuser); + + node->storage = tex; +} + +static int node_shader_gpu_tex_image(GPUMaterial *mat, + bNode *node, + bNodeExecData *UNUSED(execdata), + GPUNodeStack *in, + GPUNodeStack *out) +{ + Image *ima = (Image *)node->id; + NodeTexImage *tex = (NodeTexImage *)node->storage; + + /* We get the image user from the original node, since GPU image keeps + * a pointer to it and the dependency refreshes the original. */ + bNode *node_original = node->original ? node->original : node; + NodeTexImage *tex_original = (NodeTexImage *)node_original->storage; + ImageUser *iuser = &tex_original->iuser; + + if (!ima) { + return GPU_stack_link(mat, node, "node_tex_image_empty", in, out); + } + + GPUNodeLink **texco = &in[0].link; + if (!*texco) { + *texco = GPU_attribute(mat, CD_MTFACE, ""); + node_shader_gpu_bump_tex_coord(mat, node, texco); + } + + node_shader_gpu_tex_mapping(mat, node, in, out); + + eGPUSamplerState sampler_state = GPU_SAMPLER_DEFAULT; + + switch (tex->extension) { + case SHD_IMAGE_EXTENSION_REPEAT: + sampler_state |= GPU_SAMPLER_REPEAT; + break; + case SHD_IMAGE_EXTENSION_CLIP: + sampler_state |= GPU_SAMPLER_CLAMP_BORDER; + break; + default: + break; + } + + if (tex->interpolation != SHD_INTERP_CLOSEST) { + sampler_state |= GPU_SAMPLER_ANISO | GPU_SAMPLER_FILTER; + /* TODO(fclem): For now assume mipmap is always enabled. */ + sampler_state |= GPU_SAMPLER_MIPMAP; + } + const bool use_cubic = ELEM(tex->interpolation, SHD_INTERP_CUBIC, SHD_INTERP_SMART); + + if (ima->source == IMA_SRC_TILED) { + const char *gpu_node_name = use_cubic ? "node_tex_tile_cubic" : "node_tex_tile_linear"; + GPUNodeLink *gpu_image = GPU_image_tiled(mat, ima, iuser, sampler_state); + GPUNodeLink *gpu_image_tile_mapping = GPU_image_tiled_mapping(mat, ima, iuser); + /* UDIM tiles needs a samper2DArray and sampler1DArray for tile mapping. */ + GPU_stack_link(mat, node, gpu_node_name, in, out, gpu_image, gpu_image_tile_mapping); + } + else { + const char *gpu_node_name = use_cubic ? "node_tex_image_cubic" : "node_tex_image_linear"; + + switch (tex->projection) { + case SHD_PROJ_FLAT: { + GPUNodeLink *gpu_image = GPU_image(mat, ima, iuser, sampler_state); + GPU_stack_link(mat, node, gpu_node_name, in, out, gpu_image); + break; + } + case SHD_PROJ_BOX: { + gpu_node_name = use_cubic ? "tex_box_sample_cubic" : "tex_box_sample_linear"; + GPUNodeLink *wnor, *col1, *col2, *col3; + GPUNodeLink *vnor = GPU_builtin(GPU_WORLD_NORMAL); + GPUNodeLink *ob_mat = GPU_builtin(GPU_OBJECT_MATRIX); + GPUNodeLink *blend = GPU_uniform(&tex->projection_blend); + GPUNodeLink *gpu_image = GPU_image(mat, ima, iuser, sampler_state); + /* equivalent to normal_world_to_object */ + GPU_link(mat, "normal_transform_transposed_m4v3", vnor, ob_mat, &wnor); + GPU_link(mat, gpu_node_name, in[0].link, wnor, gpu_image, &col1, &col2, &col3); + GPU_link(mat, "tex_box_blend", wnor, col1, col2, col3, blend, &out[0].link, &out[1].link); + break; + } + case SHD_PROJ_SPHERE: { + /* This projection is known to have a derivative discontinuity. + * Hide it by turning off mipmapping. */ + sampler_state &= ~GPU_SAMPLER_MIPMAP; + GPUNodeLink *gpu_image = GPU_image(mat, ima, iuser, sampler_state); + GPU_link(mat, "point_texco_remap_square", *texco, texco); + GPU_link(mat, "point_map_to_sphere", *texco, texco); + GPU_stack_link(mat, node, gpu_node_name, in, out, gpu_image); + break; + } + case SHD_PROJ_TUBE: { + /* This projection is known to have a derivative discontinuity. + * Hide it by turning off mipmapping. */ + sampler_state &= ~GPU_SAMPLER_MIPMAP; + GPUNodeLink *gpu_image = GPU_image(mat, ima, iuser, sampler_state); + GPU_link(mat, "point_texco_remap_square", *texco, texco); + GPU_link(mat, "point_map_to_tube", *texco, texco); + GPU_stack_link(mat, node, gpu_node_name, in, out, gpu_image); + break; + } + } + } + + if (out[0].hasoutput) { + if (ELEM(ima->alpha_mode, IMA_ALPHA_IGNORE, IMA_ALPHA_CHANNEL_PACKED) || + IMB_colormanagement_space_name_is_data(ima->colorspace_settings.name)) { + /* Don't let alpha affect color output in these cases. */ + GPU_link(mat, "color_alpha_clear", out[0].link, &out[0].link); + } + else { + /* Output premultiplied alpha depending on alpha socket usage. This makes + * it so that if we blend the color with a transparent shader using alpha as + * a factor, we don't multiply alpha into the color twice. And if we do + * not, then there will be no artifacts from zero alpha areas. */ + if (ima->alpha_mode == IMA_ALPHA_PREMUL) { + if (out[1].hasoutput) { + GPU_link(mat, "color_alpha_unpremultiply", out[0].link, &out[0].link); + } + else { + GPU_link(mat, "color_alpha_clear", out[0].link, &out[0].link); + } + } + else { + if (out[1].hasoutput) { + GPU_link(mat, "color_alpha_clear", out[0].link, &out[0].link); + } + else { + GPU_link(mat, "color_alpha_premultiply", out[0].link, &out[0].link); + } + } + } + } + + return true; +} + +/* node type definition */ +void register_node_type_sh_tex_image(void) +{ + static bNodeType ntype; + + sh_node_type_base(&ntype, SH_NODE_TEX_IMAGE, "Image Texture", NODE_CLASS_TEXTURE, 0); + ntype.declare = blender::nodes::sh_node_tex_image_declare; + node_type_init(&ntype, node_shader_init_tex_image); + node_type_storage( + &ntype, "NodeTexImage", node_free_standard_storage, node_copy_standard_storage); + node_type_gpu(&ntype, node_shader_gpu_tex_image); + node_type_label(&ntype, node_image_label); + node_type_size_preset(&ntype, NODE_SIZE_LARGE); + + nodeRegisterType(&ntype); +} |