Now we’ll examine some optimization options regarding the shaders and the textures. These are not mandatory, but in the case of a more complex scene, we can gain some performance.
Let’s see the shader types. We previously discussed an importer option that specifies the shader type that is used to map the original source materials during the import. Currently, it is set to Imp_General. If you take a closer look at these shaders, we see that they support a number of texture types that essentially correspond to the texture types generally used in rendering systems. Since most of them are not used in this particular scene, these shaders do a lot of unnecessary calculations currently.
So it’s worth replacing all of our shaders with another type that only supports the minimal necessary features. Let’s select all the shaders using the method we previously discussed. Open the search panel with Ctrl+F. Our previous search was exactly the same as the one we need now, so we already have the list of all the shaders. Use Ctrl + right arrow key to mark all of them and press Enter to select them in the Flow Editor. Now we have to change the type of these shaders. We can either use the right-click menu on one of the shaders, and then choose “Change Import Source”, or we can edit the property “Import Source” here in the Pin Values editor.
We get the file selector dialog where we have to find the shader file that is most suitable for our purpose. Most of the shaders that are capable of using CompleteMap are resides here in the CompleteMap folder. So enter it. Which one should we choose from here? We don’t need the Norm shaders as we don’t use Normal Maps here. So we have to choose amongst the Flat shaders. For now, we only need the most basic one, since we only used Color Maps or in some shaders not even that. So we’ll choose Flat_Compl.
It’s also worth mention the Flat_Refl shaders are to be used on the reflective surfaces. But we only have a few such objects, so we can replace their shaders individually later. So for now we apply the simplest Flat_Compl. Click OK. All the shader modules have been replaced and the new shaders are loaded into the system. If we take a peek at the module we can see that only a Color Map and a Complete Map and some color properties are left. So this is a simpler therefore more effective shader. Of course in the process of type changing all the corresponding property values are retained, so we haven’t lost our texture and color settings. Therefore we see exactly the same on the output as before.
We can also do optimizations on the textures themselves. We usually store textures on the disk using some compressed format like png, exr or jpeg. But it’s deceptive as the GPU cannot directly render from these formats. Therefore they are decoded during the load and stored as an uncompressed image in the GPU memory. For example, a jpeg becomes a 32 bits per pixel uncompressed texture, and an exr or a hdr file becomes a 64 bit per pixel floating point one. So they can occupy a pretty large memory space.
The problem is not the occupied size itself, but the amount of data the GPU has to move when it’s rendering from these textures. The solution to this has two aspects. Firstly we should use compressed texture formats wherever possible, secondly, it’s recommended to use mipmaps. The latter means that the texture is available in several different resolutions, and the GPU always chooses the most relevant in a rendering situation thus reducing the amount of transferred data.
Mipmaps also offer another benefit. For example, take a look at the virtual screen in the background of the scene. On this video it’s a bit hard to spot, but when I move the camera the picture on the screen flickers. The reason for this is that the texture is a large one that has to be scaled to this small size during the render, but the bilinear filter the GPU uses for the task doesn’t work well with such large size differences. But if mipmaps are available the GPU can choose the right sized one thus largely reducing the flicker.
So let’s convert the textures. We start with the picture on the screen. Select the object and jump to its shader by pressing M. In the Color Map property we see that the TV_Screen jpeg texture is used. We can handle the conversions with different approaches.
One is when we specify conversion attributes for a texture. That means that we continue to store the texture in the original format on the disk, but each time the texture is loaded the system converts it into the specified format and stores that format in the memory. This is a satisfying solution but obviously will impact the loading time.
Let’s see how to set this up. At the end of each texture property field, there’s a small arrow button. By clicking we get the Attributes window. For now, we’ll only discuss two of these attributes. Firstly let’s turn on Mipmaps. We can leave the filtering on “Bicubic”, it’s usually suitable. Then turn on Convert which specifies a format conversion.
Here we can choose a target format. There are a number of formats, but here we have to focus on a few of them. On ordinary textures, we usually pick the BC1 format. It’s a rather compressed 4 bits per pixel format. Despite this, in most cases, we won’t see apparent distortions in the picture, so it’s recommended to use. Of course in bad cases, we can choose to not turn on converting and only specify Mipmaps.
If our texture also has an alpha channel, choose BC3. It’s twice as large as BC1 but can store a good quality alpha.
Or, whether or not the texture has alpha we can always choose BC7. It’s a more modern format available since DirectX 11. It’s also twice as large as BC1 but offers a higher quality compression.
In the case of a grayscale texture, it’s recommended to use BC4. It’s also twice as large as BC1 but preserves gray gradients in much higher quality.
The last one I wish to mention is the BC6H. It’s recommended for HDR textures. It can be easily remembered by its ending H.
Now we stick with BC1 being the most compressed format. Click OK. We can see in the log that the conversion has been done. So from now on at each loading, the jpeg picture will be converted into a BC1 texture and will be stored in memory in that format. Also, mipmaps are generated for it. If I now return to the fullscreen output and move the camera we can see the flickering is gone.
As I mentioned, with this attribute method the loading time can be heavily impacted, because the conversion occurs every time we load the texture. The solution is to preserve the converted format texture and load that one with the scene. Let’s open the Attributes window again. We can find a “Bake To DDS” button here. DDS is a DirectX specific file format capable of natively storing all the DirectX texture formats. In our case, we’ll create a BC1 format DDS file that also contains mipmaps. Click “Bake To DDS”. We get the File Browser window where we can specify the name and location of the result DDS. Leave it as is, click OK. With this the conversion has been done, the DDS file has been created, and also the Color Map property has been changed to the new DDS file. So next time I load the scene, it will load the texture from the converted DDS.
Notice that the file format information can be seen in the File Browser. If we enter the Textures folder, we can see the original TV_Screen jpeg image, and beside it the newly created DDS. In the info line of the file, we can see its format: +mip indicates the presence of mipmaps and the format is BC1. If the file is selected we can see the same info above the thumbnail. Let’s enter the Lightmaps folder. Here we have the EXR format CompleteMaps. They specify a 16 bits per channel floating point RGBA format in their info line. That means that if we load the texture it will be stored in the memory in that format. In other words, the info line always indicates the resulting texture format the image takes on when loaded into memory. If a file’s info line displays no format info, it means the default 8 bits per channel RGBA format.
If we actually load these images into the memory, for e.g. both version of the TV_Screen textures and an EXR file as well, we can examine their in-memory formats using the video peekers. Move the mouse over the output pins of the image modules while holding down Control. In the header of the popping up peeker window, we can see the format: this is from the DDS file, the jpeg, and the EXR file.
Of course, we wouldn’t like to do this conversion manually one by one for each texture in our scene. Fortunately, there’s is a much easier way by using the Dependency Manager panel. Its icon is found here on the side toolbar. Let’s undock it into a floating window to see it in a larger size. In this panel, we have the list of the external files referred by our scene, arranged into different categories. We can do a lot of operations here, but they will be discussed in another tutorial. Now we focus on the conversion operations only.
It’s rather easy. We have to select the textures in question in the list. We can do it using the full list because it’s easy to spot them here. Or we can specify a search, for e.g. type jpg for finding all the jpeg files, and then select them all with Ctrl+A in the list. Let’s convert all jpegs into BC1 format. Click Convert. Here we get the same Attributes window we saw before, let’s turn on the Mipmaps, and the Convert to BC1 options. Click OK.
The operation does not start yet, instead, it is added to the Task list here at the bottom. Go on and select all the exr files and click Convert. Turn on Mipmaps and Convert, but this time we specify the BC6H target format since these are HDR images. Click OK to add this task to the list as well. Now if we click EXECUTE it performs both the added tasks.
As we see all the textures are being converted, and also all the references to the image files in the scene are being replaced with references to the new DDS files. Let’s clear the search field to see the full list again. We can see that every texture path here refers to DDS files now.
But we have a problem here. We got an error message for one of the textures. It couldn’t be converted, because in the case of BC formats the image sizes must be dividable by 4. The texture is the one named “Wall_pictures”. If we select it in the list we can see that an ERROR texture was generated for it - this is the standard method of the system when it encounters a conversion or loading error. So let’s resize the image and retry the conversion.
To do this first I replace it back with the original jpeg. Click Replace, select the jpeg file and click EXECUTE. The reference has been changed back to jpeg. Later we’ll return here. Let’s resize the image, we’ll use Photoshop for this. The file can be dragged and dropped directly from this list, this is the simplest way. Drop it into Photoshop. We’ll use cropping - it’s only a few pixels, it won’t harm the image. We can see that the Height is the parameter that violates the rule of divisibility by 4. So change it to 24, by which we cut down one line. Save the image. Return to Dependency Manager and repeat the Convert process for this single file. On EXECUTE we get a confirmation dialog because the target DDS file already exists. Just click Overwrite. The conversion has been applied, and from now this is a working DDS.