如何渲染令人信服的天穹?
我正在编写一个OpenGL ES 2.0应用程序,它可以渲染一个3D岛。我已经有了代码来生成岛屿周围的天空穹顶。这是一个由三角形组成的半球,这些三角形位于岛屿上,z点朝上。
穹顶有一些非常基本的移动云,使用覆盖在自身上并移动不同速度的柏林噪声纹理创建。
但最终我需要穹顶来渲染:
- 太阳(在天空中跟踪)月亮(包括相位)
- 星星(晚上)
- 作为静态纹理的遥远土地
- 不同的颜色来模拟夜晚,黎明,黄昏,白天
我需要非常有效地做到这一点,因为它最终会在Android上结束,尽管目前它在测试工具中运行。因此,例如,太阳,月亮和星星将只是纹理,尽管它们的点可以以合理的精度绘制。
我已经有了生成圆顶的代码,以及根据日期和时间绘制太阳的代码。因此,主要是我需要的着色器以及它们提供的内容。
有没有例子可以证明这些事情?我发现了很多基本的立方体贴图,或者有限的东西,但没有达到我需要的复杂程度。显然,由于这是OpenGL ES 2.0,因此必须在着色器中完成。
我现有天空穹顶的着色器会渲染 2 层柏林噪声来模拟云彩。纹理连续包裹,因此我可以根据圆顶顶点与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);
}
我还使用时间在每帧偏移u位,以便云移动。这工作正常,除了 atan 从 -PI 到 PI,突然 texCoord0 和 texCoord1 值片段着色器插值被一个软管插值的很远的距离隔开。
描述它的最好方法是,如果我的底部有16个三角形,那么从0/16到1/16的插值有效,1/16到2/16有效,依此类推。但是,当我到达 15/16 到 0/16 时,插值器会向后移动,较大的变化会导致 frag 着色器在小空间中一遍又一遍地重复纹理,从而导致条纹,如图所示。
我看不到任何方法来获得无缝的360度视图,我认为解决这个问题的唯一方法是旋转整个圆顶,以便接缝始终在相机后面,但它仍然可能显示相机是否直指向圆顶的顶部。
使用源代码的工作示例会很棒,特别是如果它解决了这类问题。