mirror of
https://github.com/lubosz/overte.git
synced 2025-04-24 21:23:18 +02:00
Adding a simple manual exposure control to configure the tonemapping and expose it to js. Add a convenient way to access the Job._concept._data with template
This commit is contained in:
parent
2bbd5d86b8
commit
d2ebaef69e
18 changed files with 165 additions and 12 deletions
|
@ -109,6 +109,11 @@ panel.newCheckbox("Network/Physics status",
|
|||
function(value) { return (value & showNetworkStatusFlag) > 0; }
|
||||
);
|
||||
|
||||
panel.newSlider("Tone Mapping Exposure", -10, 10,
|
||||
function (value) { Scene.setEngineToneMappingExposure(value); },
|
||||
function() { return Scene.getEngineToneMappingExposure(); },
|
||||
function (value) { return (value); });
|
||||
|
||||
var tickTackPeriod = 500;
|
||||
|
||||
function updateCounters() {
|
||||
|
|
|
@ -3691,6 +3691,8 @@ void Application::displaySide(RenderArgs* renderArgs, Camera& theCamera, bool se
|
|||
renderContext._occlusionStatus = Menu::getInstance()->isOptionChecked(MenuOption::DebugAmbientOcclusion);
|
||||
renderContext._fxaaStatus = Menu::getInstance()->isOptionChecked(MenuOption::Antialiasing);
|
||||
|
||||
renderContext._toneMappingExposure = sceneInterface->getEngineToneMappingExposure();
|
||||
|
||||
renderArgs->_shouldRender = LODManager::shouldRender;
|
||||
|
||||
renderContext.args = renderArgs;
|
||||
|
|
|
@ -311,6 +311,12 @@ void Batch::blit(const FramebufferPointer& src, const Vec4i& srcViewport,
|
|||
_params.push_back(dstViewport.w);
|
||||
}
|
||||
|
||||
void Batch::generateTextureMipmap(const TexturePointer& texture) {
|
||||
ADD_COMMAND(generateTextureMipmap);
|
||||
|
||||
_params.push_back(_textures.cache(texture));
|
||||
}
|
||||
|
||||
void Batch::beginQuery(const QueryPointer& query) {
|
||||
ADD_COMMAND(beginQuery);
|
||||
|
||||
|
|
|
@ -211,6 +211,9 @@ public:
|
|||
// with xy and zw the bounding corners of the rect region.
|
||||
void blit(const FramebufferPointer& src, const Vec4i& srcRect, const FramebufferPointer& dst, const Vec4i& dstRect);
|
||||
|
||||
// Generate the mipmap for a texture
|
||||
void generateTextureMipmap(const TexturePointer& texture);
|
||||
|
||||
// Query Section
|
||||
void beginQuery(const QueryPointer& query);
|
||||
void endQuery(const QueryPointer& query);
|
||||
|
@ -292,6 +295,7 @@ public:
|
|||
COMMAND_setFramebuffer,
|
||||
COMMAND_clearFramebuffer,
|
||||
COMMAND_blit,
|
||||
COMMAND_generateTextureMipmap,
|
||||
|
||||
COMMAND_beginQuery,
|
||||
COMMAND_endQuery,
|
||||
|
|
|
@ -52,6 +52,7 @@ GLBackend::CommandCall GLBackend::_commandCalls[Batch::NUM_COMMANDS] =
|
|||
(&::gpu::GLBackend::do_setFramebuffer),
|
||||
(&::gpu::GLBackend::do_clearFramebuffer),
|
||||
(&::gpu::GLBackend::do_blit),
|
||||
(&::gpu::GLBackend::do_generateTextureMipmap),
|
||||
|
||||
(&::gpu::GLBackend::do_beginQuery),
|
||||
(&::gpu::GLBackend::do_endQuery),
|
||||
|
|
|
@ -376,12 +376,16 @@ protected:
|
|||
|
||||
// Resource Stage
|
||||
void do_setResourceTexture(Batch& batch, size_t paramOffset);
|
||||
|
||||
|
||||
// update resource cache and do the gl unbind call with the current gpu::Texture cached at slot s
|
||||
void releaseResourceTexture(uint32_t slot);
|
||||
|
||||
void resetResourceStage();
|
||||
struct ResourceStageState {
|
||||
Textures _textures;
|
||||
|
||||
int findEmptyTextureSlot() const;
|
||||
|
||||
ResourceStageState():
|
||||
_textures(MAX_NUM_RESOURCE_TEXTURES, nullptr)
|
||||
{}
|
||||
|
@ -432,6 +436,7 @@ protected:
|
|||
void do_setFramebuffer(Batch& batch, size_t paramOffset);
|
||||
void do_clearFramebuffer(Batch& batch, size_t paramOffset);
|
||||
void do_blit(Batch& batch, size_t paramOffset);
|
||||
void do_generateTextureMipmap(Batch& batch, size_t paramOffset);
|
||||
|
||||
// Synchronize the state cache of this Backend with the actual real state of the GL Context
|
||||
void syncOutputStateCache();
|
||||
|
|
|
@ -361,4 +361,4 @@ void GLBackend::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, co
|
|||
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
|
||||
|
||||
(void) CHECK_GL_ERROR();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -231,6 +231,7 @@ void GLBackend::releaseResourceTexture(uint32_t slot) {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
void GLBackend::resetResourceStage() {
|
||||
for (uint32_t i = 0; i < _resource._textures.size(); i++) {
|
||||
releaseResourceTexture(i);
|
||||
|
@ -268,3 +269,13 @@ void GLBackend::do_setResourceTexture(Batch& batch, size_t paramOffset) {
|
|||
}
|
||||
}
|
||||
|
||||
int GLBackend::ResourceStageState::findEmptyTextureSlot() const {
|
||||
// start from the end of the slots, try to find an empty one that can be used
|
||||
for (auto i = MAX_NUM_RESOURCE_TEXTURES - 1; i > 0; i--) {
|
||||
if (!_textures[i]) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -404,8 +404,9 @@ GLBackend::GLTexture* GLBackend::syncGPUObject(const Texture& texture) {
|
|||
if (bytes && texture.isAutogenerateMips()) {
|
||||
glGenerateMipmap(GL_TEXTURE_2D);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
} else if (texture.isAutogenerateMips()) {
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
|
||||
}
|
||||
|
||||
object->_target = GL_TEXTURE_2D;
|
||||
|
||||
syncSampler(texture.getSampler(), texture.getType(), object);
|
||||
|
@ -588,3 +589,37 @@ void GLBackend::syncSampler(const Sampler& sampler, Texture::Type type, GLTextur
|
|||
glTexParameterf(object->_target, GL_TEXTURE_MAX_ANISOTROPY_EXT, sampler.getMaxAnisotropy());
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
void GLBackend::do_generateTextureMipmap(Batch& batch, size_t paramOffset) {
|
||||
TexturePointer resourceTexture = batch._textures.get(batch._params[paramOffset + 0]._uint);
|
||||
if (!resourceTexture) {
|
||||
return;
|
||||
}
|
||||
|
||||
GLTexture* object = GLBackend::syncGPUObject(*resourceTexture);
|
||||
if (!object) {
|
||||
return;
|
||||
}
|
||||
|
||||
// IN 4.1 we still need to find an available slot
|
||||
auto freeSlot = _resource.findEmptyTextureSlot();
|
||||
auto bindingSlot = (freeSlot < 0 ? 0 : freeSlot);
|
||||
glActiveTexture(GL_TEXTURE0 + bindingSlot);
|
||||
glBindTexture(object->_target, object->_texture);
|
||||
|
||||
glGenerateMipmap(object->_target);
|
||||
|
||||
if (freeSlot < 0) {
|
||||
// If had to use slot 0 then restore state
|
||||
GLTexture* boundObject = GLBackend::syncGPUObject(*_resource._textures[0]);
|
||||
if (boundObject) {
|
||||
glBindTexture(boundObject->_target, boundObject->_texture);
|
||||
}
|
||||
} else {
|
||||
// clean up
|
||||
glBindTexture(object->_target, 0);
|
||||
}
|
||||
(void)CHECK_GL_ERROR();
|
||||
}
|
||||
|
|
|
@ -49,7 +49,7 @@ static const std::string FUNCTIONS_PLACEHOLDER { "/*FUNCTIONS_PLACEHOLDER*/" };
|
|||
std::string DebugDeferredBuffer::getCode(Modes mode) {
|
||||
switch (mode) {
|
||||
case DiffuseMode: {
|
||||
QString code = "return vec4(texture(%1, uv).xyz, 1.0);";
|
||||
QString code = "return vec4(pow(texture(%1, uv).xyz, vec3(1.0 / 2.2)), 1.0);";
|
||||
return code.arg(SLOT_NAMES[Diffuse].c_str()).toStdString();
|
||||
}
|
||||
case AlphaMode: {
|
||||
|
@ -73,7 +73,7 @@ std::string DebugDeferredBuffer::getCode(Modes mode) {
|
|||
return code.arg(SLOT_NAMES[Depth].c_str()).toStdString();
|
||||
}
|
||||
case LightingMode: {
|
||||
QString code = "return vec4(texture(%1, uv).xyz, 1.0);";
|
||||
QString code = "return vec4(pow(texture(%1, uv).xyz, vec3(1.0 / 2.2)), 1.0);";
|
||||
return code.arg(SLOT_NAMES[Lighting].c_str()).toStdString();
|
||||
}
|
||||
case CustomMode:
|
||||
|
|
|
@ -87,9 +87,11 @@ void FramebufferCache::createPrimaryFramebuffer() {
|
|||
auto tex = gpu::TexturePointer(gpu::Texture::create2D(colorFormat, width * 0.5, height * 0.5, defaultSampler));
|
||||
_selfieFramebuffer->setRenderBuffer(0, tex);
|
||||
|
||||
auto smoothSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_MIP_LINEAR);
|
||||
|
||||
//_lightingTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element::COLOR_RGBA_32, width, height, defaultSampler));
|
||||
//lightingTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC3, gpu::NUINT8, gpu::R11G11B10), width, height, defaultSampler));
|
||||
_lightingTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::HALF, gpu::RGBA), width, height, defaultSampler));
|
||||
_lightingTexture = gpu::TexturePointer(gpu::Texture::create2D(gpu::Element(gpu::VEC4, gpu::HALF, gpu::RGBA), width, height, smoothSampler));
|
||||
_lightingFramebuffer = gpu::FramebufferPointer(gpu::Framebuffer::create());
|
||||
_lightingFramebuffer->setRenderBuffer(0, _lightingTexture);
|
||||
_lightingFramebuffer->setDepthStencilBuffer(_primaryDepthTexture, depthFormat);
|
||||
|
|
|
@ -104,6 +104,7 @@ RenderDeferredTask::RenderDeferredTask() : Task() {
|
|||
|
||||
// Lighting Buffer ready for tone mapping
|
||||
_jobs.push_back(Job(new ToneMappingDeferred::JobModel("ToneMapping")));
|
||||
_toneMappingJobIndex = _jobs.size() - 1;
|
||||
|
||||
// Debugging Deferred buffer job
|
||||
_jobs.push_back(Job(new DebugDeferredBuffer::JobModel("DebugDeferredBuffer")));
|
||||
|
@ -164,6 +165,8 @@ void RenderDeferredTask::run(const SceneContextPointer& sceneContext, const Rend
|
|||
|
||||
setAntialiasingStatus(renderContext->_fxaaStatus);
|
||||
|
||||
setToneMappingExposure(renderContext->_toneMappingExposure);
|
||||
|
||||
renderContext->args->_context->syncCache();
|
||||
|
||||
for (auto job : _jobs) {
|
||||
|
@ -390,3 +393,18 @@ void DrawBackgroundDeferred::run(const SceneContextPointer& sceneContext, const
|
|||
});
|
||||
args->_batch = nullptr;
|
||||
}
|
||||
|
||||
|
||||
void RenderDeferredTask::setToneMappingExposure(float exposure) {
|
||||
if (_toneMappingJobIndex >= 0) {
|
||||
_jobs[_toneMappingJobIndex].edit<ToneMappingDeferred>()._toneMappingEffect.setExposure(exposure);
|
||||
}
|
||||
}
|
||||
|
||||
float RenderDeferredTask::getToneMappingExposure() const {
|
||||
if (_toneMappingJobIndex >= 0) {
|
||||
_jobs[_toneMappingJobIndex].get<ToneMappingDeferred>()._toneMappingEffect.getExposure();
|
||||
} else {
|
||||
return 0.0f;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -129,6 +129,11 @@ public:
|
|||
void setAntialiasingStatus(bool draw) { if (_antialiasingJobIndex >= 0) { _jobs[_antialiasingJobIndex].setEnabled(draw); } }
|
||||
bool doAntialiasingStatus() const { if (_antialiasingJobIndex >= 0) { return _jobs[_antialiasingJobIndex].isEnabled(); } else { return false; } }
|
||||
|
||||
int _toneMappingJobIndex = -1;
|
||||
|
||||
void setToneMappingExposure(float exposure);
|
||||
float getToneMappingExposure() const;
|
||||
|
||||
virtual void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
|
||||
|
||||
|
||||
|
|
|
@ -20,7 +20,8 @@
|
|||
|
||||
|
||||
ToneMappingEffect::ToneMappingEffect() {
|
||||
|
||||
Parameters parameters;
|
||||
_parametersBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Parameters), (const gpu::Byte*) ¶meters));
|
||||
}
|
||||
|
||||
void ToneMappingEffect::init() {
|
||||
|
@ -38,6 +39,16 @@ void ToneMappingEffect::init() {
|
|||
// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html
|
||||
//
|
||||
|
||||
struct ToneMappingParams {
|
||||
vec4 _exp_2powExp_s0_s1;
|
||||
};
|
||||
|
||||
uniform toneMappingParamsBuffer {
|
||||
ToneMappingParams params;
|
||||
};
|
||||
float getTwoPowExposure() {
|
||||
return params._exp_2powExp_s0_s1.y;
|
||||
}
|
||||
|
||||
uniform sampler2D colorMap;
|
||||
|
||||
|
@ -45,16 +56,24 @@ void ToneMappingEffect::init() {
|
|||
out vec4 outFragColor;
|
||||
|
||||
void main(void) {
|
||||
vec4 fragColor = texture(colorMap, varTexCoord0);
|
||||
vec4 fragColorRaw = textureLod(colorMap, varTexCoord0, 0);
|
||||
vec3 fragColor = fragColorRaw.xyz;
|
||||
|
||||
/* vec4 fragColorAverage = textureLod(colorMap, varTexCoord0, 10);
|
||||
float averageIntensity = length(fragColorAverage.xyz);
|
||||
|
||||
vec3 fragColor = fragColorRaw.xyz / averageIntensity;
|
||||
*/
|
||||
fragColor *= getTwoPowExposure();
|
||||
|
||||
|
||||
// if (gl_FragCoord.x > 1000) {
|
||||
// Manually gamma correct from Ligthing BUffer to color buffer
|
||||
// outFragColor.xyz = pow( fragColor.xyz , vec3(1.0 / 2.2) );
|
||||
|
||||
fragColor *= 2.0; // Hardcoded Exposure Adjustment
|
||||
vec3 x = max(vec3(0.0),fragColor.xyz-0.004);
|
||||
vec3 retColor = (x*(6.2*x+.5))/(x*(6.2*x+1.7)+0.06);
|
||||
|
||||
// fragColor *= 8; // Hardcoded Exposure Adjustment
|
||||
// fragColor = fragColor/(1.0+fragColor);
|
||||
// vec3 retColor = pow(fragColor.xyz,vec3(1/2.2));
|
||||
|
||||
|
@ -70,12 +89,19 @@ void ToneMappingEffect::init() {
|
|||
auto blitProgram = gpu::ShaderPointer(gpu::Shader::createProgram(blitVS, blitPS));
|
||||
|
||||
//auto blitProgram = gpu::StandardShaderLib::getProgram(gpu::StandardShaderLib::getDrawViewportQuadTransformTexcoordVS, gpu::StandardShaderLib::getDrawTexturePS);
|
||||
gpu::Shader::makeProgram(*blitProgram);
|
||||
gpu::Shader::BindingSet slotBindings;
|
||||
slotBindings.insert(gpu::Shader::Binding(std::string("toneMappingParamsBuffer"), 3));
|
||||
gpu::Shader::makeProgram(*blitProgram, slotBindings);
|
||||
auto blitState = std::make_shared<gpu::State>();
|
||||
blitState->setColorWriteMask(true, true, true, true);
|
||||
_blitLightBuffer = gpu::PipelinePointer(gpu::Pipeline::create(blitProgram, blitState));
|
||||
}
|
||||
|
||||
void ToneMappingEffect::setExposure(float exposure) {
|
||||
_parametersBuffer.edit<Parameters>()._exposure = exposure;
|
||||
_parametersBuffer.edit<Parameters>()._twoPowExposure = pow(2.0, exposure);
|
||||
}
|
||||
|
||||
|
||||
void ToneMappingEffect::render(RenderArgs* args) {
|
||||
if (!_blitLightBuffer) {
|
||||
|
@ -89,6 +115,9 @@ void ToneMappingEffect::render(RenderArgs* args) {
|
|||
auto lightingBuffer = framebufferCache->getLightingTexture();
|
||||
auto destFbo = framebufferCache->getPrimaryFramebuffer();
|
||||
batch.setFramebuffer(destFbo);
|
||||
|
||||
batch.generateTextureMipmap(lightingBuffer);
|
||||
|
||||
batch.setViewportTransform(args->_viewport);
|
||||
batch.setProjectionTransform(glm::mat4());
|
||||
batch.setViewTransform(Transform());
|
||||
|
@ -104,6 +133,7 @@ void ToneMappingEffect::render(RenderArgs* args) {
|
|||
batch.setModelTransform(model);
|
||||
}
|
||||
|
||||
batch.setUniformBuffer(3, _parametersBuffer);
|
||||
batch.setResourceTexture(0, lightingBuffer);
|
||||
batch.draw(gpu::TRIANGLE_STRIP, 4);
|
||||
|
||||
|
|
|
@ -27,6 +27,9 @@ public:
|
|||
|
||||
void render(RenderArgs* args);
|
||||
|
||||
void setExposure(float exposure);
|
||||
float getExposure() const { return _parametersBuffer.get<Parameters>()._exposure; }
|
||||
|
||||
private:
|
||||
|
||||
gpu::PipelinePointer _blitLightBuffer;
|
||||
|
@ -34,7 +37,10 @@ private:
|
|||
// Class describing the uniform buffer with all the parameters common to the tone mapping shaders
|
||||
class Parameters {
|
||||
public:
|
||||
|
||||
float _exposure = 0.0f;
|
||||
float _twoPowExposure = 1.0f;
|
||||
glm::vec2 spare;
|
||||
|
||||
Parameters() {}
|
||||
};
|
||||
typedef gpu::BufferView UniformBufferView;
|
||||
|
|
|
@ -84,6 +84,23 @@ public:
|
|||
const Varying getInput() const { return _concept->getInput(); }
|
||||
const Varying getOutput() const { return _concept->getOutput(); }
|
||||
|
||||
template <class T> T& edit() {
|
||||
auto theConcept = std::dynamic_pointer_cast<typename T::JobModel>(_concept);
|
||||
if (theConcept) {
|
||||
return theConcept->_data;
|
||||
}
|
||||
assert(false);
|
||||
return T();
|
||||
}
|
||||
template <class T> const T& get() const {
|
||||
auto theConcept = std::dynamic_pointer_cast<typename T::JobModel>(_concept);
|
||||
if (theConcept) {
|
||||
return theConcept->_data;
|
||||
}
|
||||
assert(false);
|
||||
return T();
|
||||
}
|
||||
|
||||
void run(const SceneContextPointer& sceneContext, const RenderContextPointer& renderContext) {
|
||||
PerformanceTimer perfTimer(getName().c_str());
|
||||
PROFILE_RANGE(getName().c_str());
|
||||
|
|
|
@ -61,6 +61,8 @@ public:
|
|||
bool _occlusionStatus = false;
|
||||
bool _fxaaStatus = false;
|
||||
|
||||
float _toneMappingExposure = 0.0;
|
||||
|
||||
RenderContext() {}
|
||||
};
|
||||
typedef std::shared_ptr<RenderContext> RenderContextPointer;
|
||||
|
|
|
@ -118,6 +118,9 @@ public:
|
|||
Q_INVOKABLE void setEngineDisplayHitEffect(bool display) { _drawHitEffect = display; }
|
||||
Q_INVOKABLE bool doEngineDisplayHitEffect() { return _drawHitEffect; }
|
||||
|
||||
Q_INVOKABLE void setEngineToneMappingExposure(float exposure) { _engineToneMappingExposure = exposure; }
|
||||
Q_INVOKABLE float getEngineToneMappingExposure() { return _engineToneMappingExposure; }
|
||||
|
||||
signals:
|
||||
void shouldRenderAvatarsChanged(bool shouldRenderAvatars);
|
||||
void shouldRenderEntitiesChanged(bool shouldRenderEntities);
|
||||
|
@ -154,6 +157,7 @@ protected:
|
|||
|
||||
bool _drawHitEffect = false;
|
||||
|
||||
float _engineToneMappingExposure = 0.0f;
|
||||
};
|
||||
|
||||
#endif // hifi_SceneScriptingInterface_h
|
||||
|
|
Loading…
Reference in a new issue