Buracos transparentes curiosos renderizam artefato

8

Então, eu estou tentando implementar um terreno "suave" no meu mecanismo de bloco, dando a cada bloco de superfície um mapa de altura.

Basicamente, o que faço para gerar esses "mapas de altura" para cada bloco é gerar as alturas em intervalos de 0,25 ao longo da borda do bloco. Então, para construir os vértices do bloco, percorro as alturas e crio triângulos de altura para altura e coloco retângulos embaixo dos triângulos para criar algo assim: insira a descrição da imagem aqui

Por exemplo, para construir o lado X positivo de um bloco, eu faço:

                    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);
                    }

Não tenho certeza se este é um erro de precisão de ponto flutuante, mas quando o processo, recebo esses buracos curiosos nas bordas dos retângulos de base (sei que são buracos porque suas cores combinam com a cor atrás deles)

O que realmente me impressiona é que, em vez de uma linha inteira, são apenas pontos aparentemente aleatórios.

insira a descrição da imagem aqui

Uma visão de mundo em wireframe não revelou discrepâncias. (tudo o que fez foi atrasar o jogo) insira a descrição da imagem aqui

Um pequeno truque rápido que fiz foi estender o (i + 1) / 4 no código para (i + 1.01f) / 4, mas prefiro obter uma solução concreta do que algo assim.

Talvez seja algo no meu shader? Meu amostrador de textura é:

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

Desde já, obrigado!

sem título
fonte

Respostas:

15

No seu diagrama, parece que a geometria que você está construindo contém junções em T - lugares onde um vértice de um triângulo deve estar exatamente na aresta de outro triângulo (resultando em uma aresta encontrando outra na forma de "T"). Devido às limitações da aritmética de precisão finita, normalmente não é possível garantir que o vértice atenda perfeitamente à borda e você encontrará rachaduras microscópicas entre elas, que geralmente aparecem como falhas de pixel único porque um pixel individual pode cair na rachadura enquanto seus vizinhos não.

Para corrigi-lo, é necessário construir sua geometria de forma que não haja junções em T, dividindo cada aresta onde um vértice deve encontrá-lo. Este diagrama mostra um exemplo:

insira a descrição da imagem aqui

EDIT: O acima se aplica a junções T em geral. Para o seu caso específico, como Ilmari Karonen apontou nos comentários, você pode construir a geometria ainda melhor assim:

insira a descrição da imagem aqui

que também evita junções em T e tem menos triângulos no geral.

Nathan Reed
fonte
6
Uma maneira melhor de evitar junções em T seria fazer a triangulação assim . Dessa forma, você só precisa de dois triângulos por segmento e nenhum novo vértice interno. (Você poderia fazê-lo com ainda menos triângulos, se você queria, o mínimo é um mais o número de segmentos.)
Ilmari Karonen
@IlmariKaronen Obrigado, tomei a liberdade de adicionar seu diagrama à resposta. :)
Nathan Reed