Jump to content

Creating Realistic Skyboxes with Vue

Josh

3,654 views

This tutorial demonstrates how to create a high-quality skybox for Leadwerks Game Engine using Vue.

Download

Required Third-Party Programs

Loading the Example

Run Vue and select the File > Open menu item.  Extract the zip file above and open the file "Cloudy Blue Skies.vue".

ccs-1-0-90786400-1443279174_thumb.jpg

Atmosphere and Clouds

You can modify the appearance of the sky with the Atmosphere Editor. Select the Atmosphere > Atmosphere Editor menu item to open this dialog.

ccs-1-0-74507200-1443279390_thumb.jpg

The clouds tab lets you adjust various properties of the cloud layers and add new ones. Skyboxes look best with multiple layers of different kinds of clouds, so don't expect to get the perfect look with just one layer.

ccs-1-0-34038100-1443279521_thumb.jpg

The load button to the right side of the cloud layer list will let you select from a wide range of different cloud types.

ccs-1-0-33700000-1443279536_thumb.jpg

Experiment with different cloud layers to get the look you want. The "Detail amount" setting in particular will really enhance the image, but don't overdo it. You can right-click and drag the mouse to look around in the main panel, so be sure to take a look around to see how the clouds affect the entire sky.

Lighting

To edit the sunlight properties in Vue, select the sunlight object in the World Browser on the right side of the main window.

ccs-1-0-12557900-1443280022.jpg

You can match the exact rotation of the default sunlight angle in Leadwerks to make your skybox line up exactly to the scene lighting. The default sunlight angle in Leadwerks is (55,-35,0). In Vue this corresponds to the values (145,0,215). To get these values we add 90 degrees to the pitch and subtract the yaw from 180. Note in Vue the order of the yaw and roll are switched.

ccs-1-0-03849300-1443279932.jpg

The sun color is very important for the overall composition of our image. In real life we're used to seeing a very high range of light levels in the sky. Computer monitors cannot represent the same range of colors, so images can easily become washed out and lose details. We want to adjust the sun color so we can get the most detail within the spectrum of a 32-bit color display. Like the rotation, the sun color can be modified in the sun properties.

ccs-1-0-14094400-1443280269.jpg

If the sunlight color is too bright, the image will be overexposed and the cloud shape will become washed out.

ccs-1-0-47473100-1443280650_thumb.jpg

If the sunlight is too dark, it will look gray and desaturated.

ccs-1-0-24631500-1443280414_thumb.jpg

The right sun brightness will give a balanced look between bright spots and shadows. This is the part in the process that requires the most artistic sense. It's a good idea to look at some screenshots or photos for comparison as you adjust your settings.

ccs-1-0-79686900-1443284709_thumb.jpg

You will get quite a lot of variation in brightness across the sky, so be sure to take a look around the whole scene when you are adjusting lighting. You can also adjust the main camera's exposure value to vary the brightness of the rendered image.

If you want to hide the sun from view you can do this by setting the "Size of the sun" and "Size of the corona" settings both to zero under the "Sun" tab in the Atmosphere Editor.

Exporting

To export our skybox the exporter module must be installed. Select the File > Export Sky menu item and the export dialog will appear.

ccs-1-0-61001600-1443280903_thumb.jpg

The "Supporting geometry" setting should be set to "Cube". Set the X value to 1536 and the Y value to 2048. This controls the width and height of the saved image. When we press the OK button, the sky will be rendered out into a vertical cube cross with those dimensions. Each face of the cubemap will be 512x512 pixels.

ccs-1-0-89630000-1443281014_thumb.jpg

By default, your skybox will be exported to the file "Documents\e-on software\Vue 2015\Objects\Atmosphere.bmp". The exported cube cross is a nonstandard orientation. To convert this into a cubemap strip ready to load in Leadwerks, use the FixVueCubemap.exe utility posted above.  Drag your exported image file onto the executable, and it will save out a cube strip in PNG format that is ready to load in Leadwerks.

ccs-1-0-34054400-1443281151_thumb.jpg

Importing

To import your skybox into Leadwerks, just drag the cubemap strip PNG file onto the Leadwerks main window. Open the converted texture from the Leadwerks Asset Browser. Set the texture mode to "Cubemap", uncheck the "Generate Mipmaps" checkbox, and check the clamp mode for the X, Y, and Z axes. Press the Save button to reconvert the texture and it will appear in a 3D view.

ccs-1-0-76222900-1443281537_thumb.jpg

You can use the skybox in the current map by selecting it in the scene settings.

ccs-1-0-23907200-1443281670.jpg

