Creating a dynamic skybox with a moving sun

We covered how to create static skyboxes in Chapter 1, SDK Game Development Hub. While they are fine for many implementations, some games require day and night cycles.

Getting ready

This recipe will show us how to create a moving sun, which can be superimposed on a regular skybox. In this case, a neutral skybox without any protruding features such as mountains will work best. We'll also learn how to make a sky that changes color during the day. In this case, no skybox is required.

We will also need a texture that should have a transparent background with a filled, white circle in it, as shown in the following figure:

Getting ready

How to do it...

  1. We begin by creating a new application class extending SimpleApplication.
  2. In the simpleInitApp method, we first need to create Geometry for the sun:
    Geometry sun = new Geometry("Sun", new Quad(1.5f, 1.5f));
  3. We need to set some rendering hints on it, as shown in the following code:
    sun.setQueueBucket(RenderQueue.Bucket.Sky);
    sun.setCullHint(Spatial.CullHint.Never);
    sun.setShadowMode(RenderQueue.ShadowMode.Off);
  4. Now, we can load a Material instance based on the unshaded material definition.
  5. For ColorMap, we load the texture with the white circle in it and apply the texture. Then, for Color we can set an almost white color with a tint of yellow in it. We also have to enable alpha in the material:
    sunMat.getAdditionalRenderState().setBlendMode(RenderState.BlendMode.Alpha);
    sunMat.setTexture("ColorMap", assetManager.loadTexture("Textures/sun.png"));
    sunMat.setColor("Color", new ColorRGBA(1f, 1f, 0.9f, 1f));

So, the basic Geometry is set up and we can create a Control class to move the sun across the sky by performing the following steps:

  1. Create a class called SunControl, which extends AbstractControl.
  2. It should have a float field called time, a reference to the application camera called cam, a Vector3f field called position, and a DirectionalLight field called directionalLight.
  3. In the controlUpdate method, we start by finding the x and z positions based on the time and multiply the result to move it some distance away. We can also make the sun move up and down by doing the same for the y value:
    float x = FastMath.cos(time) * 10f;
    float z = FastMath.sin(time) * 10f;
    float y = FastMath.sin(time ) * 5f;
    position.set(x, y, z);
  4. Then, we should set localTranslation of the sun. Since we want it to appear to be very far away, we add the camera's location. This way it will always appear to be the same distance from the camera:
    spatial.setLocalTranslation((cam.getLocation().add(position)));
  5. We also want the sun to always face the camera. This is easily done by calling the following code:
    spatial.lookAt(cam.getLocation(), Vector3f.UNIT_Y);
  6. If the directionalLight field is set, we should also set its direction. We get the direction by inverting position, as shown in the following code:
    directionalLight.setDirection(position.negate());
  7. Finally, we increase the time value by a factor of tpf (depending on how fast we want the sun to move). Since two PI in radians make up a circle, we start over once time exceeds that value, using the following code:
    time += tpf * timeFactor;
    time = time % FastMath.TWO_PI;
  8. Going back to the application class, we add the control to the Geometry sun and Geometry to the scene graph:
    sun.addControl(sunControl);
    rootNode.attachChild(sun);

The previous implementation can be enough for many games but it can be taken much further. Let's explore how to make the sun color dynamic based on its height above the horizon and how to also have a dynamic sky color by performing the following steps:

  1. First of all, let's introduce two static ColorRGBA fields in the SunControl class called dayColor and eveningColor. We also add another ColorRGBA field called sunColor.
  2. In the controlUpdate method, we take the y value of the sun and divide it so that we get a value between -1 and 1, and store this as the height.
  3. ColorRGBA has a method to interpolate two colors that we can use to get a smooth transition during the day:
    sunColor.interpolate(eveningColor, dayColor, FastMath.sqr(height));
  4. After this, we set the color of directionalLight to the same as sunColor and also set the material's Color parameter to the same:
    directionalLight.setColor(sunColor);
    ((Geometry)spatial).getMaterial().setColor("Color", sunColor);

