Photogrammetry (sometimes called “photoscanning”) gives you significantly more accurate displacement maps than bitmap approximation or multi-angle approaches at the cost of being the most demanding way in terms of hardware, software and time.

How does it generally work?

In case you have never heard of photogrammetry: It works by taking pictures of an object from different angles and using them to create a representation of it's geometry. In other words: It's an incredibly accurate way to capture displacement (and color) data for material creation.

The workflow shown in this guide works by creating a high-detail pass with the maximum amount of information possible and a smooth/flat pass. The details of the high-detail pass are then baked onto the low-detail pass, thus eliminating larger height changes and leaving only the smaller detail which creates uniform and easily tileable displacement maps at very high resolutions (theoretically up to ~32000px).

When can it be used?

Photogrammetry can generally be used on surfaces which fulfill these criteria:

  • The surface must have a strong enough displacement for it to create a detectable difference in perspective when moving the camera. What this means in concrete terms depends on your equipment. Paving stones, bricks and tree bark are pretty much always suitable, even with lower end cameras. Gravel and tiles with shallow seams are more difficult, but still doable. With a high end camera, a macro lens and a lot of patience it is even possible to use photogrammetry for leaves and fabrics. Plain/smooth wood is where photogrammetry hits its limits (at least for me right now) as the displacement changes are so subtle that they get lost in the noise during processing.
  • It must not change it's appearance (color/structure) based on the angle from which it is looked at. This rules out reflective and (partially) transparent surfaces.
  • It must provide enough detail for the software to track camera movement from one shot to the next. Smooth paint or plaster can be a challenge.
  • The general rules for good textures still apply.



  • A decent camera (DSLR or SLR, I've never personally tried it with a smartphone)
  • A tripod or monopod (I personally prefer the monopod for most situations)


  • Agisoft Metashape (or comparable photogrammetry software)
  • xNormal (or comparable baking tool)
  • An image editor

The shooting process

Shooting for photogrammetry in Metashape works similarly to shooting for bitmap approximation.
Find a suitable surface and record a serpentine pattern in which every image has a decent overlap with the next one. One big point to remember during all of this is that Metashape needs differences in perspective to properly reassemble a surface. The user manual for Metashape specifically points out that the different images should not be created by just rotating the camera around one point. It must be moved in 3D space for every shot.

This also leads to the reason that I prefer using a monopod over a tripod for photogrammetry: While 2D image stitching in ICE struggles with the perspective distortions that appear if the camera is not always facing down at an exact 90° angle Metashape is actually embracing these small differences. When I am using a monopod I usually don't even take several shots from different angles (as you would in normal photogrammetry) because I know that the slightly different tilt generated by the monopod on every image is enough for the software to work out the height profile of the surface I am recording.

Photogrammetry processing

Once you have recorded your images the photogrammetry magic can begin. The workflow I am describing here is how I do it in Agisoft Metashape but it should also be possible to perform similar steps in another photogrammetry tool.

We will begin with the detailed pass and then decimate and smooth it to get the flat pass for baking. Metashape batchjob files for this process are available on my Github but I would suggest performing the steps manually at least once as it will help you when doing troubleshooting in the future.

Metashape settings

Before doing any kind of processing in Metashape I would recommend adjusting a few settings. Go to Tools → Preferences and make sure that some settings are configured properly:

  • The GPU should be enabled
  • The Default view (in the “General” tab) should be changed to “Dense Cloud” as the default setting (“Model”) can cause Metashape to be extremely laggy when opening a large project.

Creating the detailed pass

Aligning photos

Import the photos and align the cameras via Workflow → Align Photos. The most important setting here is the quality preset. I would recommend using the “high” preset since all other presets either down- or upscale the given images. In the Advanced menu there two more options labeled Key point limit and Tie point limit. The Key Point limit defines how many points the software will extract from every photo and the Tie Point Limit then tells it how many of these points to use for the actual reconstruction. At this point you might be tempted to increase these settings to really high values but according to the user manual these settings are primarily designed to tweak the performance rather than the quality, increasing them will therefore only have a marginal impact on the result (unless the reconstruction fails completely).

Dense cloud creation

Once the alignment has finished take a look at the result. In the best case you now have a rough representation of the surface made of a couple hundred thousand points. Choose the next item in the Workflow menu: Build Dense Cloud. In this pop-up select the quality of the dense cloud you want to create. This has a huge impact on the final result so I like to go for the Ultra high setting. This is also one of the more taxing parts for the computer, so save the project and then just play around with it until you find something that finishes within a reasonable amount of time (and by “reasonable” I mean less than 15-20 hours, it's normal for it to take REALLY long.) Once it has finished you are getting your first real look at the scene. These are still just points but there are now so many of them that it is starting to look like a real model. All we need to to is connect them up to build the mesh.

Building the mesh

From the “Workflow” menu select - you guessed it - “Build Mesh”. Here are some settings which need to be adjusted: Start by setting the reconstruction method to “Height field”. This means that there will only ever be one vertex on the Z axis for every pair of X and Y Coordinates, which is not only faster by a big margin but it’s actually something we are going to need anyways since our end goal is having a height map an not a 3D model. When it comes to choosing the quality I always go for the “High” preset but this is once again something you have to try out with your computer - and your hard drive - as the resulting files can go into the double digit gigabytes, so make sure you have some disk space left.

Building the texture

It’s time to take another step downwards in the workflow tab and click on “Build texture”. I usually generate at a resolution of 24576px or 32768px, this resolution allows me to make large crops in my images and still maintain a resolution of 8192px for the final texture.


For once, exporting does not happen in the “Workflow” menu but via “File” → “Export”. In this dialog box you can export both the texture and the model file at the same time. Save the model and the texture and name them “<something>_DETAILED”. Once the first pass has been built you can start working on the second (flat pass).

Creating the flat pass

Decimating and smoothing the model

The second pass needs much less detail so the first step is to decimate the mesh. In Metashape this can be done via ToolsMeshDecimate. For the flat pass I always reduce it down to 200000 polygons. After that you need to smooth it. This is the key step that makes the baking possible. I always go for a smoothing strength of 2500.

Rebuilding the UV

Decimating the mesh unfortunately removes the UV Map from the model and the only way to rebuild it in Methashape is by actually creating a texture. The resolution of the second texture is pretty much irrelevant, 256px is just enough. This time it is important to select “Orthophoto” as the projection method as this will create one continuous UV map for baking.


Save the flat pass as “<Something>_FLAT”. You don't need to include the texture in this export.


Now that you have both passes you can bake the detailed model onto the flat model. For that purpose I would recommend xNormal for its ability to handle very large meshes due to it lacking a 3D viewport which couldn't handle the multi-gigabyte exports from Metashape. And it's not like we need it anyways, because we know that the models are nicely aligned.

Open up xNormal. On the “High-Poly Meshes” screen right-click anywhere into the black field and select “Add High Poly Mesh”, then load your High-Poly OBJ file. It will appear in the list. Then right click on the file in the list (It’s important that you click on it and not below it) and select “Base texture to bake”. Load the detailed texture. It should appear together with the mesh in one line. Then move on to the “Low definition meshes” section and load the flat pass in there. After that you can choose the appropriate baking settings in the “Baking options”. Choose resolution, export path and a file format with a high bit-depth like EXR (Make sure your image editor of choice can actually open EXR files!) or PNG. Select “height map” and “Bake base texture” as the maps to render (You can of course add more maps but I try to keep the amount of different maps down at this point and generate all the other maps once color and displacement are made seamless). You can then start the baking process. During this process you will be asked by a pop-up window to adjust the levels for the height using two sliders. Change the minimum and maximum until all the parts of the height map that you want to use are neither completely black nor completely white (If they are it's called “clamping” and it means that the area will appear completely flat in the export). You can enable the checkbox “Debug min/max clamping” to get a visual indication of which areas are currently clipping. It's fine if the edges of the texture are clipping (those will be cropped away anyways), just make sure that the central area is nicely balanced. The image below shows the “Tonemapper” pop-up with the “Debug min/max clamping” checkbox enabled. The blue areas indicate where clamping is happening.

Making it seamless

Tiling a photoscan is slightly more difficult when dealing with both a color- and a displacement map as you need to make sure that they stay in sync during the entire process. For this reason I would recommend using an image editor with support for macros (like Affinity Photo or Photoshop).
Open the raw color and displacement map as two layers. The first step should be to mark out the rough area that you want to use using guide lines. A bit like this: There should be a bit of free space as some part from outside the selected area will be moved into the texture. If your texture has a repeating pattern you should make sure that it is aligned to the grid. Here is an example for this adjustment: The bricks have to be aligned to the guide lines. Make sure to start recording your steps as a macro and perform a perspective correction to align the pattern to the guiding lines. Apply the same perspective correction to the other layer (that's what the macro is for). Delete the macro that you just recorded. You can restart it, but it's not required for the next part.
Select an area right above the target area (orange) and copy it into a new layer and move it down into the bottom part of the target area (green), like shown in this picture: Then mask away parts of the orange area to hide the seam: Repeat (or in the case of a macro replay) these actions for the displayement layer. Then merge the two color layers and the two displacement layers so that you have just two layers again. Repeat the process on both the color and displacement layer by copying the the area right next to the box to the left part inside the box: At this point you have a seamless color and displacement map inside the area your marked out. You can now crop your image down and export the two layers as separate images.

Creating the maps

Does Ambient Occlusion make sense?

Having an ambient occlusion map is only useful if your surface has strong displacement that causes crevasses to be visibly darker by receiving less ambient light. If that's not the case (for example in the case of an almost entirely flat parquet) then you can disable the ambient occlusion map.

Delighting the color map

Delighting is not always necessary for photoscanned textures since many photogrammetry tools already have built-in delighters. However, adding the delighting can still help if the photogrammetry software's delighter didn't catch everything. It can work in many different ways - Substance Alchemist even offers an AI based delighter. But the two most common methods are based on the displacement and direction of light and/or the ambient occlusion. In Substance B2M they are called “Light Equalizer” and “AO Cancellation”.

The AO Cancellation uses the ambient occlusion map to determine which parts of the image would naturally be darker in the unprocessed texture and then brightens these parts. I primarily use this method when creating textures.

Creating a roughness map

There are two ways to get roughness data. Roughness can be approximated from the displacement map (or rather the curvature which is derived from the displacement map). This method is great for creating small differences on surfaces with fairly uniform roughness. In Substance B2M this method is referred to as “Roughness from Curvature”.

It can also be influenced by the color of the texture. One thing I like to do when working with bricks (for example) is to use random color variations between the bricks to create randomness in the roughness map which would otherwise be very plain due to the bricks uniform structure. Here is what that can look like (Bricks 016):

CC0 Textures
CC0 Textures Help Section
CC0 Textures on Patreon
CC0 Textures on Twitter
CC0 Textures on Instagram

User Tools