If a BC4 block has only two values, and the search radius is greater than zero, then the second value is always interpolated, and since the BC4/5 interpolation is done with more than 8-bits in hardware (see findings here) the resulting value is slightly off.
An example being: a block with values 126 and 127 and the default search radius of 3 will achieve a best_err of zero with endpoints 127 and 123 (with six interpolated values, MODE8 in the code). When interpolated as 8-bit, and with selectors of 0 and 2, this does correctly result in values of 126 and 127. But... since the hardware is using 14- or 16-bit interpolation then the resulting values are 126.43 and 127.0.
This is small, agreed, but when mixed with solid blocks of 126 we get block artefacts as the encoder flips between solid and multi-value blocks, breaking down to blocks of 126.0 and 126.43.
This came about because we have a normal map exported from 3D Coat with essentially a dimple noise texture on it. AFAIK it was worked on at 4k then exported at 2k, by which time the dimple texture end up being a few dots. Here's the result isolated with obvious block errors:
Ignoring there's clearly something amiss with the normal map, it does nicely highlight this issue. If this were BC3 alpha it wouldn't be affected, it's only BC4/5 with the extended interpolation bits.
The easy fix (which I've already tried) is to clamp the search radius to zero when there are only two values. I think, though, a better fix may be to interpolate with more range in bc4_block::get_block_values() so that it works for more than two values (which I'll try next).
If a BC4 block has only two values, and the search radius is greater than zero, then the second value is always interpolated, and since the BC4/5 interpolation is done with more than 8-bits in hardware (see findings here) the resulting value is slightly off.
If you start with the code here:
https://github.com/richgel999/bc7enc_rdo/blob/e6990bc11829c072d9f9e37296f3335072aab4e4/rgbcx.cpp#L2801
An example being: a block with values
126
and127
and the default search radius of3
will achieve abest_err
of zero with endpoints127
and123
(with six interpolated values,MODE8
in the code). When interpolated as 8-bit, and with selectors of0
and2
, this does correctly result in values of126
and127
. But... since the hardware is using 14- or 16-bit interpolation then the resulting values are126.43
and127.0
.This is small, agreed, but when mixed with solid blocks of
126
we get block artefacts as the encoder flips between solid and multi-value blocks, breaking down to blocks of126.0
and126.43
.This came about because we have a normal map exported from 3D Coat with essentially a dimple noise texture on it. AFAIK it was worked on at 4k then exported at 2k, by which time the dimple texture end up being a few dots. Here's the result isolated with obvious block errors:
Ignoring there's clearly something amiss with the normal map, it does nicely highlight this issue. If this were BC3 alpha it wouldn't be affected, it's only BC4/5 with the extended interpolation bits.
The easy fix (which I've already tried) is to clamp the search radius to zero when there are only two values. I think, though, a better fix may be to interpolate with more range in
bc4_block::get_block_values()
so that it works for more than two values (which I'll try next).