This repository has been archived on 2023-10-09. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
blender-archive/source/blender/blenkernel/BKE_image_wrappers.hh
Jeroen Bakker b0037c5c6b Texture Painting: Fix Seam Bleeding of Non-Manifold Sections of Mesh
Fix seam bleeding of non-manifold sections of the mesh, by copying pixels
that are covered by the brush stroke.

As manifold parts are already handled, the pixel copying solution can be
very straight forward.

* Pixels are copied from the same tile. So we don't need a mechanism that
  copies and merges pixels from other tiles.
* Pixels are copied from the closest pixel that is being painted on. We
  don't need to consider that that pixel can be in different areas of the
  tile.

When we copy a pixel, we find the closest pixel in UV space that is being
directly influenced by a paint brush. We also look for the second closest
pixel, which is still a neighbor from the closest pixel. We can mix both
pixels together and store it in the destination. A mix factor is calculated
using the closest non manifold edge as a guidance.

The result of this step is a list of copy and mix commands that can be
executed to fix the seam bleeding for non-manifold sections of the mesh.

| Destination | Source 1 | Source 2 | Mix factor |
| ----------- | -------- | -------- | ---------- |
| 1780,1811   | 1780,1810| 1779,1810| 0.000000   |
| 1781,1811   | 1781,1810| 1782,1811| 0.168627   |
| 1828,1811   | 1828,1810| 1827,1811| 0.156863   |
| 1829,1811   | 1829,1810| 1828,1810| 0.188235   |
| 1830,1811   | 1830,1810| 1829,1810| 0.188235   |
| 1831,1811   | 1831,1810| 1830,1810| 0.188235   |
| 1832,1811   | 1832,1810| 1831,1810| 0.188235   |
| 1833,1811   | 1832,1810| 1832,1810| 0.000000   |

In the end we go over this list mix the sources and store the result at
the destination.

```
tile_buffer[destination] = mix(tile_buffer[source_1],
                               tile_buffer[source_2],
                               mix_factor);
```

**Encoding**
When using a large textures or large seam margins this table can grow
large and reduce performance as data retrieval is slower, than the
operations it has to perform. To improve the performance we encode the
table so less data retrieval needs to be done.

* first `DeltaCopyPixelCommand` is delta encoded from
  `CopyPixelGroup#start_destination` and `start_source_1`. The others
  are delta encoded from the previous `DeltaCopyPixelCommand`.
* For performance reasons PixelCopyGroup#pixels are ordered from
  destination (left to right) for each row a new group would be created
  as the delta encoding most likely doesn't fit. When pixels cannot be
  delta encoded a new group will also be created.

**Compression rate**
When using Suzanne the compression rate is around 36% when using a seam
margin of 4 pixels. The compression rate may vary depending on seam
margin and model. For Suzanne the compression rate was around 36% for
various resolutions.

| Resolution | Margin | Decoded size | Encoded size | Compression |
| ---------- | ------ | ------------ | ------------ | ----------- |
| 2048x2048  | 4 px   | 353.052      | 128.101      | 36%         |
| 4096x4096  | 4 px   | 700.140      | 255.137      | 36%         |
| 8192x8192  | 4 px   | 1.419.320    | 513.802      | 36%         |
| 2048x2048  | 8 px   | 721.084      | 193.629      | 26%         |
| 4096x4096  | 8 px   | 1.444.968    | 388.110      | 26%         |

Pull Request: blender/blender#105336
2023-03-09 16:11:01 +01:00

91 lines
2.2 KiB
C++

/* SPDX-License-Identifier: GPL-2.0-or-later
* Copyright 2022 Blender Foundation. */
/** \file
* \ingroup imbuf
*/
#pragma once
#include "DNA_image_types.h"
#include "BLI_math.h"
#include "BLI_math_vector_types.hh"
#include "IMB_imbuf_types.h"
namespace blender::bke::image {
/** Type to use for UDIM tile numbers (1001). */
using TileNumber = int32_t;
struct ImageTileWrapper {
ImageTile *image_tile;
ImageTileWrapper(ImageTile *image_tile) : image_tile(image_tile)
{
}
TileNumber get_tile_number() const
{
return image_tile->tile_number;
}
int2 get_tile_offset() const
{
return int2(get_tile_x_offset(), get_tile_y_offset());
}
int get_tile_x_offset() const
{
TileNumber tile_number = get_tile_number();
return (tile_number - 1001) % 10;
}
int get_tile_y_offset() const
{
TileNumber tile_number = get_tile_number();
return (tile_number - 1001) / 10;
}
};
template<typename T, int Channels = 4> struct ImageBufferAccessor {
static_assert(std::is_same_v<T, int> || std::is_same_v<T, float4>);
ImBuf &image_buffer;
ImageBufferAccessor(ImBuf &image_buffer) : image_buffer(image_buffer)
{
}
float4 read_pixel(const int2 coordinate)
{
if constexpr ((std::is_same_v<T, float4>)) {
int offset = (coordinate.y * image_buffer.x + coordinate.x) * Channels;
return float4(&image_buffer.rect_float[offset]);
}
if constexpr ((std::is_same_v<T, int>)) {
int offset = (coordinate.y * image_buffer.x + coordinate.x);
float4 result;
rgba_uchar_to_float(result,
static_cast<uchar *>(static_cast<void *>(&image_buffer.rect[offset])));
return result;
}
return float4();
}
void write_pixel(const int2 coordinate, float4 new_value)
{
if constexpr ((std::is_same_v<T, float>)) {
int offset = (coordinate.y * image_buffer.x + coordinate.x) * Channels;
copy_v4_v4(&image_buffer.rect_float[offset], new_value);
}
if constexpr ((std::is_same_v<T, int>)) {
int offset = (coordinate.y * image_buffer.x + coordinate.x);
rgba_float_to_uchar(static_cast<uchar *>(static_cast<void *>(&image_buffer.rect[offset])),
new_value);
}
}
};
} // namespace blender::bke::image