Using multiple skyboxes on a map

This tutorial will touch the issue on how Q3Map2 handles multiple skyboxes in a single map. If you've ever created a map where you have multiple skyboxes, you have probably faced the issue we are going to fix here: Q3Map2 doesn't know how to use mutliple skyboxes properly, making your lightmaps overly exposured and dull looking.

 

Illustrating the issue

I created this simple map to illustrate the issue. Here we see a properly lit map, with Oasis sky.

Properly lit map

As you can see, the shadows look nice and crisp, and there is actually difference with light and dark. Now what happens when we have 4 identical rooms, each with different skies? I copied the room 3 times, changed the sky textures so that the map has a room with Oasis, Fueldump, Goldrush and Radar skies. Here is the same room.

Over exposured lighting

As you can see, the lighting has changed a lot: Shadows are dull, and the map in general looks too bright. Also, here are the other rooms. Notice how the lighting is exactly same in every room, despite the different skies.

Bad Fueldump

Bad Goldrush

Bad Radar

So what happened here?

Q3Map2 cannot apply different skylight conditions in different areas in the map, even when the rooms are sealed with structural brushes and every outer surface is caulk. What it does instead is add up all skyshader directives that produce light into the map, and combines them to light the whole map. You could probably modify Q3Map2 to properly support multiple skyboxes as the source code is available on GitHub, but that's something a bit more advanced, and we are not going to do it. Instead we are going to work around this issue by modifying the sky shaders used in our map.

 

Fixing the shaders

First, let's take a look at the 4 sky shader we used in our map (found in fueldump.shader, skies.shader and skies_sd.shader).

//Fueldump Sky

textures/fueldump/fueldumpsky
{
    qer_editorimage textures/skies/fueldump_clouds.tga
    q3map_lightrgb 0.8 0.9 1.0
    q3map_skylight 85 3
    q3map_sun 1 .95 .9 200 210 28
    skyparms - 200 -
    surfaceparm nodlight
    surfaceparm noimpact
    surfaceparm nolightmap
    surfaceparm sky
    {
        map textures/skies/fueldump_clouds.tga
        rgbGen identity
    }
    {
        map textures/skies/fueldump_clouds.tga
        blendfunc blend
        rgbGen identity
        tcMod scroll 0.0005 0.00
        tcMod scale 2 1
    }
}

//Goldrush Sky

textures/skies/sd_goldrush
{
    qer_editorimage textures/skies/sky_8.tga
    q3map_skylight 65 3
    q3map_sun 0.3 0.3 0.45 60 35 45
    nocompress
    skyparms - 200 -
    sunshader textures/skies_sd/full_moon2
    surfaceparm noimpact
    surfaceparm nolightmap
    surfaceparm sky

    skyparms textures/skies_sd/wurzburg_env/sky 512 -


    {   fog off
        map textures/skies_sd/goldrush_clouds.tga
        tcMod scale 5 5
        tcMod scroll 0.0015 -0.003
        rgbGen identityLighting
    }


    {   fog off
        map textures/skies/nightsky1.jpg
        tcMod scale 10 10
        blendfunc add
        rgbGen identityLighting
    }

    {
        fog off
        clampmap textures/skies_sd/goldrush_mask.tga
        blendFunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
        tcMod scale 0.956 0.956
        tcMod transform 1 0 0 1 -1 -1
        // rgbGen identityLighting
        //rgbGen const ( 0.6 0.6 0.6 )
        rgbGen const ( 0.4 0.4 0.4 )
    }
}

//Radar Sky

