Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I render a convincing skydome?

I am writing an OpenGL ES 2.0 app which renders a 3D island. I already have code to generate a sky dome around the island. This is a hemisphere comprised of triangles which over lay the island with z point upwards.

The dome has some very basic moving clouds created using a perlin noise texture overlaid over itself and moving different speeds.

But ultimately I need the dome to also render:

  • Sun (which tracks across the sky) Moon (including phase)
  • Stars (at night)
  • Distant land as a static texture
  • Different colours to simulate night, dawn, dusk, day

I need to do this quite efficiently since it will end up on Android although for the moment its running in a test harness. So the sun, moon and stars for example will just be textures although their points may be plotted with reasonable accuracy.

I already have the code to generate the dome, and code to plot a sun against a date and time. So mostly its the shaders I need and what they're supplied with.

Is there any example which demonstrates these things? I've found plenty which do basic cube maps, or limited stuff, but nothing to the level of sophistication I need. Obviously since this is OpenGL ES 2.0 it has to be done in shaders.

The shader for my existing sky dome renders 2 layers of perlin noise to simulate clouds. The texture wraps continuously so I can compute the u offset based on the angle of a dome's vertex into the xy plane (by feeding the x and y into atan) and the v offset using the dome's vertex z.

The vertex shader demonstrates how I do it:

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

I also use a time to offset u a bit each frame so the clouds move. This works fine except atan goes from -PI to PI and suddenly the texCoord0 and texCoord1 values the fragment shader interpolates are separated by a large distance which hoses the interpolation.

The best way to describe it is that if I have 16 triangles at my base then interpolation from 0/16 to 1/16 works, 1/16 to 2/16 works and so on. But when I get to 15/16 to 0/16 the interpolator goes backwards and the large variation causes the frag shader to repeat the texture over and over in a small space leading to stripes as shown.

Interpolation goes haywire as angle goes into reverse

I cannot see any way to have a seamless 360 degree view I think the only way I'll fix this is if I rotate the entire dome so the seam is always behind the camera but it might still show if the camera points straight up to the top of a dome.

A working example with source would be great especially if it addresses these sorts of issues.

like image 219
locka Avatar asked Dec 13 '12 00:12

locka


1 Answers

The problem is, that you use the same vertices at an angle of 0° and 360°. So you now have a triangle where the first vertex has a texture coordinate of 15/16, 0 and the second has 0, 0 instead of 1,0. To fix that, you have to open up the sphere so you have a pair of vertices at the same spatial positions, one for 0° and one for 360°. That way the two vertices can have different texture coordinates and there won't be any disturbed textures.

To rotate them you have to use the wrapping modes, make sure, that the texture wrapping is set to repeat. If you haven't changed it, it should be set up correctly, but it can be set with function glTexParameter, the parameters are GL_TEXTURE_WRAP_S and GL_TEXTURE_WRAP_T and the value has to be GL_REPEAT. Then if a texture value is > 1 it won't wrap around but repeat the texture. Now you only need to be able to tell the vertices at the start of the sphere (for vertices at 0°) from those at the end (for vertices at 360°) so you can correct tex_u, you probably need an additional attribute for that.

like image 197
Josef Meixner Avatar answered Sep 21 '22 07:09

Josef Meixner