Replace custom ViewportColorCorrector with stageView.py color correction
Remove the custom GL post-process (ViewportColorCorrector) that bypassed HdxColorCorrectionTask. Instead pass colorCorrectionMode, ocioDisplay, ocioView, ocioColorSpace, and ocioLook directly to UsdImagingGLRenderParams and SetColorCorrectionSettings(), delegating correction to Hydra — the same approach used in usdview's stageView.renderSinglePass(). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
# ADR 0001 — Custom Viewport Color Correction (sRGB / OCIO)
|
# ADR 0001 — Custom Viewport Color Correction (sRGB / OCIO)
|
||||||
|
|
||||||
- **Status:** Accepted
|
- **Status:** Superseded (2026-06-30) — replaced by the stageView.py approach (HdxColorCorrectionTask via RenderParams)
|
||||||
- **Date:** 2026-06-27
|
- **Date:** 2026-06-27
|
||||||
- **Component:** `src/core/UsdSceneRenderer.cpp` (`ViewportColorCorrector`, `ApplyViewportColorCorrection`)
|
- **Component:** `src/core/UsdSceneRenderer.cpp` (`ViewportColorCorrector`, `ApplyViewportColorCorrection`)
|
||||||
- **Commit:** `fb1ae9d`
|
- **Commit:** `fb1ae9d`
|
||||||
@@ -181,3 +181,14 @@ expected corrected (non-black) image and sRGB is unchanged. Headless automated
|
|||||||
verification is blocked by the stale test-target CMake config (missing USD
|
verification is blocked by the stale test-target CMake config (missing USD
|
||||||
include dirs); restoring those targets would let an OCIO-vs-sRGB center-pixel
|
include dirs); restoring those targets would let an OCIO-vs-sRGB center-pixel
|
||||||
test assert this automatically.
|
test assert this automatically.
|
||||||
|
|
||||||
|
## Supersession Note (2026-06-30)
|
||||||
|
|
||||||
|
This ADR is superseded. The custom `ViewportColorCorrector` was removed in favour
|
||||||
|
of the approach used in usdview's `stageView.py`: `colorCorrectionMode`,
|
||||||
|
`ocioDisplay`, `ocioView`, `ocioColorSpace`, and `ocioLook` are now passed
|
||||||
|
directly to `UsdImagingGLRenderParams` and `SetColorCorrectionSettings()`,
|
||||||
|
delegating correction to Hydra's `HdxColorCorrectionTask`. The two failure modes
|
||||||
|
documented in D1 (black OCIO output, bind-stack corruption) may resurface; the
|
||||||
|
try/catch guard around `Render()` remains in place to mitigate the bind-stack
|
||||||
|
issue.
|
||||||
|
|||||||
+24
-345
@@ -28,16 +28,12 @@
|
|||||||
#include <pxr/base/tf/warning.h>
|
#include <pxr/base/tf/warning.h>
|
||||||
#include <pxr/base/tf/status.h>
|
#include <pxr/base/tf/status.h>
|
||||||
|
|
||||||
#include <OpenColorIO/OpenColorIO.h>
|
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
#include <exception>
|
#include <exception>
|
||||||
#include <unordered_set>
|
#include <unordered_set>
|
||||||
|
|
||||||
namespace OCIO = OCIO_NAMESPACE;
|
|
||||||
|
|
||||||
namespace UsdLayerManager {
|
namespace UsdLayerManager {
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -143,271 +139,6 @@ static GLuint LinkProgram(GLuint vs, GLuint fs) {
|
|||||||
return prog;
|
return prog;
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
|
||||||
// ViewportColorCorrector — custom GL color correction (sRGB / OCIO)
|
|
||||||
// ===========================================================================
|
|
||||||
//
|
|
||||||
// Replaces HdxColorCorrectionTask. The scene is rendered linear; Apply()
|
|
||||||
// samples that linear texture and draws a corrected fullscreen triangle into
|
|
||||||
// the currently-bound FBO. For OCIO it uses the OCIO GPU shader API directly
|
|
||||||
// (GpuShaderDesc) so the exact transform — including 1D/3D LUTs — is built and
|
|
||||||
// bound under our control, independent of Hydra.
|
|
||||||
class ViewportColorCorrector {
|
|
||||||
public:
|
|
||||||
enum class Mode { sRGB, OpenColorIO };
|
|
||||||
|
|
||||||
~ViewportColorCorrector() { DestroyGLResources(); }
|
|
||||||
|
|
||||||
/// Draw a corrected fullscreen quad into the bound FBO sampling srcTex
|
|
||||||
/// (single-sample, linear RGBA). Caller has set the GL viewport.
|
|
||||||
/// Returns true if a pass was drawn. On OCIO build failure it falls back
|
|
||||||
/// to the sRGB encode so the viewport degrades gracefully (never black).
|
|
||||||
bool Apply(GLuint srcTex, Mode mode,
|
|
||||||
const std::string& disp, const std::string& view,
|
|
||||||
const std::string& cs, const std::string& look)
|
|
||||||
{
|
|
||||||
if (!EnsureCommon()) return false;
|
|
||||||
|
|
||||||
GLuint prog = m_srgbProgram;
|
|
||||||
std::vector<LutTex>* luts = nullptr;
|
|
||||||
if (mode == Mode::OpenColorIO && EnsureOcioProgram(disp, view, cs, look)) {
|
|
||||||
prog = m_ocioProgram;
|
|
||||||
luts = &m_ocioLuts;
|
|
||||||
}
|
|
||||||
if (!prog) return false;
|
|
||||||
|
|
||||||
// Save the GL state we touch.
|
|
||||||
GLboolean depthTest = glIsEnabled(GL_DEPTH_TEST);
|
|
||||||
GLboolean blend = glIsEnabled(GL_BLEND);
|
|
||||||
GLboolean depthMask = GL_TRUE;
|
|
||||||
glGetBooleanv(GL_DEPTH_WRITEMASK, &depthMask);
|
|
||||||
glDisable(GL_DEPTH_TEST);
|
|
||||||
glDisable(GL_BLEND);
|
|
||||||
glDepthMask(GL_FALSE);
|
|
||||||
|
|
||||||
glUseProgram(prog);
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, srcTex);
|
|
||||||
glUniform1i(glGetUniformLocation(prog, "uTex"), 0);
|
|
||||||
|
|
||||||
if (luts) {
|
|
||||||
int unit = 1;
|
|
||||||
for (const auto& l : *luts) {
|
|
||||||
glActiveTexture(GL_TEXTURE0 + unit);
|
|
||||||
glBindTexture(l.target, l.id);
|
|
||||||
GLint loc = glGetUniformLocation(prog, l.sampler.c_str());
|
|
||||||
if (loc >= 0) glUniform1i(loc, unit);
|
|
||||||
++unit;
|
|
||||||
}
|
|
||||||
glActiveTexture(GL_TEXTURE0);
|
|
||||||
}
|
|
||||||
|
|
||||||
glBindVertexArray(m_vao);
|
|
||||||
glDrawArrays(GL_TRIANGLES, 0, 3);
|
|
||||||
glBindVertexArray(0);
|
|
||||||
glUseProgram(0);
|
|
||||||
|
|
||||||
// Restore state.
|
|
||||||
if (depthTest) glEnable(GL_DEPTH_TEST);
|
|
||||||
if (blend) glEnable(GL_BLEND);
|
|
||||||
glDepthMask(depthMask);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DestroyGLResources() {
|
|
||||||
DestroyOcioResources();
|
|
||||||
if (m_srgbProgram) { glDeleteProgram(m_srgbProgram); m_srgbProgram = 0; }
|
|
||||||
if (m_vao) { glDeleteVertexArrays(1, &m_vao); m_vao = 0; }
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
struct LutTex { GLuint id; GLenum target; std::string sampler; };
|
|
||||||
|
|
||||||
bool EnsureCommon() {
|
|
||||||
if (m_vao == 0) glGenVertexArrays(1, &m_vao);
|
|
||||||
if (m_srgbProgram == 0) {
|
|
||||||
GLuint vs = CompileShader(kFullscreenVS, GL_VERTEX_SHADER);
|
|
||||||
GLuint fs = CompileShader(kSrgbFS, GL_FRAGMENT_SHADER);
|
|
||||||
if (vs && fs) m_srgbProgram = LinkProgram(vs, fs);
|
|
||||||
}
|
|
||||||
return m_vao != 0 && m_srgbProgram != 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EnsureOcioProgram(const std::string& disp, const std::string& view,
|
|
||||||
const std::string& cs, const std::string& look)
|
|
||||||
{
|
|
||||||
const std::string key = disp + "|" + view + "|" + cs + "|" + look;
|
|
||||||
if (m_ocioProgram && key == m_ocioKey) return true;
|
|
||||||
if (key == m_ocioFailedKey) return false;
|
|
||||||
|
|
||||||
DestroyOcioResources();
|
|
||||||
m_ocioKey.clear();
|
|
||||||
|
|
||||||
std::string fragText;
|
|
||||||
OCIO::GpuShaderDescRcPtr desc;
|
|
||||||
try {
|
|
||||||
OCIO::ConstConfigRcPtr config = OCIO::GetCurrentConfig();
|
|
||||||
if (!config) throw std::runtime_error("no current OCIO config");
|
|
||||||
const char* srcCS = cs.empty() ? OCIO::ROLE_SCENE_LINEAR : cs.c_str();
|
|
||||||
|
|
||||||
OCIO::ConstProcessorRcPtr proc = config->getProcessor(
|
|
||||||
srcCS, disp.c_str(), view.c_str(), OCIO::TRANSFORM_DIR_FORWARD);
|
|
||||||
OCIO::ConstGPUProcessorRcPtr gpu = proc->getDefaultGPUProcessor();
|
|
||||||
|
|
||||||
desc = OCIO::GpuShaderDesc::CreateShaderDesc();
|
|
||||||
desc->setLanguage(OCIO::GPU_LANGUAGE_GLSL_1_3);
|
|
||||||
desc->setFunctionName("OCIODisplay");
|
|
||||||
desc->setResourcePrefix("ocio_");
|
|
||||||
gpu->extractGpuShaderInfo(desc);
|
|
||||||
fragText = desc->getShaderText();
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
LOG_WARNING("Custom OCIO build failed (" + key
|
|
||||||
+ "), using sRGB: " + std::string(e.what()));
|
|
||||||
m_ocioFailedKey = key;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Assemble the fragment shader: OCIO declares its samplers + the
|
|
||||||
// OCIODisplay(vec4) function; our main() samples the linear input and
|
|
||||||
// runs it through.
|
|
||||||
std::string fs = "#version 130\n"
|
|
||||||
"uniform sampler2D uTex;\n"
|
|
||||||
"in vec2 vUv;\n"
|
|
||||||
"out vec4 outColor;\n"
|
|
||||||
+ fragText +
|
|
||||||
"\nvoid main(){ outColor = OCIODisplay(texture(uTex, vUv)); }\n";
|
|
||||||
|
|
||||||
GLuint vsh = CompileShader(kFullscreenVS, GL_VERTEX_SHADER);
|
|
||||||
GLuint fsh = CompileShader(fs.c_str(), GL_FRAGMENT_SHADER);
|
|
||||||
GLuint prog = (vsh && fsh) ? LinkProgram(vsh, fsh) : 0;
|
|
||||||
if (!prog) {
|
|
||||||
LOG_WARNING("Custom OCIO shader compile/link failed for " + key
|
|
||||||
+ " — using sRGB");
|
|
||||||
m_ocioFailedKey = key;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Upload the LUT textures OCIO requested.
|
|
||||||
if (!CreateOcioTextures(desc)) {
|
|
||||||
glDeleteProgram(prog);
|
|
||||||
DestroyOcioResources();
|
|
||||||
m_ocioFailedKey = key;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
m_ocioProgram = prog;
|
|
||||||
m_ocioKey = key;
|
|
||||||
LOG_INFO("Custom OCIO program built: " + key
|
|
||||||
+ " (" + std::to_string(m_ocioLuts.size()) + " LUTs)");
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool CreateOcioTextures(const OCIO::GpuShaderDescRcPtr& desc) {
|
|
||||||
// 3D LUTs (RGB).
|
|
||||||
for (unsigned i = 0; i < desc->getNum3DTextures(); ++i) {
|
|
||||||
const char* texName = nullptr; const char* samplerName = nullptr;
|
|
||||||
unsigned edgelen = 0; OCIO::Interpolation interp = OCIO::INTERP_LINEAR;
|
|
||||||
desc->get3DTexture(i, texName, samplerName, edgelen, interp);
|
|
||||||
const float* values = nullptr;
|
|
||||||
desc->get3DTextureValues(i, values);
|
|
||||||
if (!values || edgelen == 0 || !samplerName) return false;
|
|
||||||
|
|
||||||
GLuint id = 0;
|
|
||||||
glGenTextures(1, &id);
|
|
||||||
glBindTexture(GL_TEXTURE_3D, id);
|
|
||||||
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGB32F, edgelen, edgelen, edgelen,
|
|
||||||
0, GL_RGB, GL_FLOAT, values);
|
|
||||||
GLint filt = (interp == OCIO::INTERP_NEAREST) ? GL_NEAREST : GL_LINEAR;
|
|
||||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, filt);
|
|
||||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, filt);
|
|
||||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE);
|
|
||||||
m_ocioLuts.push_back({ id, GL_TEXTURE_3D, samplerName });
|
|
||||||
}
|
|
||||||
|
|
||||||
// 1D / 2D LUTs.
|
|
||||||
for (unsigned i = 0; i < desc->getNumTextures(); ++i) {
|
|
||||||
const char* texName = nullptr; const char* samplerName = nullptr;
|
|
||||||
unsigned width = 0, height = 0;
|
|
||||||
OCIO::GpuShaderDesc::TextureType channel = OCIO::GpuShaderDesc::TEXTURE_RGB_CHANNEL;
|
|
||||||
OCIO::Interpolation interp = OCIO::INTERP_LINEAR;
|
|
||||||
desc->getTexture(i, texName, samplerName, width, height, channel, interp);
|
|
||||||
const float* values = nullptr;
|
|
||||||
desc->getTextureValues(i, values);
|
|
||||||
if (!values || width == 0 || !samplerName) return false;
|
|
||||||
|
|
||||||
const bool isRed = (channel == OCIO::GpuShaderDesc::TEXTURE_RED_CHANNEL);
|
|
||||||
const GLint internal = isRed ? GL_R32F : GL_RGB32F;
|
|
||||||
const GLenum format = isRed ? GL_RED : GL_RGB;
|
|
||||||
const GLint filt = (interp == OCIO::INTERP_NEAREST) ? GL_NEAREST : GL_LINEAR;
|
|
||||||
const GLenum target = (height > 1) ? GL_TEXTURE_2D : GL_TEXTURE_1D;
|
|
||||||
|
|
||||||
GLuint id = 0;
|
|
||||||
glGenTextures(1, &id);
|
|
||||||
glBindTexture(target, id);
|
|
||||||
if (target == GL_TEXTURE_2D) {
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, internal, width, height, 0,
|
|
||||||
format, GL_FLOAT, values);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
||||||
} else {
|
|
||||||
glTexImage1D(GL_TEXTURE_1D, 0, internal, width, 0,
|
|
||||||
format, GL_FLOAT, values);
|
|
||||||
}
|
|
||||||
glTexParameteri(target, GL_TEXTURE_MIN_FILTER, filt);
|
|
||||||
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, filt);
|
|
||||||
glTexParameteri(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
||||||
m_ocioLuts.push_back({ id, target, samplerName });
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void DestroyOcioResources() {
|
|
||||||
for (auto& l : m_ocioLuts) glDeleteTextures(1, &l.id);
|
|
||||||
m_ocioLuts.clear();
|
|
||||||
if (m_ocioProgram) { glDeleteProgram(m_ocioProgram); m_ocioProgram = 0; }
|
|
||||||
m_ocioKey.clear();
|
|
||||||
m_ocioFailedKey.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
static const char* kFullscreenVS;
|
|
||||||
static const char* kSrgbFS;
|
|
||||||
|
|
||||||
GLuint m_vao = 0;
|
|
||||||
GLuint m_srgbProgram = 0;
|
|
||||||
GLuint m_ocioProgram = 0;
|
|
||||||
std::string m_ocioKey;
|
|
||||||
std::string m_ocioFailedKey;
|
|
||||||
std::vector<LutTex> m_ocioLuts;
|
|
||||||
};
|
|
||||||
|
|
||||||
// Attribute-less fullscreen triangle; UV in [0,2] covers the [0,1] screen.
|
|
||||||
const char* ViewportColorCorrector::kFullscreenVS = R"(#version 130
|
|
||||||
out vec2 vUv;
|
|
||||||
void main() {
|
|
||||||
vec2 p = vec2(float((gl_VertexID << 1) & 2), float(gl_VertexID & 2));
|
|
||||||
vUv = p;
|
|
||||||
gl_Position = vec4(p * 2.0 - 1.0, 0.0, 1.0);
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
|
|
||||||
// Linear → sRGB encode (matches HdxColorCorrectionTask's sRGB path).
|
|
||||||
const char* ViewportColorCorrector::kSrgbFS = R"(#version 130
|
|
||||||
uniform sampler2D uTex;
|
|
||||||
in vec2 vUv;
|
|
||||||
out vec4 outColor;
|
|
||||||
vec3 lin2srgb(vec3 c) {
|
|
||||||
vec3 lo = c * 12.92;
|
|
||||||
vec3 hi = 1.055 * pow(max(c, vec3(0.0)), vec3(1.0/2.4)) - 0.055;
|
|
||||||
bvec3 cut = lessThanEqual(c, vec3(0.0031308));
|
|
||||||
return mix(hi, lo, vec3(cut));
|
|
||||||
}
|
|
||||||
void main() {
|
|
||||||
vec4 c = texture(uTex, vUv);
|
|
||||||
outColor = vec4(lin2srgb(c.rgb), c.a);
|
|
||||||
}
|
|
||||||
)";
|
|
||||||
|
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
// GLSL sources
|
// GLSL sources
|
||||||
// ---------------------------------------------------------------------------
|
// ---------------------------------------------------------------------------
|
||||||
@@ -478,9 +209,6 @@ UsdSceneRenderer::~UsdSceneRenderer() {
|
|||||||
DestroyBBoxResources();
|
DestroyBBoxResources();
|
||||||
DestroyCamWireResources();
|
DestroyCamWireResources();
|
||||||
DestroyLightWireResources();
|
DestroyLightWireResources();
|
||||||
m_colorCorrector.reset(); // deletes its GL program / LUT textures
|
|
||||||
if (m_ccLinearFBO) glDeleteFramebuffers(1, &m_ccLinearFBO);
|
|
||||||
if (m_ccLinearTex) glDeleteTextures(1, &m_ccLinearTex);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
@@ -837,9 +565,8 @@ void UsdSceneRenderer::Render(int width, int height)
|
|||||||
m_drawTarget = pxr::GlfDrawTarget::New(pxr::GfVec2i(width, height), wantMSAA);
|
m_drawTarget = pxr::GlfDrawTarget::New(pxr::GfVec2i(width, height), wantMSAA);
|
||||||
if (!m_drawTarget) { LOG_ERROR("Failed to create GlfDrawTarget"); return; }
|
if (!m_drawTarget) { LOG_ERROR("Failed to create GlfDrawTarget"); return; }
|
||||||
m_drawTarget->Bind();
|
m_drawTarget->Bind();
|
||||||
// RGBA16F so the scene is stored linear with HDR headroom: we render
|
// RGBA16F: Hydra's internal render buffers carry linear HDR values;
|
||||||
// linear (Hydra correction disabled) and apply our own color correction
|
// RGBA16F on the draw target preserves precision in the corrected output.
|
||||||
// afterwards, which needs values outside [0,1] for OCIO/ACES.
|
|
||||||
m_drawTarget->AddAttachment("color", GL_RGBA, GL_FLOAT, GL_RGBA16F);
|
m_drawTarget->AddAttachment("color", GL_RGBA, GL_FLOAT, GL_RGBA16F);
|
||||||
m_drawTarget->AddAttachment("depth",
|
m_drawTarget->AddAttachment("depth",
|
||||||
GL_DEPTH_COMPONENT, GL_FLOAT, GL_DEPTH_COMPONENT32F);
|
GL_DEPTH_COMPONENT, GL_FLOAT, GL_DEPTH_COMPONENT32F);
|
||||||
@@ -971,11 +698,28 @@ void UsdSceneRenderer::Render(int width, int height)
|
|||||||
m_renderParams.forceRefresh = m_forceRefresh;
|
m_renderParams.forceRefresh = m_forceRefresh;
|
||||||
m_renderParams.clipPlanes = m_clipPlanes;
|
m_renderParams.clipPlanes = m_clipPlanes;
|
||||||
|
|
||||||
// Color correction is done by our own GL post-process (ApplyViewport-
|
// Color correction — stageView.py approach: pass the actual mode + OCIO
|
||||||
// ColorCorrection below), not HdxColorCorrectionTask — so Hydra always
|
// params to HdxColorCorrectionTask via RenderParams and SetColorCorrection-
|
||||||
// renders linear ("disabled"). This bypasses the hdx OCIO path entirely.
|
// Settings (mirrors stageView.renderSinglePass).
|
||||||
m_renderParams.colorCorrectionMode = pxr::TfToken("disabled");
|
pxr::TfToken ccToken("disabled");
|
||||||
m_renderer->SetColorCorrectionSettings(pxr::TfToken("disabled"));
|
if (m_colorCorrectionMode == ColorCorrectionMode::sRGB)
|
||||||
|
ccToken = pxr::TfToken("sRGB");
|
||||||
|
else if (m_colorCorrectionMode == ColorCorrectionMode::OpenColorIO)
|
||||||
|
ccToken = pxr::TfToken("openColorIO");
|
||||||
|
|
||||||
|
m_renderParams.colorCorrectionMode = ccToken;
|
||||||
|
if (m_colorCorrectionMode == ColorCorrectionMode::OpenColorIO) {
|
||||||
|
m_renderParams.ocioDisplay = pxr::TfToken(m_ocioDisplay);
|
||||||
|
m_renderParams.ocioView = pxr::TfToken(m_ocioView);
|
||||||
|
m_renderParams.ocioColorSpace = pxr::TfToken(m_ocioColorSpace);
|
||||||
|
m_renderParams.ocioLook = pxr::TfToken(m_ocioLook);
|
||||||
|
}
|
||||||
|
m_renderer->SetColorCorrectionSettings(
|
||||||
|
ccToken,
|
||||||
|
pxr::TfToken(m_ocioDisplay),
|
||||||
|
pxr::TfToken(m_ocioView),
|
||||||
|
pxr::TfToken(m_ocioColorSpace),
|
||||||
|
pxr::TfToken(m_ocioLook));
|
||||||
|
|
||||||
// Guard the Hydra render: if it throws, the m_drawTarget->Unbind() below
|
// Guard the Hydra render: if it throws, the m_drawTarget->Unbind() below
|
||||||
// would be skipped, permanently unbalancing the GlfDrawTarget bind stack
|
// would be skipped, permanently unbalancing the GlfDrawTarget bind stack
|
||||||
@@ -987,11 +731,6 @@ void UsdSceneRenderer::Render(int width, int height)
|
|||||||
}
|
}
|
||||||
m_forceRefresh = false;
|
m_forceRefresh = false;
|
||||||
|
|
||||||
// --- Custom color-correction post-process (linear → sRGB / OCIO) ---
|
|
||||||
if (m_colorCorrectionMode != ColorCorrectionMode::Disabled) {
|
|
||||||
ApplyViewportColorCorrection(width, height);
|
|
||||||
}
|
|
||||||
|
|
||||||
// --- Optional grid overlay ---
|
// --- Optional grid overlay ---
|
||||||
if (m_showGrid) {
|
if (m_showGrid) {
|
||||||
// When MSAA is active, render the grid into the MSAA FBO so it is
|
// When MSAA is active, render the grid into the MSAA FBO so it is
|
||||||
@@ -1020,66 +759,6 @@ void UsdSceneRenderer::Render(int width, int height)
|
|||||||
m_drawTarget->Unbind();
|
m_drawTarget->Unbind();
|
||||||
}
|
}
|
||||||
|
|
||||||
// ===========================================================================
|
|
||||||
// Custom color-correction post-process
|
|
||||||
// ===========================================================================
|
|
||||||
|
|
||||||
void UsdSceneRenderer::ApplyViewportColorCorrection(int width, int height)
|
|
||||||
{
|
|
||||||
if (!m_drawTarget) return;
|
|
||||||
|
|
||||||
if (!m_colorCorrector)
|
|
||||||
m_colorCorrector = std::make_unique<ViewportColorCorrector>();
|
|
||||||
|
|
||||||
// (Re)create the single-sample linear copy texture + its FBO on size change.
|
|
||||||
if (m_ccLinearTex == 0 || m_ccLinearW != width || m_ccLinearH != height) {
|
|
||||||
if (m_ccLinearTex == 0) glGenTextures(1, &m_ccLinearTex);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, m_ccLinearTex);
|
|
||||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, width, height, 0,
|
|
||||||
GL_RGBA, GL_FLOAT, nullptr);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|
||||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|
||||||
glBindTexture(GL_TEXTURE_2D, 0);
|
|
||||||
|
|
||||||
if (m_ccLinearFBO == 0) glGenFramebuffers(1, &m_ccLinearFBO);
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, m_ccLinearFBO);
|
|
||||||
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
|
|
||||||
GL_TEXTURE_2D, m_ccLinearTex, 0);
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, 0);
|
|
||||||
m_ccLinearW = width;
|
|
||||||
m_ccLinearH = height;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve MSAA (no-op otherwise) so the color attachment holds the linear
|
|
||||||
// single-sample image, then copy it into m_ccLinearTex — sampling and
|
|
||||||
// writing the same texture in one pass is illegal, hence the copy.
|
|
||||||
m_drawTarget->Resolve();
|
|
||||||
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_drawTarget->GetFramebufferId());
|
|
||||||
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_ccLinearFBO);
|
|
||||||
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height,
|
|
||||||
GL_COLOR_BUFFER_BIT, GL_NEAREST);
|
|
||||||
|
|
||||||
// Draw the corrected result back into the draw-target render FBO (the MSAA
|
|
||||||
// FBO when multisampling, so it resolves together with the overlays drawn
|
|
||||||
// on top of it afterwards).
|
|
||||||
GLuint dstFbo = m_drawTarget->HasMSAA()
|
|
||||||
? m_drawTarget->GetFramebufferMSId()
|
|
||||||
: m_drawTarget->GetFramebufferId();
|
|
||||||
glBindFramebuffer(GL_FRAMEBUFFER, dstFbo);
|
|
||||||
glViewport(0, 0, width, height);
|
|
||||||
|
|
||||||
ViewportColorCorrector::Mode mode =
|
|
||||||
(m_colorCorrectionMode == ColorCorrectionMode::OpenColorIO)
|
|
||||||
? ViewportColorCorrector::Mode::OpenColorIO
|
|
||||||
: ViewportColorCorrector::Mode::sRGB;
|
|
||||||
|
|
||||||
m_colorCorrector->Apply(m_ccLinearTex, mode,
|
|
||||||
m_ocioDisplay, m_ocioView,
|
|
||||||
m_ocioColorSpace, m_ocioLook);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
// Output
|
// Output
|
||||||
// ===========================================================================
|
// ===========================================================================
|
||||||
|
|||||||
@@ -25,10 +25,6 @@
|
|||||||
|
|
||||||
namespace UsdLayerManager {
|
namespace UsdLayerManager {
|
||||||
|
|
||||||
/// Custom viewport color-correction post-process (sRGB / OCIO) implemented in
|
|
||||||
/// our own GL shader instead of HdxColorCorrectionTask. Defined in the .cpp.
|
|
||||||
class ViewportColorCorrector;
|
|
||||||
|
|
||||||
/// Bounding box display mode for selected prims.
|
/// Bounding box display mode for selected prims.
|
||||||
enum class BBoxMode {
|
enum class BBoxMode {
|
||||||
None, ///< No bounding boxes drawn
|
None, ///< No bounding boxes drawn
|
||||||
@@ -392,17 +388,6 @@ private:
|
|||||||
pxr::SdfPathVector m_cachedCameraPaths;
|
pxr::SdfPathVector m_cachedCameraPaths;
|
||||||
bool m_cameraCacheDirty = true;
|
bool m_cameraCacheDirty = true;
|
||||||
|
|
||||||
// --- Custom color-correction post-process ---------------------------------
|
|
||||||
// The scene is rendered linear (Hydra correction "disabled") and corrected
|
|
||||||
// by our own GL shader, bypassing HdxColorCorrectionTask. ApplyViewport-
|
|
||||||
// ColorCorrection() resolves the linear result into m_ccLinearTex, then
|
|
||||||
// draws a corrected fullscreen quad back into the draw-target FBO.
|
|
||||||
void ApplyViewportColorCorrection(int width, int height);
|
|
||||||
std::unique_ptr<ViewportColorCorrector> m_colorCorrector;
|
|
||||||
GLuint m_ccLinearFBO = 0; ///< FBO wrapping m_ccLinearTex (correction input)
|
|
||||||
GLuint m_ccLinearTex = 0; ///< single-sample linear copy of the Hydra output
|
|
||||||
int m_ccLinearW = 0;
|
|
||||||
int m_ccLinearH = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace UsdLayerManager
|
} // namespace UsdLayerManager
|
||||||
|
|||||||
Reference in New Issue
Block a user