Проблема со швом при отображении текстуры на сферу в OpenGL - PullRequest
12 голосов
/ 01 марта 2012

Я пытаюсь создать геометрию для представления Земли в OpenGL.У меня есть более или менее сфера (хотя ближе к эллиптическому геоиду, чем Земля).Я отображаю текстуру земной поверхности (это, вероятно, проекция Меркатора или что-то подобное).UV-координаты текстуры соответствуют широте и долготе геометрии.У меня есть две проблемы, которые я не могу решить.Я использую OpenSceneGraph, но думаю, что это общий вопрос программирования OpenGL / 3D.

  • Есть текстурный шов, который очень очевиден.Я уверен, что это происходит, потому что я не знаю, как отобразить координаты UV в XYZ, где происходит шов.Я только сопоставляю координаты UV до последней вершины, прежде чем оборачиваться ... Вам нужно отобразить две разные координаты UV в одну и ту же вершину XYZ, чтобы устранить шов.Есть ли обычно используемый прием, чтобы обойти это, или я просто делаю это неправильно?

  • На полюсах происходит сумасшедшее искажение.Я предполагаю это, потому что я отображаю одну точку УФ на полюсах (для Земли я использую [0,5,1] для Северного полюса и [0,5,0] для Южного полюса).Что бы ты сделал, хотя?Я могу с этим смириться ... но это особенно заметно в сетках с более низким разрешением.

Я приложил изображение, чтобы показать, о чем я говорю.

I suck at rendering Earth

Ответы [ 6 ]

6 голосов
/ 01 марта 2012

Обычно это делается с помощью карты куба , а не 2D-текстуры.

Однако, если вы настаиваете на использовании 2D-текстуры, вы должны создать разрыв в топологии вашей сетки. Причина, по которой вы получаете эту продольную линию, заключается в том, что у вас есть одна вершина с координатой текстуры примерно 0,9 или около того, а соседняя вершина имеет координату текстуры 0,0. Что вы действительно хотите, так это чтобы 0,9 соседствовало с текстурной координатой 1,0.

Выполнение этого означает копирование позиции на одну линию сферы. Таким образом, у вас одна и та же позиция, использованная дважды в ваших данных. Один из них привязан к текстурной координате 1.0 и соседствует с текстурной координатой 0.9. Другой имеет координату текстуры 0.0 и граничит с вершиной 0.1.

Топологически, вам нужно сделать продольный разрез по вашей сфере.

2 голосов
/ 19 января 2013

Твоя ссылка действительно помогла мне, фуркан, спасибо.
Почему ты не мог понять это? Точка, в которой я споткнулся, заключалась в том, что я не знал, что вы можете превысить интервал [0,1] при расчете координат текстуры. Это значительно облегчает переход с одной стороны текстуры на другую, поскольку OpenGL выполняет всю интерполяцию и не требует точного расчета положения, где текстура фактически заканчивается.

1 голос
/ 09 ноября 2013

Потребовалось много времени, чтобы разобраться в этом крайне раздражающем вопросе. Я программирую на C # в Unity, и я не хотел дублировать вершины. (Это может привести к будущим проблемам с моей концепцией). Так что я пошел с идеей шейдера, и она работает довольно хорошо. Хотя я уверен, что код мог бы использовать некоторую оптимизацию для работы в тяжелых условиях, мне пришлось выяснить, как перенести его на CG, но это работает. Это на тот случай, если кто-то другой, как и я, перебирает этот пост в поисках решения той же проблемы.

    Shader "Custom/isoshader" {
Properties {
        decal ("Base (RGB)", 2D) = "white" {}
    }
    SubShader {
        Pass {
        Fog { Mode Off }

        CGPROGRAM

        #pragma vertex vert
        #pragma fragment frag
        #define PI 3.141592653589793238462643383279

        sampler2D decal;

        struct appdata {
            float4 vertex : POSITION;
            float4 texcoord : TEXCOORD0;
        };

        struct v2f {
            float4 pos : SV_POSITION;
            float4 tex : TEXCOORD0;
            float3 pass_xy_position : TEXCOORD1;
        };

        v2f vert(appdata v){
            v2f  o;
            o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
            o.pass_xy_position = v.vertex.xyz;
            o.tex = v.texcoord;
            return o;
        }

        float4 frag(v2f i) : COLOR {
            float3 tc = i.tex;
            tc.x = (PI + atan2(i.pass_xy_position.x, i.pass_xy_position.z)) / (2 * PI);
            float4 color = tex2D(decal, tc);
            return color;
        }

        ENDCG
    }
}

}

0 голосов
/ 24 января 2016

Один подход как в принятом ответе.В коде, генерирующем массив атрибутов вершин, у вас будет такой код:

// FOR EVERY TRIANGLE
const float threshold = 0.7;
if(tcoords_1.s > threshold || tcoords_2.s > threshold || tcoords_3.s > threshold)
{
    if(tcoords_1.s < 1. - threshold)
    {
        tcoords_1.s += 1.;
    }
    if(tcoords_2.s < 1. - threshold)
    {
        tcoords_2.s += 1.;
    }
    if(tcoords_3.s < 1. - threshold)
    {
        tcoords_3.s += 1.;
    }
}

Если у вас есть треугольники, которые не выровнены по меридиану, вам также понадобится glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);.Вам также необходимо использовать glDrawArrays, так как вершины с одинаковым положением будут иметь разные координаты текстуры.

