UE External Texture Color Restoration Test and OCIO Configuration

How Do You Get an External Texture to Display Its Original Colors in UE?

This test focuses on solving two problems:

  • How can an externally-imported EXR and UE’s internal scene-linear data be processed identically?
  • How can external data be accurately reproduced on an 8-bit display?

I created an EXR image (linearly encoded) with two shades of blue: a pure blue at 0 0 255 and a blue at 0 0 0.75.

Similarly, I built two matching blues inside UE using emissive materials: an emissive blue at 0 0 255 and one at 0 0 0.75.

The core question: how do you guarantee that a texture imported from outside matches the code values generated internally by the renderer?

Test Setup

When I import the image (HDR_TV_Float.exr) and configure its compression as follows:

Compression Settings
EXR texture compression settings

For the EXR input, select Linear encoding.

Encoding Settings
EXR encoding settings (Linear selected)

The Encoding Override controls gamma adjustment. Since the image is a linear EXR, select the Linear option here.

The ColorSpace is set to None (which defaults to the project’s working color space). If this setting matches the project setting, no color space conversion will be applied.

For this test, the working color space is sRGB, set to None — no color space conversion.

Test Results

The result: the outer ring is the emissive material I built internally, and the center is the test image (simulating an externally-fed image in an xR workflow). As the image shows, all the values align perfectly — they are now identical.

This confirms that under these settings, UE can match internally-generated code values to externally-imported code values (the image’s gamma has been removed on input).

Test Result Comparison
Comparison: external input code values vs. internal emissive material code values

Here is a breakdown of the code values observed:

Code Value Reference Table (Before OCIO)

External Value (Float) Description Texture View (8-bit) Viewport View (8-bit, emissive material)
0 0 1 Pure blue 0 0 255 0 0 255
0 0 0.75 Half blue 0 0 255 0 0 224
1 0 0 Red 255 0 0 255 0 0
0.75 0 0 Half red 255 0 0 224 0 0
0.75 0 0.75 Purple 255 0 255 224 0 224
0.02 0.02 0.02 2% gray 39 39 39 43 43 43
0.04 0.04 0.04 4% gray 56 56 56 59 59 59
  • External code values: the original pixel values from the source image (read via EXRView), which are also the values produced by the custom emissive material inside UE. Expressed as floating-point numbers.
  • Texture view: the code values shown in the Texture viewer (captured as 8-bit data via screenshot).
  • Viewport view: the code values shown in the render viewport (emissive material, captured as 8-bit data via screenshot, with the View display mode disabled and all scene lights turned off — for testing purposes only; in production this calibration role can be handed off to a LUT).
Test Scene Hierarchy
Test scene hierarchy (Outliner)
Viewport Display Toggles
Viewport display toggles (Show Flags — Bloom, Tone Curve, etc. disabled)

What the Experiment Tells Us

  1. Once the EXR encoding is correctly linearized on import, its processing path is identical to UE’s internally-generated scene-linear data (a code value of 0 externally matches a code value of 0 internally after rendering). This guarantees that internal and external code values share the same processing state — both converted to linear for rendering.

  2. The original code values don’t match what’s shown in the Texture viewer or the render viewport. Why?

    • Texture viewer: applies some unknown transform to make it look nice for the user. Don’t trust these values.
    • Render viewport: the display transform is clearly defined — it goes through OCIO — so you know exactly what’s happening in the render pipeline.
    • Original code values: these are the ground truth. Ideally, this is the color the display should reproduce.

For xR workflows, what matters is ensuring consistency between the original code values and the code values displayed in the render viewport.

Why Don’t the Viewport Values Match the External Values?

Why does a value of 0.75 in the external file (roughly 191 in 8-bit) end up displaying as 224 once it enters the engine? (See the half-blue and half-red entries in the test above.)

This happens because the data being displayed is still raw linear data — it hasn’t been correctly mapped to the display. The current internal display is still Rec.709 linear data. To display it correctly, the render view’s OCIO needs to be configured.

OCIO Configuration

Set the OCIO working color space to WorkingColorSpace or Linear Rec709 (SRGB). Set the output to sRGB - Display - RAW, the standard sRGB display transform.

OCIO Configuration
OCIO working color space and output configuration (sRGB - Display - Raw)

Code Value Reference Table (After OCIO)

External Value (Float) Description Viewport View (8-bit, emissive material)
0 0 1 Pure blue 0 0 255
0 0 0.75 Half blue 0 0 191
1 0 0 Red 255 0 0
0.75 0 0 Half red 191 0 0
0.75 0 0.75 Purple 191 0 191
0.02 0.02 0.02 2% gray 5 5 5
0.04 0.04 0.04 4% gray 10 10 10

The original code values are perfectly restored.

Research: UE Render Logic and Parameter Influence

I found that UE’s various render settings have a significant impact on rendered color output, so I’ll go through each parameter category and examine how it affects the result.

Post Process - Exposure

The logic behind each exposure mode and how they differ (to be expanded).

Post Process - Highlight Contrast / Shadow Contrast

Highlight Contrast / Shadow Contrast
Highlight Contrast / Shadow Contrast parameter effect

Post Process - Tone Curve Amount

Tone Curve Amount
Tone Curve Amount parameter effect

Post Process - Expand Gamut

Expand Gamut
Expand Gamut parameter effect