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:
samcake 2015-12-15 18:18:42 -08:00
parent 2bbd5d86b8
commit d2ebaef69e
18 changed files with 165 additions and 12 deletions

View file

@ -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() {

View file

@ -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;

View file

@ -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);

View file

@ -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,

View file

@ -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),

View file

@ -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();

View file

@ -361,4 +361,4 @@ void GLBackend::downloadFramebuffer(const FramebufferPointer& srcFramebuffer, co
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
(void) CHECK_GL_ERROR();
}
}

View file

@ -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;
}

View file

@ -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();
}

View file

@ -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:

View file

@ -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);

View file

@ -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;
}
}

View file

@ -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);

View file

@ -20,7 +20,8 @@
ToneMappingEffect::ToneMappingEffect() {
Parameters parameters;
_parametersBuffer = gpu::BufferView(std::make_shared<gpu::Buffer>(sizeof(Parameters), (const gpu::Byte*) &parameters));
}
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);

View file

@ -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;

View file

@ -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());

View file

@ -61,6 +61,8 @@ public:
bool _occlusionStatus = false;
bool _fxaaStatus = false;
float _toneMappingExposure = 0.0;
RenderContext() {}
};
typedef std::shared_ptr<RenderContext> RenderContextPointer;

View file

@ -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