2012-03-01 10 views
12

Estoy intentando crear una geometría para representar la Tierra en OpenGL. Tengo lo que es más o menos una esfera (más cerca del geoide elíptico que la Tierra). Mapeo una textura de la superficie de la Tierra (probablemente sea una proyección Mercator o algo similar). Las coordenadas UV de la textura corresponden a la latitud y longitud de la geometría. Tengo dos problemas que no puedo resolver. Estoy usando OpenSceneGraph, pero creo que esta es una pregunta general de programación de OpenGL/3D.Problema de costura al mapear una textura en una esfera en OpenGL

  • Hay una costura de textura que es muy aparente. Estoy seguro de que esto ocurre porque no sé cómo mapear las coordenadas UV a XYZ donde ocurre la costura. Solo asocie los coords UV hasta el último vértice antes de envolverlos ... Necesitaría asignar dos coordenadas UV diferentes al mismo vértice XYZ para eliminar la costura. ¿Existe un truco comúnmente utilizado para evitar esto, o lo estoy haciendo mal?

  • Hay una loca distorsión swirly en los polos. Estoy adivinando esto porque mapeo un único punto UV en los polos (para la Tierra, uso [0.5,1] para el Polo Norte, y [0.5,0] para el Polo Sur). ¿Qué más harías? Puedo vivir con esto ... pero es muy notable en mallas de baja resolución.

He adjuntado una imagen para mostrar de lo que estoy hablando.

I suck at rendering Earth

+2

¿Puedes compartir cómo estás generando las coordenadas UV y qué método estás utilizando para texturizar el objeto? –

+0

Genérico OpenGL ES cómo versión: http://stackoverflow.com/questions/322111/any-one-know-of-an-opengl-es-example-of-an-interactive-globe-earth-for-the- iphon Problema relacionado: http://stackoverflow.com/questions/17488259/opengl-mapping-texture-to-sphere –

Respuesta

6

La forma más general, esto se maneja es el uso de un mapa cubo, no una textura 2D.

Sin embargo, si insiste en usar una textura 2D, debe crear un corte en la topología de su malla. La razón por la que obtienes esa línea longitudinal es porque tienes un vértice con una coordenada de textura de algo así como 0.9 o más, y su vértice vecino tiene una coordenada de textura de 0.0. Lo que realmente quieres es que el 0.9 uno colinda con una coordenada de 1.0 textura.

Hacer esto significa replicar la posición en una línea de la esfera. Entonces tiene la misma posición utilizada dos veces en sus datos. Uno está adjunto a una coordenada de textura de 1.0 y vecina a una coordenada de textura de 0.9. El otro tiene una coordenada de textura de 0.0, y vecinos un vértice con 0.1.

Topológicamente, debe tomar una porción longitudinal en su esfera.

+0

Eso apesta. Gracias por el aviso. Como un seguimiento rápido, ¿hay alguna posibilidad de que tenga recursos para implementar mapas de cubos para un esferoide? – Prismatic

+0

@Pris: los Cubemaps para las esferas son aptos. Puede usar directamente las posiciones de vértice (o normales) como coordenadas de textura en el mapa de cubos. El mapa de cubos en sí consiste en 6 cuadrantes dispuestos en un cubo. Las coordenadas de textura determinan una dirección desde el centro del cubo, la intersección con el cubo produce el texel. – datenwolf

2

Su enlace realmente me ha ayudado, furqan, gracias.
¿Por qué no pudiste resolverlo? Un punto donde tropecé fue que no sabía que se puede superar el intervalo [0,1] al calcular las coordenadas de la textura. Eso hace que sea mucho más fácil saltar de un lado de la textura al otro con OpenGL haciendo toda la interpolación y sin tener que calcular la posición exacta donde la textura realmente termina.

+0

Esto es muy útil. ¡Gracias! ¡Solución realmente simple y elegante! –

0

Como dijo Nicol Bolas, algunos triángulos tienen coordenadas UV que van desde ~ 0.9 a 0, por lo que la interpolación ensucia la textura alrededor de la costura. En mi código, he creado esta función para duplicar los vértices alrededor de la costura. Esto creará una línea nítida que divide esos vértices. Si tu textura solo tiene agua alrededor de la veta (el océano Pacífico?), Es posible que no notes esta línea. Espero eso ayude.

/** 
* 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; 
    } 

} 
0

También puede ir una manera sucia: interpolar posiciones X, Y entre shader vértice y shader fragmento y volver a calcular coordenadas de textura correcta en shader fragmento.Esto puede ser algo más lento, pero no implica duplicar vértices y es más simple, creo.

Por ejemplo:
de sombreado de vértices:

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

fragmento shader:

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

que tomó mucho tiempo para entender este problema muy molesto a cabo. Estoy programando con C# en Unity y no quería duplicar ningún vértice. (Podría causar problemas en el futuro con mi concepto) Así que fui con la idea del sombreador y funciona bastante bien. Aunque estoy seguro de que el código podría usar una optimización de alto rendimiento, tuve que averiguar cómo transferirlo a CG a partir de esto, pero funciona. Esto es en caso de que alguien más se encuentre con esta publicación, como lo hice yo, buscando una solución para el mismo problema.

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

Sugeriría usar una posición normal en lugar de la posición en caso de que la malla esférica no tenga un diámetro de 1 y origen de 0,0,0 – jjxtra

0

Un enfoque es igual que en la respuesta aceptada. En el código que genera el conjunto de vértices atributos que tendrá un código como el siguiente:

// 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.; 
    } 
} 

Si tiene triángulos que no son meridianos alineado también tendrá que glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);. También necesita usar glDrawArrays ya que los vértices con la misma posición tendrán diferentes coordenadas de textura.

Creo que la mejor manera de hacerlo es eliminar la raíz de todo mal, que es la interpolación de coords de textura en este caso. Como sabes básicamente todo sobre tu esfera/elipsoide, puedes calcular coords de texturas, normales, etc. en el sombreador de fragmentos en función de la posición. Esto significa que su código de CPU que genera atributos de vértice será mucho más simple y podrá usar el dibujo indexado nuevamente. Y no creo que este enfoque sea sucio. Está limpio.