I had read a few tutorials on Skyboxes, looked at source code and watched videos and decided to jump straight into coding up an implementation, suffice to say it didn't work and I had no idea why, after a while of head scratching and pulling hair out I decided that the only way forward was the way I should have done things in the first place, splitting the task into smaller tasks, initially I decided these would be:
- Draw a triangle (this is always my first step with OpenGL rendering).
- Move the triangle to the furthest viewable distance using scaling.
- Turn the triangle into a square.
- Repeat 1-3 to create the other 5 sides of the Skybox.
- Set each square to a different colour.
- replace the colour with Bitmap textures to finish the Skybox.
My first step was to generate a
Skybox class which will be responsible for rendering the Skybox, as with all my other classes this was instantiated in the MainActivities
onCreate method and passed to the
VrRenderer via the
The initial implementation of the
Skybox class was based on my
AbstractGameObject class with a few changes:
- As this class is instantiated in the MainActivities
onCreatemethod the shaders and program, which require an active OpenGL context, must be compiled in a seperate method which is called in the
- This initialisation method takes in a distance value which will be used in step 2, This was changed to a constant in the
VrRendererso it is easier to change in one place if required in future.
vertexCoordsArrayfor the skybox was changed from the original
TriangleGameObjects values in preparation for step 3.
Step 3 simply involves adding on extra vertex and the associated indices to draw the second triangle, so I combined it with step 2.
The change for step 2 involved creating a scale matrix which scaled the vertices of the skybox to 99% of the far distance.
This was done by taking the distance passed to the
initialiseSkyBox method and multiplying it by 0.99f
val SCALE = (dist * 0.99f)
and then using this as the scale for the Skybox model.
Matrix.scaleM(mScaleMatrix, 0, SCALE, SCALE, SCALE)
Once this was done, there were a few initial observations:
- The top corners of the square had been clipped.
- The square did not move with the player.
- When the new square was in front of the triangles in the scene, the triangles were still visible, this was fixed by enabling depth testing in the
VrRenderer. Observations 1 and 2 will be looked at in future steps.
Similar to step 3, Step 4 was just a case of adding more vertices and the associated indices.
To address Observation 1 in the previous Step 2 I found this tutorial which explains that, to ensure the Skybox is always rendered behind all other objects, the z component of
gl_position in the vertex shader should be set equal to the w component, therefore, when perspective division is applied the z component will equal 1 and the vertex will be at the maximum distance, for this to work I needed to set the depth function in the
GL_LEQUAL as the skybox is Equal to the maximum depth and therefore would not be rendered by the default
To address Observation 2, I modified the
GameCamera class to include getters for the look direction values and used these new functions to create a
skyboxViewMatrix which did not include the player translation, this was then passed to the Skybox's
This step was added simply to see each of the squares individually and I did not really complete this as it would have meant duplicating vertices to generate clean lines for each square.
This was the big step and went surprisingly smoothly, I added 6 bitmaps which I generated on Paint (Please do not judge these too harshly) to the projects resource folder and then modified the
SkyBox class to take these as constructor arguments.
During the development of this step I decided to create a new class
TextureData which held the width, height and a ByteBuffer containing the pixel RGB values of a bitmap image.
mTextureData of 6 of these
TextureData objects was initialised in the
init function, with each one representing one of the sky box bitmaps.
The biggest coding challenge of this was to extract the pixel data from the bitmap, to do this I created a
getPixelData function which took a Bitmap and returned a ByteBuffer.
getPixelData function first allocated memory for the byte buffer based on the size of the passed bitmap.
val bb = ByteBuffer.allocateDirect(bmp.height * bmp.width * BYTES_PER_PIXEL)
and then looped through every pixel position, calling
val pixel = bmp.getPixel(x, y) to get the value of each pixel.
the Red, Green and Blue components of the pixel were then extracted from
Color.green(pixel) each of these values was cast to a Byte using
.toByte() and put into the newly created
bb Byte Buffer.
initialiseSkyBox method was updated to generate the skybox texture, set the 6 images and parameters as detailed in the tutorial above and this one as well.
Finally the shaders were modified, The vertex shader was changed to pass the position.xyz values to the fragment shader and the fragment shader was changed to set
gl_FragColor to a
textureCube with a
samplerCube and the position.xyz values passed to it.
samplerCube was then set by calling
GLES20.glBindTexture(GLES20.GL_TEXTURE_CUBE_MAP, mTextureBuffer) in the
This task took a lot longer than expected, between life taking over and trying to do too much at the start, as a result of this I have started using Trello to organise my thoughts and plans for the future of this project.
Currently the list of tasks are:
- Investigate serialising the
TextureDataclass rather than regenerating them each time for the textures, my original 2048x2048 pixel textures took a long time to load so I need some way of speeding this up.
- Import .obj files to add more game objects to my scenes, I have an object file parser from a previous project which I shall probably reuse for this.
- I have noticed that the
loadShaderfunction is defined in multiple classes and could be in it's own class and reused more easily.
- I want to start looking at some unit testing, coming from an embedded software background, I have not done a great deal of unit testing and it is something I would love to practice.
- I am still not sure on the graphical style of the game but I would like to implement a cell shader even if this is not the final shader.
The final code for this point in development can be found here
Hopefully the next update will not take so long.