Compositor: Unify Texture node between CPU and GPU
The CPU and GPU implementations of the compositor Texture node sample the texture at different locations, producing differences. This is primarly an issue with half pixel offsets to evaluate the texture at the center of the pixels. The CPU compositor only adds the offsets if interpolation is disabled, regardless if the texture is of type image or not. Further the offset was wrong, since it was not a half pixel offset, but a full pixel one. The offsets should always be added, since it won't make a difference in the non interpolated case, and it should always be added especially if interpolation is enabled. A comment in the old code mentions something about artifacts, but it is possible that this is a consequence of the wrong offset that was used. This patch always adds a half pixel offset, following the GPU code. Pull Request: https://projects.blender.org/blender/blender/pulls/118377
This commit is contained in:
@@ -134,51 +134,34 @@ void TextureBaseOperation::update_memory_buffer_partial(MemoryBuffer *output,
|
||||
const rcti &area,
|
||||
Span<MemoryBuffer *> inputs)
|
||||
{
|
||||
const int op_width = this->get_width();
|
||||
const int op_height = this->get_height();
|
||||
const float center_x = op_width / 2;
|
||||
const float center_y = op_height / 2;
|
||||
TexResult tex_result = {0};
|
||||
float vec[3];
|
||||
const int thread_id = WorkScheduler::current_thread_id();
|
||||
const float3 offset = inputs[0]->get_elem(0, 0);
|
||||
const float3 scale = inputs[1]->get_elem(0, 0);
|
||||
const int2 size = int2(this->get_width(), this->get_height());
|
||||
for (BuffersIterator<float> it = output->iterate_with(inputs, area); !it.is_end(); ++it) {
|
||||
const float *tex_offset = it.in(0);
|
||||
const float *tex_size = it.in(1);
|
||||
float u = (it.x - center_x) / op_width * 2;
|
||||
float v = (it.y - center_y) / op_height * 2;
|
||||
/* Compute the coordinates in the [-1, 1] range and add 0.5 to evaluate the texture at the
|
||||
* center of pixels in case it was interpolated. */
|
||||
const float2 pixel_coordinates = ((float2(it.x, it.y) + 0.5f) / float2(size)) * 2.0f - 1.0f;
|
||||
/* Note that it is expected that the offset is scaled by the scale. */
|
||||
const float3 coordinates = (float3(pixel_coordinates, 0.0f) + offset) * scale;
|
||||
|
||||
/* When no interpolation/filtering happens in multitex() force nearest interpolation.
|
||||
* We do it here because (a) we can't easily say multitex() that we want nearest
|
||||
* interpolation and (b) in such configuration multitex() simply floor's the value
|
||||
* which often produces artifacts.
|
||||
*/
|
||||
if (texture_ != nullptr && (texture_->imaflag & TEX_INTERPOL) == 0) {
|
||||
u += 0.5f / center_x;
|
||||
v += 0.5f / center_y;
|
||||
}
|
||||
|
||||
vec[0] = tex_size[0] * (u + tex_offset[0]);
|
||||
vec[1] = tex_size[1] * (v + tex_offset[1]);
|
||||
vec[2] = tex_size[2] * tex_offset[2];
|
||||
|
||||
const int retval = multitex_ext(texture_,
|
||||
vec,
|
||||
nullptr,
|
||||
nullptr,
|
||||
0,
|
||||
&tex_result,
|
||||
thread_id,
|
||||
pool_,
|
||||
scene_color_manage_,
|
||||
false);
|
||||
|
||||
it.out[3] = tex_result.talpha ? tex_result.trgba[3] : tex_result.tin;
|
||||
if (retval & TEX_RGB) {
|
||||
copy_v3_v3(it.out, tex_result.trgba);
|
||||
}
|
||||
else {
|
||||
it.out[0] = it.out[1] = it.out[2] = it.out[3];
|
||||
TexResult texture_result;
|
||||
const int result_type = multitex_ext(texture_,
|
||||
coordinates,
|
||||
nullptr,
|
||||
nullptr,
|
||||
0,
|
||||
&texture_result,
|
||||
WorkScheduler::current_thread_id(),
|
||||
pool_,
|
||||
scene_color_manage_,
|
||||
false);
|
||||
|
||||
float4 color = float4(texture_result.trgba);
|
||||
color.w = texture_result.talpha ? color.w : texture_result.tin;
|
||||
if (!(result_type & TEX_RGB)) {
|
||||
copy_v3_fl(color, color.w);
|
||||
}
|
||||
copy_v4_v4(it.out, color);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user