Я пытаюсь нарисовать на экране четырехугольник с текстурой, чтобы пиксели и пиксели были идеально выровнены. Звучит довольно просто. Я рисую 2 треугольника (как TRIANGLE_LIST, так 6 вершин), используя эти шейдеры:
struct VSOutput
{
float4 position : SV_POSITION;
float2 uv : TEXCOORD0;
};
VSOutput VS_Draw(uint index : SV_VertexId)
{
uint vertexIndex = index % 6;
// compute face in [0,0]-[1,1] space
float2 vertex = 0;
switch (vertexIndex)
{
case 0: vertex = float2(0, 0); break;
case 1: vertex = float2(1, 0); break;
case 2: vertex = float2(0, 1); break;
case 3: vertex = float2(0, 1); break;
case 4: vertex = float2(1, 0); break;
case 5: vertex = float2(1, 1); break;
}
// compute uv
float2 uv = vertex;
// scale to size
vertex = vertex * (float2)outputSize;
vertex = vertex + topLeftPos;
// convert to screen space
VSOutput output;
output.position = float4(vertex / (float2)outputSize * float2(2.0f, -2.0f) + float2(-1.0f, 1.0f), 0, 1);
output.uv = uv;
return output;
}
float4 PS_Draw(VSOutput input) : SV_TARGET
{
uint2 pixelPos = (uint2)(input.uv * (float2)outputSize);
// output checker of 4x4
return (((pixelPos.x >> 2) & 1) ^ ((pixelPos.y >> 2) & 1) != 0) ? float4(0, 1, 1, 0) : float4(1, 1, 0, 0);
}
где outputSize и topLeftPos являются константами и выражаются в пиксельных единицах.
Теперь для outputSize = (102,12) и topLeftPos = (0,0) я получаю (что я ожидал):
ссылка на изображение (так как я не могу публиковать изображения)
Но для outputSize = (102,12) и topLeftPos = (0,0.5) я получаю: Выход для x = 0, y = 0.5
ссылка на изображение (так как я не могу публиковать изображения)
Как вы можете видеть, есть ультрафиолетовый разрыв, где соединяются 2 треугольника, и интерполяция ультрафиолета является неточной). Это в основном происходит (в x и y) только в позициях вокруг .5 (фактически ниже .49, он корректно привязывается к текселю 0 и выше .51, он корректно привязывается к текселю 1, но между ними я получаю этот артефакт).
Теперь для этой цели я нуждаюсь в этом, потому что важно иметь идеальное отображение пикселей. Может ли кто-нибудь просветить меня, почему это происходит?
Есть две вещи, которые вы должны рассмотреть, чтобы понять, что происходит:
Так что же происходит, когда topLeftPos=(0,0)
, значение input.uv * (float2)outputSize
всегда наполовину целое число, и оно последовательно округляется в меньшую сторону до ближайшего целого числа. Однако когда topLeftPos=(0,0.5)
, (input.uv * (float2)outputSize).y
всегда должно быть точно целым числом. Однако из-за непредсказуемых проблем точности с плавающей точкой оно иногда немного меньше, чем точное целое число, и в этом случае оно слишком сильно округляется. Здесь вы видите свои растянутые квадраты.
Поэтому, если вы хотите идеальное отображение, ваш исходный квадрат должен быть выровнен по границам пикселей, а не по центрам пикселей.