Artefactos de render de curiosos agujeros transparentes

8

Así que estoy tratando de implementar un terreno "liso" en mi motor de bloques dando a cada bloque de superficie un mapa de altura.

Básicamente, lo que hago para generar estos "mapas de altura" para cada bloque es generar las alturas a intervalos de 0.25 a lo largo del borde del bloque. Luego, para construir los vértices del bloque, recorro las alturas y creo triángulos de altura a altura y pongo rectángulos debajo de los triángulos para crear algo como esto: ingrese la descripción de la imagen aquí

Por ejemplo, para construir el lado positivo X de un bloque, hago:

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

No estoy seguro de si se trata de un error de precisión de coma flotante, pero cuando lo renderizo, obtengo estos curiosos agujeros en los bordes de los rectángulos de base (sé que son agujeros porque sus colores coinciden con el color detrás de ellos)

Lo que realmente me desconcierta es que, en lugar de una línea completa, son solo puntos aparentemente aleatorios.

ingrese la descripción de la imagen aquí

Una vista de estructura metálica del mundo no reveló ninguna discrepancia. (todo lo que hizo fue retrasar el juego) ingrese la descripción de la imagen aquí

Un pequeño truco rápido que hice fue extender el (i + 1) / 4 en el código a (i + 1.01f) / 4, pero prefiero obtener una solución concreta que algo así.

Tal vez es algo en mi sombreador? Mi muestra de textura es:

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

¡Gracias por adelantado!

intitulado
fuente

Respuestas:

15

Según su diagrama, parece que la geometría que está construyendo contiene uniones en T, lugares donde se supone que un vértice de un triángulo se encuentra exactamente en el borde de otro triángulo (lo que da como resultado que un borde se encuentre con otro en forma de "T"). Debido a las limitaciones de la aritmética de precisión finita, generalmente no se puede garantizar que el vértice se ajuste perfectamente al borde y encontrará grietas microscópicas entre ellas, que a menudo aparecen como fallas de un solo píxel porque un píxel individual puede caer en la grieta mientras que sus vecinos no.

Para solucionarlo, debe construir su geometría de tal manera que no haya uniones en T, dividiendo cada borde donde se supone que un vértice se encuentra con él. Este diagrama muestra un ejemplo:

ingrese la descripción de la imagen aquí

EDITAR: Lo anterior se aplica a las uniones en T en general. Para su caso específico, como Ilmari Karonen señaló en los comentarios, puede construir la geometría aún mejor así:

ingrese la descripción de la imagen aquí

que también evita las uniones en T y tiene menos triángulos en general.

Nathan Reed
fuente
66
Una mejor manera de evitar las uniones en T sería hacer la triangulación de esta manera . De esa manera, solo necesita dos triángulos por segmento y no nuevos vértices interiores. (Si lo desea, puede hacerlo con aún menos triángulos; el mínimo es uno más el número de segmentos).
Ilmari Karonen
@IlmariKaronen Gracias, me tomé la libertad de agregar su diagrama a la respuesta. :)
Nathan Reed