Я думаю, что лучший способ - это устранить корень всего зла, в данном случае это интерполяция координат текстуры.,Поскольку вы в основном знаете все о своей сфере / эллипсоиде, вы можете вычислять координаты текстуры, нормали и т. Д. В фрагментном шейдере на основе положения.Это означает, что код вашего процессора, генерирующий атрибуты вершин, будет намного проще, и вы сможете снова использовать индексированное рисование.И я не думаю, что такой подход грязен.Это чисто.

0 голосов
/ 16 марта 2013

Вы также можете пойти по грязному пути: интерполировать позиции X, Y между вершинным шейдером и фрагментным шейдером и пересчитать правильную координату текстуры в фрагментном шейдере. Это может быть несколько медленнее, но это не включает дублирование вершин, и это проще, я думаю.

Например:
вершинный шейдер:

#version 150 core
uniform mat4 projM;
uniform mat4 viewM;
uniform mat4 modelM;
in vec4 in_Position;
in vec2 in_TextureCoord;
out vec2 pass_TextureCoord;
out vec2 pass_xy_position;
void main(void) {
    gl_Position = projM * viewM * modelM * in_Position;
    pass_xy_position = in_Position.xy; // 2d spinning interpolates good!
    pass_TextureCoord = in_TextureCoord;
}

фрагментный шейдер:

#version 150 core
uniform sampler2D texture1;
in vec2 pass_xy_position;
in vec2 pass_TextureCoord;
out vec4 out_Color;

#define PI 3.141592653589793238462643383279

void main(void) {
    vec2 tc = pass_TextureCoord;
    tc.x = (PI + atan(pass_xy_position.y, pass_xy_position.x)) / (2 * PI); // calculate angle and map it to 0..1
    out_Color = texture(texture1, tc);
}
0 голосов
/ 03 февраля 2013

Как сказала Никол Болас, некоторые треугольники имеют координаты УФ, идущие от ~ 0,9 до 0, поэтому интерполяция портит текстуру вокруг шва. В моем коде я создал эту функцию для дублирования вершин вокруг шва. Это создаст четкую линию, разделяющую эти вершины. Если ваша текстура имеет только воду вокруг шва (Тихий океан?), Вы можете не заметить эту линию. Надеюсь, это поможет.

/**
 *  After spherical projection, some triangles have vertices with
 *  UV coordinates that are far away (0 to 1), because the Azimuth
 *  at 2*pi = 0. Interpolating between 0 to 1 creates artifacts
 *  around that seam (the whole texture is thinly repeated at
 *  the triangles around the seam).
 *  This function duplicates vertices around the seam to avoid
 *  these artifacts.
 */
void PlatonicSolid::SubdivideAzimuthSeam() {
    if (m_texCoord == NULL) {
        ApplySphericalProjection();
    }

    // to take note of the trianges in the seam
    int facesSeam[m_numFaces];

    // check all triangles, looking for triangles with vertices
    // separated ~2π. First count.
    int nSeam = 0;
    for (int i=0;i < m_numFaces; ++i) {
        // check the 3 vertices of the triangle
        int a = m_faces[3*i];
        int b = m_faces[3*i+1];
        int c = m_faces[3*i+2];
        // just check the seam in the azimuth
        float ua = m_texCoord[2*a];
        float ub = m_texCoord[2*b];
        float uc = m_texCoord[2*c];
        if (fabsf(ua-ub)>0.5f || fabsf(ua-uc)>0.5f || fabsf(ub-uc)>0.5f) {
            //test::printValue("Face: ", i, "\n");
            facesSeam[nSeam] = i;
            ++nSeam;
        }
    }

    if (nSeam==0) {
        // no changes
        return;
    }

    // reserve more memory
    int nVertex = m_numVertices;
    m_numVertices += nSeam;
    m_vertices = (float*)realloc((void*)m_vertices, 3*m_numVertices*sizeof(float));
    m_texCoord = (float*)realloc((void*)m_texCoord, 2*m_numVertices*sizeof(float));

    // now duplicate vertices in the seam
    // (the number of triangles/faces is the same)
    for (int i=0; i < nSeam; ++i, ++nVertex) {
        int t = facesSeam[i]; // triangle index
        // check the 3 vertices of the triangle
        int a = m_faces[3*t];
        int b = m_faces[3*t+1];
        int c = m_faces[3*t+2];
        // just check the seam in the azimuth
        float u_ab = fabsf(m_texCoord[2*a] - m_texCoord[2*b]);
        float u_ac = fabsf(m_texCoord[2*a] - m_texCoord[2*c]);
        float u_bc = fabsf(m_texCoord[2*b] - m_texCoord[2*c]);
        // select the vertex further away from the other 2
        int f = 2;
        if (u_ab >= 0.5f && u_ac >= 0.5f) {
            c = a;
            f = 0;
        } else if (u_ab >= 0.5f && u_bc >= 0.5f) {
            c = b;
            f = 1;
        }

        m_vertices[3*nVertex] = m_vertices[3*c];      // x
        m_vertices[3*nVertex+1] = m_vertices[3*c+1];  // y
        m_vertices[3*nVertex+2] = m_vertices[3*c+2];  // z
        // repeat u from texcoord
        m_texCoord[2*nVertex] = 1.0f - m_texCoord[2*c];
        m_texCoord[2*nVertex+1] = m_texCoord[2*c+1];
        // change this face so all the vertices have close UV
        m_faces[3*t+f] = nVertex;
    }

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...