Games are either in 2D, 3D, or a combination of both. 2D and 3D graphics have some areas in common and areas that are unique to each. The core knowledge that makes up both 2D and 3D graphics is mathematics and computer architecture. Everything in computer graphics is mathematical. This includes colors, virtual objects, and interactions with game objects, lighting, shadows, and so forth. One of the main requirements to being able to work in the computer graphics field is to have a solid understanding of different mathematics because without this knowledge, computer graphics are very difficult to master.In this article, we'll explore the basics that make up 2D and 3D graphics.
The smallest unit of memory that users work with in a computer is the bit, and the next smallest unit of memory is a byte. Eight bits make a byte, four bytes make an integer, and so forth. Bits and bytes are very important in computer graphics, where the higher the number of bits, the higher the precision we can represent. What this means is that the more bits there are in a variable, the higher the range of values that can be represented.
In the table below you can see a few common C++ data type names and the total number of bits that make up them.
In this section we examine memory bits and bytes a little more closely to gain a deeper understanding of them. We also discuss general game mathematics that can be found in computer graphics and geometry and primitives in 2D and 3D.
In computers, a bit is a value that is either a 0 or a 1 and is also known as a binary digit. The 1 or 0 value of a bit can be thought of as being either true or false, or it can be thought of as being on or being off. The bit is the smallest memory unit that is used for storing data.
The more bits that are used, the more data can be stored. A single bit is one of two values. Add another bit and, with the use of some binary mathematics, higher values can be stored. For example, in our base-10 number system we have a ones position, a tens position, a hundreds position, a thousands position, and so forth. The number in each of these positions tells us something about the value it represents. If we see a 7 in the ones position, that is a value of 7. If the ones position has a value of 5 and the tens position has a value of 7, the number is 75. It is natural for us to deduce this because this is the numbering system we’ve used throughout our lives.
Base-10 mathematics is trivial. Binary mathematics follows the same rules with the exception that instead of using base-10 we are using base-2. That means each position can be one of two values instead of one of 10 values as seen with base-10. In the first position in base-2 a value is either 0 or 1. The second position represents how many objects are in the “twos” position, the third position represents how many “fours” objects there are, and so forth
Let's take a look at the following example to understand this better.
We can see that 1 bit can store up to two values (0 through 1), 2 bits can store up to four values, 3 bits can store up to eight values, 4 bits can store up to 16 values, 5 bits can store up to 32 values, 6 bits can store up to 64 values, 7 bits can store up to 128 values, and 8 bits can store up to 256 values, or 0 through 255 in some programming languages. A char data type in the computer programming languages C and C++ is made up of 8 bits. This means a char can store values between 0 and 255. Keep this in mind because we will revisit it when we talk about colors, which are often represented as three chars with one for the red, one for the green.
If one needs to store a certain range of values, the necessary number of bits must be available. Going past a range a set of bits can store leads to overflowing.
Memory storage is typically labeled based on the total number of bits. For example, a byte is 8 bits. A kilobit is 1,000 bits, a megabit is 1 million bits, a gigabit is 1 billion bits, a terabit is 1 trillion bits, a petabit is 1 quadrillion bits, and so forth. In the computer market we usually discuss memory in terms of bytes, with a kilobyte being 1,000 bytes (8,000 bits), a megabyte being 1 million bytes, and so forth.
Actually, a megabyte is 1,024 kilobytes, and so on, but values are commonly rounded down to the nearest 100 or 1,000 in some discussions. Because we are dealing with base-2, each position is raised to the second power, so the number 210 is equal to 1,024, which is often rounded down to 1,000.
Bytes are the second-smallest memory unit we commonly use in computer programming. The ASCII characters we use can be represented in a byte, also known as a char in programming languages such as C, C++, and C#. String objects are made up of an array of bytes, with one byte for each character (which can be a letter, symbol, number, etc.) in the string.
Multibyte values are common in computer programming. Multibyte values allow us to store a higher range of values than with a single byte, which only has a range of 256 values. Multibyte values have byte-ordering, or “endianess.” The byte-ordering of a value specifies which byte is the least significant byte and which is the most significant byte. If the first byte is the least significant byte, then the last byte is the most significant byte and vice versa. The most significant byte is stored at the memory location with the lowest or highest address in the range of addresses that make up the variable. Little-endian order, which is a type of byte-ordering, has its most significant byte at the lowest address, while big-endian order has its most significant byte opposite that of the little endian order.
Little- and big-endian orders are the most common byte orders. There is also a middle-endian order.
In computer hardware the byte-ordering is very important when storing and transmitting data between two or more machines that operate using different byte-ordering. For example, Intel Pentium processors use little-endian order, and PowerPC processors (common in Mac computers) use big-endian order. To have hardware systems that use different byte-ordering be able to communicate with one another, the data must have their bytes swapped so that the destination machine is able to interpret correctly any data it receives.
This solution is simple and can be done by casting a variable to a character pointer and performing the swapping in a loop, but programmers who are unaware of byte-ordering can encounter what appear to be mysterious critical errors when trying to share and transmit data between different platforms. No action needs to be taken regarding endianness when working with machines that use the same byte-ordering, but be aware of it otherwise.
Another common type of value is called hexadecimal value. These values operate in base-16, whereas binary values operate in base-2 and our decimal system numbers operate in base-10. In hexadecimal values each position is a value between 0 and F. This means one hexadecimal position can be either 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, A, B, C, D, E, or F. The hexadecimal value “BA,” for example, has the base-10 value of 186.
The math to determine this is as follows:
BA =186 BA = (B = 11) * (16 = value of this position) + (A = 10) * (1 = value of this position) BA = (11 * 16) + (10 * 1) BA = (176) + 10 BA = 186
Hexadecimal values can come in a triplet known as the hex triplet. This is commonly used in different technologies such as HTML and cascading style sheets as a means to specify color values. In a three-component 24-bit color specified in HTML there are two values for each component. For example, pure white is #FFFFFF, where # is a symbol used to mark the start of a hexadecimal value; the first FF is the red component (with a value of 255 or 240 [15 * 16] for the first F plus 15 for the second F), the second FF is the green component, and the third FF is the blue component.
As another example, the color #68A3F8 can be translated as
Color = #68A3F8 Red = 68 = (6 * 16) + (8 * 1) which = 96 + 8 or 104 Green = A3 = (10 * 16) + (3 * 1) which = 160 + 3 or 163 Blue = F8 = (15 * 16) + (8 * 1) which = 240 + 8 or 248 Final Color = Red(104), Green(163), Blue(248)
Many tools allow colors to be chosen and displayed in hexadecimal form. Adobe Photoshop is one of these tools that allow a color to be specified by choosing it from a color picker, specifying the value of each component’s byte, or by specifying a hexadecimal triplet.
The human eye can see a lot of detail in a wide range of luminous values. When people look at something artificial like computer graphics it is at times very easy for the human eye to pick out things that are not true to nature. The closer we can get to creating scenes that are believable to the human eye, the more realistic our scenes will look to us and those around us.
In computer graphics, the representation of color values and how they are translated to the computer monitor (or TV screen) is very important to being able to create the types of imagery you are looking to create, especially if realism is the goal. As color information moves throughout the rendering pipeline, it is operated on to build the final color. The information that makes up this data can become lost due to inadequate precision in the color’s memory. Colors in computer graphics are generally in a low dynamic range of values or a high dynamic range.
Low-dynamic-range, or LDR rendering, is what is commonly referred to as rendering in the traditional manner or, in other words, basically not using a high dynamic range of values during the rendering pipeline. Commonly, although not necessarily, we use a maximum of 8 bits per component for the color values in a rendered scene. The range of colors that can be displayed is very limited—hence the “low” in LDR. When rendering in LDR, the detail, precision, and quality of the colors of the rendered scene can be lost throughout the rendering pipeline. When this happens, the final results often have various artifacts visible to the screen.
Also, saturated colors can dominate the rendered scene if colors that are brighter than the display attempt to be drawn.
Using a maximum of 8 bits per component is not necessarily a bad thing. Also, for lighting, a higher range of values is very beneficial to not only decrease rendering artifacts but also to retain as much quality throughout the pipeline as possible. This can be seen in many of today’s modern games and is becoming quite common in the industry.
When working with 8-bit color components, or less, there is a low limited range of values that can be used for a color that can have a negative impact on the final rendered scene. As we already know, 8 bits can store 256 values. For lighting, colors, and what the human eye can see this is not a lot of detail, even for a color that is made up of four 8-bit components (32-bit RGBA). Also, the limited, and in this case fixed, range of values does not allow for high dynamic contrast. If colors are too bright, they are saturated, and eventually the detail can’t be seen. In reality the human eye can see detail in a very large range of luminance colors, and we can adapt to different levels of brightness.
Modern video games, and other graphical products, make use of high dynamic-range rendering, also known as HDR rendering. The idea behind HDR rendering is to use more bits per color component so that not only a higher precision of values can be stored, but also a higher range. Using HDR for rendering allows us to retain quality and to decrease rendering artifacts such as banding. Once rendered, this HDR data has various algorithms applied to it to map those high ranges of values to a range that can be rendered to modern TV screens and monitors. This adjustment, which happens after adding various special effects such as blooms, streaks, and so on, is known as exposure adjustment and is something the human eye does naturally when moving from a dark environment to a light one and vice versa (e.g., getting up in the middle of the night and turning on the light after spending hours in the dark).
Exposure adjustment is also known as tone mapping.
The purpose of HDR rendering is to preserve lighting and color detail throughout the rendering pipeline and to display that information to the screen. Before displaying the results to the screen, a process commonly referred to as tone mapping is performed to transform that HDR information into a range that can be displayed on a monitor. This tone mapping process attempts to mimic the human eye’s ability to adjust to the information it is viewing by adapting to the visual information. This can be seen in many games, such as Ubisoft’s Splinter Cell Double Agent for the Xbox.
Using a higher precision for colors can allow you to retain more data during renderings.
HDR is definitely important to research when learning and trying to implement computer graphics because the final results of a rendered scene can be affected by the quality and detail of the image that is generated. In games, graphics are very important, and this quality, as well as the ability to add various and natural special effects, can help improve the look of the overall rendered scene. HDR has become standard in modern 3D video games.