nori/src/gui.cpp

135 lines
4.3 KiB
C++

#include <nori/gui.h>
#include <nori/block.h>
#include <nanogui/shader.h>
#include <nanogui/label.h>
#include <nanogui/slider.h>
#include <nanogui/layout.h>
#include <nanogui/renderpass.h>
#include <nanogui/texture.h>
NORI_NAMESPACE_BEGIN
NoriScreen::NoriScreen(const ImageBlock &block)
: nanogui::Screen(nanogui::Vector2i(block.getSize().x(), block.getSize().y() + 36),
"Nori", false),
m_block(block) {
using namespace nanogui;
inc_ref();
/* Add some UI elements to adjust the exposure value */
Widget *panel = new Widget(this);
panel->set_layout(new BoxLayout(Orientation::Horizontal, Alignment::Middle, 10, 10));
new Label(panel, "Exposure value: ", "sans-bold");
Slider *slider = new Slider(panel);
slider->set_value(0.5f);
slider->set_fixed_width(150);
slider->set_callback(
[&](float value) {
m_scale = std::pow(2.f, (value - 0.5f) * 20);
}
);
panel->set_size(nanogui::Vector2i(block.getSize().x(), block.getSize().y()));
perform_layout();
panel->set_position(
nanogui::Vector2i((m_size.x() - panel->size().x()) / 2, block.getSize().y()));
/* Simple gamma tonemapper as a GLSL shader */
m_renderPass = new RenderPass({ this });
m_renderPass->set_clear_color(0, Color(0.3f, 0.3f, 0.3f, 1.f));
m_shader = new Shader(
m_renderPass,
/* An identifying name */
"Tonemapper",
/* Vertex shader */
R"(#version 330
uniform ivec2 size;
uniform int borderSize;
in vec2 position;
out vec2 uv;
void main() {
gl_Position = vec4(position.x * 2 - 1, position.y * 2 - 1, 0.0, 1.0);
// Crop away image border (due to pixel filter)
vec2 total_size = size + 2 * borderSize;
vec2 scale = size / total_size;
uv = vec2(position.x * scale.x + borderSize / total_size.x,
1 - (position.y * scale.y + borderSize / total_size.y));
})",
/* Fragment shader */
R"(#version 330
uniform sampler2D source;
uniform float scale;
in vec2 uv;
out vec4 out_color;
float toSRGB(float value) {
if (value < 0.0031308)
return 12.92 * value;
return 1.055 * pow(value, 0.41666) - 0.055;
}
void main() {
vec4 color = texture(source, uv);
color *= scale / color.w;
out_color = vec4(toSRGB(color.r), toSRGB(color.g), toSRGB(color.b), 1);
})"
);
// Draw 2 triangles
uint32_t indices[3 * 2] = {
0, 1, 2,
2, 3, 0
};
float positions[2 * 4] = {
0.f, 0.f,
1.f, 0.f,
1.f, 1.f,
0.f, 1.f
};
m_shader->set_buffer("indices", VariableType::UInt32, {3*2}, indices);
m_shader->set_buffer("position", VariableType::Float32, {4, 2}, positions);
const Vector2i &size = m_block.getSize();
m_shader->set_uniform("size", nanogui::Vector2i(size.x(), size.y()));
m_shader->set_uniform("borderSize", m_block.getBorderSize());
// Allocate texture memory for the rendered image
m_texture = new Texture(
Texture::PixelFormat::RGBA,
Texture::ComponentFormat::Float32,
nanogui::Vector2i(size.x() + 2 * m_block.getBorderSize(),
size.y() + 2 * m_block.getBorderSize()),
Texture::InterpolationMode::Nearest,
Texture::InterpolationMode::Nearest);
draw_all();
set_visible(true);
}
void NoriScreen::draw_contents() {
// Reload the partially rendered image onto the GPU
m_block.lock();
const Vector2i &size = m_block.getSize();
m_shader->set_uniform("scale", m_scale);
m_renderPass->resize(framebuffer_size());
m_renderPass->begin();
m_renderPass->set_viewport(nanogui::Vector2i(0, 0),
nanogui::Vector2i(m_pixel_ratio * size[0],
m_pixel_ratio * size[1]));
m_texture->upload((uint8_t *) m_block.data());
m_shader->set_texture("source", m_texture);
m_shader->begin();
m_shader->draw_array(nanogui::Shader::PrimitiveType::Triangle, 0, 6, true);
m_shader->end();
m_renderPass->set_viewport(nanogui::Vector2i(0, 0), framebuffer_size());
m_renderPass->end();
m_block.unlock();
}
NORI_NAMESPACE_END