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:
For the EXR input, select Linear encoding.
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).
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).
What the Experiment Tells Us
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.
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.
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
Post Process - Tone Curve Amount
Post Process - Expand Gamut