nanogui
nanogui copied to clipboard
Draw image with single pixels
Hello!
Is their a possibility to draw pixelbased on a canvas element? Because I coded a raytracer and now i want to implement a gui with liveview of the render process. So I have an array with color-values and I want to paint them on the screen, everytime a new pixel gets calculated. Do you have any idea how I can do this?
Thank you :)
One way I could think of is keeping a texture image in memory, update it with each pixel and reassign the new texture to the imageViewer: https://stackoverflow.com/questions/9863969/updating-a-texture-in-opengl-with-glteximage2d
Okay I tried it with this code: canvas.h:
#pragma once
#include <nanogui/nanogui.h>
#include <stdint.h>
NAMESPACE_BEGIN(nanogui)
struct Color3 {
double r;
double g;
double b;
};
class Canvas2D : ImageView {
friend class ImageView;
public:
Canvas2D(Widget *parent, int x, int y);
void drawPixel(double r, double g, double b, int x, int y);
void clearDisplay();
private:
void updateImage();
int imageSizeX;
int imageSizeY;
typedef unsigned __int8 u8;
u8 ***screenData;
public:
EIGEN_MAKE_ALIGNED_OPERATOR_NEW
};
NAMESPACE_END(nanogui)
canvas.cpp:
Canvas2D::Canvas2D(Widget *parent, int x, int y)
: ImageView(parent, 0) {
setSize({ x, y });
imageSizeX = x;
imageSizeY = y;
screenData = new u8**[y];
for (int i = 0; i < y; i++) {
screenData[i] = new u8*[x];
for (int j = 0; j < x; j++) {
screenData[i][j] = new u8[3];
}
}
mShader.init("ImageViewShader", defaultImageViewVertexShader, defaultImageViewFragmentShader);
MatrixXu indices(3, 2);
indices.col(0) << 0, 1, 2;
indices.col(1) << 2, 3, 1;
MatrixXf vertices(2, 4);
vertices.col(0) << 0, 0;
vertices.col(1) << 1, 0;
vertices.col(2) << 0, 1;
vertices.col(3) << 1, 1;
mShader.bind();
mShader.uploadIndices(indices);
mShader.uploadAttrib("vertex", vertices);
clearDisplay();
glGenTextures(1, &mImageID);
glBindTexture(GL_TEXTURE_2D, mImageID);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, x, y, 0, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*)screenData);
updateImage();
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_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);
// Enable textures
glEnable(GL_TEXTURE_2D);
updateImageParameters();
}
void Canvas2D::drawPixel(double r, double g, double b, int x, int y) {
if (x < 0 || x > imageSizeX || y < 0 || y > imageSizeY) return;
screenData[y][x][0] = (u8) (r * 255);
screenData[y][x][1] = (u8) (g * 255);
screenData[y][x][2] = (u8) (b * 255);
updateImage();
return;
}
void Canvas2D::clearDisplay() {
for (int i = 0; i < imageSizeY; i++) {
for (int j = 0; j < imageSizeX; j++) {
screenData[i][j][0] = screenData[i][j][1] = screenData[i][j][2] = 0;
}
}
}
void Canvas2D::updateImage() {
glBindTexture(GL_TEXTURE_2D, mImageID);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, imageSizeX, imageSizeY, GL_RGB, GL_UNSIGNED_BYTE, (GLvoid*)screenData);
return;
}
NAMESPACE_END(nanogui)
and main.cpp:
nanogui::init();
if(THREAD_COUNT <= 0){
std::cout << "invalid THREAD_COUNT, check settings.h" << std::endl;
return 0;
}
//Init GUI
screen = new Screen(Vector2i(800, 500), "Raytracer");
{
bool enabled = true;
FormHelper *gui = new FormHelper(screen);
ref<Window> window2 = gui->addWindow(Eigen::Vector2i(10, 10), "Settings");
Window *window = new Window(screen, "Render");
window->setPosition(Vector2i(425, 15));
window->setSize(Vector2i(640, 480));
window->setLayout(new GroupLayout());
canvas2d = new Canvas2D(window, 640, 480);
...
screen->setVisible(true);
screen->performLayout();
window->center();
nanogui::mainloop();
}
nanogui::shutdown();
There is an image output, but only random colors. And there is an error message "error 0500 after convex fill". Do you know how to fix it? Thank you! :)
Does the image clear properly after you call clearDisplay()? If so then it would be a mismatch between the image formats in memory and on the GPU.
If you are using glTexSubImage2D it would actually be possible to update just the one pixel in the texture (which removes the need to keep it in memory all together).