added new layer input pass
This commit is contained in:
@@ -188,6 +188,7 @@ std::vector<RenderPassDescriptor> OpenGLRenderPass::BuildLayerPassDescriptors(
|
|||||||
pass.passId = passProgram.passId;
|
pass.passId = passProgram.passId;
|
||||||
pass.layerId = state.layerId;
|
pass.layerId = state.layerId;
|
||||||
pass.shaderId = state.shaderId;
|
pass.shaderId = state.shaderId;
|
||||||
|
pass.layerInputTexture = layerInputTexture;
|
||||||
pass.sourceTexture = passSourceTexture;
|
pass.sourceTexture = passSourceTexture;
|
||||||
pass.sourceFramebuffer = passIndex == 0 ? layerInputFramebuffer : passSourceFramebuffer;
|
pass.sourceFramebuffer = passIndex == 0 ? layerInputFramebuffer : passSourceFramebuffer;
|
||||||
pass.destinationTexture = passDestinationTexture;
|
pass.destinationTexture = passDestinationTexture;
|
||||||
@@ -226,6 +227,7 @@ void OpenGLRenderPass::RenderLayerPass(
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
RenderShaderProgram(
|
RenderShaderProgram(
|
||||||
|
pass.layerInputTexture,
|
||||||
pass.sourceTexture,
|
pass.sourceTexture,
|
||||||
pass.destinationFramebuffer,
|
pass.destinationFramebuffer,
|
||||||
*pass.passProgram,
|
*pass.passProgram,
|
||||||
@@ -243,6 +245,7 @@ void OpenGLRenderPass::RenderLayerPass(
|
|||||||
}
|
}
|
||||||
|
|
||||||
void OpenGLRenderPass::RenderShaderProgram(
|
void OpenGLRenderPass::RenderShaderProgram(
|
||||||
|
GLuint layerInputTexture,
|
||||||
GLuint sourceTexture,
|
GLuint sourceTexture,
|
||||||
GLuint destinationFrameBuffer,
|
GLuint destinationFrameBuffer,
|
||||||
PassProgram& passProgram,
|
PassProgram& passProgram,
|
||||||
@@ -267,7 +270,7 @@ void OpenGLRenderPass::RenderShaderProgram(
|
|||||||
const std::vector<GLuint> temporalHistoryTextures = mRenderer.TemporalHistory().ResolveTemporalHistoryTextures(state, sourceTexture, state.isTemporal ? historyCap : 0);
|
const std::vector<GLuint> temporalHistoryTextures = mRenderer.TemporalHistory().ResolveTemporalHistoryTextures(state, sourceTexture, state.isTemporal ? historyCap : 0);
|
||||||
const GLuint feedbackTexture = mRenderer.FeedbackBuffers().ResolveReadTexture(state);
|
const GLuint feedbackTexture = mRenderer.FeedbackBuffers().ResolveReadTexture(state);
|
||||||
const ShaderTextureBindings::RuntimeTextureBindingPlan texturePlan =
|
const ShaderTextureBindings::RuntimeTextureBindingPlan texturePlan =
|
||||||
mTextureBindings.BuildLayerRuntimeBindingPlan(passProgram, sourceTexture, state, feedbackTexture, sourceHistoryTextures, temporalHistoryTextures);
|
mTextureBindings.BuildLayerRuntimeBindingPlan(passProgram, sourceTexture, layerInputTexture, state, feedbackTexture, sourceHistoryTextures, temporalHistoryTextures);
|
||||||
mTextureBindings.BindRuntimeTexturePlan(texturePlan);
|
mTextureBindings.BindRuntimeTexturePlan(texturePlan);
|
||||||
glBindVertexArray(mRenderer.FullscreenVertexArray());
|
glBindVertexArray(mRenderer.FullscreenVertexArray());
|
||||||
glUseProgram(passProgram.program);
|
glUseProgram(passProgram.program);
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ private:
|
|||||||
const TextBindingUpdater& updateTextBinding,
|
const TextBindingUpdater& updateTextBinding,
|
||||||
const GlobalParamsUpdater& updateGlobalParams);
|
const GlobalParamsUpdater& updateGlobalParams);
|
||||||
void RenderShaderProgram(
|
void RenderShaderProgram(
|
||||||
|
GLuint layerInputTexture,
|
||||||
GLuint sourceTexture,
|
GLuint sourceTexture,
|
||||||
GLuint destinationFrameBuffer,
|
GLuint destinationFrameBuffer,
|
||||||
PassProgram& passProgram,
|
PassProgram& passProgram,
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ struct RenderPassDescriptor
|
|||||||
std::string passId;
|
std::string passId;
|
||||||
std::string layerId;
|
std::string layerId;
|
||||||
std::string shaderId;
|
std::string shaderId;
|
||||||
|
GLuint layerInputTexture = 0;
|
||||||
GLuint sourceTexture = 0;
|
GLuint sourceTexture = 0;
|
||||||
GLuint sourceFramebuffer = 0;
|
GLuint sourceFramebuffer = 0;
|
||||||
GLuint destinationTexture = 0;
|
GLuint destinationTexture = 0;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
#include <gl/gl.h>
|
#include <gl/gl.h>
|
||||||
|
|
||||||
|
constexpr GLuint kLayerInputTextureUnit = 0;
|
||||||
constexpr GLuint kDecodedVideoTextureUnit = 1;
|
constexpr GLuint kDecodedVideoTextureUnit = 1;
|
||||||
constexpr GLuint kSourceHistoryTextureUnitBase = 2;
|
constexpr GLuint kSourceHistoryTextureUnitBase = 2;
|
||||||
constexpr GLuint kPackedVideoTextureUnit = 2;
|
constexpr GLuint kPackedVideoTextureUnit = 2;
|
||||||
|
|||||||
@@ -117,7 +117,11 @@ void ShaderTextureBindings::AssignLayerSamplerUniforms(GLuint program, const Run
|
|||||||
{
|
{
|
||||||
const GLuint shaderTextureBase = ResolveShaderTextureBase(state, historyCap);
|
const GLuint shaderTextureBase = ResolveShaderTextureBase(state, historyCap);
|
||||||
|
|
||||||
const GLint videoInputLocation = glGetUniformLocation(program, "gVideoInput");
|
const GLint layerInputLocation = FindSamplerUniformLocation(program, "gLayerInput");
|
||||||
|
if (layerInputLocation >= 0)
|
||||||
|
glUniform1i(layerInputLocation, static_cast<GLint>(kLayerInputTextureUnit));
|
||||||
|
|
||||||
|
const GLint videoInputLocation = FindSamplerUniformLocation(program, "gVideoInput");
|
||||||
if (videoInputLocation >= 0)
|
if (videoInputLocation >= 0)
|
||||||
glUniform1i(videoInputLocation, static_cast<GLint>(kDecodedVideoTextureUnit));
|
glUniform1i(videoInputLocation, static_cast<GLint>(kDecodedVideoTextureUnit));
|
||||||
|
|
||||||
@@ -136,7 +140,7 @@ void ShaderTextureBindings::AssignLayerSamplerUniforms(GLuint program, const Run
|
|||||||
|
|
||||||
if (state.feedback.enabled)
|
if (state.feedback.enabled)
|
||||||
{
|
{
|
||||||
const GLint feedbackSamplerLocation = glGetUniformLocation(program, "gFeedbackState");
|
const GLint feedbackSamplerLocation = FindSamplerUniformLocation(program, "gFeedbackState");
|
||||||
if (feedbackSamplerLocation >= 0)
|
if (feedbackSamplerLocation >= 0)
|
||||||
glUniform1i(feedbackSamplerLocation, static_cast<GLint>(ResolveFeedbackTextureUnit(state, historyCap)));
|
glUniform1i(feedbackSamplerLocation, static_cast<GLint>(ResolveFeedbackTextureUnit(state, historyCap)));
|
||||||
}
|
}
|
||||||
@@ -160,12 +164,14 @@ void ShaderTextureBindings::AssignLayerSamplerUniforms(GLuint program, const Run
|
|||||||
ShaderTextureBindings::RuntimeTextureBindingPlan ShaderTextureBindings::BuildLayerRuntimeBindingPlan(
|
ShaderTextureBindings::RuntimeTextureBindingPlan ShaderTextureBindings::BuildLayerRuntimeBindingPlan(
|
||||||
const PassProgram& passProgram,
|
const PassProgram& passProgram,
|
||||||
GLuint layerInputTexture,
|
GLuint layerInputTexture,
|
||||||
|
GLuint originalLayerInputTexture,
|
||||||
const RuntimeRenderState& state,
|
const RuntimeRenderState& state,
|
||||||
GLuint feedbackTexture,
|
GLuint feedbackTexture,
|
||||||
const std::vector<GLuint>& sourceHistoryTextures,
|
const std::vector<GLuint>& sourceHistoryTextures,
|
||||||
const std::vector<GLuint>& temporalHistoryTextures) const
|
const std::vector<GLuint>& temporalHistoryTextures) const
|
||||||
{
|
{
|
||||||
RuntimeTextureBindingPlan plan;
|
RuntimeTextureBindingPlan plan;
|
||||||
|
plan.bindings.push_back({ "originalLayerInput", "gLayerInput", originalLayerInputTexture, kLayerInputTextureUnit });
|
||||||
plan.bindings.push_back({ "layerInput", "gVideoInput", layerInputTexture, kDecodedVideoTextureUnit });
|
plan.bindings.push_back({ "layerInput", "gVideoInput", layerInputTexture, kDecodedVideoTextureUnit });
|
||||||
|
|
||||||
for (std::size_t index = 0; index < sourceHistoryTextures.size(); ++index)
|
for (std::size_t index = 0; index < sourceHistoryTextures.size(); ++index)
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ public:
|
|||||||
RuntimeTextureBindingPlan BuildLayerRuntimeBindingPlan(
|
RuntimeTextureBindingPlan BuildLayerRuntimeBindingPlan(
|
||||||
const PassProgram& passProgram,
|
const PassProgram& passProgram,
|
||||||
GLuint layerInputTexture,
|
GLuint layerInputTexture,
|
||||||
|
GLuint originalLayerInputTexture,
|
||||||
const RuntimeRenderState& state,
|
const RuntimeRenderState& state,
|
||||||
GLuint feedbackTexture,
|
GLuint feedbackTexture,
|
||||||
const std::vector<GLuint>& sourceHistoryTextures,
|
const std::vector<GLuint>& sourceHistoryTextures,
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ cbuffer GlobalParams
|
|||||||
{{PARAMETER_UNIFORMS}}};
|
{{PARAMETER_UNIFORMS}}};
|
||||||
|
|
||||||
Sampler2D<float4> gVideoInput;
|
Sampler2D<float4> gVideoInput;
|
||||||
|
Sampler2D<float4> gLayerInput;
|
||||||
{{SOURCE_HISTORY_SAMPLERS}}{{TEMPORAL_HISTORY_SAMPLERS}}{{FEEDBACK_SAMPLER}}{{TEXTURE_SAMPLERS}}
|
{{SOURCE_HISTORY_SAMPLERS}}{{TEMPORAL_HISTORY_SAMPLERS}}{{FEEDBACK_SAMPLER}}{{TEXTURE_SAMPLERS}}
|
||||||
{{TEXT_SAMPLERS}}
|
{{TEXT_SAMPLERS}}
|
||||||
float4 sampleVideo(float2 tc)
|
float4 sampleVideo(float2 tc)
|
||||||
@@ -46,6 +47,11 @@ float4 sampleVideo(float2 tc)
|
|||||||
return gVideoInput.Sample(tc);
|
return gVideoInput.Sample(tc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
float4 sampleLayerInput(float2 tc)
|
||||||
|
{
|
||||||
|
return gLayerInput.Sample(tc);
|
||||||
|
}
|
||||||
|
|
||||||
float4 sampleSourceHistory(int framesAgo, float2 tc)
|
float4 sampleSourceHistory(int framesAgo, float2 tc)
|
||||||
{
|
{
|
||||||
if (gSourceHistoryLength <= 0)
|
if (gSourceHistoryLength <= 0)
|
||||||
|
|||||||
@@ -1,18 +1,36 @@
|
|||||||
{
|
{
|
||||||
"id": "feedback-highlight-accumulator",
|
"id": "feedback-highlight-accumulator",
|
||||||
"name": "Feedback Highlight Accumulator",
|
"name": "Feedback Highlight Accumulator",
|
||||||
"description": "Demonstrates shader-local feedback by accumulating only the brightest-looking parts of the image over a timed window, then resetting and starting again. The highlight selection is an approximation based on a luminance threshold rather than an exact percentile reduction.",
|
"description": "Demonstrates shader-local feedback by storing a rolling 4-frame per-pixel highlight history in RGBA and displaying only the newest 3 frames. The highlight selection is an approximation based on a luminance threshold rather than an exact percentile reduction.",
|
||||||
"category": "Feedback",
|
"category": "Feedback",
|
||||||
"entryPoint": "shadeVideo",
|
"entryPoint": "buildHistory",
|
||||||
|
"passes": [
|
||||||
|
{
|
||||||
|
"id": "history",
|
||||||
|
"source": "shader.slang",
|
||||||
|
"entryPoint": "buildHistory",
|
||||||
|
"output": "historyBuffer"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "display",
|
||||||
|
"source": "shader.slang",
|
||||||
|
"entryPoint": "displayHistory",
|
||||||
|
"inputs": [
|
||||||
|
"historyBuffer"
|
||||||
|
],
|
||||||
|
"output": "layerOutput"
|
||||||
|
}
|
||||||
|
],
|
||||||
"feedback": {
|
"feedback": {
|
||||||
"enabled": true
|
"enabled": true,
|
||||||
|
"writePass": "history"
|
||||||
},
|
},
|
||||||
"parameters": [
|
"parameters": [
|
||||||
{
|
{
|
||||||
"id": "highlightThreshold",
|
"id": "highlightThreshold",
|
||||||
"label": "Highlight Threshold",
|
"label": "Highlight Threshold",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"default": 0.99,
|
"default": 0.92,
|
||||||
"min": 0.5,
|
"min": 0.5,
|
||||||
"max": 1.5,
|
"max": 1.5,
|
||||||
"step": 0.001,
|
"step": 0.001,
|
||||||
@@ -30,23 +48,23 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "accumulateAmount",
|
"id": "accumulateAmount",
|
||||||
"label": "Accumulate Amount",
|
"label": "Contribution Amount",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"default": 0.2,
|
"default": 0.6,
|
||||||
"min": 0.0,
|
"min": 0.0,
|
||||||
"max": 2.0,
|
"max": 2.0,
|
||||||
"step": 0.001,
|
"step": 0.001,
|
||||||
"description": "How much of each bright sample gets added into the running feedback image."
|
"description": "Strength of each frame's highlight contribution before it is shifted into the rolling history."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "displayGain",
|
"id": "displayGain",
|
||||||
"label": "Display Gain",
|
"label": "Display Gain",
|
||||||
"type": "float",
|
"type": "float",
|
||||||
"default": 1.0,
|
"default": 2.5,
|
||||||
"min": 0.0,
|
"min": 0.0,
|
||||||
"max": 4.0,
|
"max": 8.0,
|
||||||
"step": 0.01,
|
"step": 0.01,
|
||||||
"description": "Brightness applied to the accumulated feedback when displayed."
|
"description": "Brightness applied to the sum of the most recent three stored highlight samples."
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"id": "baseMix",
|
"id": "baseMix",
|
||||||
@@ -56,17 +74,7 @@
|
|||||||
"min": 0.0,
|
"min": 0.0,
|
||||||
"max": 1.0,
|
"max": 1.0,
|
||||||
"step": 0.01,
|
"step": 0.01,
|
||||||
"description": "Amount of the live source image kept under the accumulation."
|
"description": "Amount of the live source image kept under the recent highlight history display."
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": "resetSeconds",
|
|
||||||
"label": "Reset Seconds",
|
|
||||||
"type": "float",
|
|
||||||
"default": 10.0,
|
|
||||||
"min": 1.0,
|
|
||||||
"max": 60.0,
|
|
||||||
"step": 0.1,
|
|
||||||
"description": "Length of each accumulation window before the feedback resets."
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,29 +3,45 @@ float luminance(float3 color)
|
|||||||
return dot(color, float3(0.2126, 0.7152, 0.0722));
|
return dot(color, float3(0.2126, 0.7152, 0.0722));
|
||||||
}
|
}
|
||||||
|
|
||||||
float currentResetPhase(float timeSeconds, float resetInterval)
|
float4 buildHistory(ShaderContext context)
|
||||||
{
|
{
|
||||||
float safeInterval = max(resetInterval, 0.001);
|
|
||||||
float cycleIndex = floor(timeSeconds / safeInterval);
|
|
||||||
return frac(cycleIndex * 0.5) >= 0.5 ? 1.0 : 0.0;
|
|
||||||
}
|
|
||||||
|
|
||||||
float4 shadeVideo(ShaderContext context)
|
|
||||||
{
|
|
||||||
float safeResetSeconds = max(resetSeconds, 0.001);
|
|
||||||
float phase = currentResetPhase(context.time, safeResetSeconds);
|
|
||||||
|
|
||||||
float4 previousFeedback = sampleFeedback(context.uv);
|
float4 previousFeedback = sampleFeedback(context.uv);
|
||||||
bool phaseMatches = context.feedbackAvailable > 0 && abs(previousFeedback.a - phase) < 0.25;
|
float4 previousHistory = context.feedbackAvailable > 0
|
||||||
float3 previousAccumulation = phaseMatches ? previousFeedback.rgb : float3(0.0, 0.0, 0.0);
|
? previousFeedback
|
||||||
|
: float4(0.0, 0.0, 0.0, 0.0);
|
||||||
|
|
||||||
|
// Highlight selection is always based on the live source image only.
|
||||||
|
// The stored feedback never feeds back into the threshold test.
|
||||||
float currentLuma = luminance(context.sourceColor.rgb);
|
float currentLuma = luminance(context.sourceColor.rgb);
|
||||||
float thresholdWidth = max(softness, 0.0001);
|
float thresholdWidth = max(softness, 0.0001);
|
||||||
float brightMask = smoothstep(highlightThreshold - thresholdWidth, highlightThreshold + thresholdWidth, currentLuma);
|
float brightMask = smoothstep(highlightThreshold - thresholdWidth, highlightThreshold + thresholdWidth, currentLuma);
|
||||||
|
|
||||||
float3 currentContribution = context.sourceColor.rgb * brightMask * max(accumulateAmount, 0.0);
|
// Treat RGBA as a 4-slot rolling scalar history:
|
||||||
float3 nextAccumulation = saturate(previousAccumulation + currentContribution);
|
// R = current frame, G = 1 frame ago, B = 2 frames ago, A = 3 frames ago.
|
||||||
|
// Each frame shifts the history forward and drops the oldest sample.
|
||||||
|
float currentContribution = currentLuma * brightMask * max(accumulateAmount, 0.0);
|
||||||
|
float4 nextHistory = float4(
|
||||||
|
currentContribution,
|
||||||
|
previousHistory.r,
|
||||||
|
previousHistory.g,
|
||||||
|
previousHistory.b
|
||||||
|
);
|
||||||
|
|
||||||
float3 displayColor = context.sourceColor.rgb * saturate(baseMix) + nextAccumulation * max(displayGain, 0.0);
|
return saturate(nextHistory);
|
||||||
return float4(saturate(displayColor), phase);
|
}
|
||||||
|
|
||||||
|
float4 displayHistory(ShaderContext context)
|
||||||
|
{
|
||||||
|
// In the display pass, context.sourceColor is the same-frame historyBuffer
|
||||||
|
// produced by buildHistory().
|
||||||
|
float4 history = context.sourceColor;
|
||||||
|
float recentEnergy = history.r + history.g + history.b;
|
||||||
|
float3 originalColor = sampleLayerInput(context.uv).rgb;
|
||||||
|
float highlightBoost = recentEnergy * max(displayGain, 0.0);
|
||||||
|
float3 sourceHue = originalColor / max(max(originalColor.r, originalColor.g), max(originalColor.b, 0.0001));
|
||||||
|
float3 displayColor =
|
||||||
|
originalColor +
|
||||||
|
sourceHue * highlightBoost * 1.5;
|
||||||
|
|
||||||
|
return float4(saturate(displayColor), 1.0);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user