XFSDK Texture Works
Demo
Overview
TextureWorks is an Unreal Editor texture-processing toolkit with four main operations:
- Normal / Height - PBRify — generates Normal and experimental Height maps from Color / Albedo textures using the bundled PBRify models.
- Normal to AO — generates texture-space visibility/AO from a tangent-space normal map using reconstructed, existing, or PBRify-predicted height data.
- Normal to Curvature — generates signed curvature maps from tangent-space normal textures using GPU compute.
- Texture Packer — combines channels from existing textures into configurable packed outputs such as ORD, ORM, ALBM, or custom layouts.
TextureWorks also provides bulk processing, reusable Profiles & Naming, configurable output suffixes, and shared settings across single and batch workflows.
All processing runs inside the Unreal Editor and produces regular Unreal texture assets that can be saved and used directly in your material workflow.
Requirements
The standard From Normal and Existing Height Texture AO paths require:
- A rendering GPU
- SM5-capable compute shaders
- Threaded rendering
- A valid tangent-space normal map
No neural model is required for these two modes.
The experimental PBRify from Albedo height source additionally requires the PBRify Height model and Unreal's Windows NNE DirectML path.
Quick Start
- Open Texture Works.
- Select the source Normal Map.
- Choose an appropriate Preset.
- Select the AO Height Source.
- Configure Wrap Texture for the source.
- Configure Flip Green Channel if required by the stored normal orientation.
- Leave Pack AO Into Mask Texture disabled for a standalone
\_AOtexture. - Click Generate AO.
- Inspect the generated texture and save it in Unreal Editor.
AO Height Sources
Texture Works supports three height-field sources.
| Height Source | Height Data | Required Inputs | Height Iterations |
|---|---|---|---|
| From Normal | Reconstructed internally from the selected normal | Normal | Enabled |
| Existing Height Texture | Existing linear height texture | Normal + Height | Disabled |
| PBRify from Albedo (Experimental) | Predicted internally using PBRify Height | Normal + Albedo | Disabled |
From Normal
This is the default deterministic workflow.
TextureWorks reconstructs a height field from the selected tangent-space normal map, then evaluates texture-space visibility over that reconstructed surface.
The method follows the material-surface occlusion approach described by Activision in:
The normal map is converted into surface slopes, those slopes are integrated into an approximate height field, and the resulting field is used for an orthographic GTAO-style visibility bake.
TextureWorks reuses UE5-style GTAO horizon-search and inner-integral concepts where practical, but it is not a byte-exact port of Activision's internal production tool or UE5's scene-depth GTAO pass.
The plugin removes scene-specific features such as camera projection, scene depth, HZB traversal, temporal filtering, and screen-space fade because the bake operates directly on texture data.
No standalone height texture is created by this AO operation.
Use Height Iterations to control the reconstruction workload.
Activision's full material-surface occlusion model is broader than the baked texture generated here.
The TextureWorks output represents the texture-side visibility result. The plugin does not modify Unreal Engine lighting shaders to reproduce the complete Call of Duty material model, including direct micro-shadowing or indirect specular cone occlusion.
Existing Height Texture
Use this when you already have an artist-authored, previously generated, or otherwise intended height map.
The height texture must:
- Match the normal texture dimensions
- Use supported source data
- Have sRGB disabled
- Represent linear scalar height data
Grayscale textures provide height directly. Supported color height textures use the red channel.
Invalid or missing height data causes the operation to fail rather than silently falling back to normal reconstruction.
PBRify from Albedo
This experimental mode predicts height from a paired color/albedo texture using the PBRify Height model.
The selected normal is still required and continues to provide the normal-derived surface information used by the AO calculation.
The predicted height exists only for the current AO operation. No standalone height texture is created.
If you already generated a PBRify height texture using Normal / Height - PBRify, use Existing Height Texture instead to avoid running inference again.
Presets
TextureWorks provides two AO starting presets.
| Preset | Height Iterations | Angles | Taps | Radius Pixels | Edge Guard | Height Scale | Wrap |
|---|---|---|---|---|---|---|---|
| UV Atlas / Unwrapped | 64 | 4 | 6 | 6 | 2 | 8 | Off |
| Tileable Material | 64 | 4 | 12 | 24 | 0 | 16 | On |
Presets are starting points. Material scale, texture resolution, normal strength, UV layout, and intended appearance still matter.
UV Atlas / Unwrapped
Use this for packed mesh textures and non-tileable UV layouts.
It uses:
- A shorter AO search radius
- Fewer taps
- Wrapping disabled
- Normal Edge Guard enabled
This reduces contamination between unrelated texture regions, but it cannot make separate UV islands behave as one physically continuous height field.
Tileable Material
Use this for seamless textures where opposite texture borders represent continuous surface data.
It uses:
- Texture wrapping
- A larger search radius
- More taps
- Stronger height response
Do not use this preset blindly on packed UV atlases.
Settings
| Setting | Description |
|---|---|
| Height Iterations | Reconstruction work used only by From Normal. Higher values increase GPU work and may improve convergence. |
| Angles | Number of AO search directions. Higher values increase sampling work. |
| Taps | Samples taken along each search direction. Higher values increase sampling density and cost. |
| Radius Pixels | AO search distance measured in source-texture pixels. |
| Normal Edge Guard | Pushes AO toward white around strong neighboring normal/slope discontinuities. 0 disables it. |
| Height Scale | Scales the depth contribution of the selected/reconstructed height field. |
| Wrap Texture | Allows sampling across opposite image borders. Enable for genuinely seamless textures. |
| Flip Green Channel | Inverts the normal-map Y component during this operation without modifying the source texture. |
| Apply Albedo Interreflection | Uses matching albedo brightness to approximate diffuse interreflection in the AO visibility result. |
Recommended Tuning Order
- Confirm the correct normal, dimensions, green-channel orientation, and height source.
- Choose the appropriate preset and boundary behavior.
- Tune Height Scale.
- Tune Radius Pixels.
- For packed atlases, adjust Normal Edge Guard.
- Adjust Angles and Taps only if more sampling quality is needed.
- Adjust Height Iterations only when using From Normal.
- Add Albedo Interreflection after establishing a useful baseline.
Change one setting at a time when comparing results.
Albedo Interreflection
Apply Albedo Interreflection optionally modifies visibility using a matching-resolution albedo texture.
This is an artist-facing luminance approximation intended to reduce excessive darkening on brighter surfaces.
The output remains grayscale.
If the albedo is missing, unreadable, or the wrong size, optional interreflection is skipped.
**Note:** This option is separate from **PBRify from Albedo**. PBRify uses albedo to predict height, while Albedo Interreflection adjusts the resulting AO visibility.
Packing AO Into an Existing Mask
Texture Works can write the generated AO directly into one channel of an existing mask texture.
This is separate from the full Texture Packer operation.
Enable:
Pack AO Into Mask Texture
Then choose either Quixel or Custom.
Quixel
The Quixel preset:
- Searches for
\_ORD - Falls back to
\_ORM - Writes AO into the Red channel
Custom
Custom mode allows you to configure:
- Mask Suffix
- AO Channel: R, G, B, or A
The target mask must match the AO dimensions and use compatible BGRA8 source data.
The selected destination channel is replaced while the other source channels are preserved.
Successful packing changes the mask to linear mask compression and marks the existing asset dirty.
If AO insertion fails, TextureWorks attempts to create a standalone AO texture instead. Check the completion message and Output Log to confirm which output was produced.
Output
Standalone AO
Without mask packing, TextureWorks creates or updates a grayscale texture using the configured AO suffix.
Default:
Brick\_Normal → Brick\_AO
The stored values represent visibility:
1.0 = highly visible / unoccluded
0.0 = heavily occluded
Despite the \_AO asset name, the texture stores visibility rather than inverted occlusion.
Generated AO textures use:
- G8 source data
- Grayscale compression
- sRGB Off
- No mipmaps initially
Existing Assets
If the calculated output name already contains a compatible UTexture2D, TextureWorks updates that texture.
It does not automatically create numbered duplicates or request overwrite confirmation.
Use another output suffix when you want to preserve multiple comparisons.
Generated or modified textures are marked dirty and must still be saved through Unreal Editor.
Standalone Height Generation
Texture Works uses height internally but is not the standalone height-generation operation.
To create a reusable height texture, use Normal / Height - PBRify.
That operation supports:
- Height prediction from Color / Albedo
- Height reconstruction from an Existing Normal Map
You can then return to Texture Works and choose Existing Height Texture to use the saved result.
Artifact Mitigation
Packed UV atlases can produce dark borders, dots, or halos because neighboring pixels in texture space may belong to unrelated parts of the mesh.
Recommended starting fixes:
- Use UV Atlas / Unwrapped
- Keep Wrap Texture disabled
- Reduce Radius Pixels
- Reduce Height Scale if the response is too aggressive
- Increase Normal Edge Guard carefully
- Verify the normal's green-channel orientation
**Important:** Disabling Wrap Texture only changes sampling at the outer image border. It does not detect or isolate internal UV islands.
Normal Edge Guard can reduce seam artifacts, but it can also suppress legitimate AO detail if pushed too far.
Tileable Materials
For continuous repeating materials:
- Start with Tileable Material
- Keep Wrap Texture enabled
- Tune Height Scale before increasing radius
- Use larger radius/tap values only when broader cavity response is required
A texture should only use wrapping when opposite borders genuinely represent neighboring surface points.
Batch Conversion
Multiple normal maps can be processed in one operation.
- Select two or more normal textures in the Content Browser.
- Right-click the selection.
- Choose Bulk Texture Works.
- Configure or load the intended shared settings.
- Inspect matched albedo/height inputs where applicable.
- Run the batch.
- Review processed and failed items.
- Save successful generated assets.
Bulk AO uses the same shared settings as single-texture AO.
Existing Height in Bulk
When Existing Height Texture is selected, TextureWorks tries to locate a matching height texture for each normal.
Height matching checks the current configured height suffix and common aliases such as:
\_Height
\_Displacement
\_Disp
\_H
Inspect the matched-height list before starting the batch.
A missing required height causes that item to fail while other valid items can continue.
PBRify Height in Bulk
When PBRify from Albedo is selected, each normal requires a readable, matching-resolution albedo.
A missing required albedo causes that item to fail without discarding successful results from other items.
Mask Packing in Bulk
Bulk AO inherits the single AO panel's packing settings.
Configure the intended:
- Packing preset
- Custom suffix
- Destination channel
before running the bulk workflow.
Each normal attempts to resolve its own mask texture. One manually selected mask is not reused for every material in the batch.
Normal Orientation
AO reads the source normal pixels directly.
It does not automatically apply the Texture Editor asset's green-channel flip setting when reading those pixels.
If surface detail appears inverted:
- Test the normal in a material under directional lighting.
- Compare AO with Flip Green Channel disabled and enabled.
- Keep the interpretation consistent across AO, Curvature, and normal-derived height generation.
Avoid applying an extra flip to pixels that were already converted during PBRify output generation.
Troubleshooting
AO or Curvature fails without a rendering GPU
Use a normal Unreal Editor rendering session with SM5 compute and threaded rendering.
NullRHI and similar non-rendering environments cannot run this workflow.
Existing Height Texture fails
Check:
- Matching native dimensions
- sRGB Off
- Supported source format
- Valid scalar data
- Correct source texture
Height Iterations is disabled
This is expected with:
- Existing Height Texture
- PBRify from Albedo
Only From Normal performs AO's internal normal-to-height reconstruction.
Dark UV-island borders
Check:
- UV Atlas / Unwrapped preset
- Wrap disabled
- Lower radius
- Lower Height Scale
- Normal Edge Guard
AO looks inverted or structurally wrong
Verify the tangent-space normal convention and test Flip Green Channel.
Mask packing did not modify the expected texture
Check:
- Pack AO Into Mask Texture
- Quixel/Custom preset
- Destination channel
- Selected mask
- Matching dimensions
- BGRA8 source data
A standalone AO may have been generated as fallback.
Batch height is not found
Check:
- Height is in the same material folder
- Material basename matches
- Configured Height suffix
- Common height aliases
- Bulk matched-height list
Current Limitations
Texture Works is a texture-space process.
It does not:
- Bake mesh geometry
- Understand internal UV-island boundaries
- Assign physical units to reconstructed height
- Automatically create material instances
- Automatically connect generated textures to materials
- Automatically save dirty texture assets
- Provide a dedicated cancellation control
The current release also does not automatically chain Normal, Height, AO, Curvature, and Texture Packing as one dependency graph.
References
- Material Advances in Call of Duty: WWII
- Practical Realtime Strategies for Accurate Indirect Occlusion
Related TextureWorks Operations
TextureWorks 2.3.1 includes:
- Texture Works
- Normal / Height - PBRify
- Normal to Curvature
- Texture Packer
- Common Profiles & Naming
- Bulk processing for all four operations
Use Profiles & Naming to keep generator suffixes, AO settings, packing recipes, and other shared settings consistent between materials and batch operations.