Любопытные прозрачные дыры рендеринга артефакт

8

Поэтому я пытаюсь реализовать "гладкую" местность в моем движке блоков, давая каждому блоку поверхности карту высот.

По сути, для создания этих «карт высот» для каждого блока я создаю высоты с интервалами 0,25 по краю блока. Затем, чтобы построить вершины блока, я перебираю высоту и создаю треугольники от высоты до высоты и помещаю прямоугольники под треугольниками, чтобы создать что-то вроде этого: введите описание изображения здесь

Например, чтобы построить X положительную сторону блока, я делаю:

                    Vector2[] uv = BlockUtil.UVMappings[(byte)side]; // uv[0] = top left

                    float height;
                    float nextHeight;
                    float min;

                    float tex_len = 1f / EngineGlobals.TEXTURE_ATLAS_SIDE;

                    for (int i = 0; i < 4; i++)
                    {
                        height = (float)b.Heights[4, i] / 255f;

                        nextHeight = (float)b.Heights[4, i + 1] / 255f;

                        min = Math.Min(height, nextHeight);

                        //create the triangles at the top
                        if (nextHeight > height)
                        {
                            int offset = ch.vertexMap.Count;

                            AddVertex(ch, 0, 0, uv[0] + new Vector2(tex_len * (i / 4f), (1-height)*tex_len), blockPos, new Vector3(1f, height, i / 4f), tr, isliquid);
                            AddVertex(ch, 0, 0, uv[0] + new Vector2(tex_len * ((i + 1) / 4f), 0), blockPos, new Vector3(1f, height, (i + 1) / 4f), tr, isliquid);
                            AddVertex(ch, 0, 0, uv[0] + new Vector2(tex_len * ((i + 1) / 4f), 0), blockPos, new Vector3(1f, nextHeight, (i + 1) / 4f), tr, isliquid);

                            AddTriIndices(offset, 0, 1, 2);
                        }
                        else if (nextHeight < height)
                        {
                            int offset = ch.vertexMap.Count;

                            AddVertex(ch, 0, 0, uv[0] + new Vector2(tex_len * (i / 4f), (1-height)*tex_len), blockPos, new Vector3(1f, height, i / 4f), tr, isliquid);
                            AddVertex(ch, 0, 0, uv[0] + new Vector2(tex_len * (i / 4f), (1-nextHeight)*tex_len), blockPos, new Vector3(1f, nextHeight, i / 4f), tr, isliquid);
                            AddVertex(ch, 0, 0, uv[0] + new Vector2(tex_len * ((i + 1) / 4f), (1 - nextHeight) * tex_len), blockPos, new Vector3(1f, nextHeight, (i + 1) / 4f), tr, isliquid);

                            AddTriIndices(offset, 0, 1, 2);
                        }
                        // else: heights are equal; ignore

                        // create the base rectangle
                        AddVertex(ch, 0, 0, uv[0] + new Vector2(tex_len * (float)i / 4f + tex_len / 4f, (1 - min) * tex_len), blockPos, new Vector3(1, min, (float)(i + 1) / 4f), tr, isliquid);
                        AddVertex(ch, 0, 1, uv[0] + new Vector2(tex_len * (float)i/4f, (1 - min) * tex_len), blockPos, new Vector3(1, min, (float)i / 4f), tr, isliquid);
                        AddVertex(ch, 0, 2, uv[0] + new Vector2(tex_len * (float)i / 4f + tex_len / 4f, tex_len), blockPos, new Vector3(1, 0, (float)(i + 1) / 4f), tr, isliquid);
                        AddVertex(ch, 0, 3, uv[0] + new Vector2(tex_len * (float)i / 4f, tex_len), blockPos, new Vector3(1, 0, (float)i / 4f), tr, isliquid);

                        AddIndices(ch, 0, 1, 2, 2, 1, 3, isliquid);
                    }

Я не уверен, является ли это ошибкой точности с плавающей точкой, но когда я рендерил ее, я получаю эти любопытные дыры на границах базовых прямоугольников (я знаю, что это дыры, потому что их цвета соответствуют цвету позади них)

Что меня действительно отталкивает, так это то, что вместо целой строки это просто случайные точки.

введите описание изображения здесь

Каркасный взгляд на мир не выявил никаких расхождений. (все, что он сделал, это сделало игру задержкой) введите описание изображения здесь

Быстрый маленький взлом, который я сделал, заключался в расширении (i + 1) / 4 в коде до (i + 1.01f) / 4, но я бы предпочел получить конкретное решение, чем что-то подобное.

Возможно, это что-то в моем шейдере? Мой образец текстуры:

Texture TextureAtlas;
sampler TextureSampler = sampler_state
{
        texture = <TextureAtlas>;
    magfilter = POINT;
    minfilter = POINT;
    mipfilter = POINT;
    AddressU = WRAP;
    AddressV = WRAP;
};

Заранее спасибо!

без названия
источник

Ответы:

15

Из вашей диаграммы, похоже, что геометрия, которую вы строите, содержит T-соединения - места, где вершина одного треугольника должна лежать точно на краю другого треугольника (в результате чего одно ребро встречается с другим в форме "T"). Из-за ограничений арифметики конечной точности, вершина, как правило, не может гарантировать идеальное совпадение с краем, и вы обнаружите микроскопические трещины между ними, которые часто проявляются как однопиксельные сбои, потому что отдельный пиксель может попасть в трещину в то время как его соседи нет.

Чтобы исправить это, вы должны построить свою геометрию таким образом, чтобы не было Т-образных переходов, разбивая каждое ребро там, где должна встречаться вершина. Эта диаграмма показывает пример:

введите описание изображения здесь

РЕДАКТИРОВАТЬ: Вышесказанное относится к Т-образным переходам в целом. Для вашего конкретного случая, как отметил Илмари Каронен в комментариях, вы можете построить геометрию еще лучше, как это:

введите описание изображения здесь

который также избегает Т-образных переходов и имеет меньше треугольников в целом.

Натан Рид
источник
6
Лучший способ избежать Т-образных переходов - сделать триангуляцию следующим образом . Таким образом, вам нужно всего два треугольника на сегмент, а не новые внутренние вершины. (Вы можете сделать это с еще меньшим количеством треугольников, если хотите, минимум - один плюс количество сегментов.)
Ильмари Каронен
@IlmariKaronen Спасибо, я позволил себе добавить свою диаграмму в ответ. :)
Натан Рид