textures/skies/sd_wurzburgsky
{
    nocompress
    qer_editorimage textures/skies/sky_8.tga
    q3map_lightimage textures/skies/n_blue2.tga
    q3map_nofog
    q3map_globaltexture
    q3map_lightsubdivide 256
    q3map_sun 0.130 0.080 0.020 20 165 5
    q3map_sun 0.281 0.288 0.370 80 35 40
    q3map_sun 0.281 0.288 0.370 15 215 60
    q3map_sun 0.281 0.288 0.370 10 35 50
    q3map_sun 0.281 0.288 0.370 10 35 45
    q3map_sun 0.281 0.288 0.370 10 35 35
    q3map_sun 0.281 0.288 0.370 10 35 30
    q3map_sun 0.281 0.288 0.370 10 40 40
    q3map_sun 0.281 0.288 0.370 10 45 40
    q3map_sun 0.281 0.288 0.370 10 30 40
    q3map_sun 0.281 0.288 0.370 10 25 40
    q3map_surfacelight 30

    surfaceparm noimpact
    surfaceparm nolightmap
    surfaceparm sky

    skyparms - 512 -
    sunshader textures/skies_sd/small_moon

    {
        map textures/skies_sd/wurzburg_clouds.tga
        tcMod scale 2.5 2.5
        tcMod scroll 0.003 -0.0015
        rgbGen identityLighting
    }
    {
        map textures/skies_sd/ydnar_lightning.tga
        blendFunc GL_SRC_ALPHA GL_ONE
        rgbGen wave triangle -8 9 0 0.137
        alphaGen wave noise -3 4 0 2.37
        tcMod scale 3 3
        tcMod scroll 0.003 -0.0015
    }
    {
        clampmap textures/skies_sd/wurzburg_fogmask.tga
        blendFunc GL_SRC_ALPHA GL_ONE_MINUS_SRC_ALPHA
        tcMod scale 0.956 0.956
        tcMod transform 1 0 0 1 -1 -1
        rgbGen identitylighting
    }
}

//Oasis Sky

textures/skies_sd/sd_siwasky
{
    nocompress
    qer_editorimage textures/skies_sd/nero_bluelight.tga
    q3map_lightimage textures/skies_sd/siwa_clouds.tga

    q3map_sun 0.75 0.70 0.6 135 199 49
    q3map_skylight 75 3
    sunshader textures/skies_sd/siwasunbright

    surfaceparm noimpact
    surfaceparm nolightmap
    surfaceparm nodlight
    surfaceparm sky

    skyparms textures/skies_sd/wurzburg_env/sky 512 -

    {
        fog off
        clampmap textures/skies_sd/siwa_mask.tga
        tcMod scale 0.956 0.956
        tcMod transform 1 0 0 1 -1 -1
        rgbGen identityLighting

    }
}

If you are unfamiliar with shaders, you might want to take a moment to look at the Q3Map2 Shader Manual before proceeding.

To fix our issues with lighting, we need to modify the shaders so that there is only one sky in our map that produces light, while the rest are just pretty looking textures. To do this, we need to figure out exactly which directives in the shaders produce light. The shader manual helps us out here. These directives in sky shaders affect the lighting in our map (not all of them are used in these shaders obviously):

q3map_lightimage
q3map_lightmapFilterRadius
q3map_lightRGB
q3map_lightSubdivide
q3map_noFast
q3map_skylight
q3map_sun
q3map_sunExt
q3map_surfacelight

In general we don't need to worry that much about most of these, as they just modify the lighting instead of producing it. The most commonly seen directives that actually produce light are q3map_skylight, q3map_sun, q3map_sunExt and q3map_surfacelight in conjunction with q3map_lightSubdivide. To understand what these do, read the descriptions of them in the shader manual.

Now that we know which directives we need to modify, it's time to start editing the shaders. First, we create a shader file that includes the skies used in our map. Next, we change the paths of the shaders so that we don't mess up the original sky shaders in the game. So for example, I renamed the shaders as following:

textures/skytut/fueldump
textures/skytut/goldrush
textures/skytut/radar
textures/skytut/oasis

Now we need to comment out every light producing directive in every shader except for one, which is going to be the only shader that produces light in the map. Another option is to change the intensity values of the light-producing shaders to 0. I chose Fueldump sky shader to produce light into the map, and commented out every light producing directive in the rest of the shaders. Now, in the map with 4 identical rooms each with different sky shader, here is the room with Oasis sky.

New lighting in Oasis

And a picture showing the lighting in every room.

New lighting in all rooms

Now the rooms are all lit by the Fueldump sky, and we have proper difference between shadow and light again. The lighting is exactly same in every room, but they are not overly bright. As mentioned earlier, it is not possible to have different sky lighting conditions in different parts of the map, but with this method it's possible to use multiple skyboxes while still maintaining crisp shadows and keeping the brightness level of the map natural.

 

Design notes

 

Tutorial by Aciz