Bump map: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
mNo edit summary
No edit summary
 
(84 intermediate revisions by 39 users not shown)
Line 1: Line 1:
A texture used to give a material the illusion of depth through refracting light cast upon it (pixel by pixel). The normal map is the successor to the "bump map", and is perhaps the main reason that games such as Doom 3, FarCry, and Half-Life 2 are able to look so much better than previous games. While bump maps only used one channel to decode the height of each pixel, normal maps use all three:
{{LanguageBar}}


* The '''red''' channel contains the horizontal facing (X-axis)
[[File:Brickwall021a normalcompare.jpg|thumb|200px|A material's [[albedo]] (left) compared to its bump map.]]
**0 = facing left
**128 = facing forward
**255 = facing right.
* The '''green''' channel includes the vertical facing (Y-axis)
**0 = facing up
**128 = facing forward
**255 = facing down
* The '''blue''' channel decodes the height of the pixel (Z-axis)
**0 = deepest
**127 = maximum depth capable of receiving light
**255 = at the material surface.


With these 3 channels, every pixel has a normal vector, means the engine knows in which direction the pixel is facing and can thereby calculate shadows and highlights.
[[File:Example of bump mapping.jpg|thumb|200px|The above material in-game.]]


A normal map is largely useless for really flat surfaces like smooth concrete or metal. But even rough concrete sometimes has enough depth to it to make a normal map worthwhile. [http://planetpixelemporium.com/tutorialpages/normal.html This] is an old article that explains the concept well.
Textures often called '''Bump Maps''', or '''Normal Maps''' are used to simulate three-dimensional details on a two-dimensional surface by manipulating its lighting.


The normal map should be created in the same dimensions as the image texture (color map) it will go with. There are a few options for how to create one, depending on the quality you want, your skill with various programs, or your patience.
{{note|
* Static props using bump maps can not be lit per-vertex, except in {{csgobranch}}. [[$lightmap]] is also incompatible with [[$bumpmap]].
* Bump maps cannot be used on normal [[decals]] and [[overlay]]s, except in {{csgobranch}}. In order for these to function properly, the surface that they are placed on must also have a bumpmap. Bumped "decals" can be created in any engine branch, regardless of underlying geometry, by using a [[Decals#brush_decals|brush or displacement with a $decal material]] instead.
}}


== Format ==


==Normal Map from an image editing program==
Each pixel in a bump map contains the (x, y, z) coordinates that define a [[normal]]ized [[vector]].


Because of this each color channel in a bump map has a meaning:


===Photoshop or Paint Shop Pro===
; Red
: Horizontal facing (X axis)
:* 0 = left
:* 128 = forward, or facing viewer
:* 255 = right
; Green
: Vertical facing (Y axis)
:* 0 = up
:* 128 = forward, or facing viewer
:* 255 = down
; Blue
: Height (Z axis).
:* 0 = facing 'in' to the texture, away from the viewer. This is a 'bad' value. Anything under 128 means that the surface should be facing away from the player, which is not possible.
:* 128 = maximum depth capable of receiving dynamic light. It's a bad idea to go under this.
:* 255 = facing 'out' of the texture towards the viewer.


{{note|As of Jasc Paint Shop Pro version 9, the NVIDIA normal map plugin no longer works. Use Photoshop or GIMP instead.}}
<!-- Someone please test if it works with v. 8 -->


If you have Photoshop or Paint Shop Pro, you can use [[Wikipedia:NVIDIA|NVIDIA's]] normal map tools, available [http://developer.nvidia.com/object/photoshop_dds_plugins.html here]. To use them, open a copy of the texture and apply the Normal Map filter. Fiddle with the settings until you get something that you feel will be bumpy enough for your texture. The greater the range of colors, the greater the range of angles on the surface. A very flat surface will look that medium blue color all over. If you want to have more control over it, change the image to grayscale and fiddle with brightness and contrast to make certain details stand out before applying the filter. Be sure to change the texture back to RGB mode, or else the normal map tool will not work. Do this step multiple times to get different details to stand out, and then put the images together in layers with the blend mode set to overlay.
{{note|A flat bump map should be [128, 128, 255]. <code>dev/flat_normal</code> is a flat bump map present in every game.}}


[[Image:Beforenormal.jpg|thumb|left|Image texture (color map).]]
The three channels represent a normal vector for every pixel which represents the direction that the pixel is facing in 3D space. This allows the engine to generate shadows and highlights on a two-dimensional surface, or give a 3D model more detail.
[[Image:afternormal.jpg|thumb|left|Normal map created from texture on left using NVIDIA's filter.]]<br style="clear:both">


A bump map is largely useless for really flat surfaces like smooth concrete or metal, but even smooth concrete sometimes has enough depth to it to make one worthwhile, especially if used in conjunction with a [[cubemap]].


===GIMP===
[[file:normalmap.gif]]


[[Image:gimp_normal1.jpg|thumb|220px|Place the windows in a useful way]]Download the tool for GIMP [http://nifelheim.dyndns.org/%7ecocidius/normalmap/ here] and install it following the instructions in the readme.
== Creation ==
After restarting GIMP, open a copy of the texture you want to create a normal map for and click:


<code>Filters -> Map -> Normalmap...</code>
A bump map should be rendered in Tangent space and use vector directions X+ Y- Z+.


Before starting, you should click the <code>3D Preview</code> and place it next to the normalmap window. It will automatically update when you change your settings and will be a great help checking the results in GIMP. Select a shape similar to what your texture will be placed on ingame (e.g. a texture for brushes fits onto a cube whereas a modelskin might look better on a sphere). Make use of the UV scale to find a good setting.
{{note|There are basically two sets of rules for normal maps: [[DirectX]] and [[OpenGL]]. Their interpretation of green channels are opposite. [[Source]] takes the former, whereas [[Source 2]] (even with DX11) takes the latter. Thus, the green channel may need to be inverted depending on the software used to create it. }}
Now focus on the <code>Normalmap</code> window again. These are the most useful settings:
[[Image:gimp_normal2.jpg|thumb|220px|The 3D Preview is used to check the settings]]
* Filter - different ways to calculate your normal map. You'll have to try out which one works best for your texture
* Minimum Z - the lowest height on your normal map
* Scale - Use this to control the height of your surface. Results change when you choose a different filter
* Conversion - Let's you choose what is used to create the normal map. Max/Min RGB or only one color channel can be very helpful sometimes
''Checkboxes:''
* Wrap - Normal map will pattern seamless. Definitely use it for map textures!
* Invert X/Y - If your normal map seems to be upside down, this will help
Check the 3D Preview from several angles, also move the light around. When you've found the best setting, hit <code>OK</code> and your texture is converted to the normal map!


However you may want to fine tune the result since GIMP considers RGB color #7f7fff to be flat, while Valve considers #8080ff to be flat. This means that you will have to increase every pixel in your image by 1 unit red and 1 unit green. {{todo|How on earth do you do that? Is there some way to adjust the standard flat color?}}
=== Programs ===


After fine tuning it, can either go with that right away or rework it by hand. For example, you could duplicate the layer and use some overlay effects (Dodge, Addition, ...) or paint on the texture yourself if you want to create a flat part. (Again, RGB #8080ff is flat according to Valve.)
[[File:Test_bump.jpg|thumb|600px|Bump maps as created by various programs.]]
[[Image:gimp_normal3.jpg|thumb|220px|left|Normal map was reworked by hand.]]<br style="clear:both">


Various programs can automate the creation of bump maps, either by image analysis or by using 3D geometry the user provides.


==Normal Map from a Height Map==
;2D
:[[Normal Map Creation in Photoshop or Paint Shop Pro|Photoshop or Paint Shop Pro]]
:[http://www.youtube.com/watch?v=WsFe-E-33IQ Substance Designer]
:[[Normal Map Creation in The GIMP|The GIMP]]
:[http://www.youtube.com/watch?v=xDZDWvTUz-c nDo]
:[http://filterforge.com/filters/8774.html Filter Forge]
:{{Xblahmt|4}}
:[https://charles.hollemeersch.net/njob/ nJob]
;3D
:[[Normal Map Creation in XSI|XSI]]
:[[Normal Map Creation in ZBrush|ZBrush]]
:[[Normal Map Creation in Lightwave|Lightwave]]
:[[Normal Map Creation in 3ds Max|3ds Max]]
:[[Normal Map Creation in Maya|Maya]]
:[[Normal Map Creation in Blender|Blender]]
:[[Materialize]]
:[http://www.nvidia.com/object/melody_home.html NVIDIA Melody]
:[http://planetpixelemporium.com/tutorialpages/normal2.html Cinema 4D]
:[[NormalMapper]]
:[http://xnormal.net/ xNormal]
;Other
:[https://cpetry.github.io/NormalMap-Online/ NormalMap Online]: A website for generating normal maps online.
:[https://sourceforge.net/projects/ssbumpgenerator/ SSBump Generator 5.3]: Despite the name, it can also be used to generate bumpmaps, not just [[$ssbump|self-shadowing bump maps]].
:[http://store.steampowered.com/app/325910/ Substance B2M3]: Previously known as BitMap2Material.
:[https://sites.google.com/site/ccdsurgeon/download/ InsaneBump]: Specifically made to be a free alternative to the now-superseded software "CrazyBump" (may trip antiviruses due to incompatibility)


{{todo|I think there is a Valve tool for this.}}
=== Conversion ===


==== In [[VTFEdit]] ====


==Normal Map from a 3D program==
When converting your texture:
*[http://developer.nvidia.com/object/melody_home.html NVIDIA Melody]
*[[NormalMapper]]


# Choose your image format. Uncompressed formats like BGR888 are higher-quality than compressed formats like DXT1, but be wary of file size.
# Check the "Normal map" box in the texture's flags list after the import is complete. It's about 1/5 of the way down the list.


===XSI===
{{tip|VTFEdit can automatically generate bump maps. See the bottom-right of the import screen.}}
*ver 5.0: [http://www.softimage.com/products/xsi/v5/nfvt/default.asp Ultimapper]
*ver 4.x: GPU Surface FX2 {{note|You need XSI foundation or higher to take advantage of this feature, as the Mod Tool has a resolution limit.}}
*ver 3.5: GPU surface FX


The name has been changing, but the basic usage is the same:
==== In [[Vtex (Source 1)|Vtex]] ====


*Move the high resolution model over the low resolution model.
# Save your normal map as a [[TGA]]. Give it a name that ends in ''_normal''. The ''_normal'' at the end of the name will affect how [[Vtex (Source 1)|Vtex]] converts it. For the brick wall example, we would name the file <code>brickwall_normal.tga</code>.
*Select the low resolution one.
# Add <code>nocompress 1</code> and <code>normal 1</code> to <texture filename>.txt in the same folder as your texture, then compile.
*Select GPU Surface FX2 (or Ultimapper in ver 5.0) from Get>Property.
*In GPU Surface FX2 menu:
**Pick the high resolution model (or group).
**Make sure '''normal''' is checked.
**Change settings such as normal map resolution, path, sampling, as you want.
**If you are ready, click '''Regenerate''' button. XSI will make the normal map.
**To preview the result, set Preview Display and Hardware correctly and click '''Create Preview'''.
**'''Important:''' It is necessary to '''invert the green channel of the output file'''. If you don't do this, your up/down normals will appear '''inverted''' in the engine. The simplest way to do this in Photoshop is to open a channel window, (window:channel) select the green channel (which controls up/down), and press Ctrl+I.


===ZBrush2===
=== Implementation ===


==Getting the Normal Map in the Game==
Normal maps can be generated from a basetexture, using the Sobel Operator, by sampling the HSB Brightness of each pixel and adjoining pixel to determine the scale of the output Hue and Saturation values that are subsequently converted to RGB for the SetPixel operation.
 
<syntaxhighlight lang=cpp>
<ol><li>Save your normal map as a [[TGA]] in the same resolution as the original texture. Give it a name that ends in _normal. Then convert it to a VTF. The _normal at the end of the name will affect how [[Vtex]] converts it. For the brick wall example, we would name the file <code>brickwall_normal.tga</code>. Place the new VTF in the same place as your original texture.</li>
Bitmap image = (Bitmap) Bitmap.FromFile(@"yourpath/yourimage.jpg");
<li>Add this line to the VMT somewhere between the braces:<br/>
int w = image.Width - 1;
 
int h = image.Height - 1;
<pre>"$bumpmap" "texture name"</pre>
float sample_l;
and fill in the path and name of your normal map VTF. Here's an example VMT for a normal-mapped material:
float sample_r;
<pre><nowiki>"LightmappedGeneric"
float sample_u;
float sample_d;
float x_vector;
float y_vector;
Bitmap normal = new Bitmap(image.Width, image.Height);
for (int y = 0; y < w + 1; y++)
{
{
    "$basetexture" "walls/brickwall"
for (int x = 0; x < h + 1; x++)
    "$surfaceprop" "brick"
{
    "$bumpmap" "walls/brickwall_normal"
if (x > 0) { sample_l = image.GetPixel(x - 1, y).GetBrightness(); }
}</nowiki></pre>
else { sample_l = image.GetPixel(x, y).GetBrightness(); }
 
if (x < w) { sample_r = image.GetPixel(x + 1, y).GetBrightness(); }
{{Note|Multiple materials can use the same normal map file.}}</li>
else { sample_r = image.GetPixel(x, y).GetBrightness(); }
 
if (y > 1) { sample_u = image.GetPixel(x, y - 1).GetBrightness(); }
<li>When compiling your maps with vtex, Add this line to the <texture filename>.txt file in the same directory as your textures then compile with vtex.
else { sample_u = image.GetPixel(x, y).GetBrightness(); }
<br/>
if (y < h) { sample_d = image.GetPixel(x, y + 1).GetBrightness(); }
<pre>"nocompress" "1"
else { sample_d = image.GetPixel(x, y).GetBrightness(); }
"normal" "1"</pre></li></ol>
x_vector = (((sample_l - sample_r) + 1) * .5f) * 255;
y_vector = (((sample_u - sample_d) + 1) * .5f) * 255;
Color col = Color.FromArgb(255, (int)x_vector, (int)y_vector, 255);
normal.SetPixel(x, y, col);
}
}
</syntaxhighlight>
See {{ent|$bumpmap}}.


==See also==
==See also==


[[Creating Materials]]
*{{ent|$ssbump}}: creation and usage of Valve's new self-shadowing bump maps.
 
*[http://planetpixelemporium.com/tutorialpages/normal.html An old article that explains normal mapping quite well]
[[Height2Normal]]
*[http://wiki.polycount.com/wiki/Normal_map Polycount wiki page about normal maps]
 
*[[Creating a Material]]
 
 
[[category:material system]][[category:tutorials]]


{{otherlang:en}} {{otherlang:en:jp|Creating Normal Maps:jp}}
[[Category:Material System]]
[[Category:Glossary]]
[[Category:Tutorials]]

Latest revision as of 08:47, 14 April 2025

English (en)Español (es)Français (fr)Português do Brasil (pt-br)Русский (ru)中文 (zh)Translate (Translate)
A material's albedo (left) compared to its bump map.
The above material in-game.

Textures often called Bump Maps, or Normal Maps are used to simulate three-dimensional details on a two-dimensional surface by manipulating its lighting.

Note.pngNote:
  • Static props using bump maps can not be lit per-vertex, except in CS:GO engine branch. $lightmap is also incompatible with $bumpmap.
  • Bump maps cannot be used on normal decals and overlays, except in CS:GO engine branch. In order for these to function properly, the surface that they are placed on must also have a bumpmap. Bumped "decals" can be created in any engine branch, regardless of underlying geometry, by using a brush or displacement with a $decal material instead.

Format

Each pixel in a bump map contains the (x, y, z) coordinates that define a normalized vector.

Because of this each color channel in a bump map has a meaning:

Red
Horizontal facing (X axis)
  • 0 = left
  • 128 = forward, or facing viewer
  • 255 = right
Green
Vertical facing (Y axis)
  • 0 = up
  • 128 = forward, or facing viewer
  • 255 = down
Blue
Height (Z axis).
  • 0 = facing 'in' to the texture, away from the viewer. This is a 'bad' value. Anything under 128 means that the surface should be facing away from the player, which is not possible.
  • 128 = maximum depth capable of receiving dynamic light. It's a bad idea to go under this.
  • 255 = facing 'out' of the texture towards the viewer.


Note.pngNote:A flat bump map should be [128, 128, 255]. dev/flat_normal is a flat bump map present in every game.

The three channels represent a normal vector for every pixel which represents the direction that the pixel is facing in 3D space. This allows the engine to generate shadows and highlights on a two-dimensional surface, or give a 3D model more detail.

A bump map is largely useless for really flat surfaces like smooth concrete or metal, but even smooth concrete sometimes has enough depth to it to make one worthwhile, especially if used in conjunction with a cubemap.

Normalmap.gif

Creation

A bump map should be rendered in Tangent space and use vector directions X+ Y- Z+.

Note.pngNote:There are basically two sets of rules for normal maps: DirectX and OpenGL. Their interpretation of green channels are opposite. Source takes the former, whereas Source 2 (even with DX11) takes the latter. Thus, the green channel may need to be inverted depending on the software used to create it.

Programs

Bump maps as created by various programs.

Various programs can automate the creation of bump maps, either by image analysis or by using 3D geometry the user provides.

2D
Photoshop or Paint Shop Pro
Substance Designer
The GIMP
nDo
Filter Forge
XBLAH's Modding Tool XBLAH's Modding Tool
nJob
3D
XSI
ZBrush
Lightwave
3ds Max
Maya
Blender
Materialize
NVIDIA Melody
Cinema 4D
NormalMapper
xNormal
Other
NormalMap Online: A website for generating normal maps online.
SSBump Generator 5.3: Despite the name, it can also be used to generate bumpmaps, not just self-shadowing bump maps.
Substance B2M3: Previously known as BitMap2Material.
InsaneBump: Specifically made to be a free alternative to the now-superseded software "CrazyBump" (may trip antiviruses due to incompatibility)

Conversion

In VTFEdit

When converting your texture:

  1. Choose your image format. Uncompressed formats like BGR888 are higher-quality than compressed formats like DXT1, but be wary of file size.
  2. Check the "Normal map" box in the texture's flags list after the import is complete. It's about 1/5 of the way down the list.
Tip.pngTip:VTFEdit can automatically generate bump maps. See the bottom-right of the import screen.

In Vtex

  1. Save your normal map as a TGA. Give it a name that ends in _normal. The _normal at the end of the name will affect how Vtex converts it. For the brick wall example, we would name the file brickwall_normal.tga.
  2. Add nocompress 1 and normal 1 to <texture filename>.txt in the same folder as your texture, then compile.

Implementation

Normal maps can be generated from a basetexture, using the Sobel Operator, by sampling the HSB Brightness of each pixel and adjoining pixel to determine the scale of the output Hue and Saturation values that are subsequently converted to RGB for the SetPixel operation.

Bitmap image = (Bitmap) Bitmap.FromFile(@"yourpath/yourimage.jpg");
int w = image.Width - 1;
int h = image.Height - 1;
float sample_l;
float sample_r;
float sample_u;
float sample_d;
float x_vector;
float y_vector;
Bitmap normal = new Bitmap(image.Width, image.Height);
for (int y = 0; y < w + 1; y++)
{
	for (int x = 0; x < h + 1; x++)
	{
		if (x > 0) { sample_l = image.GetPixel(x - 1, y).GetBrightness(); }
		else { sample_l = image.GetPixel(x, y).GetBrightness(); }
		if (x < w) { sample_r = image.GetPixel(x + 1, y).GetBrightness(); }
		else { sample_r = image.GetPixel(x, y).GetBrightness(); }
		if (y > 1) { sample_u = image.GetPixel(x, y - 1).GetBrightness(); }
		else { sample_u = image.GetPixel(x, y).GetBrightness(); }
		if (y < h) { sample_d = image.GetPixel(x, y + 1).GetBrightness(); }
		else { sample_d = image.GetPixel(x, y).GetBrightness(); }
		x_vector = (((sample_l - sample_r) + 1) * .5f) * 255;
		y_vector = (((sample_u - sample_d) + 1) * .5f) * 255;
		Color col = Color.FromArgb(255, (int)x_vector, (int)y_vector, 255);
		normal.SetPixel(x, y, col);
		}
	}

See $bumpmap.

See also