I'm trying to display a Coin3D/Open Inventor scene with QT in a QGLWidget, by using the SoOffscreenRenderer and I need help converting it to a QImage
What I tried so far, is render the scene into SoOffscreenRenderer and get the buffer like this:
unsigned char * getCoinCubeImgBuffer(){
// [...] create the scene, add lightning and camera
SoOffscreenRenderer offscreenRenderer(vpRegion);
offscreenRenderer.setComponents(
SoOffscreenRenderer::Components::RGB_TRANSPARENCY
);
SbBool ok = offscreenRenderer.render(root);
// to be sure that something is actually rendered
// save the buffer content to a file
SbBool ok = offscreenRenderer.render(root);
qDebug() << "SbBool ok?" << ok;
qDebug() << "wasFileWrittenRGB" <<
offscreenRenderer.writeToRGB("C:/test-gl.rgb");
qDebug() << "wasFileWrittenPS" <<
offscreenRenderer.writeToPostScript("C:/test-gl.ps");
unsigned char * imgbuffer = offscreenRenderer.getBuffer();
return imgbuffer;
}
and then create a QImage from the buffer data:
QImage convertImgBuffer(){
unsigned char *const imgBuffer = getCoinCubeImgBuffer();
QImage img(imgBuffer, windowWidth, windowHeight, QImage::Format_ARGB32);
// Important!
img = img.rgbSwapped();
QImage imgGL = convertToGLFormat(img);
return imgGL;
}
Would this be the correct way to do it?
As described in this question about drawing a QImage, I'm able to draw it if the source is a picture.
e: To make sure that my buffer actually contains a scene, I write the buffer content to two files. You can view .rgb and .ps files for example with IrfanView plus its plugins.
e2: Just figured out, that I have to use img.rgbSwapped(). Now it's showing the scene black&white and without lightning. I will investigate further.
e3: With the code like this, you need to adapt the OpenGL call in this way to render in color
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tex.width(),
tex.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, tex.bits());
First format is GL_RGB, second GL_RGBA. The cube is still completely black though.
e4: It was an error in my scene, you have to add a light before you add the rest and especially before you add the camera.
So now I can either use `QGLWidget`s functions like `bindTexture()` or use direct OpenGL calls, but I'm not sure how exactly. It would be great if someone could push me in the right direction.
Can't I just use OpenGL like this?
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &offscreenBufferTexture);
glBindTexture(GL_TEXTURE_2D, offscreenBufferTexture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imgGL.width(),
imgGL.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE,
imgGL.bits());
Or maybe `glDrawPixels()`?
glDrawPixels(imgGL.width(), imgGL.height(), GL_RGBA,
GL_UNSIGNED_BYTE, imgGL.bits());
I figured out how to draw a QImage with OpenGL, see this thread. So there seems to be a problem with the buffer or the conversion of it.
Here's the resulting code which renders the scene correctly.
First create a valid scene
void loadCoinScene(){
// Init Coin
SoDB::init();
// The root node
root = new SoSeparator;
root->ref();
// Add the light _before_ you add the camera
SoDirectionalLight * light = new SoDirectionalLight;
root->addChild(light);
vpRegion.setViewportPixels(0, 0, coinSceneWidth, coinSceneHeight);
SoPerspectiveCamera *perscam = new SoPerspectiveCamera();
root->addChild(perscam);
SbRotation cameraRotation = SbRotation::identity();
cameraRotation *= SbRotation(SbVec3f(0, 1, 0), 0.4f);
perscam->orientation = cameraRotation;
SoCube * cube = new SoCube;
root->addChild(cube);
// make sure that the cube is visible
perscam->viewAll(root, vpRegion);
}
Then render the scene into the Offscreen Buffer and convert it to a QImage:
QImage getCoinCubeImgBuffer(){
SoOffscreenRenderer offscreenRenderer(vpRegion);
offscreenRenderer.setComponents(
SoOffscreenRenderer::Components::RGB_TRANSPARENCY
);
offscreenRenderer.render(root);
QImage img(offscreenRenderer.getBuffer(), coinSceneWidth,
coinSceneHeight, QImage::Format_ARGB32);
// Important!
return img.rgbSwapped();
}
If you now want to render the QImage with OpenGL, use my solution from my Render QImage with OpenGL question and change the loadTexture2() method to this:
QImage loadTexture2(GLuint &textureID){
glEnable(GL_TEXTURE_2D); // Enable texturing
glGenTextures(1, &textureID); // Obtain an id for the texture
glBindTexture(GL_TEXTURE_2D, textureID); // Set as the current texture
QImage im = getCoinCubeImgBuffer();
// Convert to OpenGLs unnamed format
// The resulting GL format is GL_RGBA
QImage tex = QGLWidget::convertToGLFormat(im);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tex.width(), tex.height(), 0,
GL_RGBA, GL_UNSIGNED_BYTE, tex.bits());
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glDisable(GL_TEXTURE_2D);
return tex;
}
I don't think there is a need to feed the texture through QImage. The following is a working example:
#include <QApplication>
#include <QGLWidget>
#include <Inventor/SoInput.h>
#include <Inventor/SoOffscreenRenderer.h>
#include <Inventor/nodes/SoSeparator.h>
static GLuint textureID(0);
class GLWidget : public QGLWidget
{
public:
explicit GLWidget() : QGLWidget() {}
~GLWidget() {}
protected:
void initializeGL() {
glShadeModel(GL_FLAT);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
static GLfloat lightAmbient[4] = { 1.0, 1.0, 1.0, 1.0 };
glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient);
}
void paintGL() {
if (!textureID)
return;
glClearColor(0.4f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
glBindTexture(GL_TEXTURE_2D, textureID);
glBegin(GL_QUADS);
glTexCoord2f(0,0); glVertex3f(-1, -1, -1);
glTexCoord2f(1,0); glVertex3f( 1, -1, -1);
glTexCoord2f(1,1); glVertex3f( 1, 1, -1);
glTexCoord2f(0,1); glVertex3f(-1, 1, -1);
glEnd();
glDisable(GL_TEXTURE_2D);
}
void resizeGL(int width, int height) {
int side = qMin(width, height);
glViewport((width - side) / 2, (height - side) / 2, side, side);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(-1.0, 1.0, -1.0, 1.0, 0.0, 1000.0);
glMatrixMode(GL_MODELVIEW);
}
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
GLWidget glWidget;
glWidget.show();
static const char * inlineSceneGraph[] = {
"#Inventor V2.1 ascii\n",
"\n",
"Separator {\n",
" PerspectiveCamera { position 0 0 5 }\n",
" DirectionalLight {}\n",
" Rotation { rotation 1 0 0 0.3 }\n",
" Cone { }\n",
" BaseColor { rgb 1 0 0 }\n",
" Scale { scaleFactor .7 .7 .7 }\n",
" Cube { }\n",
"\n",
" DrawStyle { style LINES }\n",
" ShapeHints { vertexOrdering COUNTERCLOCKWISE }\n",
" Coordinate3 {\n",
" point [\n",
" -2 -2 1.1, -2 -1 1.1, -2 1 1.1, -2 2 1.1,\n",
" -1 -2 1.1, -1 -1 1.1, -1 1 1.1, -1 2 1.1\n",
" 1 -2 1.1, 1 -1 1.1, 1 1 1.1, 1 2 1.1\n",
" 2 -2 1.1, 2 -1 1.1, 2 1 1.1, 2 2 1.1\n",
" ]\n",
" }\n",
"\n",
" Complexity { value 0.7 }\n",
" NurbsSurface {\n",
" numUControlPoints 4\n",
" numVControlPoints 4\n",
" uKnotVector [ 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 ]\n",
" vKnotVector [ 0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0 ]\n",
" }\n",
"}\n",
NULL
};
SoInput in;
in.setStringArray(inlineSceneGraph);
glWidget.resize(600, 600);
SoOffscreenRenderer renderer(SbViewportRegion(glWidget.width(),
glWidget.height()));
renderer.setComponents(SoOffscreenRenderer::RGB_TRANSPARENCY);
renderer.setBackgroundColor(SbColor(.0f, .0f, .8f));
SoSeparator *rootScene = SoDB::readAll(&in);
rootScene->ref();
renderer.render(rootScene);
rootScene->unref();
glEnable(GL_TEXTURE_2D); // Enable texturing
glGenTextures(1, &textureID); // Obtain an id for the texture
glBindTexture(GL_TEXTURE_2D, textureID); // Set as the current texture
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
renderer.getViewportRegion().getViewportSizePixels()[0],
renderer.getViewportRegion().getViewportSizePixels()[1],
0, GL_BGRA, GL_UNSIGNED_BYTE, renderer.getBuffer());
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glDisable(GL_TEXTURE_2D);
return app.exec();
}
A simple example of rendering a local scene with SoWin, Coin3D ( download SoWin from here )
SoSeparator * CreateScene(SoSeparator* root)
{
root->ref();
root->setName("root_node");
SoPerspectiveCamera * camera = new SoPerspectiveCamera;
camera->setName("simple_camera");
SbRotation cameraRotation = SbRotation::identity();
cameraRotation *= SbRotation(SbVec3f(1, 0, 0), -0.4f);
cameraRotation *= SbRotation(SbVec3f(0, 1, 0), 0.4f);
camera->orientation = cameraRotation;
SoCone* cone1= new SoCone();
cone1->setName("Cone1");
SoBaseColor * color = new SoBaseColor;
color->setName("myColor");
root->addChild(camera);
root->addChild(cone1);
root->addChild(color);
return root;
}
Render scene created above
void RenderLocal()
{
HWND window = SoWin::init("IvExample");
if (window==NULL) exit(1);
SoWinExaminerViewer * viewer = new SoWinExaminerViewer(window);
SoSeparator * root = new SoSeparator;
auto* data = CreateScene(root);
data->getNumChildren();
viewer->setSceneGraph(root);
viewer->show();
SoWin::show(window);
SoWin::mainLoop();
delete viewer;
root->unref();
}
Render scene from iv file
int RenderFile()
{
HWND window = SoWin::init("Iv");
if (window==NULL) exit(1);
SoWinExaminerViewer * viewer = new SoWinExaminerViewer(window);
SoInput sceneInput;
if ( !sceneInput.openFile( "example.iv" ) )
return -1;
if ( !sceneInput.openFile(cPath) )
return -1;
SoSeparator *root =SoDB::readAll( &sceneInput );
root->ref();
viewer->setSceneGraph(root);
viewer->show();
SoWin::show(window);
SoWin::mainLoop();
delete viewer;
}
Related
I have trouble displaying a texture using OpenGL and C++. I am loading the texture from a RGB Raw file, binding it to a texture, but when I am trying to display the texture on a quad, the texture is white. I am using glGetError and it catches no errors. Here is my code:
#include <iostream>
#include <windows.h> // Standard header for MS Windows applications
#include <GL/gl.h> // Open Graphics Library (OpenGL) header
#include <GL/glut.h> // The GL Utility Toolkit (GLUT) Header
using namespace std;
typedef struct {
int width;
int height;
char* title;
float field_of_view_angle;
float z_near;
float z_far;
} glutWindow;
glutWindow win;
float rotation, distance1;
bool light=TRUE;
bool trans=FALSE;
GLuint tex;
void drawFlatFace(float size)
{
glBegin(GL_QUADS);
glTexCoord2f (0, 0);
glVertex3f(-size, -2*size, size);
glTexCoord2f (1.0, 0);
glVertex3f(size, -2*size, size);
glTexCoord2f (1.0, 1.0);
glVertex3f(size, 2*size, size);
glTexCoord2f (0, 1.0);
glVertex3f(-size, 2*size, size);
glEnd();
}
void drawBlock(float size){
glColor3f(1.0f,1.0f,1.0f);
glPushMatrix();
drawFlatFace (size);
glRotatef (90, 0, 1, 0);
drawFlatFace (size);
glRotatef (90, 0, 1, 0);
drawFlatFace (size);
glRotatef (90, 0, 1, 0);
drawFlatFace (size);
glPopMatrix();
}
GLuint loadTexture(const char * filename) {
GLuint text[1];
GLubyte* bits = new GLubyte[512*256*3];
FILE *file;
fopen_s(&file,filename,"rb");
fread(&bits,(512*256*3),1,file);
fclose(file);
glGenTextures(1,&text[0]);
glBindTexture(GL_TEXTURE_2D,text[0]);
glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_MODULATE);
glTexImage2D(GL_TEXTURE_2D, 0, 3,256,512,0,GL_RGB,GL_UNSIGNED_BYTE,bits);
free(bits);
return text[0];
}
void display()
{
GLenum err;
err = glGetError();
if (err != GL_NO_ERROR)
{
cerr << "OpenGL error: " << gluErrorString(err) << endl;
}
tex=loadTexture("tex.raw");
glBindTexture(GL_TEXTURE_2D,tex);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen and Depth Buffer
glLoadIdentity();
gluLookAt( 10+distance1 ,1.4,0, 0,0,0, 0,1,0); // Define a viewing transformation
glRotatef(rotation, 0.f, 1.f, 0.f);
drawBlock(3);
if (light) glEnable(GL_LIGHTING); // Enable Lighting
else glDisable(GL_LIGHTING); // Disable Lighting
if (trans) glEnable (GL_BLEND); // Enable Transparency
else glDisable(GL_BLEND); // Disable Transparency
glutSwapBuffers();
}
void initialize ()
{
glMatrixMode(GL_PROJECTION); // select projection matrix
glViewport(0, 0, win.width, win.height); // set the viewport
glLoadIdentity(); // reset projection matrix
GLfloat aspect = (GLfloat) win.width / win.height;
gluPerspective(win.field_of_view_angle, aspect, win.z_near, win.z_far); // set up a perspective projection matrix
glMatrixMode(GL_MODELVIEW); // specify which matrix is the current matrix
glHint( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); // specify implementation-specific hints
GLfloat amb_light[] = { 0.3, 0.3, 0.3, 1.0 };
GLfloat diffuse[] = { 0.4, 0.4, 0.4, 1.0 };
GLfloat specular[] = { 0.5, 0.5, 0.5, 1.0 };
glLightModelfv( GL_LIGHT_MODEL_AMBIENT, amb_light );
glLightfv( GL_LIGHT0, GL_SPECULAR, specular );
glLightfv( GL_LIGHT0, GL_DIFFUSE, diffuse );
GLfloat light_position[] = { 1.0, 1.0, 0, 0.0 };
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glEnable( GL_LIGHT0 );
glEnable( GL_COLOR_MATERIAL );
glEnable( GL_LIGHTING );
//glLightModeli( GL_LIGHT_MODEL_TWO_SIDE, GL_FALSE );
glEnable(GL_TEXTURE_2D); // Enable Texture Mapping
glShadeModel(GL_SMOOTH); // Enable Smooth Shading
glClearColor(0.0f, 0.0f, 0.0f, 0.5f); // Black Background
glClearDepth(1.0f); // Depth Buffer Setup
glEnable(GL_DEPTH_TEST); // Enables Depth Testing
glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do
glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR);
}
void keyboard ( int key, int mousePositionX, int mousePositionY )
{
switch ( key )
{
case GLUT_KEY_DOWN:
if (distance1<9){
distance1=distance1+.3;
}
break;
case GLUT_KEY_UP:
if (distance1>-9){
distance1=distance1-.3;
}
break;
case GLUT_KEY_LEFT:
rotation=rotation-2;
break;
case GLUT_KEY_RIGHT:
rotation=rotation+2;
break;
case GLUT_KEY_PAGE_UP:
light=TRUE;
break;
case GLUT_KEY_PAGE_DOWN:
light=FALSE;
break;
case GLUT_KEY_HOME:
trans=TRUE;
break;
case GLUT_KEY_END:
trans=FALSE;
break;
default:
break;
}
}
int main(int argc, char **argv)
{
// set window values
win.width = 800;
win.height = 600;
win.title = "Test Texture";
win.field_of_view_angle = 45;
win.z_near = 1.0f;
win.z_far = 500.0f;
// initialize and run program
glutInit(&argc, argv); // GLUT initialization
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH ); // Display Mode
glutInitWindowSize(win.width,win.height); // set window size
glutCreateWindow(win.title); // create Window
// Callback functions
glutDisplayFunc(display); // register Display Function
glutIdleFunc( display ); // register Idle Function
glutSpecialFunc( keyboard ); // register Keyboard Handler
initialize();
glutMainLoop(); // run GLUT mainloop
return 0;
}
I want to add that the Raw file is displayed correctly when loaded in IrfanView as 256x512, 24BPP, RGB (the size is 393,216 bytes or 3*512*256)
This line:
glTexParameteri( GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR);
Shouldn't be in your main intialisation, but instead needs to be done when setting up the texture itself (i.e. after binding).
You may also want to set the MAG_FILTER, and the texture coordinate wrapping modes, as a general rule.
Also, you appear to be loading the texture every time you render. Don't do that, performance will be terrible and you'll run out of memory as GL will keep allocating new textures.
I'm trying to display the text in my application using freetype. At first I thought that this built-in function (which would be quite natural for the library intended to draw the text). But there was only a function to display the symbol.Then I decided to take the characters one by one into a texture. But here again I was disappointed: all guides one texture uses a single image (probably glTexSubImage2D can help me?).Now I put a symbol on the texture and texture to opengl element.Here's my code (it's quite messy, but now I'm just trying to understand how it works):
//init:
if (FT_Init_FreeType(&ft)) {
fprintf(stderr, "Could not init freetype library\n");
return 0;
}
if (FT_New_Face(ft, fontfilename, 0, &face)) {
fprintf(stderr, "Could not open font %s\n", fontfilename);
return 0;
}
FT_Set_Pixel_Sizes(face, 0, 48); FT_GlyphSlot g = face->glyph;
and from display():
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(1.0 ,1.0, 1.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();//load identity matrix
std::string s = "QWERTYOG0l ";
for(int i = 0; i < s.size(); i++){
FT_Load_Char( face, s[i], FT_LOAD_RENDER );
FT_GlyphSlot g = face->glyph;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR); // Linear Filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR); // Linear Filtering
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
gluBuild2DMipmaps( GL_TEXTURE_2D,
GL_RED,
g->bitmap.width,
g->bitmap.rows,
GL_RED,
GL_UNSIGNED_BYTE,
g->bitmap.buffer );
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);glVertex3f(0.1f*i-0.1,0.07f,0.0f); //top left
glTexCoord2f(0.0f, 1.0f);glVertex3f(0.1f*i,0.07f,0.0f); //top right
glTexCoord2f(1.0f, 1.0f);glVertex3f(0.1f*i,-0.07f,0.0f); // bottom right
glTexCoord2f(1.0f, 0.0f);glVertex3f(0.1f*i-0.1,-0.07f,0.0f); //bottom left
glEnd();
}
As you can see the "O" and "T" is correct (if I change bottom left and top right corners of texture it will be absolutely correct). But other symbols seems like shifted (for example "E" is shifted at left from top to bottom).
The full code:
#include <math.h>
#include <iostream>
#include <GL/glew.h>
#include <GL/glut.h>
#include <ft2build.h>
#include FT_FREETYPE_H
FT_Library ft;
FT_Face face;
const char *fontfilename = "LucidaTypewriterBold.ttf";
GLuint texture[10];
GLint uniform_mytexture;
int setup() {
if (FT_Init_FreeType(&ft)) {
fprintf(stderr, "Could not init freetype library\n");
return 0;
}
if (FT_New_Face(ft, fontfilename, 0, &face)) {
fprintf(stderr, "Could not open font %s\n", fontfilename);
return 0;
}
FT_Set_Pixel_Sizes(face, 0, 48);
FT_Load_Char( face, 'O', FT_LOAD_RENDER );
FT_GlyphSlot g = face->glyph;
glGenTextures(1, &texture[0]); // Create The Texture
glBindTexture(GL_TEXTURE_2D, texture[0]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR); // Linear Filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR); // Linear Filtering
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, g->bitmap.width, g->bitmap.rows, GL_RED, GL_UNSIGNED_BYTE, g->bitmap.buffer);
return 1;
}
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
glClearColor(1.0 ,1.0, 1.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();//load identity matrix
std::string s = "QWERTYOG0l ";
for(int i = 0; i < s.size(); i++){
FT_Load_Char( face, s[i], FT_LOAD_RENDER );
FT_GlyphSlot g = face->glyph;
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR); // Linear Filtering
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR); // Linear Filtering
//glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
gluBuild2DMipmaps( GL_TEXTURE_2D,
GL_RED,
g->bitmap.width,
g->bitmap.rows,
GL_RED,
GL_UNSIGNED_BYTE,
g->bitmap.buffer );
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);glVertex3f(0.1f*i-0.1,0.07f,0.0f); //top left
glTexCoord2f(0.0f, 1.0f);glVertex3f(0.1f*i,0.07f,0.0f); //top right
glTexCoord2f(1.0f, 1.0f);glVertex3f(0.1f*i,-0.07f,0.0f); // bottom right
glTexCoord2f(1.0f, 0.0f);glVertex3f(0.1f*i-0.1,-0.07f,0.0f); //bottom left
glEnd();
}
//glActiveTexture(GL_TEXTURE0);
//glBindTexture(GL_TEXTURE_2D, texture[0]); // Select Our Texture
// glUniform1i(uniform_mytexture, /*GL_TEXTURE*/0);
glutPostRedisplay();
glutSwapBuffers();
}
void TimerFunction(int value)
{
}
int main(int argc, char *argv[])
{
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
glutInitWindowSize(800,600);
glutCreateWindow("Hello World");
//glutTimerFunc(30, TimerFunction, 1);
glewInit();
glEnable (GL_TEXTURE_2D);
setup();
glutDisplayFunc(display);
glutMainLoop();
return 0;
}
I have been looking into this for a bit, and while this answer is possibly incomplete, maybe it can help you figure it out.
Preliminary Note
Before I get to what I have found, I need to point out a problem with your texture coordinates. You have this:
glTexCoord2f(0.0f, 0.0f);glVertex3f(0.1f*i-0.1,0.07f,0.0f); //top left
glTexCoord2f(0.0f, 1.0f);glVertex3f(0.1f*i,0.07f,0.0f); //top right
glTexCoord2f(1.0f, 1.0f);glVertex3f(0.1f*i,-0.07f,0.0f); // bottom right
glTexCoord2f(1.0f, 0.0f);glVertex3f(0.1f*i-0.1,-0.07f,0.0f); //bottom left
when it should look like this:
glTexCoord2f(0.0f, 0.0f);glVertex3f(0.1f*i-0.1,0.07f,0.0f); //top left
glTexCoord2f(1.0f, 0.0f);glVertex3f(0.1f*i,0.07f,0.0f); //top right
glTexCoord2f(1.0f, 1.0f);glVertex3f(0.1f*i,-0.07f,0.0f); // bottom right
glTexCoord2f(0.0f, 1.0f);glVertex3f(0.1f*i-0.1,-0.07f,0.0f); //bottom left
note how the top left corresponds to 0, 0 in texture coordinates, and 1, 1 corresponds to the bottom right. This is because (kind of guessing here) freetype puts treats the top left as its origin.
The Stuff That May Help
Freetype will not generate a bitmap whose dimensions are necessarily power-of-two, which is often required for mipmapping (see: https://gamedev.stackexchange.com/a/7929 ).
So if you want to test this (note: do not actually use this in your code; this is only for illustration) you can replace your gluBuild2DMipmaps call in display with the following (be sure to #include <cstring>:
int pitch = g->bitmap.pitch;
if (pitch < 0) {
pitch = -pitch;
}
unsigned char data[4096] = {0};
for (int row = 0; row < g->bitmap.rows; ++row) {
std::memcpy(data + 64 * row, g->bitmap.buffer + pitch * row, pitch);
}
gluBuild2DMipmaps(
GL_TEXTURE_2D,
GL_RGBA,
64,
64,
GL_RED,
GL_UNSIGNED_BYTE,
data
);
What it does is copy the bitmap buffer to the upper left corner of a different 64x64-byte buffer, and then builds the mipmaps from that. This is the result:
Further Notes
My illustration code is bad because it copies the bitmap data for each glyph every redraw, and it does not take into account the actual size of the bitmap buffer, or if pitch is greater than 64. You also probably do not want to be (re)generating your mipmaps every redraw, either, but if you are just trying to learn how to get words into OpenGL do not worry about it :)
Edit: I had to use a different font than you because I do not have yours.
As tecu said, the correct solution is using textures with power of two size.
Also before that answer i found another solution:
glPixelStorei( GL_UNPACK_ALIGNMENT, 1 ); before gluBuild2DMipmaps. But here you get more problems like gray border around texture.
For those who are asking similar goals I want to share my experience:
Make black on a transparent background:
GLfloat swizzleMask[] = { 0,0,0, GL_RED};
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_SWIZZLE_RGBA, swizzleMask);
UPD there is a more simple and obvious solution withput using an OpenGL extension.
gluBuild2DMipmaps( GL_TEXTURE_2D,
GL_ALPHA,
g->bitmap.width,
g->bitmap.rows,
GL_RGBA,
GL_UNSIGNED_BYTE,
g->bitmap.buffer )
Connect all the letters in a single texture
I think that this is better for perfomance, but not sure that I change the right way.
if(text[i] == ' ') left += 20; else
for (int row = 0; row < g->bitmap.rows; ++row) {
std::memcpy(data + left + 64*(strSize*(row + 64 - g->bitmap_top))
, g->bitmap.buffer + pitch * row, pitch);
}
left += g->advance.x >> 6;
It will better if you calculate width and height (and round to power of two) before connecting in data array.
If you want kerning you should write its own slower implementation of memcpy, where you will add (not fully change) the value and check exceeding of UCHAR_MAX.
My final result:
My program displays video frames as OpenGL textures.
I have problems with OpenGL initialization. To see video I need to start rendering thread, stop it and start again. I think I am missing something in CRenderThread::InitOpenGL() function. What should I do for correct OpenGL initialization?
My environment:
Windows 7 x64
Microsoft Visual Studio 2008 x64
Here is the code:
#include "RenderThread.h"
#include <QtDebug>
#include <vm_time.h>
static Ipp32u UMCToInternalFormat(UMC::ColorFormat format)
{
switch(format)
{
case UMC::BGR24: return GL_BGR;
case UMC::BGR32: return GL_BGRA;
case UMC::RGB24: return GL_RGB;
case UMC::RGB32: return GL_RGBA;
}
return 0;
}
CRenderThread::CRenderThread(const WId& rnWindowHandle)
: m_bInitialized(false)
, m_WindowHandle(rnWindowHandle)
, m_Texture(0)
, m_fTextureWidth(0.0f)
, m_fTextureHeight(0.0f)
, m_nFrameWidth(0)
, m_nFrameHeight(0)
, m_nWindowWidth(0)
, m_nWindowHeight(0)
{
}
void CRenderThread::PrepareWork()
{
// Wait until first frame comes
if(!m_bAbort)
Suspend();
}
void CRenderThread::DoOnStop()
{
if(m_WindowGLResourceContext)
{
wglDeleteContext(m_WindowGLResourceContext);
m_WindowGLResourceContext = 0;
}
ReleaseDC(m_WindowHandle, m_WindowDC);
if(m_Texture)
{
glDeleteTextures(1, &m_Texture);
m_Texture = 0;
}
}
void CRenderThread::InitOpenGL()
{
PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, PFD_MAIN_PLANE, 0, 0, 0, 0
};
m_WindowDC = GetDC(m_WindowHandle);
if(!m_WindowDC)
return;
if(!SetPixelFormat(m_WindowDC, ChoosePixelFormat(m_WindowDC, &pfd), &pfd))
return;
m_WindowGLResourceContext = wglCreateContext(m_WindowDC); // create rendering context
if(!m_WindowGLResourceContext)
return;
if(!wglMakeCurrent(m_WindowDC, m_WindowGLResourceContext)) // set it as current
return;
// OpenGL context already tied to output window
// to disable all slow GL components
// it is not mandatory to disable all if we have accelerated card
glClearColor(0.0f, 170.0f, 255.0f, 1.0f);
glClearDepth(1.0);
glDepthFunc(GL_NEVER);
// disable slow GL extensions
glDisable(GL_DEPTH_TEST); glDisable(GL_ALPHA_TEST); glDisable(GL_BLEND);
glDisable(GL_DITHER); glDisable(GL_FOG); glDisable(GL_STENCIL_TEST);
glDisable(GL_LIGHTING); glDisable(GL_LOGIC_OP); glDisable(GL_TEXTURE_1D);
glDisable(GL_TEXTURE_2D);
glPixelTransferi(GL_MAP_COLOR, GL_FALSE);
glPixelTransferi(GL_RED_SCALE, 1); glPixelTransferi(GL_RED_BIAS, 0);
glPixelTransferi(GL_GREEN_SCALE, 1); glPixelTransferi(GL_GREEN_BIAS, 0);
glPixelTransferi(GL_BLUE_SCALE, 1); glPixelTransferi(GL_BLUE_BIAS, 0);
glPixelTransferi(GL_ALPHA_SCALE, 1); glPixelTransferi(GL_ALPHA_BIAS, 0);
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &m_Texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glViewport(0, 0, m_nWindowWidth, m_nWindowHeight);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glRasterPos2i(-1, 1); // move to the upper left corner
glPixelZoom(1.0, -1.0); // top to bottom
SwapBuffers(m_WindowDC);
m_bInitialized = true;
}
void CRenderThread::SetRenderFrame(PVideoData pFrame)
{
Q_ASSERT(pFrame.get());
{
//boost::mutex::scoped_lock Lock(m_FrameMutex);
m_pFrameToRender = pFrame;
}
// Resume thread to render current frame
Resume();
}
void CRenderThread::DoWork()
{
IppiSize CurWinSize;
UMC::Status nStatus = UMC::UMC_OK;
::RECT rect;
GetClientRect(m_WindowHandle, &rect);
CurWinSize.height = rect.bottom;
CurWinSize.width = rect.right;
if(!m_bInitialized)
InitOpenGL();
if(CurWinSize.width > IPP_MAX_16S || CurWinSize.height > IPP_MAX_16S) // window seems to be destroyed
return;
// reinit buffers if window size has been changed
if(CurWinSize.height != m_nWindowHeight || CurWinSize.width != m_nWindowWidth)
{
m_nWindowWidth = CurWinSize.width;
m_nWindowHeight = CurWinSize.height;
glViewport(0, 0, m_nWindowWidth, m_nWindowHeight);
}
// Render frame
{
//boost::mutex::scoped_lock Lock(m_FrameMutex);
if(m_pFrameToRender.get())
{
if(m_nWindowWidth && m_nWindowHeight)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
m_nFrameWidth = m_pFrameToRender->GetWidth();
m_nFrameHeight = m_pFrameToRender->GetHeight();
m_nRenderFormat = UMCToInternalFormat(m_pFrameToRender->GetColorFormat());
glTexImage2D(GL_TEXTURE_2D, 0, 3, m_nFrameWidth, m_nFrameHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, m_pFrameToRender->GetBufferPointer());
//glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_nFrameWidth, m_nFrameHeight, m_nRenderFormat, GL_UNSIGNED_BYTE, m_pFrameToRender->GetBufferPointer());
glBegin(GL_POLYGON);
glTexCoord2i(0, 0); glVertex2f(-1.0, 1.0);
glTexCoord2i(1, 0); glVertex2f( 1.0, 1.0);
glTexCoord2i(1, 1); glVertex2f( 1.0, -1.0);
glTexCoord2i(0, 1); glVertex2f(-1.0, -1.0);
glEnd();
glFlush();
SwapBuffers(m_WindowDC); // to draw on physical screen
}
}
}
// Wait for next frame to render
if(!m_bAbort)
Suspend();
}
Couple thoughts, not all necessarily related to your problem:
glClearColor(0.0f, 170.0f, 255.0f, 1.0f);
Clear color is clamped to the range of [0,1], not [0,255].
// disable slow GL extensions
glDisable(GL_DEPTH_TEST); glDisable(GL_ALPHA_TEST); glDisable(GL_BLEND);
glDisable(GL_DITHER); glDisable(GL_FOG); glDisable(GL_STENCIL_TEST);
glDisable(GL_LIGHTING); glDisable(GL_LOGIC_OP); glDisable(GL_TEXTURE_1D);
glDisable(GL_TEXTURE_2D);
These (and most all opengl settings) are disabled by default. These are all doing nothing. Not hurting anything though.
glPixelTransferi(GL_MAP_COLOR, GL_FALSE);
glPixelTransferi(GL_RED_SCALE, 1); glPixelTransferi(GL_RED_BIAS, 0);
glPixelTransferi(GL_GREEN_SCALE, 1); glPixelTransferi(GL_GREEN_BIAS, 0);
glPixelTransferi(GL_BLUE_SCALE, 1); glPixelTransferi(GL_BLUE_BIAS, 0);
glPixelTransferi(GL_ALPHA_SCALE, 1); glPixelTransferi(GL_ALPHA_BIAS, 0);
Again, these are all the defaults.
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &m_Texture);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
This might actually be a real problem. glTexParameter only effects the currently bound texture, but you're calling them here with no texture bound. So these are doing nothing. When you actually do use a texture later, it will have mipmapping set on the min filter, which could cause it not to be displayed. Move your glTexParameter calls to after you have bound the texture that you want them to effect.
I want to draw a 2D array of pixel data (RGB / grayscale values) on the screen as fast as possible, using OpenGL. The pixel data changes frequently.
I had hoped that I would find a simple function that would let me push in a pointer to an array representing the pixel data, since this is probably the fastest approach. Unfortunately, I have found no such function.
What is the best way to accomplish this task?
Maybe glDrawPixels is the function you are looking for? Though if the data is static it would be better to create a texture with it, and then draw that each frame.
I recently had a similar problem, as I am trying to render a video to screen (ie repeatedly upload pixel data to the VRAM), my approach is:
use glTexImage2D and glTexSubImage2D to upload the data to the texture (ie bind the texture (and texture unit, if applicable) before calling that)
in my case as the video frame rate (usually about 24 fps) is lower than the framerate of my application (aimed at 60 fps), in order to avoid uploading the same data again I use a framebuffer object (check out glGenFramebuffers/glBindFramebuffer/glDeleteFramebuffers) and link my texture with the framebuffer (glFramebufferTexture2D). I then upload that texture once, and draw the same frame multiple times (just normal texture access with glBindTexture)
I don't know which platform you are using, but as I am targetting Mac I use some Apple extensions to ensure the data transfer to the VRAM happens through DMA (ie make glTexSubImage2D return immediately to let the CPU do other work) - please feel free to ask me for more info if you are using Mac too
also as you are using just grayscale, you might want to consider just using a GL_LUMINANCE texture (ie 1 byte per pixel) rather than RGB based format to make the upload faster (but that depends on the size of your texture data, I was streaming HD 1920x1080 video so I needed to make sure to keep it down)
also be aware of the format your hardware is using to avoid unnecessary data conversions (ie normally it seems better to use BGRA data than for example just RGB)
finally, in my code I replaced all the fixed pipeline functionality with shaders (in particular the conversion of the data from grayscale or YUV format to RGB), but again all that depends on the size of your data, and the workload of your CPU or GPU
Hope this helps, feel free to message me if you need further info
I would think the fastest way would be to draw a screen sized quad with ortho projection and use a pixel shader and Texture Buffer Object to draw directly to the texture in the pixel shader. Due to latency transferring to/from the TBO you may want to see if double buffering would help.
If speed isn't much of a concern (you just need fairly interactive framerates) glDrawPixels is easy to use and works well enough for many purposes.
My solution for getting dynamically changing image data to the screen in OpenGL,
#define WIN32_LEAN_AND_MEAN
#include "wx/wx.h"
#include "wx/sizer.h"
#include "wx/glcanvas.h"
#include "BasicGLPane.h"
// include OpenGL
#ifdef __WXMAC__
#include "OpenGL/glu.h"
#include "OpenGL/gl.h"
#else
#include <GL/glu.h>
#include <GL/gl.h>
#endif
#include "ORIScanMainFrame.h"
BEGIN_EVENT_TABLE(BasicGLPane, wxGLCanvas)
EVT_MOTION(BasicGLPane::mouseMoved)
EVT_LEFT_DOWN(BasicGLPane::mouseDown)
EVT_LEFT_UP(BasicGLPane::mouseReleased)
EVT_RIGHT_DOWN(BasicGLPane::rightClick)
EVT_LEAVE_WINDOW(BasicGLPane::mouseLeftWindow)
EVT_SIZE(BasicGLPane::resized)
EVT_KEY_DOWN(BasicGLPane::keyPressed)
EVT_KEY_UP(BasicGLPane::keyReleased)
EVT_MOUSEWHEEL(BasicGLPane::mouseWheelMoved)
EVT_PAINT(BasicGLPane::render)
END_EVENT_TABLE()
// Test data for image generation. floats range 0.0 to 1.0, in RGBRGBRGB... order.
// Array is 1024 * 3 long. Note that 32 * 32 is 1024 and is the largest image we can randomly generate.
float* randomFloatRGB;
float* randomFloatRGBGrey;
BasicGLPane::BasicGLPane(wxFrame* parent, int* args) :
wxGLCanvas(parent, wxID_ANY, args, wxDefaultPosition, wxDefaultSize, wxFULL_REPAINT_ON_RESIZE)
{
m_context = new wxGLContext(this);
randomFloatRGB = new float[1024 * 3];
randomFloatRGBGrey = new float[1024 * 3];
// In GL images 0,0 is in the lower left corner so the draw routine does a vertical flip to get 'regular' images right side up.
for (int i = 0; i < 1024; i++) {
// Red
randomFloatRGB[i * 3] = static_cast <float> (rand()) / static_cast <float> (RAND_MAX);
// Green
randomFloatRGB[i * 3 + 1] = static_cast <float> (rand()) / static_cast <float> (RAND_MAX);
// Blue
randomFloatRGB[i * 3 + 2] = static_cast <float> (rand()) / static_cast <float> (RAND_MAX);
// Telltale 2 white pixels in 0,0 corner.
if (i < 2) {
randomFloatRGB[i * 3] = randomFloatRGB[i * 3 + 1] = randomFloatRGB[i * 3 + 2] = 1.0f;
}
randomFloatRGBGrey[i * 3] = randomFloatRGB[i * 3];
randomFloatRGBGrey[i * 3 + 1] = randomFloatRGB[i * 3];
randomFloatRGBGrey[i * 3 + 2] = randomFloatRGB[i * 3];
}
// To avoid flashing on MSW
SetBackgroundStyle(wxBG_STYLE_CUSTOM);
}
BasicGLPane::~BasicGLPane()
{
delete m_context;
}
void BasicGLPane::resized(wxSizeEvent& evt)
{
// wxGLCanvas::OnSize(evt);
Refresh();
}
int BasicGLPane::getWidth()
{
return GetSize().x;
}
int BasicGLPane::getHeight()
{
return GetSize().y;
}
void BasicGLPane::render(wxPaintEvent& evt)
{
assert(GetParent());
assert(GetParent()->GetParent());
ORIScanMainFrame* mf = dynamic_cast<ORIScanMainFrame*>(GetParent()->GetParent());
assert(mf);
switch (mf->currentMainView) {
case ORIViewSelection::ViewCamera:
renderCamera(evt);
break;
case ORIViewSelection::ViewDepth:
renderDepth(evt);
break;
case ORIViewSelection::ViewPointCloud:
renderPointCloud(evt);
break;
case ORIViewSelection::View3DModel:
render3DModel(evt);
break;
default:
renderNone(evt);
}
}
void BasicGLPane::renderNone(wxPaintEvent& evt) {
if (!IsShown())
return;
SetCurrent(*(m_context));
glPushAttrib(GL_ALL_ATTRIB_BITS);
glClearColor(0.08f, 0.11f, 0.15f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glFlush();
SwapBuffers();
glPopAttrib();
}
GLuint makeOpenGlTextureFromDataLuninanceFloats(int width, int height, float* f) {
GLuint textureID;
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &textureID);
// "Bind" the newly created texture : all future texture functions will modify this texture
glBindTexture(GL_TEXTURE_2D, textureID);
// Give the image to OpenGL
glTexImage2D(GL_TEXTURE_2D, 0, GL_FLOAT, width, height, 0, GL_FLOAT, GL_LUMINANCE, f);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
return textureID;
}
GLuint makeOpenGlTextureFromRGBInts(int width, int height, unsigned int* f) {
GLuint textureID;
glEnable(GL_TEXTURE_2D);
glGenTextures(1, &textureID);
// "Bind" the newly created texture : all future texture functions will modify this texture
glBindTexture(GL_TEXTURE_2D, textureID);
// Give the image to OpenGL
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_INT, f);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
return textureID;
}
/// <summary>
/// Range of each float is 0.0f to 1.0f
/// </summary>
/// <param name="width"></param>
/// <param name="height"></param>
/// <param name="floatRGB"></param>
/// <returns></returns>
GLuint makeOpenGlTextureFromRGBFloats(int width, int height, float* floatRGB) {
GLuint textureID;
// 4.6.0 NVIDIA 457.30 (R Keene machine, 11/25/2020)
// auto sss = glGetString(GL_VERSION);
glGenTextures(1, &textureID);
// "Bind" the newly created texture : all future texture functions will modify this texture
glBindTexture(GL_TEXTURE_2D, textureID);
// Give the image to OpenGL
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_FLOAT, floatRGB);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
return textureID;
}
void BasicGLPane::DrawTextureToScreenFloat(int w, int h, float* floatDataPtr, GLuint (*textureFactory)(int width, int height, float* floatRGB)) {
if (w <= 0 || h <= 0 || floatDataPtr == NULL || w > 5000 || h > 5000) {
assert(false);
return;
}
SetCurrent(*(m_context));
glPushAttrib(GL_ALL_ATTRIB_BITS);
glPushMatrix();
glPushClientAttrib(GL_CLIENT_ALL_ATTRIB_BITS);
glClearColor(0.15f, 0.11f, 0.02f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_TEXTURE_2D);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// 4.6.0 NVIDIA 457.30 (R Keene machine, 11/25/2020)
// auto sss = glGetString(GL_VERSION);
float onePixelW = (float)getWidth() / (float)w;
float onePixelH = (float)getHeight() / (float)h;
float orthoW = w;
float orthoH = h;
if (onePixelH > onePixelW) {
orthoH = h * onePixelH / onePixelW;
}
else {
orthoW = w * onePixelW / onePixelH;
}
// We want the image at the top of the window, not the bottom if the window is too tall.
int topOfScreen = (float)getHeight() / onePixelH;
// If the winjdow resizes after creation you need to change the viewport.
glViewport(0, 0, getWidth(), getHeight());
gluOrtho2D(0.0, orthoW, (double)topOfScreen - (double)orthoH, topOfScreen);
GLuint myTextureName = textureFactory(w, h, floatDataPtr);
glBegin(GL_QUADS);
{
// This order of UV coords and verticies will do the vertical flip of the image to get the 'regular' image 0,0
// in the top left corner.
glTexCoord2f(0.0f, 1.0f); glVertex3f(0.0f, 0.0f, 0.0f);
glTexCoord2f(1.0f, 1.0f); glVertex3f(0.0f + w, 0.0f, 0.0f);
glTexCoord2f(1.0f, 0.0f); glVertex3f(0.0f + w, 0.0f + h, 0.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f(0.0f, 0.0f + h, 0.0f);
}
glEnd();
glDeleteTextures(1, &myTextureName);
glFlush();
SwapBuffers();
glPopClientAttrib();
glPopMatrix();
glPopAttrib();
}
void BasicGLPane::DrawTextureToScreenMat(wxPaintEvent& evt, cv::Mat m, float brightness) {
m.type();
if (m.empty()) {
renderNone(evt);
return;
}
if (m.type() == CV_32FC1) { // Grey scale.
DrawTextureToScreenFloat(m.cols, m.rows, (float*)m.data, makeOpenGlTextureFromDataLuninanceFloats);
}
if (m.type() == CV_32FC3) { // Color.
DrawTextureToScreenFloat(m.cols, m.rows, (float*)m.data, makeOpenGlTextureFromRGBFloats);
}
else {
renderNone(evt);
}
}
void BasicGLPane::renderCamera(wxPaintEvent& evt) {
if (!IsShown())
return;
DrawTextureToScreenMat(evt, ORITopControl::Instance->im_white);
}
void BasicGLPane::renderDepth(wxPaintEvent& evt) {
if (!IsShown())
return;
DrawTextureToScreenMat(evt, ORITopControl::Instance->depth_map);
}
void BasicGLPane::render3DModel(wxPaintEvent& evt) {
if (!IsShown())
return;
SetCurrent(*(m_context));
glPushAttrib(GL_ALL_ATTRIB_BITS);
glPushMatrix();
glClearColor(0.08f, 0.11f, 0.15f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glFlush();
SwapBuffers();
glPopMatrix();
glPopAttrib();
}
void BasicGLPane::renderPointCloud(wxPaintEvent& evt) {
if (!IsShown())
return;
boost::unique_lock<boost::mutex> lk(ORITopControl::Instance->pointCloudCacheMutex);
SetCurrent(*(m_context));
glPushAttrib(GL_ALL_ATTRIB_BITS);
glPushMatrix();
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glViewport(0, 0, getWidth(), getHeight());
glClearColor(0.08f, 0.11f, 0.15f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (ORITopControl::Instance->pointCloudCache.size() > 0) {
glMatrixMode(GL_PROJECTION);
gluPerspective( /* field of view in degree */ 40.0,
/* aspect ratio */ 1.0,
/* Z near */ 1.0, /* Z far */ 500.0);
glMatrixMode(GL_MODELVIEW);
gluLookAt(100, 70, 200, // Eye
25, 25, 25, // Look at pt
0, 0, 1); // Up Vector
glPointSize(2.0);
glBegin(GL_POINTS);
// Use explicit for loop because pointCloudFragments can grow asynchronously.
for (int i = 0; i < ORITopControl::Instance->pointCloudCache.size(); i++) {
auto frag = ORITopControl::Instance->pointCloudCache[i];
auto current_point_cloud_ptr = frag->cloud;
glPushMatrix();
// glMultMatrixf(frag->xform.data());
for (size_t n = 0; n < current_point_cloud_ptr->size(); n++) {
glColor3ub(255, 255, 255);
glVertex3d(current_point_cloud_ptr->points[n].x, current_point_cloud_ptr->points[n].y, current_point_cloud_ptr->points[n].z);
}
glPopMatrix();
}
glEnd();
}
glFlush();
SwapBuffers();
glPopMatrix();
glPopAttrib();
}
I've been attempting to render text onto an openGL window using SDL and the SDL_TTF library on windows XP, VS2010.
Versions:
SDL version 1.2.14
SDL TTF devel 1.2.10
openGL (version is at least 2-3 years old).
I have successfully created an openGL window using SDL / SDL_image and can render lines / polygons onto it with no problems.
However, moving onto text it appears that there is some flaw in my current program, I am getting the following result when trying this code here
for those not willing to pastebin here are only the crutial code segments:
void drawText(char * text) {
glLoadIdentity();
SDL_Color clrFg = {0,0,255,0}; // set colour to blue (or 'red' for BGRA)
SDL_Surface *sText = TTF_RenderUTF8_Blended( fntCourier, text, clrFg );
GLuint * texture = create_texture(sText);
glBindTexture(GL_TEXTURE_2D, *texture);
// draw a polygon and map the texture to it, may be the source of error
glBegin(GL_QUADS); {
glTexCoord2i(0, 0); glVertex3f(0, 0, 0);
glTexCoord2i(1, 0); glVertex3f(0 + sText->w, 0, 0);
glTexCoord2i(1, 1); glVertex3f(0 + sText->w, 0 + sText->h, 0);
glTexCoord2i(0, 1); glVertex3f(0, 0 + sText->h, 0);
} glEnd();
// free the surface and texture, removing this code has no effect
SDL_FreeSurface( sText );
glDeleteTextures( 1, texture );
}
segment 2:
// create GLTexture out of SDL_Surface
GLuint * create_texture(SDL_Surface *surface) {
GLuint texture = 0;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D, texture);
// The SDL_Surface appears to have BGR_A formatting, however this ends up with a
// white rectangle no matter which colour i set in the previous code.
int Mode = GL_RGB;
if(surface->format->BytesPerPixel == 4) {
Mode = GL_RGBA;
}
glTexImage2D(GL_TEXTURE_2D, 0, Mode, surface->w, surface->h, 0, Mode,
GL_UNSIGNED_BYTE, surface->pixels);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
return &texture;
}
Is there an obvious bit of code I am missing?
Thank you for any help on this subject.
I've been trying to learn openGL and SDL for 3 days now, so please forgive any misinformation on my part.
EDIT:
I notice that using
TTF_RenderUTF8_Shaded
TTF_RenderUTF8_Solid
Throw a null pointer exception, meaning that there is an error within the actual text rendering function (I suspect), I do not know how this means TTF_RenderUTF8_Blended returns a red square but I suspect all troubles hinge on this.
I think the problem is in the glEnable(GL_TEXTURE_2D) and glDisable(GL_TEXTURE_2D) functions which must be called every time the text is painted on the screen.And maybe also the color conversion between the SDL and GL surface is not right.
I have combined create_texture and drawText into a single function that displays the text properly. That's the code:
void drawText(char * text, TTF_Font* tmpfont) {
SDL_Rect area;
SDL_Color clrFg = {0,0,255,0};
SDL_Surface *sText = SDL_DisplayFormatAlpha(TTF_RenderUTF8_Blended( tmpfont, text, clrFg ));
area.x = 0;area.y = 0;area.w = sText->w;area.h = sText->h;
SDL_Surface* temp = SDL_CreateRGBSurface(SDL_HWSURFACE|SDL_SRCALPHA,sText->w,sText->h,32,0x000000ff,0x0000ff00,0x00ff0000,0x000000ff);
SDL_BlitSurface(sText, &area, temp, NULL);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, sText->w, sText->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, temp->pixels);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glEnable(GL_TEXTURE_2D);
glBegin(GL_QUADS); {
glTexCoord2d(0, 0); glVertex3f(0, 0, 0);
glTexCoord2d(1, 0); glVertex3f(0 + sText->w, 0, 0);
glTexCoord2d(1, 1); glVertex3f(0 + sText->w, 0 + sText->h, 0);
glTexCoord2d(0, 1); glVertex3f(0, 0 + sText->h, 0);
} glEnd();
glDisable(GL_TEXTURE_2D);
SDL_FreeSurface( sText );
SDL_FreeSurface( temp );
}
screenshot
I'm initializing OpenGL as follows:
int Init(){
glClearColor( 0.1, 0.2, 0.2, 1);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho( 0, 600, 300, 0, -1, 1 );
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
if( glGetError() != GL_NO_ERROR ){
return false;
}
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_COLOR, GL_ONE_MINUS_SRC_ALPHA);
}
I think you should just add glEnable(GL_BLEND), because the code for the text surface says TTF_RenderUTF8_Blended( fntCourier, text, clrFg ) and you have to enable the blending abilities of opengl.
EDIT
Okay, I finally took the time to put your code through a compiler. Most importantly, compiler with -Werror so that warning turn into errors
GLuint * create_texture(SDL_Surface *surface) {
GLuint texture = 0;
/*...*/
return &texture;
}
I didn't see it first, because that's something like C coder's 101 and is quite unexpected: You must not return pointers to local variables!. Once the functions goes out of scope the pointer returned will point to nonsense only. Why do you return a pointer at all? Just return a integer:
GLuint create_texture(SDL_Surface *surface) {
GLuint texture = 0;
/*...*/
return texture;
}
Because of this you're also not going to delete the texture afterward. You upload it to OpenGL, but then loose the reference to it.
Your code misses a glEnable(GL_TEXTURE_2D) that's why you can't see any effects of texture. However your use of textures is suboptimal. They way you did it, you recreate a whole new texture each time you're about to draw that text. If that happens in a animation loop, you'll
run out of texture memory rather soon
slow it down significantly
(1) can be addressed by not generating a new texture name each redraw
(2) can be addresses by uploading new texture data only when the text changes and by not using glTexImage2D, but glTexSubImage2D (of course, if the dimensions of the texture change, it must be glTexImage2D).
EDIT, found another possible issue, but first fix your pointer issue.
You should make sure, that you're using GL_REPLACE or GL_MODULATE texture environment mode. If using GL_DECAL or GL_BLEND you end up with red text on a red quad.
There was leaking memory of of the function in my previous post and the program was crashing after some time...
I improved this by separating the texture loading and displaying:
The first function must be called before the SDL loop.It loads text string into memory:
Every string loaded must have different txtNum parameter
GLuint texture[100];
SDL_Rect area[100];
void Load_string(char * text, SDL_Color clr, int txtNum, const char* file, int ptsize){
TTF_Font* tmpfont;
tmpfont = TTF_OpenFont(file, ptsize);
SDL_Surface *sText = SDL_DisplayFormatAlpha(TTF_RenderUTF8_Solid( tmpfont, text, clr ));
area[txtNum].x = 0;area[txtNum].y = 0;area[txtNum].w = sText->w;area[txtNum].h = sText->h;
glGenTextures(1, &texture[txtNum]);
glBindTexture(GL_TEXTURE_2D, texture[txtNum]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, sText->w, sText->h, 0, GL_BGRA, GL_UNSIGNED_BYTE, sText->pixels);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
SDL_FreeSurface( sText );
TTF_CloseFont(tmpfont);
}
The second one displays the string, must be called in the SDL loop:
void drawText(float coords[3], int txtNum) {
glBindTexture(GL_TEXTURE_2D, texture[txtNum]);
glEnable(GL_TEXTURE_2D);
glBegin(GL_QUADS); {
glTexCoord2f(0, 0); glVertex3f(coords[0], coords[1], coords[2]);
glTexCoord2f(1, 0); glVertex3f(coords[0] + area[txtNum].w, coords[1], coords[2]);
glTexCoord2f(1, 1); glVertex3f(coords[0] + area[txtNum].w, coords[1] + area[txtNum].h, coords[2]);
glTexCoord2f(0, 1); glVertex3f(coords[0], coords[1] + area[txtNum].h, coords[2]);
} glEnd();
glDisable(GL_TEXTURE_2D);
}