Some time ago strolling around the net I was caught in a Cuphead trailer, I was amazed by the care details of this game, I had the feeling of being catapulted into an American cinema in the 30s. The trailer has fantastic colors, the game is designed and colored in the 30s style and is looked at with the eyes of a 30s people. As I was looking at it has flashed into my mind: how would I look at Cuphead today in 2017 ?
Movies of that time were affected by film defects due to time and use: scratches, dust, mould, finger grease and unstoppable vinegar syndrome. Scratches and dust are already contained in Cuphead, also in the best conditions of perfect preservation in a fresh and dry place and the absence of fingerprints there remains the problem of vinegar syndrome, which is related to the emulsion of the colour films of that time. The film begins to lose the image’s blue and green components, slowly degrading to the sepia colour and then increasingly to the red.
We’ll see how to create and apply this effect to the game by means of shaders. On the net there are shaders for many games, but what are they and how do you make a shader?
The tutorial is addressed to those who would like to approach the shader world but don’t know from where to start. I will try to avoid the use of overly specialized terms or concepts, this will be a journey to give a methodology and the first rudiments of digital image processing theory and application.
To create a video-game, procedures are applied (rendering) that allow to generate a digital image using appropriate programs from a set of information: three-dimensional objects description, viewpoint, lighting, etc.. These images aren’t created directly on screen, but are temporarily stored in the graphics card memory and are sent to the monitor later. Before sending these images to the monitor, they can be further modified (post-processing) through the algorithms implementation, the algorithm implementation activity is known as “Shading”, the specific algorithm implemented is known as “Shader”.
Well! Now the time has arrived to dirty our hands: to reach the first steps we need to be clear not only from an intuitive point of view, what is the color, brightness and contrast of an image also from a way mathematical/geometric.
1. GRAYSCALE IMAGES
These black-and-white images are also images with tones, from black to white. From a digital point of view, they are 8-bit images (256 grey tones) from Black (value 0) to White (value 255).
We represent this image using a Cartesian diagram (Fig. 1.0) where on the abscissa we put the gray tones of the original image and on the ordinate the gray tones of the displayed image. In this space that we will say “of images”, the gray tones of the original image and the one displayed are in a biunivocal correspondence (1:1) and the relation that represents them is a segment (set of discrete points) that is an angle of 45° with the abscissa axis.
The image brightness is the total amount of light that appears to be output. Adjusting the brightness of an image results in a brighter/darker picture than the original image.
In the “images” space corresponds to translating the segment parallel to itself along the ordinate axis. However, grey tones are 8-bit positive numbers and this has restrictions. (Fig. 1.1)
Translating the segment up/down corresponds to increase/decrease the value of all gray tones by the same amount, this operation according to the image may cause display defects.
Let’s see the upward translation indicated by the red arrow with the “+” sign that corresponds to the increase in brightness: in the example, 85 is added to the value of the gray tones of the displayed image, but the gray tones of the original image after 170 are all displayed by the value 255 (white). The brightness increase improves the image’s visibility, but according to the image from a certain point onwards details are lost being displayed completely white, in this case the brightness increase is a defect for the displayed image.
Let’s see the downward translation indicated by the red arrow with the sign “-” which corresponds to a decrease in brightness: in the example we subtract 85 from the value of the gray tones of the displayed image, but the gray tones of the original image between 0 and 85 are all displayed by the value 0 (black). The brightness decrease reduces the image’s visibility, but according to the image from a certain point onwards details are lost being displayed completely black, also in this case the brightness decrease is a defect for the displayed image.
The contrast in an image is the difference in brightness between the light and dark parts. Adjusting the contrast of an image means obtaining a grayscale image with a greater/minor separation of tones than the original image.
In the “images” space the segment rotates around the Cartesian axes origin. However, grey tones are 8-bit positive numbers and this has restrictions. (Fig. 1.2)
Rotating the segment at an angle greater than/lower than 45° increases/decreases the grey tones separation in proportion to the angle selected, this operation may cause display defects according to the image.
Let’s see the counter-clockwise rotation marked by the red arrow with the “+” sign that reflects the increase in contrast: in the example the grey tones of the original image between 0 and 85 are displayed between 0 and 255 and after the value 85 are all displayed with the value 255 (white). The increase in contrast henances the brightness difference between brightness and dark parts of the image by expanding gray tones in a ratio of about 1:3, which means that at 1 gray tone difference of the original image will correspond to 3 gray tones difference of the displayed image, so image details will be more distinct at the detriment of gray tones greater than 85. As the contrast increases according to the image from a certain point onwards, details become completely white, this is a displayed image defect.
Let’s see the hourly rotation marked by the red arrow with the sign “-” which reflects a decrease in contrast: in the example all 256 grey tones of the original image are displayed between 0 and 85. The decrease in contrast reduces the difference in brightness between bright and dark parts of the image with the “compression” gray tones in a ratio of about 3:1, this means that at 3 gray tones difference of the original image corresponds to 1 gray tone only of the displayed image, so the image details are less distinguished, this is a displayed image defect.
1.3 COLOUR IMAGES
The colors can be represented in different ways, the representation that we will adopt will be the one known as the RGB. In this representation all colours are defined through the three basic colours Red, Green and Blue. From the digital image viewpoint, any colour is described as RGB (red, green, blue) where red, green and blue are its 8-bit components. (Fig. 1.3)
Of the whole colour spectrum we are able to represent 16777216 (256x256x256x256) colors. In a digital image these 24bit are known as “bit-colour”. In the example figure, the color Orange is equal to the trio of RGB values (255,128,0)
All that has already mentioned about brightness and contrast apply exactly the same to colour components. Grey tones are a colour subset whose components have the identical value: for example, the grey tone 127 corresponds to the colour RGB (127,127,127,127).
Now from a mathematical viewpoint we know what it is:
- A Grayscale image
- The Brightness
- The Contrast
- A Colour image
In further we also know how to adjust these quantities
Brightness: Increase/decrease all gray tones of the same amount and we know that according to the image this may cause loss of detail. The change in brightness means that the angle of the “segment” can only assume two values 0° and 45°.
Contrast: Increase/decreases the gray tones separation in proportion to the selected angle and we know that according to the image this may cause loss of detail/visibility. Contrast variation means that the “segment” angle can assume values between 0° and 90°, in specific 45° is the angle that allows the biunivocal correspondence between the grey tones of the original image and the displayed one.
Note: These basics are already enough to perform a simple monitor calibration using its brightness and contrast controls by designing appropriate calibration images and without specific instrumentation or to verify by means of these images the calibration performed by specific instrumentation.
2. THE SHADERS
At the beginning the programming languages were in assembly completely oriented to the machine hardware. Later on, there were different language standardizations and the development of a software layer (API, an application programming interface) between rendering software and GPUs with C/C++ syntax, shifting the assembly code complexity to code compilation.
The graphic languages started in 1982 at SGI (Silicon Graphics Incorporated) a graphic terminal developer that developed the graphic workstation known as IRIX. Until the mid-1990s the SGI openGL was the standard de facto. In the mid 90’s Microsoft took OpenGL as a 3D system for Windows NT. Microsoft soon bought RenderMorphics, whose Reality Lab created a standardized 3D video game library known as Direct3D.
Microsoft, SGI and HP-MS made efforts to unify Direct3D and OpenGL which produced XGS (Fahrenheit system) but failed shortly afterwards.
There are currently two shading programming languages:
- GLSL (OpenGL Shading Language) language for OpenGL shading
- HLSL (High Level Shading Language) for Direct3D shading
These are the languages used by both those who write shaders in software houses and by shader programmers who are independent from the videogame vendor.
Actually there is a third language “Cg” which is however of higher level whose compiler can generate both OpenGL and HLSL code.
These are used to change the visual appearance of a video game (“modding”)
The design and use of shaders is in three phases:
- Writing Shading FUNCTIONS
To create a graphic effect you need the proper technical expertise in digital image processing and knowledge of GSLS/HLSL languages. These functions are written by experts in this field.
- Configuring the numerical PARAMETERS used by shading functions
The shading functions graphical effect can be made easier to set up (from the shading function programmer) by passing numerical values to functions. The modder’s main job in creating game presets is to establish the appropriate parameter values to achieve the desired graphical effect. We will do this with Cuphead.
- Shaders ACTIVATION
We have the game and shading functions, how to insert the shaders in the game?
The used technique is known as “injection”. An automatic Windows feature is used: when an executable calls up a function of any library, Windows first searches this library in the directory where the executable is present, if it does not find it then continues the search in system directory. So to run the shaders you have to put the new libraries in the directory where the program executable is present whose names and extensions are the same as the original libraries, the new libraries also contain the shaders appropriately called. This can be done either manually or through a program called “injector” of shaders in the game.
There are shaders written by independent programmers: ENBSeries, GemFX, SweetFX and their forks. Both for potentiality and ease of use, it’s no coincidence that there are a multitude of presets, we will use CeeJay’s SweetFX and the Crosire’s Reshade injector. SweetFX is a universal shader for modding and from version 2.0 upwards supports DirectX 8 to DirectX 11 and OpenGL, also supports Windows x32/x64 versions. ReShade is the most advanced existing injector. Starting from Reshade 3.0 version the injector integrates SweetFX, is DirectX 12 compatible and has its own in game menu.
The tutorial’s goal is educational and is aimed at the shaders use, therefore we will not use Reshade 3.0, but the previous version of easier use “ReShade 2.0.3 with SweetFX 2.0” deprecated from Reshade 3.0. This version is still online, but not on the official website.
We decompress the file ReShade 2.0.3 with SweetFX 2.0 (.rar, 1367 Kb), inside the compressed file we will find:
The available shaders code is found in the subdirectory “Shaders”. The file we will edit is “SweetFX_settings.txt” and the injector program is “ReShade Setup.exe”.
The file consists of three sections:
Description: This section contains the author’s notes.
Choose effects: Here you choose which effect to apply, the value available is “1” to enable the shader and “0” to disable the shader. When you change the video game appearance, the correct way to proceed is to disable all effects and then enable only those effects you are using one by one.
Effect settings: Here there are the shader parameters effects, changing these parameters the effect changes. For every parameter there is a description, the minimum/maximum values and are shown the default values. Some parameters are easy to understand while for others it is less simple, time after time you search the information needed to understand what that parameter does and then verify the effect.
2.1.1 “VINEGAR SYNDROME” EFFECT
At the beginning of the article we said we would like to apply the effect of vinegar syndrome or sepia effect to Cuphead. There is no unique way to make an effect. We have to do a few trials and then, depending on the expertise, choose the one that achieves the best effect: first we completely desaturate the image in a neutral way in relation to RGB colours, transforming it into grey tones and then we colour it with the colour shade that we will choose. To do this we will use the two shaders “MONOCHROME” to desaturate the images and “SEPIA” to color them. We can edit the file “SweetFX_settings. txt” with any text editor such as Windows notepad.
2.1.1a SHADER “MONOCHROME”
In the section “Choose effect” firstly we disable the unused shaders (setting the value to 0) and we enable the MONOCHROME shader:
#define USE_MONOCHROME 1 //[0 or 1] Monochrome : Monochrome makes the colors disappear.
In the section “Effect settings” we set the MONOCHROME shader parameters value:
Monochrome_conversion_values float3(0.33,0.33,0.33) //[0.00 to 1.00] Percentage of RGB to include (should sum up to 1.00)
Monochrome_color_saturation 0.00 //[0.00 to 2.00] Percentage of saturation to keep. Default is 0.00, values above 1.00 boost saturation above normal.
By using the first parameter we desaturate the RGB components (all three to 33%). According to the images you could get better/worst results by preferring the desaturation of one component over another.
By the second parameter we determine that each pixel in the image is fully desaturated.
2.1.1b SHADER “SEPIA”
In the section “Choose effect” We enable the shader SEPIA:
#define USE_SEPIA 1 //[0 or 1] Sepia : Sepia tones the image.
In the section “Effect settings” we set the shader SEPIA parameters value:
ColorTone float3(1.12, 0.66, 0.20) //[0.00 to 2.55, 0.00 to 2.55, 0.00 to 2.55] What color to tint the image
GreyPower 0.00 //[0.00 to 1.00] How much desaturate the image before tinting it
SepiaPower 0.60 //[0.00 to 1.00] How much to tint the image
The shader is named “SEPIA” with the default author setting. Obviously, by changing the value of the first parameter (ColorTone) we can use any color, as starting point we consider a typical sepia colouring given by the RGB coordinates (112,66,20).
By using the second parameter (GreyPower) we choose how much to desaturate the image. The image is already desaturated by the MONOCHROME shader, so let’s set this parameter to 0.00. We could have done everything just with the SEPIA shader, but the MONOCHROME shader has in addition the control over the desaturation of the individual RGB components of the image, for tutorial purposes and for the values chosen for the parameters it’s just an occasion to see how to configure the shaders.
By using the third parameter (SepiaPower) we set the colour shade intensity.
After saving the changes it’s time to inject the shaders.
We run “ReShade Setup. exe”, the dialog box will open as in Fig.2.1 (Shot 1)
Reshade will try to recognize the DirectX/OpenGL used version via the game/application executable, just click on “Select Game” to select the game/application executable. If the automatic detection is successful, the files shown in Fig. 2.1 will be copied to the game/application directory (Shot from 2 to 5)
If the automatic detection fails (Fig. 2.1 – Shot 6) then the DirectX/OpenGL version should be manually selected
After DirectX/OpenGL selection click on “Run Game”
To uninstall the shaders just delete the files shown in Fig. 2.1 (Shot 2 to 5)
You don’t install the shaders in Windows system directories
You must disable the same shaders in the game options because these may be in conflict with unpredictable results.
Currently for some online games there are “anticheater” systems that prevent the execution of the game and in extreme cases can cause the player’s ban. Reshade how also benchmarking systems could be wrongly identified as “cheat” systems. You should be aware of before using them.
3. CUPHEAD “MODDING”
Original game colour images:
In these images we see the of our ‘sepia’ filter effect: ColorTone float3(1.12, 0.66, 0.20)
The advanced vinegar syndrome consists of degrading the green and blue components and highlighting the red component: ColorTone float3(1.60, 0.50, 0.00)
This is a “steampunk” effect of an old widescreen black and white TV. The images of old black and white TVs usually have bluish tones: ColorTone float3(0.70, 1.40, 2.20)
eNJoy aND STay TuNeD WiTH uS!
Raffaele “MOS” Sanapo
This tutorial is just for educational purposes and all information is provided “as is as”. Links to third party websites are provided only for your advantage and information. There is no control and no responsibility for the content or websites of others that are linked. We disclaim all responsibilities for any loss, costs or damages direct or indirect resulting from the use of this tutorial. The use of collected or downloaded information from this tutorial it’s at your exclusive risk or danger. All product names, logos, and brands are property of their respective owners. All company, product and service names used in this tutorial are for identification purposes only. Use of these names, logos, and brands doesn’t imply endorsement of them.