GPUMaterial: Rework/simplify image texture filtering

This use the latest GPUTexture change to use the sampler state to avoid
the pole issues instead of using GLSL hacks.

This should fix T73942: Eevee mipmaps not respecting border mode.

Note that this also fix some discrepencies between cycles and eevee (like
boxmapping + clip).
This commit is contained in:
Clément Foucault 2020-06-03 16:18:28 +02:00
parent 054923c860
commit b2dcff4c21
Notes: blender-bot 2023-02-13 23:27:23 +01:00
Referenced by issue #73942, Eevee mipmaps not respecting border mode
2 changed files with 57 additions and 201 deletions

View File

@ -218,15 +218,8 @@ void tex_box_sample_smart(
tex_box_sample_cubic(texco, N, ima, color1, color2, color3);
}
void node_tex_image_box(vec3 texco,
vec3 N,
vec4 color1,
vec4 color2,
vec4 color3,
sampler2D ima,
float blend,
out vec4 color,
out float alpha)
void tex_box_blend(
vec3 N, vec4 color1, vec4 color2, vec4 color3, float blend, out vec4 color, out float alpha)
{
/* project from direction vector to barycentric coordinates in triangles */
N = abs(N);
@ -271,70 +264,6 @@ void node_tex_image_box(vec3 texco,
alpha = color.a;
}
void tex_clip_linear(vec3 co, sampler2D ima, vec4 icolor, out vec4 color, out float alpha)
{
vec2 tex_size = vec2(textureSize(ima, 0).xy);
vec2 minco = min(co.xy, 1.0 - co.xy);
minco = clamp(minco * tex_size + 0.5, 0.0, 1.0);
float fac = minco.x * minco.y;
color = mix(vec4(0.0), icolor, fac);
alpha = color.a;
}
void tex_clip_nearest(vec3 co, sampler2D ima, vec4 icolor, out vec4 color, out float alpha)
{
vec4 minco = vec4(co.xy, 1.0 - co.xy);
color = (any(lessThan(minco, vec4(0.0)))) ? vec4(0.0) : icolor;
alpha = color.a;
}
void tex_clip_cubic(vec3 co, sampler2D ima, vec4 icolor, out vec4 color, out float alpha)
{
vec2 tex_size = vec2(textureSize(ima, 0).xy);
co.xy *= tex_size;
/* texel center */
vec2 tc = floor(co.xy - 0.5) + 0.5;
vec2 w0, w1, w2, w3;
cubic_bspline_coefs(co.xy - tc, w0, w1, w2, w3);
/* TODO Optimize this part. I'm sure there is a smarter way to do that.
* Could do that when sampling? */
#define CLIP_CUBIC_SAMPLE(samp, size) \
(float(all(greaterThan(samp, vec2(-0.5)))) * float(all(lessThan(ivec2(samp), itex_size))))
ivec2 itex_size = textureSize(ima, 0).xy;
float fac;
fac = CLIP_CUBIC_SAMPLE(tc + vec2(-1.0, -1.0), itex_size) * w0.x * w0.y;
fac += CLIP_CUBIC_SAMPLE(tc + vec2(0.0, -1.0), itex_size) * w1.x * w0.y;
fac += CLIP_CUBIC_SAMPLE(tc + vec2(1.0, -1.0), itex_size) * w2.x * w0.y;
fac += CLIP_CUBIC_SAMPLE(tc + vec2(2.0, -1.0), itex_size) * w3.x * w0.y;
fac += CLIP_CUBIC_SAMPLE(tc + vec2(-1.0, 0.0), itex_size) * w0.x * w1.y;
fac += CLIP_CUBIC_SAMPLE(tc + vec2(0.0, 0.0), itex_size) * w1.x * w1.y;
fac += CLIP_CUBIC_SAMPLE(tc + vec2(1.0, 0.0), itex_size) * w2.x * w1.y;
fac += CLIP_CUBIC_SAMPLE(tc + vec2(2.0, 0.0), itex_size) * w3.x * w1.y;
fac += CLIP_CUBIC_SAMPLE(tc + vec2(-1.0, 1.0), itex_size) * w0.x * w2.y;
fac += CLIP_CUBIC_SAMPLE(tc + vec2(0.0, 1.0), itex_size) * w1.x * w2.y;
fac += CLIP_CUBIC_SAMPLE(tc + vec2(1.0, 1.0), itex_size) * w2.x * w2.y;
fac += CLIP_CUBIC_SAMPLE(tc + vec2(2.0, 1.0), itex_size) * w3.x * w2.y;
fac += CLIP_CUBIC_SAMPLE(tc + vec2(-1.0, 2.0), itex_size) * w0.x * w3.y;
fac += CLIP_CUBIC_SAMPLE(tc + vec2(0.0, 2.0), itex_size) * w1.x * w3.y;
fac += CLIP_CUBIC_SAMPLE(tc + vec2(1.0, 2.0), itex_size) * w2.x * w3.y;
fac += CLIP_CUBIC_SAMPLE(tc + vec2(2.0, 2.0), itex_size) * w3.x * w3.y;
#undef CLIP_CUBIC_SAMPLE
color = mix(vec4(0.0), icolor, fac);
alpha = color.a;
}
void tex_clip_smart(vec3 co, sampler2D ima, vec4 icolor, out vec4 color, out float alpha)
{
tex_clip_cubic(co, ima, icolor, color, alpha);
}
void node_tex_image_empty(vec3 co, out vec4 color, out float alpha)
{
color = vec4(0.0);

View File

@ -19,6 +19,8 @@
#include "../node_shader_util.h"
#include "GPU_draw.h"
/* **************** OUTPUT ******************** */
static bNodeSocketTemplate sh_node_tex_image_in[] = {
@ -57,31 +59,6 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat,
GPUNodeStack *in,
GPUNodeStack *out)
{
static const char *names[] = {
"node_tex_image_linear",
"node_tex_image_nearest",
"node_tex_image_cubic",
"node_tex_image_smart",
};
static const char *names_tiled[] = {
"node_tex_tile_linear",
"node_tex_tile_nearest",
"node_tex_tile_cubic",
"node_tex_tile_smart",
};
static const char *names_box[] = {
"tex_box_sample_linear",
"tex_box_sample_nearest",
"tex_box_sample_cubic",
"tex_box_sample_smart",
};
static const char *names_clip[] = {
"tex_clip_linear",
"tex_clip_nearest",
"tex_clip_cubic",
"tex_clip_smart",
};
Image *ima = (Image *)node->id;
NodeTexImage *tex = node->storage;
@ -91,26 +68,11 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat,
NodeTexImage *tex_original = node_original->storage;
ImageUser *iuser = &tex_original->iuser;
const char *gpu_node_name = (tex->projection == SHD_PROJ_BOX) ? names_box[tex->interpolation] :
names[tex->interpolation];
bool do_texco_extend = (tex->extension != SHD_IMAGE_EXTENSION_REPEAT);
const bool do_texco_clip = (tex->extension == SHD_IMAGE_EXTENSION_CLIP);
if (do_texco_extend && (tex->projection != SHD_PROJ_BOX) &&
ELEM(tex->interpolation, SHD_INTERP_CUBIC, SHD_INTERP_SMART)) {
gpu_node_name = "node_tex_image_cubic_extend";
/* We do it inside the sampling function */
do_texco_extend = false;
}
GPUNodeLink *norm, *col1, *col2, *col3, *input_coords, *gpu_image;
GPUNodeLink *vnor, *ob_mat, *blend;
GPUNodeLink **texco = &in[0].link;
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);
@ -118,108 +80,73 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat,
node_shader_gpu_tex_mapping(mat, node, in, out);
eGPUSamplerState sampler_state = GPU_SAMPLER_MAX;
eGPUSamplerState sampler_state = 0;
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;
sampler_state |= GPU_get_mipmap() ? GPU_SAMPLER_MIPMAP : 0;
}
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(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,
names_tiled[tex->interpolation],
in,
out,
GPU_image_tiled(mat, ima, iuser, sampler_state),
GPU_image_tiled_mapping(mat, ima, iuser));
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:
if (do_texco_clip) {
/* This seems redundant, but is required to ensure the texco link
* is not freed by GPU_link, as it is still needed for GPU_stack_link.
* Intermediate links like this can only be used once and are then
* freed immediately, but if we make it the output link of a set_rgb
* node it will be kept and can be used multiple times. */
GPU_link(mat, "set_rgb", *texco, texco);
GPU_link(mat, "set_rgb", *texco, &input_coords);
}
if (do_texco_extend) {
GPU_link(
mat, "point_texco_clamp", *texco, GPU_image(mat, ima, iuser, sampler_state), texco);
}
GPU_stack_link(
mat, node, gpu_node_name, in, out, GPU_image(mat, ima, iuser, sampler_state));
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:
vnor = GPU_builtin(GPU_WORLD_NORMAL);
ob_mat = GPU_builtin(GPU_OBJECT_MATRIX);
blend = GPU_uniform(&tex->projection_blend);
gpu_image = GPU_image(mat, ima, iuser, sampler_state);
}
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, &norm);
{
/* See SHD_PROJ_FLAT for explanation. */
GPU_link(mat, "set_rgb", *texco, texco);
GPU_link(mat, "set_rgb", *texco, &input_coords);
in[0].link = input_coords;
}
GPU_link(mat,
gpu_node_name,
*texco,
norm,
GPU_image(mat, ima, iuser, sampler_state),
&col1,
&col2,
&col3);
GPU_stack_link(
mat, node, "node_tex_image_box", in, out, norm, col1, col2, col3, gpu_image, blend);
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:
}
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);
if (do_texco_clip) {
/* See SHD_PROJ_FLAT for explanation. */
GPU_link(mat, "set_rgb", *texco, texco);
GPU_link(mat, "set_rgb", *texco, &input_coords);
}
if (do_texco_extend) {
GPU_link(
mat, "point_texco_clamp", *texco, GPU_image(mat, ima, iuser, sampler_state), texco);
}
GPU_stack_link(
mat, node, gpu_node_name, in, out, GPU_image(mat, ima, iuser, sampler_state));
GPU_stack_link(mat, node, gpu_node_name, in, out, gpu_image);
break;
case SHD_PROJ_TUBE:
}
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);
if (do_texco_clip) {
/* See SHD_PROJ_FLAT for explanation. */
GPU_link(mat, "set_rgb", *texco, texco);
GPU_link(mat, "set_rgb", *texco, &input_coords);
}
if (do_texco_extend) {
GPU_link(
mat, "point_texco_clamp", *texco, GPU_image(mat, ima, iuser, sampler_state), texco);
}
GPU_stack_link(
mat, node, gpu_node_name, in, out, GPU_image(mat, ima, iuser, sampler_state));
GPU_stack_link(mat, node, gpu_node_name, in, out, gpu_image);
break;
}
if (tex->projection != SHD_PROJ_BOX) {
if (do_texco_clip) {
gpu_node_name = names_clip[tex->interpolation];
in[0].link = input_coords;
GPU_stack_link(mat,
node,
gpu_node_name,
in,
out,
GPU_image(mat, ima, iuser, sampler_state),
out[0].link);
}
}
}