Compositor: Add sampling methods for full frame
Current sampling methods have off by one issues on full frame: - Bilinear sampling do not fully sample bottom and left image border, creating edges. - Single elem buffers are not sampled at all when they should be at least on the borders to smooth edges. - EWA filtering is partially implemented on `ReadBufferOperation`, it needs to be moved to `MemoryBuffer` on full frame. In order to not affect tiled implementation, this commit creates specific sampling methods for full frame needs. Reviewed By: jbakker Differential Revision: https://developer.blender.org/D12164
This commit is contained in:
parent
8f4730e66f
commit
a95e56b741
|
@ -405,12 +405,48 @@ void MemoryBuffer::addPixel(int x, int y, const float color[4])
|
|||
}
|
||||
}
|
||||
|
||||
static void read_ewa_elem(void *userdata, int x, int y, float result[4])
|
||||
{
|
||||
const MemoryBuffer *buffer = static_cast<const MemoryBuffer *>(userdata);
|
||||
buffer->read_elem_checked(x, y, result);
|
||||
}
|
||||
|
||||
void MemoryBuffer::read_elem_filtered(
|
||||
const float x, const float y, float dx[2], float dy[2], float *out) const
|
||||
{
|
||||
BLI_assert(this->m_datatype == DataType::Color);
|
||||
|
||||
const float deriv[2][2] = {{dx[0], dx[1]}, {dy[0], dy[1]}};
|
||||
|
||||
float inv_width = 1.0f / (float)this->getWidth(), inv_height = 1.0f / (float)this->getHeight();
|
||||
/* TODO(sergey): Render pipeline uses normalized coordinates and derivatives,
|
||||
* but compositor uses pixel space. For now let's just divide the values and
|
||||
* switch compositor to normalized space for EWA later.
|
||||
*/
|
||||
float uv_normal[2] = {get_relative_x(x) * inv_width, get_relative_y(y) * inv_height};
|
||||
float du_normal[2] = {deriv[0][0] * inv_width, deriv[0][1] * inv_height};
|
||||
float dv_normal[2] = {deriv[1][0] * inv_width, deriv[1][1] * inv_height};
|
||||
|
||||
BLI_ewa_filter(this->getWidth(),
|
||||
this->getHeight(),
|
||||
false,
|
||||
true,
|
||||
uv_normal,
|
||||
du_normal,
|
||||
dv_normal,
|
||||
read_ewa_elem,
|
||||
const_cast<MemoryBuffer *>(this),
|
||||
out);
|
||||
}
|
||||
|
||||
/* TODO(manzanilla): to be removed with tiled implementation. */
|
||||
static void read_ewa_pixel_sampled(void *userdata, int x, int y, float result[4])
|
||||
{
|
||||
MemoryBuffer *buffer = (MemoryBuffer *)userdata;
|
||||
buffer->read(result, x, y);
|
||||
}
|
||||
|
||||
/* TODO(manzanilla): to be removed with tiled implementation. */
|
||||
void MemoryBuffer::readEWA(float *result, const float uv[2], const float derivatives[2][2])
|
||||
{
|
||||
if (m_is_a_single_elem) {
|
||||
|
|
|
@ -191,23 +191,96 @@ class MemoryBuffer {
|
|||
|
||||
void read_elem(int x, int y, float *out) const
|
||||
{
|
||||
memcpy(out, get_elem(x, y), m_num_channels * sizeof(float));
|
||||
memcpy(out, get_elem(x, y), get_elem_bytes_len());
|
||||
}
|
||||
|
||||
void read_elem_checked(int x, int y, float *out) const
|
||||
{
|
||||
if (x < m_rect.xmin || x >= m_rect.xmax || y < m_rect.ymin || y >= m_rect.ymax) {
|
||||
clear_elem(out);
|
||||
}
|
||||
else {
|
||||
read_elem(x, y, out);
|
||||
}
|
||||
}
|
||||
|
||||
void read_elem_checked(float x, float y, float *out) const
|
||||
{
|
||||
if (x < m_rect.xmin || x >= m_rect.xmax || y < m_rect.ymin || y >= m_rect.ymax) {
|
||||
clear_elem(out);
|
||||
}
|
||||
else {
|
||||
read_elem(x, y, out);
|
||||
}
|
||||
}
|
||||
|
||||
void read_elem_bilinear(float x, float y, float *out) const
|
||||
{
|
||||
/* Only clear past +/-1 borders to be able to smooth edges. */
|
||||
if (x <= m_rect.xmin - 1.0f || x >= m_rect.xmax || y <= m_rect.ymin - 1.0f ||
|
||||
y >= m_rect.ymax) {
|
||||
clear_elem(out);
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_is_a_single_elem) {
|
||||
if (x >= m_rect.xmin && x < m_rect.xmax - 1.0f && y >= m_rect.ymin &&
|
||||
y < m_rect.ymax - 1.0f) {
|
||||
memcpy(out, m_buffer, get_elem_bytes_len());
|
||||
return;
|
||||
}
|
||||
|
||||
/* Do sampling at borders to smooth edges. */
|
||||
const float last_x = getWidth() - 1.0f;
|
||||
const float rel_x = get_relative_x(x);
|
||||
float single_x = 0.0f;
|
||||
if (rel_x < 0.0f) {
|
||||
single_x = rel_x;
|
||||
}
|
||||
else if (rel_x > last_x) {
|
||||
single_x = rel_x - last_x;
|
||||
}
|
||||
|
||||
const float last_y = getHeight() - 1.0f;
|
||||
const float rel_y = get_relative_y(y);
|
||||
float single_y = 0.0f;
|
||||
if (rel_y < 0.0f) {
|
||||
single_y = rel_y;
|
||||
}
|
||||
else if (rel_y > last_y) {
|
||||
single_y = rel_y - last_y;
|
||||
}
|
||||
|
||||
BLI_bilinear_interpolation_fl(m_buffer, out, 1, 1, m_num_channels, single_x, single_y);
|
||||
return;
|
||||
}
|
||||
|
||||
BLI_bilinear_interpolation_fl(m_buffer,
|
||||
out,
|
||||
getWidth(),
|
||||
getHeight(),
|
||||
m_num_channels,
|
||||
get_relative_x(x),
|
||||
get_relative_y(y));
|
||||
}
|
||||
|
||||
void read_elem_sampled(float x, float y, PixelSampler sampler, float *out) const
|
||||
{
|
||||
switch (sampler) {
|
||||
case PixelSampler::Nearest:
|
||||
this->read_elem(x, y, out);
|
||||
read_elem_checked(x, y, out);
|
||||
break;
|
||||
case PixelSampler::Bilinear:
|
||||
case PixelSampler::Bicubic:
|
||||
/* No bicubic. Current implementation produces fuzzy results. */
|
||||
this->readBilinear(out, x, y);
|
||||
read_elem_bilinear(x, y, out);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void read_elem_filtered(
|
||||
const float x, const float y, float dx[2], float dy[2], float *out) const;
|
||||
|
||||
/**
|
||||
* Get channel value at given coordinates.
|
||||
*/
|
||||
|
@ -403,6 +476,8 @@ class MemoryBuffer {
|
|||
y = y + m_rect.ymin;
|
||||
}
|
||||
|
||||
/* TODO(manzanilla): to be removed with tiled implementation. For applying #MemoryBufferExtend
|
||||
* use #wrap_pixel. */
|
||||
inline void read(float *result,
|
||||
int x,
|
||||
int y,
|
||||
|
@ -425,6 +500,7 @@ class MemoryBuffer {
|
|||
}
|
||||
}
|
||||
|
||||
/* TODO(manzanilla): to be removed with tiled implementation. */
|
||||
inline void readNoCheck(float *result,
|
||||
int x,
|
||||
int y,
|
||||
|
@ -582,6 +658,21 @@ class MemoryBuffer {
|
|||
return get_memory_width() * get_memory_height();
|
||||
}
|
||||
|
||||
void clear_elem(float *out) const
|
||||
{
|
||||
memset(out, 0, this->m_num_channels * sizeof(float));
|
||||
}
|
||||
|
||||
template<typename T> T get_relative_x(T x) const
|
||||
{
|
||||
return x - m_rect.xmin;
|
||||
}
|
||||
|
||||
template<typename T> T get_relative_y(T y) const
|
||||
{
|
||||
return y - m_rect.ymin;
|
||||
}
|
||||
|
||||
void copy_single_elem_from(const MemoryBuffer *src,
|
||||
int channel_offset,
|
||||
int elem_size,
|
||||
|
|
Loading…
Reference in New Issue