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