Disabling mipmap generation will reduce the size of a 1024x1024 cubemap from 32 to 24 mb. Due to the way the image is displayed, mipmaps aren't needed anyways.

Final Render

For the final render, we want each cubemap face to be 1024x1024 pixels. However, we can get a better quality image if we render at a larger resolution and then downsample the image. In Vue, select the File > Export menu item again to open the export dialog. This time enter 6144 for the X value and 8192 for the Y value. Don't press the OK button until you are ready to take a long break, because the image will take a long time to render. When you're done you will have a huge image file of your skybox with a 2048x2048 area for each cubemap face.

If we resize the image file in a regular paint program, it will create seams along the edges of the cubemap faces. Instead, we're going to pass a parameter to the conversion utility to tell it to downsample the image by a factor of 50%. The "downsample.bat" file is set up to do this, so just double-click on this to launch the executable with the correct parameters. The resulting cubemap strip will be 6144x1024 pixels, with a 1024x1024 area for each face. However, due to the original rendering resolution this will appear less grainy then if we had rendered directly to this resolution.

Import this texture into Leadwerks as before and enjoy your finished high-quality skybox. Always do a low-resolution pass before rendering the final image, as it can take a long time to process.



3 Comments


Recommended Comments

Incidentally, the Vue interface was my main inspiration for the look of Leadwerks GUI.

Share this comment


Link to comment

I Love vue, but standalone 3D Studio max has some excellent wrap-around shaders and cube-mapping options built in. For those of you that cannot afford an expensive renderer add-on for 3D Studio max, (you are using scanline or mental ray) and want to know how, message me and I will throw together a tutorial. 

Share this comment


Link to comment

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Add a comment...

