Images: add mirror extension type
This adds a new mirror image extension type for shaders and geometry nodes (next to the existing repeat, extend and clip options). See D16432 for a more detailed explanation of `wrap_mirror`. This also adds a new sampler flag `GPU_SAMPLER_MIRROR_REPEAT`. It acts as a modifier to `GPU_SAMPLER_REPEAT`, so any `REPEAT` flag must be set for the `MIRROR` flag to have an effect. Differential Revision: https://developer.blender.org/D16432
This commit is contained in:
parent
8c14992db2
commit
a501a2dbff
Notes:
blender-bot
2023-02-14 18:10:37 +01:00
Referenced by commit 8afcecdf1f
, Cycles: update Intel Graphics compiler to 101.4032 on Windows
Referenced by issue blender/blender-addons#103359, Images as Planes: texture extension setting does not work
|
@ -952,6 +952,9 @@ void CUDADevice::tex_alloc(device_texture &mem)
|
|||
case EXTENSION_CLIP:
|
||||
address_mode = CU_TR_ADDRESS_MODE_BORDER;
|
||||
break;
|
||||
case EXTENSION_MIRROR:
|
||||
address_mode = CU_TR_ADDRESS_MODE_MIRROR;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
|
|
|
@ -909,6 +909,9 @@ void HIPDevice::tex_alloc(device_texture &mem)
|
|||
* because it's unsupported in HIP. */
|
||||
address_mode = hipAddressModeClamp;
|
||||
break;
|
||||
case EXTENSION_MIRROR:
|
||||
address_mode = hipAddressModeMirror;
|
||||
break;
|
||||
default:
|
||||
assert(0);
|
||||
break;
|
||||
|
|
|
@ -856,7 +856,7 @@ void MetalDevice::tex_alloc(device_texture &mem)
|
|||
/* sampler_index maps into the GPU's constant 'metal_samplers' array */
|
||||
uint64_t sampler_index = mem.info.extension;
|
||||
if (mem.info.interpolation != INTERPOLATION_CLOSEST) {
|
||||
sampler_index += 3;
|
||||
sampler_index += 4;
|
||||
}
|
||||
|
||||
/* Image Texture Storage */
|
||||
|
|
|
@ -202,6 +202,14 @@ template<typename TexT, typename OutT = float4> struct TextureInterpolator {
|
|||
return clamp(x, 0, width - 1);
|
||||
}
|
||||
|
||||
static ccl_always_inline int wrap_mirror(int x, int width)
|
||||
{
|
||||
const int m = abs(x + (x < 0)) % (2 * width);
|
||||
if (m >= width)
|
||||
return 2 * width - m - 1;
|
||||
return m;
|
||||
}
|
||||
|
||||
/* ******** 2D interpolation ******** */
|
||||
|
||||
static ccl_always_inline OutT interp_closest(const TextureInfo &info, float x, float y)
|
||||
|
@ -226,6 +234,10 @@ template<typename TexT, typename OutT = float4> struct TextureInterpolator {
|
|||
ix = wrap_clamp(ix, width);
|
||||
iy = wrap_clamp(iy, height);
|
||||
break;
|
||||
case EXTENSION_MIRROR:
|
||||
ix = wrap_mirror(ix, width);
|
||||
iy = wrap_mirror(iy, height);
|
||||
break;
|
||||
default:
|
||||
kernel_assert(0);
|
||||
return zero();
|
||||
|
@ -268,6 +280,12 @@ template<typename TexT, typename OutT = float4> struct TextureInterpolator {
|
|||
niy = wrap_clamp(iy + 1, height);
|
||||
iy = wrap_clamp(iy, height);
|
||||
break;
|
||||
case EXTENSION_MIRROR:
|
||||
nix = wrap_mirror(ix + 1, width);
|
||||
ix = wrap_mirror(ix, width);
|
||||
niy = wrap_mirror(iy + 1, height);
|
||||
iy = wrap_mirror(iy, height);
|
||||
break;
|
||||
default:
|
||||
kernel_assert(0);
|
||||
return zero();
|
||||
|
@ -331,6 +349,17 @@ template<typename TexT, typename OutT = float4> struct TextureInterpolator {
|
|||
nniy = wrap_clamp(iy + 2, height);
|
||||
iy = wrap_clamp(iy, height);
|
||||
break;
|
||||
case EXTENSION_MIRROR:
|
||||
pix = wrap_mirror(ix - 1, width);
|
||||
nix = wrap_mirror(ix + 1, width);
|
||||
nnix = wrap_mirror(ix + 2, width);
|
||||
ix = wrap_mirror(ix, width);
|
||||
|
||||
piy = wrap_mirror(iy - 1, height);
|
||||
niy = wrap_mirror(iy + 1, height);
|
||||
nniy = wrap_mirror(iy + 2, height);
|
||||
iy = wrap_mirror(iy, height);
|
||||
break;
|
||||
default:
|
||||
kernel_assert(0);
|
||||
return zero();
|
||||
|
@ -403,6 +432,11 @@ template<typename TexT, typename OutT = float4> struct TextureInterpolator {
|
|||
iy = wrap_clamp(iy, height);
|
||||
iz = wrap_clamp(iz, depth);
|
||||
break;
|
||||
case EXTENSION_MIRROR:
|
||||
ix = wrap_mirror(ix, width);
|
||||
iy = wrap_mirror(iy, height);
|
||||
iz = wrap_mirror(iz, depth);
|
||||
break;
|
||||
default:
|
||||
kernel_assert(0);
|
||||
return zero();
|
||||
|
@ -480,6 +514,16 @@ template<typename TexT, typename OutT = float4> struct TextureInterpolator {
|
|||
niz = wrap_clamp(iz + 1, depth);
|
||||
iz = wrap_clamp(iz, depth);
|
||||
break;
|
||||
case EXTENSION_MIRROR:
|
||||
nix = wrap_mirror(ix + 1, width);
|
||||
ix = wrap_mirror(ix, width);
|
||||
|
||||
niy = wrap_mirror(iy + 1, height);
|
||||
iy = wrap_mirror(iy, height);
|
||||
|
||||
niz = wrap_mirror(iz + 1, depth);
|
||||
iz = wrap_mirror(iz, depth);
|
||||
break;
|
||||
default:
|
||||
kernel_assert(0);
|
||||
return zero();
|
||||
|
@ -595,6 +639,22 @@ template<typename TexT, typename OutT = float4> struct TextureInterpolator {
|
|||
nniz = wrap_clamp(iz + 2, depth);
|
||||
iz = wrap_clamp(iz, depth);
|
||||
break;
|
||||
case EXTENSION_MIRROR:
|
||||
pix = wrap_mirror(ix - 1, width);
|
||||
nix = wrap_mirror(ix + 1, width);
|
||||
nnix = wrap_mirror(ix + 2, width);
|
||||
ix = wrap_mirror(ix, width);
|
||||
|
||||
piy = wrap_mirror(iy - 1, height);
|
||||
niy = wrap_mirror(iy + 1, height);
|
||||
nniy = wrap_mirror(iy + 2, height);
|
||||
iy = wrap_mirror(iy, height);
|
||||
|
||||
piz = wrap_mirror(iz - 1, depth);
|
||||
niz = wrap_mirror(iz + 1, depth);
|
||||
nniz = wrap_mirror(iz + 2, depth);
|
||||
iz = wrap_mirror(iz, depth);
|
||||
break;
|
||||
default:
|
||||
kernel_assert(0);
|
||||
return zero();
|
||||
|
|
|
@ -301,10 +301,12 @@ enum SamplerType {
|
|||
SamplerFilterNearest_AddressRepeat,
|
||||
SamplerFilterNearest_AddressClampEdge,
|
||||
SamplerFilterNearest_AddressClampZero,
|
||||
SamplerFilterNearest_AddressMirroredRepeat,
|
||||
|
||||
SamplerFilterLinear_AddressRepeat,
|
||||
SamplerFilterLinear_AddressClampEdge,
|
||||
SamplerFilterLinear_AddressClampZero,
|
||||
SamplerFilterLinear_AddressMirroredRepeat,
|
||||
|
||||
SamplerCount
|
||||
};
|
||||
|
@ -313,7 +315,9 @@ constant constexpr array<sampler, SamplerCount> metal_samplers = {
|
|||
sampler(address::repeat, filter::nearest),
|
||||
sampler(address::clamp_to_edge, filter::nearest),
|
||||
sampler(address::clamp_to_zero, filter::nearest),
|
||||
sampler(address::mirrored_repeat, filter::nearest),
|
||||
sampler(address::repeat, filter::linear),
|
||||
sampler(address::clamp_to_edge, filter::linear),
|
||||
sampler(address::clamp_to_zero, filter::linear),
|
||||
sampler(address::mirrored_repeat, filter::linear),
|
||||
};
|
||||
|
|
|
@ -47,9 +47,11 @@ class MetalKernelContext {
|
|||
case 0: return texture_array[tid].tex.sample(sampler(address::repeat, filter::nearest), coords);
|
||||
case 1: return texture_array[tid].tex.sample(sampler(address::clamp_to_edge, filter::nearest), coords);
|
||||
case 2: return texture_array[tid].tex.sample(sampler(address::clamp_to_zero, filter::nearest), coords);
|
||||
case 3: return texture_array[tid].tex.sample(sampler(address::repeat, filter::linear), coords);
|
||||
case 4: return texture_array[tid].tex.sample(sampler(address::clamp_to_edge, filter::linear), coords);
|
||||
case 5: return texture_array[tid].tex.sample(sampler(address::clamp_to_zero, filter::linear), coords);
|
||||
case 3: return texture_array[tid].tex.sample(sampler(address::mirrored_repeat, filter::nearest), coords);
|
||||
case 4: return texture_array[tid].tex.sample(sampler(address::repeat, filter::linear), coords);
|
||||
case 5: return texture_array[tid].tex.sample(sampler(address::clamp_to_edge, filter::linear), coords);
|
||||
case 6: return texture_array[tid].tex.sample(sampler(address::clamp_to_zero, filter::linear), coords);
|
||||
case 7: return texture_array[tid].tex.sample(sampler(address::mirrored_repeat, filter::linear), coords);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -24,6 +24,14 @@ ccl_device_inline int svm_image_texture_wrap_clamp(int x, int width)
|
|||
return clamp(x, 0, width - 1);
|
||||
}
|
||||
|
||||
ccl_device_inline int svm_image_texture_wrap_mirror(int x, int width)
|
||||
{
|
||||
const int m = abs(x + (x < 0)) % (2 * width);
|
||||
if (m >= width)
|
||||
return 2 * width - m - 1;
|
||||
return m;
|
||||
}
|
||||
|
||||
ccl_device_inline float4 svm_image_texture_read(const TextureInfo &info, int x, int y, int z)
|
||||
{
|
||||
const int data_offset = x + info.width * y + info.width * info.height * z;
|
||||
|
@ -85,6 +93,10 @@ ccl_device_inline float4 svm_image_texture_read_2d(int id, int x, int y)
|
|||
x = svm_image_texture_wrap_clamp(x, info.width);
|
||||
y = svm_image_texture_wrap_clamp(y, info.height);
|
||||
}
|
||||
else if (info.extension == EXTENSION_MIRROR) {
|
||||
x = svm_image_texture_wrap_mirror(x, info.width);
|
||||
y = svm_image_texture_wrap_mirror(y, info.height);
|
||||
}
|
||||
else {
|
||||
if (x < 0 || x >= info.width || y < 0 || y >= info.height) {
|
||||
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
@ -109,6 +121,11 @@ ccl_device_inline float4 svm_image_texture_read_3d(int id, int x, int y, int z)
|
|||
y = svm_image_texture_wrap_clamp(y, info.height);
|
||||
z = svm_image_texture_wrap_clamp(z, info.depth);
|
||||
}
|
||||
else if (info.extension == EXTENSION_MIRROR) {
|
||||
x = svm_image_texture_wrap_mirror(x, info.width);
|
||||
y = svm_image_texture_wrap_mirror(y, info.height);
|
||||
z = svm_image_texture_wrap_mirror(z, info.depth);
|
||||
}
|
||||
else {
|
||||
if (x < 0 || x >= info.width || y < 0 || y >= info.height || z < 0 || z >= info.depth) {
|
||||
return make_float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
|
|
|
@ -226,6 +226,7 @@ NODE_DEFINE(ImageTextureNode)
|
|||
extension_enum.insert("periodic", EXTENSION_REPEAT);
|
||||
extension_enum.insert("clamp", EXTENSION_EXTEND);
|
||||
extension_enum.insert("black", EXTENSION_CLIP);
|
||||
extension_enum.insert("mirror", EXTENSION_MIRROR);
|
||||
SOCKET_ENUM(extension, "Extension", extension_enum, EXTENSION_REPEAT);
|
||||
|
||||
static NodeEnum projection_enum;
|
||||
|
|
|
@ -65,6 +65,8 @@ typedef enum ExtensionType {
|
|||
EXTENSION_EXTEND = 1,
|
||||
/* Clip to image size and set exterior pixels as transparent. */
|
||||
EXTENSION_CLIP = 2,
|
||||
/* Repeatedly flip the image horizontally and vertically. */
|
||||
EXTENSION_MIRROR = 3,
|
||||
|
||||
EXTENSION_NUM_TYPES,
|
||||
} ExtensionType;
|
||||
|
|
|
@ -97,11 +97,13 @@ BLI_INLINE void workbench_material_get_image(
|
|||
case SH_NODE_TEX_IMAGE: {
|
||||
const NodeTexImage *storage = static_cast<NodeTexImage *>(node->storage);
|
||||
const bool use_filter = (storage->interpolation != SHD_INTERP_CLOSEST);
|
||||
const bool use_repeat = (storage->extension == SHD_IMAGE_EXTENSION_REPEAT);
|
||||
const bool use_mirror = (storage->extension == SHD_IMAGE_EXTENSION_MIRROR);
|
||||
const bool use_repeat = use_mirror || (storage->extension == SHD_IMAGE_EXTENSION_REPEAT);
|
||||
const bool use_clip = (storage->extension == SHD_IMAGE_EXTENSION_CLIP);
|
||||
SET_FLAG_FROM_TEST(*r_sampler, use_filter, GPU_SAMPLER_FILTER);
|
||||
SET_FLAG_FROM_TEST(*r_sampler, use_repeat, GPU_SAMPLER_REPEAT);
|
||||
SET_FLAG_FROM_TEST(*r_sampler, use_clip, GPU_SAMPLER_CLAMP_BORDER);
|
||||
SET_FLAG_FROM_TEST(*r_sampler, use_mirror, GPU_SAMPLER_MIRROR_REPEAT);
|
||||
break;
|
||||
}
|
||||
case SH_NODE_TEX_ENVIRONMENT: {
|
||||
|
|
|
@ -35,7 +35,8 @@ typedef enum eGPUSamplerState {
|
|||
GPU_SAMPLER_CLAMP_BORDER = (1 << 5), /* Clamp to border color instead of border texel. */
|
||||
GPU_SAMPLER_COMPARE = (1 << 6),
|
||||
GPU_SAMPLER_ANISO = (1 << 7),
|
||||
GPU_SAMPLER_ICON = (1 << 8),
|
||||
GPU_SAMPLER_MIRROR_REPEAT = (1 << 8), /* Requires any REPEAT flag to be set. */
|
||||
GPU_SAMPLER_ICON = (1 << 9),
|
||||
|
||||
GPU_SAMPLER_REPEAT = (GPU_SAMPLER_REPEAT_S | GPU_SAMPLER_REPEAT_T | GPU_SAMPLER_REPEAT_R),
|
||||
} eGPUSamplerState;
|
||||
|
|
|
@ -1595,14 +1595,17 @@ id<MTLSamplerState> MTLContext::generate_sampler_from_state(MTLSamplerState samp
|
|||
MTLSamplerAddressMode clamp_type = (sampler_state.state & GPU_SAMPLER_CLAMP_BORDER) ?
|
||||
MTLSamplerAddressModeClampToBorderColor :
|
||||
MTLSamplerAddressModeClampToEdge;
|
||||
MTLSamplerAddressMode repeat_type = (sampler_state.state & GPU_SAMPLER_MIRROR_REPEAT) ?
|
||||
MTLSamplerAddressModeMirrorRepeat :
|
||||
MTLSamplerAddressModeRepeat;
|
||||
descriptor.rAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_R) ?
|
||||
MTLSamplerAddressModeRepeat :
|
||||
repeat_type :
|
||||
clamp_type;
|
||||
descriptor.sAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_S) ?
|
||||
MTLSamplerAddressModeRepeat :
|
||||
repeat_type :
|
||||
clamp_type;
|
||||
descriptor.tAddressMode = (sampler_state.state & GPU_SAMPLER_REPEAT_T) ?
|
||||
MTLSamplerAddressModeRepeat :
|
||||
repeat_type :
|
||||
clamp_type;
|
||||
descriptor.borderColor = MTLSamplerBorderColorTransparentBlack;
|
||||
descriptor.minFilter = (sampler_state.state & GPU_SAMPLER_FILTER) ?
|
||||
|
|
|
@ -548,12 +548,13 @@ GLuint GLTexture::samplers_[GPU_SAMPLER_MAX] = {0};
|
|||
void GLTexture::samplers_init()
|
||||
{
|
||||
glGenSamplers(GPU_SAMPLER_MAX, samplers_);
|
||||
for (int i = 0; i <= GPU_SAMPLER_ICON - 1; i++) {
|
||||
for (int i = 0; i < GPU_SAMPLER_ICON; i++) {
|
||||
eGPUSamplerState state = static_cast<eGPUSamplerState>(i);
|
||||
GLenum clamp_type = (state & GPU_SAMPLER_CLAMP_BORDER) ? GL_CLAMP_TO_BORDER : GL_CLAMP_TO_EDGE;
|
||||
GLenum wrap_s = (state & GPU_SAMPLER_REPEAT_S) ? GL_REPEAT : clamp_type;
|
||||
GLenum wrap_t = (state & GPU_SAMPLER_REPEAT_T) ? GL_REPEAT : clamp_type;
|
||||
GLenum wrap_r = (state & GPU_SAMPLER_REPEAT_R) ? GL_REPEAT : clamp_type;
|
||||
GLenum repeat_type = (state & GPU_SAMPLER_MIRROR_REPEAT) ? GL_MIRRORED_REPEAT : GL_REPEAT;
|
||||
GLenum wrap_s = (state & GPU_SAMPLER_REPEAT_S) ? repeat_type : clamp_type;
|
||||
GLenum wrap_t = (state & GPU_SAMPLER_REPEAT_T) ? repeat_type : clamp_type;
|
||||
GLenum wrap_r = (state & GPU_SAMPLER_REPEAT_R) ? repeat_type : clamp_type;
|
||||
GLenum mag_filter = (state & GPU_SAMPLER_FILTER) ? GL_LINEAR : GL_NEAREST;
|
||||
GLenum min_filter = (state & GPU_SAMPLER_FILTER) ?
|
||||
((state & GPU_SAMPLER_MIPMAP) ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR) :
|
||||
|
@ -577,7 +578,7 @@ void GLTexture::samplers_init()
|
|||
|
||||
char sampler_name[128] = "\0\0";
|
||||
SNPRINTF(sampler_name,
|
||||
"%s%s%s%s%s%s%s%s%s%s",
|
||||
"%s%s%s%s%s%s%s%s%s%s%s",
|
||||
(state == GPU_SAMPLER_DEFAULT) ? "_default" : "",
|
||||
(state & GPU_SAMPLER_FILTER) ? "_filter" : "",
|
||||
(state & GPU_SAMPLER_MIPMAP) ? "_mipmap" : "",
|
||||
|
@ -585,6 +586,7 @@ void GLTexture::samplers_init()
|
|||
(state & GPU_SAMPLER_REPEAT_S) ? "S" : "",
|
||||
(state & GPU_SAMPLER_REPEAT_T) ? "T" : "",
|
||||
(state & GPU_SAMPLER_REPEAT_R) ? "R" : "",
|
||||
(state & GPU_SAMPLER_MIRROR_REPEAT) ? "-mirror" : "",
|
||||
(state & GPU_SAMPLER_CLAMP_BORDER) ? "_clamp_border" : "",
|
||||
(state & GPU_SAMPLER_COMPARE) ? "_compare" : "",
|
||||
(state & GPU_SAMPLER_ANISO) ? "_aniso" : "");
|
||||
|
@ -612,7 +614,7 @@ void GLTexture::samplers_update()
|
|||
|
||||
float aniso_filter = min_ff(max_anisotropy, U.anisotropic_filter);
|
||||
|
||||
for (int i = 0; i <= GPU_SAMPLER_ICON - 1; i++) {
|
||||
for (int i = 0; i < GPU_SAMPLER_ICON; i++) {
|
||||
eGPUSamplerState state = static_cast<eGPUSamplerState>(i);
|
||||
if ((state & GPU_SAMPLER_ANISO) && (state & GPU_SAMPLER_MIPMAP)) {
|
||||
glSamplerParameterf(samplers_[i], GL_TEXTURE_MAX_ANISOTROPY_EXT, aniso_filter);
|
||||
|
|
|
@ -1754,6 +1754,7 @@ enum {
|
|||
#define SHD_IMAGE_EXTENSION_REPEAT 0
|
||||
#define SHD_IMAGE_EXTENSION_EXTEND 1
|
||||
#define SHD_IMAGE_EXTENSION_CLIP 2
|
||||
#define SHD_IMAGE_EXTENSION_MIRROR 3
|
||||
|
||||
/* image texture */
|
||||
#define SHD_PROJ_FLAT 0
|
||||
|
|
|
@ -4690,6 +4690,30 @@ static const EnumPropertyItem node_subsurface_method_items[] = {
|
|||
"automatically adjusted to match color textures"},
|
||||
{0, NULL, 0, NULL, NULL}};
|
||||
|
||||
static const EnumPropertyItem prop_image_extension[] = {
|
||||
{SHD_IMAGE_EXTENSION_REPEAT,
|
||||
"REPEAT",
|
||||
0,
|
||||
"Repeat",
|
||||
"Cause the image to repeat horizontally and vertically"},
|
||||
{SHD_IMAGE_EXTENSION_EXTEND,
|
||||
"EXTEND",
|
||||
0,
|
||||
"Extend",
|
||||
"Extend by repeating edge pixels of the image"},
|
||||
{SHD_IMAGE_EXTENSION_CLIP,
|
||||
"CLIP",
|
||||
0,
|
||||
"Clip",
|
||||
"Clip to image size and set exterior pixels as transparent"},
|
||||
{SHD_IMAGE_EXTENSION_MIRROR,
|
||||
"MIRROR",
|
||||
0,
|
||||
"Mirror",
|
||||
"Repeatedly flip the image horizontally and vertically"},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
/* -- Common nodes ---------------------------------------------------------- */
|
||||
|
||||
static void def_group_input(StructRNA *UNUSED(srna))
|
||||
|
@ -5431,25 +5455,6 @@ static void def_sh_tex_image(StructRNA *srna)
|
|||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
static const EnumPropertyItem prop_image_extension[] = {
|
||||
{SHD_IMAGE_EXTENSION_REPEAT,
|
||||
"REPEAT",
|
||||
0,
|
||||
"Repeat",
|
||||
"Cause the image to repeat horizontally and vertically"},
|
||||
{SHD_IMAGE_EXTENSION_EXTEND,
|
||||
"EXTEND",
|
||||
0,
|
||||
"Extend",
|
||||
"Extend by repeating edge pixels of the image"},
|
||||
{SHD_IMAGE_EXTENSION_CLIP,
|
||||
"CLIP",
|
||||
0,
|
||||
"Clip",
|
||||
"Clip to image size and set exterior pixels as transparent"},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
PropertyRNA *prop;
|
||||
|
||||
prop = RNA_def_property(srna, "image", PROP_POINTER, PROP_NONE);
|
||||
|
@ -5516,25 +5521,6 @@ static void def_geo_image_texture(StructRNA *srna)
|
|||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
static const EnumPropertyItem prop_image_extension[] = {
|
||||
{SHD_IMAGE_EXTENSION_REPEAT,
|
||||
"REPEAT",
|
||||
0,
|
||||
"Repeat",
|
||||
"Cause the image to repeat horizontally and vertically"},
|
||||
{SHD_IMAGE_EXTENSION_EXTEND,
|
||||
"EXTEND",
|
||||
0,
|
||||
"Extend",
|
||||
"Extend by repeating edge pixels of the image"},
|
||||
{SHD_IMAGE_EXTENSION_CLIP,
|
||||
"CLIP",
|
||||
0,
|
||||
"Clip",
|
||||
"Clip to image size and set exterior pixels as transparent"},
|
||||
{0, NULL, 0, NULL, NULL},
|
||||
};
|
||||
|
||||
PropertyRNA *prop;
|
||||
|
||||
RNA_def_struct_sdna_from(srna, "NodeGeometryImageTexture", "storage");
|
||||
|
|
|
@ -114,6 +114,15 @@ class ImageFieldsFunction : public fn::MultiFunction {
|
|||
return std::clamp(x, 0, width - 1);
|
||||
}
|
||||
|
||||
static int wrap_mirror(const int x, const int width)
|
||||
{
|
||||
const int m = std::abs(x + (x < 0)) % (2 * width);
|
||||
if (m >= width) {
|
||||
return 2 * width - m - 1;
|
||||
}
|
||||
return m;
|
||||
}
|
||||
|
||||
static float4 image_pixel_lookup(const ImBuf &ibuf, const int px, const int py)
|
||||
{
|
||||
if (px < 0 || py < 0 || px >= ibuf.x || py >= ibuf.y) {
|
||||
|
@ -173,6 +182,17 @@ class ImageFieldsFunction : public fn::MultiFunction {
|
|||
piy = wrap_clamp(piy, height);
|
||||
break;
|
||||
}
|
||||
case SHD_IMAGE_EXTENSION_MIRROR: {
|
||||
ppix = wrap_mirror(pix - 1, width);
|
||||
ppiy = wrap_mirror(piy - 1, height);
|
||||
nix = wrap_mirror(pix + 1, width);
|
||||
niy = wrap_mirror(piy + 1, height);
|
||||
nnix = wrap_mirror(pix + 2, width);
|
||||
nniy = wrap_mirror(piy + 2, height);
|
||||
pix = wrap_mirror(pix, width);
|
||||
piy = wrap_mirror(piy, height);
|
||||
break;
|
||||
}
|
||||
default:
|
||||
return float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
@ -233,6 +253,12 @@ class ImageFieldsFunction : public fn::MultiFunction {
|
|||
piy = wrap_clamp(piy, height);
|
||||
break;
|
||||
}
|
||||
case SHD_IMAGE_EXTENSION_MIRROR:
|
||||
nix = wrap_mirror(pix + 1, width);
|
||||
niy = wrap_mirror(piy + 1, height);
|
||||
pix = wrap_mirror(pix, width);
|
||||
piy = wrap_mirror(piy, height);
|
||||
break;
|
||||
default:
|
||||
case SHD_IMAGE_EXTENSION_REPEAT:
|
||||
pix = wrap_periodic(pix, width);
|
||||
|
@ -282,6 +308,11 @@ class ImageFieldsFunction : public fn::MultiFunction {
|
|||
iy = wrap_clamp(iy, height);
|
||||
return image_pixel_lookup(ibuf, ix, iy);
|
||||
}
|
||||
case SHD_IMAGE_EXTENSION_MIRROR: {
|
||||
ix = wrap_mirror(ix, width);
|
||||
iy = wrap_mirror(iy, height);
|
||||
return image_pixel_lookup(ibuf, ix, iy);
|
||||
}
|
||||
default:
|
||||
return float4(0.0f, 0.0f, 0.0f, 0.0f);
|
||||
}
|
||||
|
|
|
@ -61,6 +61,9 @@ static int node_shader_gpu_tex_image(GPUMaterial *mat,
|
|||
case SHD_IMAGE_EXTENSION_CLIP:
|
||||
sampler_state |= GPU_SAMPLER_CLAMP_BORDER;
|
||||
break;
|
||||
case SHD_IMAGE_EXTENSION_MIRROR:
|
||||
sampler_state |= GPU_SAMPLER_REPEAT | GPU_SAMPLER_MIRROR_REPEAT;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue