SkillAgentSearch skills...

NormalTextureProcessor

Analyze and convert normal textures

Install / Use

/learn @erich666/NormalTextureProcessor
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

NormalTextureProcessor (aka NTP)

Description

This C++ project examines normals textures (i.e., textures for applying bumps to surfaces), checking them for validity, and cleaning them up or converting them as desired.

Sample conversion

A considerable percentage of normals textures out there have badly computed Z (and sometimes X and Y) values. In practice you could always derive Z from X and Y in your shaders. However, that can have a (usually small) performance hit. Also, files for glTF and USD files, among others, expect that the XYZ values are valid. Give the model to someone else and their viewer may not fix the Z values and then give a different result. So, if you want to store XYZ triplets with proper Z values, use this program to analyze and clean up your normals textures.

How Normals Go Bad

First and foremost, it's hard for us humans to see when a normals textures is bad. There's a certain color to the textures, but unless they're wildly wrong, it's not easy to notice that the Z values are incorrect. Because they're hard to detect, normals can go bad and not be noticed. That's why I wrote this program, because I kept running into incorrect normals that the creators didn't realize were wrong.

There are plenty of ways that the XYZ values for a normals texture can go bad. The tool producing the normals texture, typically converting from a grayscale heightfield, can be faulty. As much as I like Normalmap Online, github, I know it produces imprecise normals textures. I hope to examine and evaluate other programs as I go, which is why I provide copious algorithm notes below - there's a right way to convert from XYZ to RGB. I have also seen evidence that along the fringes of cutout objects the normals will be off; I'm not sure why.

Another source of error occurs when a normals texture is resized. If you blithely change the dimensions of a texture, the new normals texture will look reasonable and probably work fairly well. But, it will likely be wrong, a little bit or a lot. Blending two or more normalized normals together will rarely give a normalized result. After resizing, the Z values stored should be rederived. X and Y values may also stray a bit, creating vectors that are illegal. Whatever the case, this program can clean up such data. That said, a more accurate way to resize would be to work from the original heightfield image and convert to a normals texture after resizing.

Finally, I've heard - see tweet replies - that artists sometimes modify the separate RGB channels for effect.

Installation

Develop on Windows 10 on Visual Studio 2022 (double-click NormalTextureProcessor.sln, build Release version). You might be able to compile and run it elsewhere, as the program is pretty Windows-free and is purely command-line driven, with no graphical user interface.

The executable is available for download, zip file here. Last updated March 20, 2024.

Test Suite

Optionally, after building the Release version, you should be able to go into the test_files directory and double-click on run_test_suite.bat. As the README in that directory notes, running this test file will create various separate output directories where the results are put. You can look at run_test_suite.bat to see various options in use and look at the resulting files.

Usage

Quick start: to analyze textures in a particular directory, run the exe like so:

NormalTextureProcessor.exe -a -v -idir path_to_your_input_directory

This will give an analysis of the textures in that directory. At the end of the analysis will be a summary like this: File types found, by category:

  Standard normals textures: chessboard_normal.png Knight_normal.png
    Standard normals textures that are not perfectly normalized: Knight_normal.png
  XY-only normals textures: bishop_black_normal.png bishop_white_normal.png Castle_normal.png

"XY-only normals textures" are ones where the analyzer found that some of the Z values stored are wrong, they don't form normalized normals. This can sometimes be on purpose, but if you're expecting RGB to convert to XYZ, then those textures are poorly formed and should be cleaned up (see more about that below). The "Standard normals textures" are basically fine. Those list as not perfectly normalized are just that: the Z's are close, but could be better. Cleaning these, too, is a fine thing to do, though usually not as critical.

Currently the system is limited to reading in 8-bit PNG and TGA (Targa) textures. 16-bit PNG images can be read in, but are currently converted to 8 bits. To avoid name collisions with PNGs, Targa files read in and manipulated are output with the suffix "_TGA" added to the input file name, output as PNGs. JPEG is not supported, and I warn against using it unless forced, as its lossy compression is almost guaranteed to generate RGB's that convert to XYZ's that are not normalized.

A fussy note on terminology: I use the term "normals texture" for any (usually blue-ish) texture that stores normal directions as RGB (or just RG) values. "Normal texture" can be confusing - what's an "abnormal texture"? I avoid the word "map" and always use "texture," but you'll see "normal map" commonly used instead of "normal texture" elsewhere. Technically, the "map" is how the texture is applied to a surface, a separate function irrelevant here. Various analysis messages in the program talk about "one level" or "two levels", which refers to 8-bit values. For example, if a blue channel value was expected to be 228 but was 230, that is two levels of difference.

Analysis

To list out all the command-line options, simply don't put any:

NormalTextureProcessor.exe

This list of options will be confusing, so here are some typical combinations. To analyze all PNG and TGA textures in the current directory:

NormalTextureProcessor.exe -a -v

The '-v' is optional. It means "verbose", giving you additional information during processing, mostly about what files were cleaned. I recommend trying it on and off to see what you're missing.

A report is generated when '-a' is selected. The lists at the end of the analysis can be used to copy and paste specific files into the program itself, e.g.:

NormalTextureProcessor.exe -izneg -a bishop_black_normal.png bishop_white_normal.png Castle_normal.png

Adding '-izneg' says to assume the incoming textures are "standard" -1 to 1 Z-value textures and analyze them as such. This setting is recommended for checking compliance with the glTF and USD standards, for example. By putting specific files on the command line, the program processes only them. This example doesn't do much, just analyzes the (already analyzed) set of files again. The main use is that you can specify individual files for other operations, such as cleanup and conversion.

You can also feed in an input directory located elsewhere:

NormalTextureProcessor.exe -a -idir flora/materials/normal_textures > analysis.log 2>&1

The "> analysis.log 2>&1" is optional, it's a way to put all the analysis and any warnings or errors into a file.

There are three main types of normals textures this program recognizes. They are:

  • RGB "standard" normals textures: each color channel is interpreted as representing numbers that go from -1.0 to 1.0, for all channels.
  • RGB normal, "Z-zero": as above, but the blue, Z, channel is instead interpreted as going from 0.0 to 1.0.
  • heightfield texture: an elevation texture, usually a grayscale image, typically with black representing the lowest elevation and white the highest.

The first type, where Z goes from -1 to 1, is the type that is the most popular nowadays. Usually this texture is applied to a surface, so that the normals on the texture are relative to the interpolated (shading) normal of the surface itself. So, for example, a curved surface made of polygons is rendered. At a given surface location (typically) the shading normal is interpolated from vertex normals, defining the normal (Z) direction of the surface, along with X and Y basis vectors. This is known as "tangent space". The normal is retrieved from the normals texture and modifies the surface normal.

This first type has a few variants to it. One is that Y direction on the texture can be up or down. That is, the normals texture can be thought of as "X to the right, Y up" for how its normal is interpreted. A normal of, say, (0.2, 0.3, 0.933) can be thought of as pointing 0.2 to the right, 0.3 up. That is the OpenGL interpretation. The normal's Y direction could also be considered as pointing down, i.e., think of the upper left corner as the origin and Y pointing down. This is the DirectX interpretation. See this page for more information and examples, and search this page for "invert" for a wider view. OpenGL and DirectX are two major different forms you'll run across, i.e., no one reverses the X direction normally. If you use the wrong type of texture in a renderer you will see artifacts such as objects looking like they're being lit from below, for example. The glTF file format requires the OpenGL style normals texture, USD assumes OpenGL but can use scale and bias settings to support DirectX. Currently the analysis program does not have code to tell OpenGL-style files from DirectX-style files. I have some ideas how to guess using statistics as to which format a normals

Related Skills

View on GitHub
GitHub Stars111
CategoryDevelopment
Updated2mo ago
Forks2

Languages

C++

Security Score

95/100

Audited on Jan 6, 2026

No findings