如何渲染令人信服的天幕?

时间:2012-12-13 00:34:15

标签: java opengl-es-2.0 libgdx jogl

我正在编写一个渲染3D岛的OpenGL ES 2.0应用程序。我已经有了在岛上生成天空圆顶的代码。这是一个由三角形组成的半球,它以z点向上铺设岛屿。

圆顶有一些非常基本的移动云,使用perlin噪声纹理覆盖在自身上并移动不同的速度。

但最终我还需要圆顶渲染:

  • 太阳(横跨天空)月亮(包括阶段)
  • 星星(晚上)
  • 遥远的土地作为静态纹理
  • 模拟夜晚,黎明,黄昏,白天的不同颜色

我需要非常有效地完成这项任务,因为它最终会在Android上运行,尽管它目前正在测试中运行。因此,例如太阳,月亮和星星只是纹理,尽管它们的点可以以合理的精度绘制。

我已经有了生成圆顶的代码,以及用于根据日期和时间绘制太阳的代码。所以主要是我需要的着色器以及它们提供的是什么。

有没有举例说明这些事情?我发现很多基本的立方体地图或有限的东西,但没有达到我需要的复杂程度。显然,因为这是OpenGL ES 2.0,它必须在着色器中完成。

我现有天空圆顶的着色器渲染2层perlin噪声来模拟云。纹理连续包裹,因此我可以根据圆顶顶点到xy平面的角度(通过将x和y馈入atan)和使用圆顶顶点z的v偏移来计算u偏移。

顶点着色器演示了我是如何做到的:

#ifdef GL_ES
precision mediump float;
precision mediump int;
#endif

attribute vec4 aVertex;

uniform mat4 uPMVMatrix;
uniform float uTime;
uniform float uSkyDomeRadius;

const float PI = 3.1415926535897932384626433832795;

varying vec2 texCoord0, texCoord1;

void main()
{
    vec2 centre = vec2(600., 600.);

    gl_Position = uPMVMatrix * aVertex;

    float slow_time = uTime / 100.;

    vec2 dome_point = aVertex.xy - centre;
    float tex_u = atan(dome_point.x, dome_point.y);// / (.25 * PI);
    float tex_v = aVertex.z / (uSkyDomeRadius * .5);

    texCoord0 = vec2(tex_u / 2.0 + slow_time, tex_v / 2.0);
    texCoord1 = vec2(tex_u + slow_time / 2.0, tex_v);
}

我还使用时间来每帧偏移一点,以便云移动。这很好用,除非atan从-PI到PI,突然texCoord0和texCoord1值片段着色器插值被一个大距离分开,这会占用插值。

描述它的最好方法是,如果我的底部有16个三角形,那么从0/16到1/16的插值有效,1/16到2/16有效,依此类推。但是当我到达15/16到0/16时,插值器向后移动并且大的变化使得碎片着色器在一个小的空间中反复重复纹理,导致如图所示的条纹。

Interpolation goes haywire as angle goes into reverse

我看不出有任何方法可以获得无缝的360度视图我认为唯一可以解决这个问题的方法是,如果我旋转整个圆顶使得接缝始终位于相机后面,但如果相机指向直线,它仍然会显示直到顶部的圆顶。

使用source的工作示例会很棒,特别是如果它解决了这些问题。

1 个答案:

答案 0 :(得分:3)

问题是,您在0°和360°的角度使用相同的顶点。所以你现在有一个三角形,其中第一个顶点的纹理坐标为15 / 16,0,第二个顶点的颜色为0,0而不是1,0。要解决此问题,您必须打开球体,以便在相同的空间位置有一对顶点,一个用于0°,一个用于360°。这样,两个顶点可以具有不同的纹理坐标,并且不会有任何受干扰的纹理。

要旋转它们,您必须使用包装模式,确保纹理包装设置为重复。如果您没有更改它,则应该正确设置,但可以使用函数glTexParameter进行设置,参数为GL_TEXTURE_WRAP_SGL_TEXTURE_WRAP_T,值必须为{{ 1}}。然后,如果纹理值> 1它不会环绕但重复纹理。现在你只需要能够告诉球体起点的顶点(对于0°的顶点)与末端的顶点(对于360°的顶点),这样你就可以纠正GL_REPEAT,你可能需要一个该附加属性。