Handling the sky color will take a bit more work. To do this, perform the following steps:

  1. We begin by creating a new class called SkyControl extending AbstractControl.
  2. Like SunControl, the SkyControl class needs a Camera field called cam. It also needs a ColorRGBA field called color and three static ColorRGBA fields for different times in the day:
    private static final ColorRGBA dayColor = new ColorRGBA(0.5f, 0.5f, 1f, 1f);
    private static final ColorRGBA eveningColor = new ColorRGBA(1f, 0.7f, 0.5f, 1f);
    private static final ColorRGBA nightColor = new ColorRGBA(0.1f, 0.1f, 0.2f, 1f);
  3. The SkyControl class needs to know about the sun's location so we add a SunControl field called sun.
  4. In the controlUpdate method, we set the localTranslation of the spatial to the location of the cam.
  5. Next, we get the sun's height and if it is higher than 0, we interpolate the color between eveningColor and dayColor. Otherwise, we interpolate between the eveningColor and nightColor instead. Then, we set the resulting color in the sky's material's Color parameter, as shown in the following code:
    if(sunHeight> 0){
      color.interpolate(eveningColor, dayColor, FastMath.pow(sunHeight, 4));
    } else {
      color.interpolate(eveningColor, nightColor, FastMath.pow(sunHeight, 4));
    }
    ((Geometry)spatial).getMaterial().setColor("Color", color);
  6. Going back to the application class, we create a box shaped Geometry called sky
  7. for the control with 10f sides.
  8. Like the sun geometry, sky should have the Sky QueueBucket, ShadowMode.Offand CullHint.Never settings applied to it.
  9. In addition, we should call getAdditionalRenderState and set FaceCullMode to FaceCullMode.Off.

How it works...

Always causing the geometries of this recipe follow the camera around is one of the parts that make this recipe work. The other trick is using the Sky QueueBucket. The Sky QueueBucket can be thought of as lists of items to be rendered. Everything in the Sky bucket is rendered first. Because it's rendered first, other things will be rendered on top of it. This is why it appears to be far away even though it's really close to the camera.

We also use the direction of the sun from the camera for DirectionalLight in the scene, making it follow the sun as it moves across the sky.

When updating the control, we handle the movement of the sun using the time value, which increases with each update. Using FastMath.sin and FastMath.cos for the x and z values, we get it to move in a circle around the camera. Using FastMath.sin again for the y value will move it in an arc above (and below) the horizon. By multiplying the y value, we can get it to rise higher in the sky.

The resulting position was added to the camera's location to always make the sun centered around the camera. Since the sun is a simple quad, we also had to rotate it to face the camera with every update.

We went on to change the color of the sun based on the height above the horizon. We used the interpolate method of ColorRGBA to do this. Interpolation requires a value between 0.0 and 1.0. That's why we needed to divide the y value by the max y value (or amplitude) in the case where we've multiplied it earlier to get a higher arc in the sky.

The movement of the box simulating the sky is similar. We just keep it centered around the camera so that even if it's a small box, it appears to cover the whole sky. Normally, we wouldn't see the sides of the box when we're inside it so we set FaceCullMode to Off to always make it render the sides.

SkyControl was fitted with three instances of ColorRGBA: dayColor with a bluish tint, eveningColor with orange, and nightColor almost black. The SunControl was supplied to the control and used to interpolate between the colors based on the height of the sun. Anything above 0.0f is considered day.

In this implementation, the whole sky changes color with the sun. Any further development of SkyControl could include a more complex shape, such as a cylinder or sphere where only the vertices on the same side as the sun change color. Clouds can be implemented and they also use a quad that moves in the xz-plane.

Another improvement would be to have a night-time, star-filled skybox outside of the box we made and fade the alpha value of nightColor to let it gradually shine through the night time.

There's more...

If we try the recipe with the unshaded material definition for the sky, it will work well in most cases. However, when it comes to the postprocessor water filter, it will not pick up the sky color properly. To achieve this, we will have to make some modifications to its material. We don't need to actually change any of the .vert or .frag files, but can create a new Material Definition (.j3md) file.

To make things as easy as possible, we can copy the Unshaded.j3md file. Refer to the following code within the Unshaded.j3md file:

VertexShader GLSL100:   Common/MatDefs/Misc/Unshaded.vert

Replace the previous line with the following line:

VertexShader GLSL100:   Common/MatDefs/Misc/Sky.vert

This means we'll be using the vertex shader normally used by the Sky material to handle the positions of the vertices for the renderer.

We also need to change the WorldParameters segment to contain the following:

ViewMatrix
ProjectionMatrix
WorldMatrix
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.188.198.94