2012-03-01 43 views
12

OpenGLで地球を表すためにジオメトリを作成しようとしています。私は、地球がある楕円ジオイドに近いほど球体が多いです。私は地球の表面のテクスチャをマッピングします(おそらく、それはメルフェルタ投影法または同様のものです)。テクスチャのUV座標は、ジオメトリの緯度と経度に対応します。私は解決できない2つの問題があります。私はOpenSceneGraphを使用していますが、これは一般的なOpenGL/3Dプログラミングの質問だと思います。OpenGLの球にテクスチャをマッピングする際のSeamの問題

  • 非常に明白なテクスチャシームがあります。これは、シームが発生するXYZにUV座標をマップする方法がわからないために起こります。私は周りを包み込む前に最後の頂点までUV座標をマップするだけです...継ぎ目をなくすために2つの異なるUV座標を同じXYZ頂点にマッピングする必要があります。これを回避するためによく使われるトリックがありますか、それとも間違っていますか?

  • 極で狂った渦巻き歪みが発生しています。私はこれを推測しています。なぜなら、私は極で1つのUVポイントをマップするからです(地球では、北極は[0.5,1]、南極は[0.5,0])。あなたは他に何をしますか?私はこれで生き生きとすることができます...しかし、解像度の低いメッシュでは非常に目立ちます。

私が話していることを示すためにイメージを添付しました。

I suck at rendering Earth

+2

UV座標の生成方法や、オブジェクトのテクスチャに使用している方法を共有できますか? –

+0

汎用OpenGL ESのバージョンアップ方法:http://stackoverflow.com/questions/322111/anyone-know-of-an-opengl-es-example-of-an-interactive-globe-earth-for-the- iphon関連する問題:http://stackoverflow.com/questions/17488259/opengl-mapping-texture-to-sphere –

答えて

6

これは処理される一般的な方法は、キューブマップなく、2Dテクスチャを使用することによるものです。

ただし、2Dテクスチャの使用を強くお勧めする場合は、メッシュのトポロジにブレークを作成する必要があります。その縦線を得る理由は、0.9程度のテクスチャ座標を持つ頂点が1つあり、その隣接頂点のテクスチャ座標が0.0であるためです。あなたが本当に望むのは、0.9のものが1.0のテクスチャ座標に隣接しているということです。

これは、球の1行下に位置を複製することを意味します。したがって、データ内で同じ位置が2回使用されます。 1つはテクスチャ座標1.0に関連付けられ、テクスチャ座標は0.9に近接します。もう一方はテクスチャ座標が0.0で、頂点には0.1が隣接しています。

トポロジー的には、球面を縦にスライスする必要があります。

+0

それは吸う。ヘッドアップをありがとう。迅速なフォローアップとして、回転楕円面のキューブマップを実装するためのリソースがあれば教えてください。 – Prismatic

+0

@Pris:球のためのキューブマップはtirivialです。テクスチャ座標として頂点位置(または法線)をキューブマップに直接使用することができます。キューブマップ自体は、立方体状に配置された6つの四角形で構成されています。テクスチャ座標はキューブの中心からの方向を決定し、キューブ自体との交差点はテクセルを生成する。 – datenwolf

2

あなたのリンクは本当に助けてくれました。
なぜあなたはそれを理解できませんでしたか?私が遭遇したポイントは、テクスチャ座標を計算するときにあなたが[0,1]間隔を超えることができるかどうか分からなかったことです。これは、OpenGLがすべての補間を行い、テクスチャが実際に終了する正確な位置を計算する必要なしに、テクスチャの一方の側から他方の側へジャンプする方がずっと簡単です。

+0

これは非常に便利です。ありがとう!本当にシンプル&エレガントなソリューション! –

0

Nicol Bolas氏によると、いくつかの三角形はUV座標が〜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; 
    } 

} 
0

また汚い方法で行くことができます:頂点シェーダとフラグメントシェーダの間での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); 
} 
1

それはこの非常に厄介な問題を把握するために長い時間がかかりました。私はUnityでC#を使ってプログラミングしていますが、頂点を複製する必要はありませんでした。 (私のコンセプトの将来の問題を引き起こすだろう)だから私はシェイダーのアイデアを出して、それはかなりうまく動きます。私はコードが重視最適化を使用することができると確信していますが、私はそれを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

球のメッシュの直径が1でなく、起点が0,0,0の場合に、位置の代わりにノーマルを使用することをお勧めします – jjxtra

0

一つのアプローチは、受け入れ答えのようです。あなたは子午線整列あなたもglTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);をお勧めしますではありません三角形を持っている場合は

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

:頂点の配列を生成するコードでは、あなたがこのようなコードになります属性。また、同じ位置の頂点が異なるテクスチャ座標を持つため、glDrawArraysを使用する必要があります。

私は、この場合のテクスチャ座標補間であるすべての悪の根を排除することが良い方法だと思います。基本的に球/楕円についてすべて知っているので、位置に基づいてフラグメントシェーダーのテクスチャ座標、法線などを計算できます。つまり、頂点属性を生成するCPUコードがはるかに単純になり、インデックス付きの図面を再度使用することができます。そして私はこのアプローチが汚いとは思わない。それはきれいだ。

関連する問題