Material Series
Building Height & Normal Maps from Geometry
Version 2.0, Updated Jun 2024 using Octane 2023.1.3 and Cinema 4D 2024.4
About This Guide
This guide explores how to create both height maps and tangent space normal maps from geometry objects in our scene using Cinema 4D and Octane. For more general information about the bump and normal channels and how to use the maps built here, check out this guide.
This guide is split into two parts:
Part I covers some general pointers about creating normal maps.
Part II is a series of step-by-step setups for creating both kinds of maps in native C4D and Octane.
PDF Version of this guide can be found here
Part I: General Information
Height Maps
Height maps can be thought of as a stack of slices along one dimension. Each slice is represented by a different gray value, with white being the topmost layer, black being the bottom-most, and each intermediate layer being a value on a gradient between them. When this 2D stack is compressed into a flat image, we end up with a pixel grid of different grays, each one representing a different height.
We’ll see the term “height map” and “depth map” used interchangeably through this guide. The creation method and tech is similar (using a depth pass/AOV), but height map is what the material building world calls the final image when it’s used for bump/displacement, and depth map is what the post production world calls it when it’s used as a pass/AOV for depth of field/volume effects and the like.
Both the bump and displacement channels use height maps to determine how much to displace polygons (displacement) or fake a certain amount of height using shading (bump).
Normal Maps
Normal maps are used exclusively by the Normal channel in a material. Unlike height maps that are only grayscale, they contain data in the R, G, and B channels that tell the render engine how to tilt normals to add extra surface detail to an object without adding extra geometry (more on that here).
Camera considerations
Height maps are best made with an orthographic/parallel camera (more on that here). Because this tech essentially works on one dimension, If a perspective camera is used, lens distortion will create inaccurate raised or lowered areas and we might get some unwanted/unexpected curvature or weirdness when feeding it into either the bump or (especially) displacement channels.
Normal maps are a bit more forgiving because of the way the tech works (direct tilting rather than raising and estimating). A standard perspective camera might work better if we’re looking for a decal or bas-relief scene, but if we’re after a pattern (especially a repeating one), ortho/parallel is still the way to go.
Geometry considerations
Real-world scale isn’t strictly needed when creating height or maps, but it’s helpful in some cases if we’re trying to size objects to each other appropriately. What we really want to make sure of is that our depth is correctly scaled to the height and width of the image.
Even if the geometry looks right in the orthographic view, if something like a coin rim is way too deep compared to the face, the whole map will look wrong when applied to the final geometry. This is especially important for a height map used for displacement since bump/normal have to flatten everything out anyway due to them not truly displacing geometry.
Detail of the models. Both height and normal maps are sensitive to the smoothness of the surface of our objects. If we’re building something like a sphere, cylinder or other curved object, we want to make sure we give it enough segments either through simply adding more subdivisions in the primitives/NURBS construction pieces or by using a Subdivision Surface object. If the geometry is too rough, we’ll get faceting that shows up in the bump/normal/displacement effect if we get too close to the model.
Background: By default, if there’s nothing in a portion of the scene, Octane will produce gray pixels, and C4D will produce black ones. Both of these are bad for normal maps and will cause problems (as seen above). Unaltered portions of the normal map need the color to be (in a linear color space) R:0.5, G:0.5, B:1. We can get that by putting in a plane object that’s flat-on to the camera with the normals facing the correct direction (+Z for C4D world space, -Z for Octane world space & Octane camera space).
Phong angle: If we’re bringing in a CAD model, it may not have a phong tag or it may be set to 0°. This causes the model to look jaggy and triangulated, which will translate over to our height or normal maps. This is fixed by setting the phong angle to 40°, and/or deleting the Normal Tag if one exists.
Polygon normals: Polygon normals should always be facing “out” (blue instead of orange when seen in polygons mode). Normal maps are especially sensitive to this and will create all manner of visual errors if some of the polygon normals are flipped (see above).
Map quality
Resolution is how dense the pixel grid is. An “acceptable” resolution depends fully on how large of an area of the model the texture has to cover and how close the camera is to it.
Bit depth controls how many different colors (or grays) the image can contain. The higher the bit depth, the more height levels or normal angles it can store. 8-bit is usually fine for bump maps. Normal maps may have to go to 16-bit depending on how much movement in the normals there are and. This is especially true for object space maps, but some tangent space maps may benefit from 16 bit if the camera gets close. Displacement maps should start at 16-bit and go to 32-bit if they’re high detail.
Height maps (good ones, anyway) can’t be antialiased, because the intermediate grays in the antialiasing would read as different heights to the system, and we’d end up with these weird ramps around the edges of our curves. The only way to smooth curves out along the X/Z axis is to make the texture more dense, meaning a higher resolution map. The curves will still be steppy if we zoom in too much, but there’s usually a point where the density is tight enough that it reads as smooth to our eyes.
Normal maps are a bit more forgiving in this regard because antialiasing pixels basically just tilt the normals to form smooth curves. That said, it’ll still look low res and blurry if we get too close, so higher res normal maps are needed for tighter shots.
The color space is very important for both height and normal maps to work properly. Both should be linear for best results. When viewed on a nonlinear monitor (which we all have), normal maps rendered in a linear color space appear washed out. This is a monitor issue, not a map one - the maps are correct.
If we look at the image in an image editor like Affinity Photo or Photoshop, the colors in our normal maps are rich, we probably rendered it in the wrong space. If it’s a dark blue like on the far left above, we really effed it up bad :D.
Height maps are harder to diagnose just by looking at them, so we need to make sure to double check the color space settings when rendering. Bump will generally work out ok even if we have the wrong color space, but displacement will suffer pretty badly from the wrong space, especially if the pattern is geometric.
In all cases, when we bring the maps back into Octane, it’s important to set the color space to non-color data in the Image Texture node so Octane knows that they’re linear.
There’s some weirdness with Octane’s Live Viewer representation of the Normal pass. It shows as tonemapped when we run the render, but if we save directly from the Live Viewer using the sRGB color space (NOT Linear sRGB), the resulting map will come out correct.
Tangent Space vs. Object Space Normal Maps
This guide covers building Tangent Space normal maps that can be applied to any geometry to add fine detail to a surface without increasing the polycount. It doesn’t cover Object Space normal maps which are specific to one piece of geometry and usually used to visually smooth out low-poly models. Object space normal map creation is done in a process called Baking, and while Octane is capable of this, it’s generally done during the sculpting/asset development workflow and out of the scope of this guide.
World Space vs. Camera Space Calculations
When applying normal maps to our materials, we generally only worry about whether they are Tangent space or Object space. When we’re building normal maps, we’ll be thinking in terms of World Space vs. Camera Space.
If we’re using a World Space system, that means the normal values are calculated based on the world’s axis. World Y is “up” and “down”, World X is “left” and “right”, and World Z is “forward” and “backward”. This is a much easier setup, but it means our camera has to be fixed in place and aligned to the world axis and then we have to move all of our geometry around and frame up our final scene that way or the maps will come out wrong.
If we’re using a Camera Space system, then we’re using the camera’s coordinates. “Forward” and “backward” are now toward and away from the camera, and “up”, “down”, “left” and “right” are relative to the camera’s Y and X axes in its current position. Camera space systems allow for more freedom and flexibility of both the camera and object position, but in the case of normal maps, some extra steps have to be taken to create a good map.
In both C4D and Octane, height maps are most easily created in camera space, while normal maps are generally created in world space.
Converting a height map to a normal map
It’s possible to convert height maps to normal maps with varying levels of success. The advantage here is that if we only have a height map and not the original source data, but need it to be a normal map, this is quite a bit faster than recreating the source data from scratch. The one main drawback is that height maps are not antialiased, so that can lead to artifacts in the resulting normal maps when the camera gets too close. Silverwing has a good tutorial on how to do this.
How map creation works
The fastest and easiest way to make maps is using the multi-pass (C4D) or AOV (Octane) system.
Height Maps
C4D’s Depth pass is set up by default for depth of field camera effects in post. This is close to what we want, but not exactly. DoF works the way a camera lens does - there’s a focus plane, and things both in front of and behind the focus place go out of focus the further they get from the focus plane. This translates to the values going toward white again as they get further back from the max z-depth (focus distance). Great for DoF, bad for height maps, since objects further back will read as higher than closer ones when used as a height map and fed into bump and displacement.
To fix this, we just have to make sure DoF Map Rear Blur is off in the Details tab of the camera. Now the focal plane (Focus Distance) and anything further away from it renders black in the map, and the DOF Map Front Blur End point and anything closer to it renders white in the map. We need to leave DOF Map Front Blur Start at 0cm. It’s fiddly, but it makes sense once we’re used to it.
Octane’s z-depth pass is set up by default for fog effects which work the opposite way a height map does. As a result, things closest to the camera show up black, and then get lighter and lighter as they go back into space until they hit the furthest point which renders white. We also need to add a z-depth pass to control the z-depth max, otherwise it defaults to 500cm (or 5m).
This means two things for us - first off, we need the camera object to be right up against the frontmost object (meaning we have to use an ortho/parallel camera to see anything at all). Second, we need to find some way to invert the map after we render it. We can do that by building a custom AOV, inverting it in post in an image editor, or inverting it in the Image Texture node when we bring it into an Octane material.
Normal Maps
C4D itself doesn’t really have a concept of camera space normal map creation. It can be done with lengthy hacks and workarounds, but it’s probably not worth it since Octane’s method is super easy.
For world space normal map creation, we use C4D’s Material Normal pass. This pass was made for… reasons… and it reverses the Z axis (compared to how the rest of the program works), so if the camera is facing “front”, normals are flipped on Z and what should be blue shows yellow. This is annoying.
We can fix it by swinging the camera around so it’s facing the other way on Z, but this means we need to work kind of backwards (flip all our geometry around 180 degrees on R.H). There are other ways to reverse just the Z axis by using the Normal tag to alter the model’s geometric normals (not great for reusability, doesn’t work on procedural shapes, have to remember to do it for every model) or fixing it in post (extra step, not great for auditioning different maps quickly).
We’re going to explore two different AOVs in Octane that can make normal maps. The built-in Normal AOV is really fast and easy, but it’s world space only. If we want to use camera space, we need to set up a custom Global Texture AOV which takes a few extra steps.
Fortunately, both the world space Normal AOV and camera space Global Texture AOV works on the same Z-axis as we’d expect, so we don’t have to reposition the geometry or flip the camera or anything, which is fantastic.
In both cases the best way to save the map is directly exporting from the Live Viewer.
Important: For the world space Normal AOV, we need to choose sRGB (NOT Linear sRGB, weird as that is), to save correctly. If we’re setting up a custom Global Texture AOV to make a Camera space normal map, then we need to set it to Linear sRGB (NOT sRGB) to get the colors to render correctly.
Part II: Step-by-step Setups
Creating a height map using native C4D
" target="_blank" " alt="OTOYnumba1"
The depth pass is camera space, so the camera object itself can be placed anywhere in relation to the model, but it’s much easier to set the scene up if the camera is aligned to and facing exactly along either the X, Y, or Z axis.
- Make the camera parallel: Camera>Object>Projection>Parallel
- Frame the scene: Camera>Object>Zoom to make everything larger or smaller, and then move the objects around to lay out the composition. Move the camera back far enough to prevent clipping.
- Set the maximum depth: Camera>Object Tab>Focus Distance is the maximum depth value of the depth map. It should be just a little further away than the furthest point in the geometry we want to have in the map. Anything beyond the focus distance will be clipped to black.
- Set the minimum depth: Turn ON Camera>Details>DOF Map Front Blur>End is the nearest point of the depth map (100% white). Set this to a little bit in front of the closest point in the geometry (we can see this visually in the viewports). Leave DOF Map Front Blur>Start at 0.
- Important: Leave DOF Map Rear Blur>End OFF. If this is on, then the values start to go toward white again behind the focal plane because of the way the blur system works.
- Add the depth pass: Go to C4D’s Render Settings (Ctl-B/Cmd-B). On the left, we need to enable Multi-pass, then from the Multi-pass button below, select Depth to get the depth pass.
- Set the render settings: Under Regular Image, we can just turn off Save - we don’t need the beauty pass. Under Multi-pass, we need to turn on Save, set the name and location of the passes, and choose a format. OpenEXR tends to work the best. If we twirl down the arrow to the left of Format, we get more options. Compression method should be PIZ. Depth (bit depth) can either be 16-bit which is more than enough for bump, or 32-bit which is sometimes needed for displacement. Uncheck Multi-Layer file.
- Render to Picture Viewer. This will generate just the Depth pass and save it. In the PV’s Layer tab, if we choose “single pass” and click “Depth” we can see what the map looks like. It can then be brought into Octane and hooked up to either the Bump or Displacement channels in an Image Texture node. Remember to set the Image Texture node’s Color Space to non-color data.
Creating a height map using Octane
-
Set the C4D camera to Parallel (Camera object>Object tab>Projection: Parallel).
Important: we need to make sure the camera doesn’t go behind the frontmost point (intersect the geometry), or clipping will occur. Also, the focus distance needs to remain at 2000cm or the framing between C4D and Octane will not match up. - Make the Octane camera orthographic. This is in the Octane Camera Tag, in the Thinlens or Universal tab. If we’re using a Thin Lens camera type, it’s a checkbox that says “Orthographic”. If we’re using the Universal camera type, it’s a mode that’s also called Orthographic.
- Set the minimum depth. In Octane, this is achieved by putting the camera object itself right up against the frontmost piece of geometry. It’s orthographic, so this will not affect the framing. Putting the camera at world zero will make future calculations easier.
- Frame the scene. Place all the objects as desired and use the C4D Camera Object’s Zoom field to make everything larger or smaller.
- Add the Z-depth AOV. This is done in C4D’s Render settings>Octane Renderer section>Render AOV group tab>Render AOVs section>Add render AOV>info>Z-Depth AOV. We also need to tick the Enable checkbox at the top or else we won’t see any of the passes.
-
Set the Z-depth max. First, we need to figure out what our max is. We can do that fairly quickly by making a plane, setting the orientation to -Z, and then moving it back in space until it’s just beyond the furthest point of our geometry. Then we can add the P.Z value of that plane to our camera’s P.Z value and get the z-depth max.
Important: The Z-depth Max value is always in meters, and C4D’s default scale is centimeters, so we need to divide our final value by 100 unless we’ve altered the scene scale. If the far boundary of the furthest object is 200cm away from the camera, then our z-depth max should be 2. -
Render & Save from Live Viewer. Set the kernel to DL, render. Click the Z tab at the bottom to see the depth map. When complete, in the Live Viewer, choose File>Save Image As… File type can be PNG or EXR - both will render in the correct linear space. Bit Depth can be 8 (PNG) for strictly bump use, or 16/32 for displacement use. Force tone mapping should stay off.
Important: As weird as it seems, Color space should be sRGB and NOT Linear sRGB. The saved file will still be linear (even with PNG), but changing it to Linear sRGB will destroy the map. - Invert the map. Either do this in post, or in the ImageTexture node when using it in a material.
Creating a normal map via world space using Cinema 4D
-
Set up the camera. If we’re doing bump map-like patterns, trying to make the map tileable, or doing some sort of geometric texture, then we’ll probably want a parallel camera. If we’re making a decoration or bas-relief type thing where perspective distortion either doesn’t matter or looks better, then we can use a perspective camera.
Important: the camera’s rotation values should be locked at R.H: 180°, R.P: 0°, R.B: 0°, and the camera should have a positive P.Z value so it’s “behind” all the objects. If we’re using a parallel camera, we need it to be far enough out on Z so it’s not clipping any of the objects. - Set up the scene. Place all the objects in “front” of the camera and turn them around 180 degrees on H so they’re facing the camera. If we’re using a parallel camera, we can use its Zoom value to make everything in the scene larger or smaller as needed. If we’re using a perspective camera, we just move the camera back and forth on Z until the objects fill the frame appropriately.
- Put a backdrop in the scene. If there’s nothing in the far background of the scene (from the camera’s perspective), parts of the render will show up black. This will mess up the normal map. The easiest way to get a neutral backdrop is to insert a large Plane object in the scene that fills the whole frame, set the Orientation to +Z and place it behind all the objects. The backdrop in the resulting render should be blue, not yellow.
- Add the normal pass: Go to C4D’s Render Settings (Ctl-B/Cmd-B). On the left, we need to enable Multi-pass, then from the Multi-pass button below, select Material Normal to get the normal pass.
- Set the render settings: Under Regular Image, we can just turn off Save - we don’t need the beauty pass. Under Multi-pass, we need to turn on Save, set the name and location of the passes, and choose a Format. OpenEXR tends to work the best. If we twirl down the arrow to the left of Format, we get more options. Compression method should be PIZ. Depth (bit depth) should be 16-bit. Uncheck Multi-Layer file.
- Render to Picture Viewer. This will generate just the normal pass and save it. In the PV’s Layer tab, if we choose “single pass” and click “Material Normal” we can see what the map looks like.It can then be brought into Octane and hooked up to the Normal channel in an Image Texture node. Remember to set the Image Texture node’s Color Space to non-color data.
Creating a normal map via world space using Octane
-
Set up the camera. If we’re doing bump map-like patterns, trying to make the map tileable, or doing some sort of geometric texture, then we’ll probably want a parallel camera (make sure to leave the focal distance at 2000 cm so it matches the C4D viewport). If we’re making a decoration or decal-type thing where perspective distortion either doesn’t matter or looks better, then we can use a perspective camera.
Important: the camera’s rotation values should be locked at R.H: 0°, R.P: 0°, R.B: 0°, and the camera should have a negative P.Z value so it’s “in front of” all the objects. If we’re using a parallel camera, we need it to be far enough on -Z so it’s not clipping any of the objects. - Set up the scene. Place all the objects in the scene like we typically do. If we’re using a parallel camera, we can use its Zoom value to make everything in the scene larger or smaller as needed. If we’re using a perspective camera, we just move the camera back and forth on Z until the objects fill the frame appropriately.
- Put a backdrop in the scene. If there’s nothing in the far background of the scene (from the camera’s perspective), parts of the render will show up gray. This will mess up the normal map. The easiest way to get a neutral backdrop is to insert a large Plane object in the scene that fills the whole frame, set the Orientation to -Z and place it behind all the objects. If we start the Live Viewer and pick “Normal” from the kernel dropdown that default to DL, we can see whether we did this right - if the background is blue, we’re good. If it’s yellow, we didn’t set it up right.
- Set the Live Viewer Mode. Since there are no settings for the Geometric Normal AOV, we can just use the built-in Normal mode in the Live Viewer. To get this, we need to drop down the Kernel Selector dropdown (says DL by default), and choose Normal.
-
Render & Save from Live Viewer. Start the render and let it finish. When complete, in the Live Viewer menus, choose File>Save Image As… File type can be PNG or EXR - both will render in the correct linear space. Bit Depth can be 8 or 16-bit. Most of the time 8 is ok, but in some high detail maps may benefit from 16-bit.
Important: As weird as it seems, Color space should be sRGB and NOT Linear sRGB. The saved file will still be linear (even with PNG), but changing it to Linear sRGB will destroy the map.
Creating a normal map via camera space using Octane
- Set up the camera. This can be any camera type, in any orientation - total freedom!
- Set up the scene. Again, we’re free to put the objects wherever we want and frame them up like a normal render.
- Set a backdrop. This is a little trickier since the plane needs to be exactly parallel to the focal plane in order to render correctly. The easiest way to do that is to create a Plane object, set the orientation to -Z, parent it to the camera, zero out the coordinates (Alt-0 / Opt-0), and then move it on Z until it’s behind the furthest object in the scene and make it large enough to fill the whole frame.
- Set the render settings. Choose Octane Renderer from the Renderer dropdown.
- Add the AOV. In the Render AOVs section, click the Add render AOV button, then in the Custom submenu, choose Global Texture AOV. Now we need to go to the AOV groups tab and click the Node editor button. With the node editor active, we can hit spacebar or shift-c and search for Normal and then pull in a normal node. This hooks into the Global Texture’s Texture slot. Now we can select the Normal node and in the options on the right, the Normal type should be Geometric, Coordinate system should be Camera, and Normalize result should be checked.
- Preview the AOV. If we start the Live Viewer, we should see a couple of tabs in the bottom left. One of them should be called Glob1 - that’s our global texture pass. If we click that, we should see what looks like a normal map. If the background is yellow, we’ll need to make sure our plane is set to -Z orientation. If it’s any other color than cornflower blue, it’s probably not aligned properly with the camera and we need to check the rotation values.
-
Render & Save from Live Viewer. Start the render and let it finish. When complete, in the Live Viewer menus, choose File>Save Image As… File type should be EXR using this method, not PNG (since PNG can’t be set to Linear sRGB easily). Bit Depth should be 16-bit.
Important: Unlike the built-in Normal info pass in the Live Viewer, the custom AOV we just built needs to be set to Linear sRGB and NOT sRGB when saving, otherwise the colors will be wrong and the normal map will be screwy.
Wrap Up
If you made it through this guide, you probably have a much better understanding of how to build your own height and normal maps via a few different techniques.
Author Notes
-
OG014 Building Height & Normal Maps from Geometry Version 2.0, Updated Aug 2024 using Octane 2023.1.3 and Cinema 4D 2024.4
-
Changes from 1.0 - full rewrite
-
This guide originally appeared on https://be.net/scottbenson and https://help.otoy.com/hc/en-us/sections/202796803-Cinema-4D
-
All rights reserved.
-
The written guide may be distributed freely and can be used for personal or professional training, but not modified or sold. The assets distributed within this guide are either generated specifically for this guide and released as cc0, or sourced from cc0 sites, so they may be used for any reason, personal or commercial.