×   Pasted as rich text.   Paste as plain text instead

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

  • Blog Entries

    • By Josh in Josh's Dev Blog 0
      I wanted to see if any of the terrain data can be compressed down, mostly to reduce GPU memory usage. I implemented some fast texture compression algorithms for BC1, BC3, BC4, BC5, and BC7 compression. BC6 and BC7 are not terribly useful in this situation because they involve a complex lookup table, so data from different textures can't be mixed and matched. I found two areas where texture compression could be used, in alpha layers and normal maps. I implemented BC3 compression for terrain alpha and could not see any artifacts. The compression is very fast, always less than one second even with the biggest textures I would care to use (4096 x 4096).
      For normals, BC1 (DXT1 and BC3 (DXT5) produce artifacts: (I accidentally left tessellation turned on high in these shots, which is why the framerate is low):

      BC5 gives a better appearance on this bumpy area and closely matches the original uncompressed normals. BC5 takes 1 byte per pixel, one quarter the size of uncomompressed RGBA. However, it only supports two channels, so we need one texture for normals and another for tangents, leaving us with a total 50% reduced size.

      Here are the results:
      2048 x 2048 Uncompressed Terrain:
      Heightmap = 2048 * 2048 * 2 = 8388608 Normal / tangents map = 16777216 Secret sauce = 67108864 Secret sauce 2 = 16777216 Total = 104 MB 2048 x 2048 Compressed Terrain:
      Heightmap = 2048 * 2048 * 2 = 8388608 Normal map = 4194304 Tangents = 4194304 Secret sauce = 16777216 Secret sauce 2 = 16777216 Total = 48 MB Additionally, for editable terrain an extra 32 MB of data needs to be stored, but this can be dumped once the terrain is made static. There are other things you can do to reduce the file size but it would not change the memory usage, and processing time is very high for "super-compression" techniques. I investigated this thoroughly and found the best compression methods for this situation that are pretty much instantaneous with no noticeable loss of quality, so I am satisfied.
    • By jen in jen's Blog 0
      My small project will be called Foregate, it will be a dark medieval Diablo style single player action RPG.
      The graphics will be simple, no PBR, 256x256 map, reasonably low-res models.
      Camera style? Top-down-ish I think? Like in Diablo exactly - and because the camera is not directly in-front of the 3 models, I can get away with low-resolution assets - bonus. Also, with top-down view, I won't have to worry about high resolution sky-boxes. 
      What's my plan for this project?
      I plan to make this project as small and as simple as possible, possibly release it as open-source, and have fun with it of course.
      My previous experience with game development (1-2 years ago?) was amateurish I think, still is now. I want to give it a go again, this time with experience although my skill in C++ is not really that good? Maybe I can improve it in this project.
      More about the game
      The content is not set in stone yet but I have a general idea of how the mechanics is going to look and feel - Diablo-ish obviously. It'll have monsters (ancient & mythical probably), loot when killing a monster, gold as in-game currency, visual grid inventory, player stats (level, strength, agility, vitality, energy, &c.). 
      The game will be single-player. Possibly a coop multiplayer also? I don't have any interest in making massive multi-player. 
      I started my development yesterday with the basic preparations (setting up project environment, &c.), today I made my first step in developing the core components; worker class, game state, task class.
      I have a game state that keeps a single source of truth for the entire application; all game data will be stored in this class as "states". 
      I also have a "Worker" which will do the processing of tasks in the game.
      I also have "object" class, this can be a monster, the player, a weapon, a prop, or an NPC.
      So the idea is to have a CQRS type of interaction between the classes and the data. Any action in the game will be interpreted as "Task" for the Worker class. The worker class iterates through the Task. Tasks can be created by any class interfaced with the Worker class trough "addNewTask" and the new tasks can be of a certain type i.e.: ATTACK, IDLE, SAVE_GAME, EXIT_GAME, the new task will also have a payload data and it's processed according to its task type e.g. an ATTACK with payload "{ Damage: 10, Target: MonsterA }" will reduce the health of MonsterA by 10 - the worker class will change the game state; find MonsterA in MonsterState and reduce its health by 10. 
      I think it's advantageous to have this type of centralized module where all actions are processed; I can do all sorts of procedures during the processes, maybe debug data, filter actions, mutate payloads, and such.
      How much time am I going to put into this?
      A couple of hours a day for 3 days a week maybe.
      So it's all a rough sketch for now and it's heading the right direction. I'll have more to report later on. 

      This is Forgate Castle, minus the castle, in the map Forgate; the starting location for the player. The fortification will have merchants, and quest givers.
       
    • By jen in jen's Blog 4
      I've got a bit of free time on my hands for a while. I plan to take up Leadwerks again and come up with a simple project to have fun with.
      I use VSCode at work and I don't have a Windows machine at the moment. Codeblocks is a bit dated now and I'm not sure if it has all the features to fit my workflow so I can't use it. 
      It's a bit fiddly to build C++ apps on VSCode. There's a prescribed method of setting up your C++ project in the official hubs but I don't think those are necessary in my case, I just need a script that builds the project which I have below.
      By the way, VSCode website is here: https://code.visualstudio.com/
      Have fun.
      Save as build.sh in your project's root folder and run using build.sh or build.sh -r for release. Works for Stable version LW and any previous versions(?) - at least v4.5 (archived). BETA branch (4.6?) doesn't seem to build for me, missing Leadwerks.a file.
      Disclaimer: not really fully tested for bigger projects? I've only tested this with sample projects that have no additional project folder structure in its Source so I'm not sure if the script will propagate its search for CPP files thoroughly and process each file successfully in bigger projects. Back-up project files before running the script for the first time please.
      Updated: 26/02/2020
      #!/bin/sh if [ $1 == "-h" ]; then echo "Build script is set to DEBUG by default. Set -r in first argument of the script to build for release."; exit 1; fi if [ $1 != "" ] && [ $1 != "-r" ] && [ $1 != "-h" ]; then echo "Type build.sh -h to see brief help description."; exit 1; fi debugFlags='-g -DDEBUG -D_DEBUG'; debugLibPath='Debug'; PROJECT_PATH=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P ) LEADWERKS_PATH=~/.local/share/Steam/steamapps/common/Leadwerks declare a compiledFiles; declare a includedFilePaths; if [ $1 == "-r" ]; then debugFlags=''; debugLibPath='Release'; fi truncate -s 0 $PROJECT_PATH/build.log echo "Leadwerks Path: $LEADWERKS_PATH" echo "Project Path: $PROJECT_PATH" echo "Compiling source..." if [ ! -d "$PROJECT_PATH/Compilations" ]; then mkdir $PROJECT_PATH/Compilations; fi if [ ! -d "$PROJECT_PATH/Compilations/$debugLibPath" ]; then mkdir $PROJECT_PATH/Compilations/$debugLibPath; fi for f in $(find $PROJECT_PATH/Source -name '*.h' ); do includedFilePaths+=("-I $(dirname "${f}")") done includedFilesJoined=$(printf "%s " "${includedFilePaths[@]}") includedFilesJoined="-${includedFilesJoined:1}" for f in $(find $PROJECT_PATH/Source -name '*.cpp' ); do originalSourcePath=$PROJECT_PATH/Source; compilationsPath=$PROJECT_PATH/Compilations/$debugLibPath; compiledFilePath=${f/.cpp/'.o'}; compiledFilePath=${compiledFilePath/$originalSourcePath/$compilationsPath}; mkdir -p $(dirname "${compiledFilePath}") g++ -std=c++0x $debugFlags -w -fexceptions -msse3 -DDG_DISABLE_ASSERT -DZLIB \ -DPLATFORM_LINUX -D_NEWTON_STATIC_LIB -DFT2_BUILD_LIBRARY -DOPENGL \ -Dunix -D__STEAM__ -D_POSIX_VER -D_POSIX_VER_64 -DDG_THREAD_EMULATION \ -D_STATICLIB -DDG_USE_THREAD_EMULATION -DGL_GLEXT_PROTOTYPES -DLEADWERKS_3_1 \ -DLUA_USE_LINUX -D_GLIBCXX_USE_CXX11_ABI=1 -D_CUSTOM_JOINTS_STATIC_LIB -fPIC -O2 -std=c++0x \ $includedFilesJoined \ -I $LEADWERKS_PATH/Include/Libraries/VHACD/src/VHACD_Lib/inc \ -I $LEADWERKS_PATH/Include/Libraries/NewtonDynamics/sdk/dMath \ -I $LEADWERKS_PATH/Include/Libraries/NewtonDynamics/sdk/dgNewton \ -I $LEADWERKS_PATH/Include/Libraries/NewtonDynamics/sdk/dContainers \ -I $LEADWERKS_PATH/Include/Libraries/NewtonDynamics/sdk/dgCore \ -I $LEADWERKS_PATH/Include/Libraries/NewtonDynamics/sdk/dgTimeTracker \ -I $LEADWERKS_PATH/Include/Libraries/NewtonDynamics/sdk/dgPhysics \ -I $LEADWERKS_PATH/Include/Libraries/NewtonDynamics/sdk/dCustomJoints \ -I $LEADWERKS_PATH/Include/Libraries/tolua++-1.0.93/include \ -I $LEADWERKS_PATH/Include/Libraries/lua-5.1.4 \ -I $LEADWERKS_PATH/Include/Libraries/freetype-2.4.7/include \ -I $LEADWERKS_PATH/Include/Libraries/enet-1.3.1/include \ -I $LEADWERKS_PATH/Include/Libraries/RecastNavigation/DebugUtils/Include \ -I $LEADWERKS_PATH/Include/Libraries/RecastNavigation/Detour/Include \ -I $LEADWERKS_PATH/Include/Libraries/RecastNavigation/DetourCrowd/Include \ -I $LEADWERKS_PATH/Include/Libraries/RecastNavigation/DetourTileCache/Include \ -I $LEADWERKS_PATH/Include/Libraries/RecastNavigation/Recast/Include \ -I $LEADWERKS_PATH/Include/Libraries/libvorbis/include \ -I $LEADWERKS_PATH/Include/Libraries/NewtonDynamics/packages/thirdParty/timeTracker \ -I $LEADWERKS_PATH/Include/Libraries/libvorbis/lib \ -I $LEADWERKS_PATH/Include/Libraries/libogg/include \ -I $LEADWERKS_PATH/Include \ -I $LEADWERKS_PATH/Include/Libraries/zlib-1.2.5 \ -I $LEADWERKS_PATH/Include/Libraries/zlib-1.2.5/contrib/minizip \ -I $LEADWERKS_PATH/Include/Libraries/freetype-2.4.7/include/freetype \ -I $LEADWERKS_PATH/Include/Libraries/freetype-2.4.7/include/freetype/config \ -I $LEADWERKS_PATH/Include/Libraries/LuaJIT/dynasm \ -I $LEADWERKS_PATH/Include/Libraries/glew-1.6.0/include \ -c $f -o $compiledFilePath; compiledFiles+=($compiledFilePath) done compiledFilesJoined=$(printf "%s " "${compiledFiles[@]}") compiledFilesJoined="/${compiledFilesJoined:1}" echo "Building project..."; g++ -o $PROJECT_PATH/${PWD##*/} $compiledFilesJoined -s "$LEADWERKS_PATH/Library/Linux/$debugLibPath/Leadwerks.a" -ldl \ -lopenal -lGL -lGLU "$LEADWERKS_PATH/Library/Linux/libluajit.a" $PROJECT_PATH/libsteam_api.so \ -lX11 -lXext -lXrender -lXft -lpthread -lcurl "$LEADWERKS_PATH/Library/Linux/libopenvr_api.so" echo "Build complete. See build.log for result summary." About the game: I'm kind of disappointed with the Action RPGs released recently. I want to give the genre a shot, present my own interpretation of how it should look like? Dark, medieval, less electric, modest if not rare use of glow shaders... maybe?

×
×
  • Create New...