2022-02-10 23:07:11 +01:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
* Copyright 2022 Blender Foundation. */
|
2022-01-28 08:37:12 +01:00
|
|
|
|
|
|
|
/** \file
|
2022-04-04 14:00:33 +02:00
|
|
|
* \ingroup imbuf
|
2022-01-28 08:37:12 +01:00
|
|
|
*/
|
|
|
|
|
|
|
|
#pragma once
|
|
|
|
|
|
|
|
#include "DNA_image_types.h"
|
|
|
|
|
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: https://projects.blender.org/blender/blender/pulls/105336
2023-03-09 16:11:01 +01:00
|
|
|
#include "BLI_math.h"
|
2023-01-04 00:14:55 +01:00
|
|
|
#include "BLI_math_vector_types.hh"
|
2022-04-04 14:00:33 +02:00
|
|
|
|
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: https://projects.blender.org/blender/blender/pulls/105336
2023-03-09 16:11:01 +01:00
|
|
|
#include "IMB_imbuf_types.h"
|
|
|
|
|
2022-04-04 14:00:33 +02:00
|
|
|
namespace blender::bke::image {
|
|
|
|
|
2022-12-21 08:42:19 +01:00
|
|
|
/** Type to use for UDIM tile numbers (1001). */
|
|
|
|
using TileNumber = int32_t;
|
|
|
|
|
2022-01-28 08:37:12 +01:00
|
|
|
struct ImageTileWrapper {
|
|
|
|
ImageTile *image_tile;
|
2023-03-29 16:50:54 +02:00
|
|
|
ImageTileWrapper(ImageTile *image_tile) : image_tile(image_tile) {}
|
2022-01-28 08:37:12 +01:00
|
|
|
|
2022-12-21 08:42:19 +01:00
|
|
|
TileNumber get_tile_number() const
|
2022-01-28 08:37:12 +01:00
|
|
|
{
|
|
|
|
return image_tile->tile_number;
|
|
|
|
}
|
|
|
|
|
2022-04-04 14:00:33 +02:00
|
|
|
int2 get_tile_offset() const
|
|
|
|
{
|
|
|
|
return int2(get_tile_x_offset(), get_tile_y_offset());
|
|
|
|
}
|
|
|
|
|
2022-01-28 08:37:12 +01:00
|
|
|
int get_tile_x_offset() const
|
|
|
|
{
|
2022-12-21 08:42:19 +01:00
|
|
|
TileNumber tile_number = get_tile_number();
|
2022-01-28 08:37:12 +01:00
|
|
|
return (tile_number - 1001) % 10;
|
|
|
|
}
|
|
|
|
|
|
|
|
int get_tile_y_offset() const
|
|
|
|
{
|
2022-12-21 08:42:19 +01:00
|
|
|
TileNumber tile_number = get_tile_number();
|
2022-01-28 08:37:12 +01:00
|
|
|
return (tile_number - 1001) / 10;
|
|
|
|
}
|
|
|
|
};
|
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: https://projects.blender.org/blender/blender/pulls/105336
2023-03-09 16:11:01 +01:00
|
|
|
|
|
|
|
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;
|
|
|
|
|
2023-03-29 16:50:54 +02:00
|
|
|
ImageBufferAccessor(ImBuf &image_buffer) : image_buffer(image_buffer) {}
|
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: https://projects.blender.org/blender/blender/pulls/105336
2023-03-09 16:11:01 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2022-04-04 14:00:33 +02:00
|
|
|
} // namespace blender::bke::image
|