Related
I am working on a Particle Filter that needs to compare coordinates of pixels detected from a VideoCapture with coordinates of sample points on a 3D object. Currently, there are no sample points, but just one (top left corner of the cube you see on the (left) image below). However, contours are detected and drawn (there are not many as it's dark, but please ignore that for now) on the video stream, which means I can easily get their [x,y] coordinates.
The problem here was that I could not (properly) translate 3D coordinates of the object into 2D screen coordinates. I am using perspective projection as I obviously draw 3D objects.
Left - Video Capture / Right - coordinates(first comes the "translated" screen followed by object coordinates relative to origin point (0,0) in the centre, and then normalized errors, which represent the error in distance from the origin point (ignore that for now)):
The problem was that for each particle I was getting projection and model view matrices over and over. I simply moved the code for obtaining matrices before the for loop in order to get them only once. It worked!!!
Here is the initialisation of the screen and vectors that I am using:
int width = 640;
int height = 480;
Mat image;
int iteration = 0;
struct Coordinates{ //stores coordinates of drawn particles
GLdouble x;
GLdouble y;
GLdouble z;
GLdouble angle;
};
struct ScreenCoordinates{ //stores coordinates on screen
GLdouble x;
GLdouble y;
GLdouble z;
};
float X_object, Y_object, Z_object, cube_angle;
GLdouble sum_of_errors;
GLint viewport[4];
GLdouble mvmatrix[16], projmatrix[16];
vector<Coordinates> points(number_of_particles);
vector<GLdouble> errors(number_of_particles);
vector<GLdouble> normalizedErrors(number_of_particles);
vector<GLint> indexes(number_of_particles);
vector<GLdouble> weights(number_of_particles);
vector<vector<Point>> Contours;// = new vector<vector<Point>>(number_of_particles); //2D vector for storing the edges coordinates
vector<Point> screenCoordinates;
vector<ScreenCoordinates> point_on_screen(number_of_particles);
Particles are drawn in the following way:
void drawParticle()
{
//glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear color and depth buffers
glPushAttrib(GL_POLYGON_BIT | GL_ENABLE_BIT | GL_COLOR_BUFFER_BIT) ;
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE) ;
//glDisable(GL_LIGHTING) ;
glBegin(GL_QUADS); // Begin drawing the color cube with size 3cm x 3.5cm x 4cm
// Top face (y = 1.0f)
// Define vertices in counter-clockwise (CCW) order with normal pointing out
glColor3f(0.0f, 1.0f, 0.0f); // Green
glVertex3f( 1.75f, 1.75f, -4.0f);
glVertex3f(-1.75f, 1.75f, -4.0f);
glVertex3f(-1.75f, 1.75f, 1.0f);
glVertex3f( 1.75f, 1.75f, 1.0f);
// Bottom face (y = -1.0f)
glColor3f(1.0f, 0.5f, 0.0f); // Orange
glVertex3f( 1.75f, -1.75f, 1.0f);
glVertex3f(-1.75f, -1.75f, 1.0f);
glVertex3f(-1.75f, -1.75f, -4.0f);
glVertex3f( 1.75f, -1.75f, -4.0f);
// Front face (z = 1.0f)
glColor3f(1.0f, 0.0f, 0.0f); // Red
glVertex3f( 1.75f, 1.75f, 1.0f);
glVertex3f(-1.75f, 1.75f, 1.0f);
glVertex3f(-1.75f, -1.75f, 1.0f);
glVertex3f( 1.75f, -1.75f, 1.0f);
// Back face (z = -1.0f)
glColor3f(1.0f, 1.0f, 0.0f); // Yellow
glVertex3f( 1.75f, -1.75f, -4.0f);
glVertex3f(-1.75f, -1.75f, -4.0f);
glVertex3f(-1.75f, 1.75f, -4.0f);
glVertex3f( 1.75f, 1.75f, -4.0f);
// Left face (x = -1.0f)
glColor3f(0.0f, 0.0f, 1.0f); // Blue
glVertex3f(-1.75f, 1.75f, 1.0f);
glVertex3f(-1.75f, 1.75f, -4.0f);
glVertex3f(-1.75f, -1.75f, -4.0f);
glVertex3f(-1.75f, -1.75f, 1.0f);
// Right face (x = 1.0f)
glColor3f(1.0f, 0.0f, 1.0f); // Magenta
glVertex3f(1.75f, 1.75f, -4.0f);
glVertex3f(1.75f, 1.75f, 1.0f);
glVertex3f(1.75f, -1.75f, 1.0f);
glVertex3f(1.75f, -1.75f, -4.0f);
glEnd(); // End of drawing color-cube
glPopAttrib() ;
}
Display function:
void display()
{
// clear the window
glClear( GL_COLOR_BUFFER_BIT );
// show the current camera frame
//based on the way cv::Mat stores data, you need to flip it before displaying it
cv::Mat tempimage;
cv::flip(image, tempimage, 0);
glDrawPixels( tempimage.size().width, tempimage.size().height, GL_RGB, GL_UNSIGNED_BYTE, tempimage.ptr() );
//////////////////////////////////////////////////////////////////////////////////
// Here, set up new parameters to render a scene viewed from the camera.
//set viewport
glViewport(0, 0, tempimage.size().width, tempimage.size().height);
//set projection matrix using intrinsic camera params
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
float aspect = tempimage.size().width*1.0/tempimage.size().height;
//gluPerspective is arbitrarily set, you will have to determine these values based
//on the intrinsic camera parameters
gluPerspective(60.0f, aspect, 0.1f, 100.0f);
//you will have to set modelview matrix using extrinsic camera params
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0, 0, 5, 0, 0, 0, 0, 1, 0);
glGetIntegerv (GL_VIEWPORT, viewport);
glGetDoublev (GL_MODELVIEW_MATRIX, mvmatrix);
glGetDoublev (GL_PROJECTION_MATRIX, projmatrix);
/////////////////////////////////////////////////////////////////////////////////
// Drawing routine
//initialise certain number of particles
for(int i = 0; i<number_of_particles; i++){
// uniformly distributed for the very first generation
if((points[i].x == NULL) || (points[i].y = NULL)){
//randomly generate X,Y,Z and angle values
X_object = RandomNumber(-5.0, 5.0);//rand() % 10 - 2.5;
Y_object = RandomNumber(-5.0, 5.0);//rand() % 10 - 2.5;
Z_object = 23;
cube_angle = rand() % 360 + 1;
}else{
//printf("second generation should improve the distribution");
//set sigma accordingly
//use best particle coordinates for the next X and Y
X_object = points[i].x;
Y_object = points[i].y;
Z_object = points[i].z;
cube_angle = points[i].angle;
}
points[i].x = X_object;
points[i].y = Y_object;
points[i].z = Z_object;
points[i].angle = cube_angle;
gluProject(X_object, Y_object, Z_object, mvmatrix, projmatrix, viewport, &point_on_screen[i].x, &point_on_screen[i].y, &point_on_screen[i].z);
printf("Screen: %f, %f\n", point_on_screen[i].x, point_on_screen[i].y);
//now that the camera params have been set, draw your 3D shapes
//first, save the current matrix
//glPushMatrix();
//move to the position where you want the 3D object to go
glLoadIdentity();
glTranslatef(X_object, Y_object, -Z_object); //this is an arbitrary position for demonstration
glRotatef(cube_angle, 1.0f, 1.0f, 1.0f); // Rotate about (1,1,1)-axis [NEW]
drawParticle();
//glPopMatrix();
}
scoreParticles();
// show the rendering on the screen
glutSwapBuffers();
// post the next redisplay
glutPostRedisplay();
}
This is the exact way of translation:
glGetIntegerv (GL_VIEWPORT, viewport);
glGetDoublev (GL_MODELVIEW_MATRIX, mvmatrix);
glGetDoublev (GL_PROJECTION_MATRIX, projmatrix);
gluProject(X_object, Y_object, Z_object, mvmatrix, projmatrix, viewport, &point_on_screen[i].x, &point_on_screen[i].y, &point_on_screen[i].z);
printf("Screen: %f, %f\n", point_on_screen[i].x, point_on_screen[i].y);
The question now is how can I include sample points between vertices? Taking into account the way I draw particles, how should I divide the space between all vertices into equal parts and place a point there so that this point can be translated into screen coordinates and then compared to pixel coordinates?
Just for clarification: the error in distance for each particle is found and a certain weight is assigned, after that I use Stochastic Sampling for redistribution of particles over following population.
void text(string str)
{
for (int i = 0; i < str.length(); i++)
{
glColor3f(0.0f, 0.0f, 0.0f);
glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, str[i]);
}
}
void render(void)
{
int width = glutGet(GLUT_WINDOW_WIDTH);
int height = glutGet(GLUT_WINDOW_HEIGHT);
if (height == 0) height = 1;
GLfloat aspect = (GLfloat)width / (GLfloat)height;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_SCISSOR_TEST);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0f, aspect, 0.1f, 100.0f);
// Top view - top left
glViewport(0, 0, width/2, height/2);
glScissor(0, 0, width/2, height/2);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
PilotView(0.0f, 0.0f, -5.0f, 0.0f, 0.0f, 0.0f, 1.0f);
glRasterPos3f(-0.1f, -0.1f, 4.0f);
text("Front");
diode();
// Corner view - top right
glViewport(width/2, 0, width/2, height/2);
glScissor(width/2, 0, width/2, height/2);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
PilotView(0.0f, 0.0f, -5.0f, 0.0f, -90.0f, 0.0f, 1.0f);
glRasterPos3f(4.0f, -0.1f, 0.1f);
text("Right");
diode();
// Front view - bottom left
glViewport(0, height/2, width/2, height/2);
glScissor(0, height/2, width/2, height/2);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
PilotView(0.0f, 0.0f, -5.0f, 90.0f, 0.0f, 0.0f, 1.0f);
glRasterPos3f(-0.1f, 4.0f, 0.0f);
text("Top");
diode();
// Right view - bottom right
glViewport(width/2, height/2, width/2, height/2);
glScissor(width/2, height/2, width/2, height/2);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
PilotView(0.0f, 0.0f, -5.0f, 20.0f, 0.0f, 0.0f, 1.0f);
glRasterPos3f(-0.1f, 4.0f, 0.0f);
text("Fro4nt");
diode();
glDisable(GL_SCISSOR_TEST);
glutSwapBuffers();
}
I'm not sure where the white "Front" and the yellow "Top"/"Right" is coming from (in terms of color). They all should be black. Does anyone know what the issue is?
Here is what the output looks like:
As suspected, this may come as a shock but glRasterPos (...) actually tracks the "current" color when you call that function. That is, whatever color was set before glRasterPos (...) was called, applies as the "current color" for drawing operations at that position. Think of it almost as the rasterizer's analog to glVertex (...), as I will explain below.
You need to set the current color before you call glRasterPos (...), to that end you should remove the glColor3f (...) call completely from your text (...) function, or perhaps modify that function to do both - set the color and then the raster pos, then draw the text.
glRasterPos — specify the raster position for pixel operations:
The current raster position consists of three window coordinates (x, y, z), a clip coordinate value (w), an eye coordinate distance, a valid bit, and associated color data and texture coordinates.
I'm writing a program that draws a rotating cube (with texture) in the middle of the screen followed by a small yellow sphere that orbits around the cube. The idea is to make the sphere as a spot light source that illuminates the cube.
Here is the problem: as you can see in the images below, I'm failing to achieve the spot light effect. It seems that the entire cube gets lighted:
I'm setting GL_SPOT_DIRECTION to be the cube position. I didn't set surface normals because I'm struggling to understand how to compute them for the cube, and I'm not sure a simple graphic application like this really requires it.
I'm sharing the code below:
main.cpp:
#include <QApplication>
#include "glwidget.h"
int main(int argc, char* argv[])
{
QApplication app(argc, argv);
GLWidget gl_widget;
gl_widget.show();
return app.exec();
}
GLWidget.h:
#pragma once
#include <QGLWidget>
#include <QImage>
class GLWidget : public QGLWidget
{
Q_OBJECT
public:
explicit GLWidget(QWidget* parent = 0);
virtual ~GLWidget();
void _draw_texture_cube(int w, int h);
void _draw_light();
/* OpenGL initialization, viewport resizing, and painting */
void initializeGL();
void paintGL();
void resizeGL( int width, int height);
/* enable the user to interact directly with the scene using the keyboard */
void keyPressEvent(QKeyEvent *e);
private:
int _width;
int _height;
QImage* _img;
GLuint _texture;
float xrot;
float yrot;
float zrot;
bool _light_on;
bool _must_rotate;
bool _pause_light;
GLfloat _light_pos[3];
GLfloat _cube_pos[3];
GLUquadricObj* _quadratic;
protected slots:
void _tick();
};
GLWidget.cpp:
#include "GLWidget.h"
#include <iostream>
#include <QKeyEvent>
#include <QTimer>
#include <cmath>
#define LIGHT_MOVEMENT_SPEED 20.0f // Degrees per second
#define pi 3.141592654f
GLWidget::GLWidget(QWidget *parent)
: QGLWidget(parent), _img(NULL), _light_on(true), _must_rotate(true),
_pause_light(false), _quadratic(NULL)
{
_width = 0;
_height = 0;
_texture = 0;
xrot = 0.f;
yrot = 0.f;
zrot = 0.f;
// Set central cube position
_cube_pos[0] = 0.0f;
_cube_pos[1] = 0.0f;
_cube_pos[2] = -7.0f;
// Set light position
_light_pos[0] = 0.5f;
_light_pos[1] = 0.5f;
_light_pos[2] = -7.0f;
}
GLWidget::~GLWidget()
{
if (_img)
delete _img;
glDeleteTextures(1, &_texture);
}
void GLWidget::_tick()
{
update(); // triggers paintGL()
QTimer::singleShot(33, this, SLOT(_tick()));
}
void GLWidget::initializeGL()
{
std::cout << "GLWidget::initializeGL" << std::endl;
glClearColor(0.0f, 0.0f, 0.0f, 1.0f); // Black Background
glEnable(GL_CULL_FACE);
/* Load bitmap */
glEnable(GL_TEXTURE_RECTANGLE_ARB);
glPixelStorei (GL_UNPACK_ALIGNMENT, 1);
if (!_img)
{
std::cout << "GLWidget::paintGL: loading image" << std::endl;
QImage tmp(":/crate.jpg");
if (tmp.isNull())
{
std::cout << "GLWidget::paintGL: !!! Failed QImage #1" << std::endl;
return;
}
_img = new QImage(QGLWidget::convertToGLFormat(tmp));
}
/* Convert bitmap into texture */
// Create The Texture
glGenTextures(1, &_texture);
// Typical Texture Generation Using Data From The Bitmap
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, _texture);
// Generate The Texture
glTexImage2D(GL_TEXTURE_RECTANGLE_ARB, 0,
GL_RGBA, _img->width(), _img->height(), 0,
GL_RGBA, GL_UNSIGNED_BYTE, _img->bits());
if (glGetError() != GL_NO_ERROR)
{
std::cout << "GLWidget::paintGL: !!! Failed glTexImage2D" << std::endl;
return;
}
/* Setup lighting */
glShadeModel(GL_SMOOTH); //Smooth color shading
// Light properties
GLfloat AmbientLight[4] = {0.2, 0.2, 0.2, 1.0};
GLfloat DiffuseLight[4] = {0.8, 0.8, 0.8, 1.0}; // color
GLfloat SpecularLight[4] = {1.0, 1.0, 1.0, 1.0}; // bright
GLfloat SpecRef[] = {0.7f, 0.7f, 0.7f, 1.0f};
GLubyte Shine = 60.0;
//glLightModelfv(GL_LIGHT_MODEL_LOCAL_VIEWER, AmbientLight);
glLightfv(GL_LIGHT0, GL_AMBIENT, AmbientLight);
glLightfv(GL_LIGHT0, GL_DIFFUSE, DiffuseLight);
glLightfv(GL_LIGHT0, GL_SPECULAR, SpecularLight);
glLightfv(GL_LIGHT0, GL_POSITION, _light_pos);
glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE);
glMaterialfv(GL_FRONT, GL_SPECULAR, SpecRef); // refletância do material
glMaterialf(GL_FRONT, GL_SHININESS, Shine); // concentração do brilho
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, 0);
//glColorMaterial(GL_FRONT,GL_DIFFUSE);
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
// Sphere
_quadratic = gluNewQuadric(); // Create A Pointer To The Quadric Object
gluQuadricNormals(_quadratic, GLU_SMOOTH); // Create Smooth Normals
gluQuadricTexture(_quadratic, GL_TRUE); // Create Texture Coords
/* Start the timer */
_tick();
}
/* Draw the central cube with texture
*/
void GLWidget::_draw_texture_cube(int w, int h)
{
glPushMatrix();
glTranslatef(_cube_pos[0], _cube_pos[1], _cube_pos[2]);
glRotatef ( xrot, 1.0, 0.0, 0.0 );
glRotatef ( yrot, 0.0, 1.0, 0.0 );
glRotatef ( zrot, 0.0, 0.0, 1.0 );
glColor3f(1.0f, 1.0f, 1.0f);
glBegin(GL_QUADS); // Draw A Cube
// Front Face
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
glTexCoord2f(w, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);
glTexCoord2f(w, h); glVertex3f( 1.0f, 1.0f, 1.0f);
glTexCoord2f(0.0f, h); glVertex3f(-1.0f, 1.0f, 1.0f);
// Back Face
glTexCoord2f(w, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
glTexCoord2f(w, h); glVertex3f(-1.0f, 1.0f, -1.0f);
glTexCoord2f(0.0f, h); glVertex3f( 1.0f, 1.0f, -1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
// Top Face
glTexCoord2f(0.0f, h); glVertex3f(-1.0f, 1.0f, -1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f);
glTexCoord2f(w, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f);
glTexCoord2f(w, h); glVertex3f( 1.0f, 1.0f, -1.0f);
// Bottom Face
glTexCoord2f(w, h); glVertex3f(-1.0f, -1.0f, -1.0f);
glTexCoord2f(0.0f, h); glVertex3f( 1.0f, -1.0f, -1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);
glTexCoord2f(w, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
// Right face
glTexCoord2f(w, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f);
glTexCoord2f(w, h); glVertex3f( 1.0f, 1.0f, -1.0f);
glTexCoord2f(0.0f, _img->height()); glVertex3f( 1.0f, 1.0f, 1.0f);
glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f);
// Left Face
glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f);
glTexCoord2f(w, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f);
glTexCoord2f(w, h); glVertex3f(-1.0f, 1.0f, 1.0f);
glTexCoord2f(0.0f, h); glVertex3f(-1.0f, 1.0f, -1.0f);
glEnd();
glPopMatrix();
if (_must_rotate)
{
xrot += 0.6f;
yrot += 0.4f;
zrot += 0.8f;
}
}
/* Draw light source and light model (sphere)
*/
void GLWidget::_draw_light()
{
if (_light_on)
{
glEnable(GL_LIGHT0); // enable lights that we use
}
else
{
glDisable(GL_LIGHT0);
}
static float light_angle = 25.0f;
if (!_pause_light) // stop moving the light source
{
light_angle += LIGHT_MOVEMENT_SPEED * 0.1;
if (light_angle > 360.0f)
light_angle -= 360.0f;
}
/* Set light source position */
_light_pos[0] = 4.0f * (float) cos(light_angle * pi / 180.0f);
_light_pos[1] = 4.0f * (float) sin(light_angle * pi / 180.0f);
_light_pos[2] = -7;
glLightfv(GL_LIGHT0, GL_POSITION, _light_pos);
GLfloat SpotDir[] = {_cube_pos[0], _cube_pos[1], _cube_pos[2], 0.0 };
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, SpotDir);
glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 150.0);
glLightf(GL_LIGHT0, GL_SPOT_EXPONENT, 15.0);
/* Set the light model position to be the same as the light source */
glPushMatrix();
glTranslatef(_light_pos[0], _light_pos[1], _light_pos[2]);
glColor3ub(255, 255, 0); // yellow
gluSphere(_quadratic, 0.2f, 32, 32); // draw sphere
glPopMatrix();
}
void GLWidget::paintGL()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear Screen And Depth Buffer
glMatrixMode ( GL_MODELVIEW ); // Select The Model View Matrix
glLoadIdentity(); // Reset The Current Modelview Matrix
/* Draw central cube */
glEnable(GL_TEXTURE_RECTANGLE_ARB);
glBindTexture(GL_TEXTURE_RECTANGLE_ARB, _texture); // Select Our Texture
_draw_texture_cube(_img->width(), _img->height());
glDisable(GL_TEXTURE_RECTANGLE_ARB);
/* Draw light source and light model*/
_draw_light();
}
void GLWidget::resizeGL( int w, int h)
{
_width = w;
_height = h;
glViewport(0, 0, w, h);
glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix
if (h == 0) // Calculate The Aspect Ratio Of The Window
gluPerspective ( 60, ( float ) w, 0.4, 500.0 );
else
gluPerspective ( 60, ( float ) w / ( float ) h, 0.4, 500.0 );
glMatrixMode ( GL_MODELVIEW ); // Select The Model View Matrix
glLoadIdentity ( ); // Reset The Model View Matrix
gluLookAt(0.0, 0.0, 2.0, // eye
0.0, 0.0, 0.0, // center
0.0, 1.0, 0.0); // up
}
void GLWidget::keyPressEvent(QKeyEvent *e)
{
switch (e->key())
{
case Qt::Key_L:
if (_light_on)
_light_on = false;
else
_light_on = true;
break;
case Qt::Key_P:
if (_pause_light)
_pause_light = false;
else
_pause_light = true;
break;
case Qt::Key_R:
if (_must_rotate)
_must_rotate = false;
else
_must_rotate = true;
break;
default:
break;
}
}
Lighting.pro:
QT += core gui opengl
SOURCES += \
GLWidget.cpp \
main.cpp
HEADERS += \
GLWidget.h
RESOURCES += \
resource.qrc
What needs to be changed in this application in order to achieve the desired effect?
You do not specify any normals for your cube faces. As OpenGL is a state machine, it will use the default surface normal for all vertices, hence all of your faces. As the normal vector is crucial for the lighting, all of your faces will be lit almost identical (vertex postions still are different, but the effect is weak).
You should also be aware that the fixed function lighting of OpenGL is done per vertex. If you really want to see a good spotlight on the cuve, you would either need to tessalate it so more vertices are used where the lighting equation is actually evaluated, or use shaders for per-fragment lighting.
I want to be able to render something into a texture, on OpenGL, so I can further use it whenever I want, without rendering everything over again. This website here gave me the guidelines to do it, without using the FrameBuffer. I don't want to do it with the FrameBuffer Object due to compatibility issues, since this old machine is not supporting it. I have done some code, which creates my texture, renders my scene, and I then create a Quad to render the texture on it. The only problem is that the texture is being rendered like an "Alpha Mask", it means, looks like it's only taking into account the Alpha Value, maintaining my rectangle always with the same color, but just changing the transparency on pixels. Here is some code I've done so far:
void CreateTexture ()
{
xSize = 512;
ySize = 512; //size of texture
//new array
char* colorBits = new char[ xSize * ySize * 3 ];
//texture creation..
glGenTextures(1,&texture);
glBindTexture(GL_TEXTURE_2D,texture);
glTexImage2D(GL_TEXTURE_2D,0 ,3 , xSize,
ySize, 0 , GL_RGB,
GL_UNSIGNED_BYTE, colorBits);
//you can set other texture parameters if you want
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//clean up
delete[] colorBits;
}
Then:
int viewport[4];
glGetIntegerv(GL_VIEWPORT,(int*)viewport);
glViewport(0,0,xSize,ySize);
DrawScene(hDC);
//save data to texture using glCopyTexImage2D
glBindTexture(GL_TEXTURE_2D,texture);
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA,
0,0, xSize, ySize, 0);
glClearColor(.0f, 0.5f, 0.5f, 1.0f); // Set The Clear Color To Medium Blue
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glViewport(viewport[0],viewport[1],viewport[2],viewport[3]);
// glBindTexture(GL_TEXTURE_2D,texture);
And Finally:
glEnable(GL_TEXTURE_2D); // Enable 2D Texture Mapping
glBlendFunc(GL_DST_COLOR,GL_ONE); // Set Blending Mode
glEnable(GL_BLEND);
glClear(GL_COLOR_BUFFER_BIT);
glBindTexture(GL_TEXTURE_2D,texture);
glRotatef(theta, 0.0f, 0.0f, 0.01f);
glBegin(GL_QUADS);
//Front Face
glTexCoord2f(0.0f, 0.0f);
glVertex3f(-0.5, -0.5f, 0.5f);
glTexCoord2f(1.0f, 0.0f);
glVertex3f( 0.5f, -0.5f, 0.5f);
glTexCoord2f(1.0f, 1.0f);
glVertex3f( 0.5f, 0.5f, 0.5f);
glTexCoord2f(0.0f, 1.0f);
glVertex3f(-0.5f, 0.5f, 0.5f);
glEnd();
SwapBuffers(hDC);
The DrawScene() function simply renders a rectangle with a triangle on top of it, with each vertice having different colors.. nothing special.
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
glClear(GL_COLOR_BUFFER_BIT );//| GL_DEPTH_BUFFER_BIT);
glPushMatrix();
// glRotatef(theta, 0.0f, 0.0f, 1.0f);
glBegin(GL_QUADS);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex3f(-1.0f, -1.0f, 1.0f);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex3f( 1.0f, -1.0f, 1.0f);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex3f( 1.0f, 1.0f, 1.0f);
glColor3f(1.0f, 1.0f, 1.0f);
glVertex3f(-1.0f, 1.0f, 1.0f);
glEnd();
glBegin(GL_TRIANGLES);
glColor3f(1.0f, 0.0f, 0.0f);
glVertex2f(0.0f, 1.0f);
glColor3f(0.0f, 1.0f, 0.0f);
glVertex2f(0.87f, -0.5f);
glColor3f(0.0f, 0.0f, 1.0f);
glVertex2f(-0.87f, -0.5f);
glEnd();
glPopMatrix();
I've found something on nVidia website which looks useful, for someone who cannot also do offscreen rendering with FBO:
http://developer.download.nvidia.com/SDK/9.5/Samples/samples.html
This website contains one project called "Simple P-Buffer", which basically contains an implementation of a P-buffer. The idea of the sample is that you make context switching to the pBuffer, while you want to draw pixels on offscreen mode, let's say. After drawing your scene with the normal rendering functions, we use glReadPixels to read the data from the pBuffer into an array of unsigned bytes (GLubyte). After that, we do context-switching once again, setting it back to the screen context, so that you can use glReadPixels to read the content from our array.
The method before FBOs were available was to use an alternate render buffer (see glDrawBuffer(GL_AUX0), then copy pixels from that buffer (see glReadBuffer) to the texture (see glCopyTexImage2D. Rendering directly into a texture requires FBOs.
Recently I have been looking into OpenGL, and I've got up to the stage were I want to texture things. I thought I would start with texturing a simple cube.
I currently have this code, and understand fully how it works:
#include <glut.h>
#define WINDOW_WIDTH 400
#define WINDOW_HEIGHT 400
float angle = 30.0f;
void Draw() {
glLoadIdentity(); //Reset the drawing perspective
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //Clears the buffers
//Add positioned light
GLfloat lightColor0[] = {0.5f, 0.5f, 0.5f, 1.0f}; //Color intensity
GLfloat lightPos0[] = {0.0f, 0.0f, 0.0f, 1.0f}; //Positioned at..
glLightfv(GL_LIGHT0, GL_DIFFUSE, lightColor0); //Set our light colour
glLightfv(GL_LIGHT0, GL_POSITION, lightPos0); //Set our light position
glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 200);
glRotatef(angle,1.0f,1.0f,1.0f); //Rotate around the origin
glScalef(0.4f, 0.4f, 0.4f); //Scale the shape down
glBegin(GL_QUADS); //Start drawing a Quad
glColor3f(1.0f,0.0f,0.0f); //Set the colour to Red
glVertex3f( 1.0f, 1.0f,-1.0f); //Top right of the quad (Top Face)
glVertex3f(-1.0f, 1.0f,-1.0f); //Top left of the quad (Top Face)
glVertex3f(-1.0f, 1.0f, 1.0f); //Bottom left of the quad (Top Face)
glVertex3f( 1.0f, 1.0f, 1.0f); //Bottom right of the quad (Top Face)
glVertex3f( 1.0f,-1.0f, 1.0f); //Top right of the quad (Bottom Face)
glVertex3f(-1.0f,-1.0f, 1.0f); //Top left of the quad (Bottom Face)
glVertex3f(-1.0f,-1.0f,-1.0f); //Bottom left of the quad (Bottom Face)
glVertex3f( 1.0f,-1.0f,-1.0f); //Bottom right of the quad (Bottom Face)
glVertex3f( 1.0f, 1.0f, 1.0f); //Top right of the quad (Front Face)
glVertex3f(-1.0f, 1.0f, 1.0f); //Top left of the quad (Front Face)
glVertex3f(-1.0f,-1.0f, 1.0f); //Bottom left of the quad (Front Face)
glVertex3f( 1.0f,-1.0f, 1.0f); //Bottom right of the quad (Front Face)
glVertex3f( 1.0f,-1.0f,-1.0f); //Top right of the quad (Back Face)
glVertex3f(-1.0f,-1.0f,-1.0f); //Top left of the quad (Back Face)
glVertex3f(-1.0f, 1.0f,-1.0f); //Bottom left of the quad (Back Face)
glVertex3f( 1.0f, 1.0f,-1.0f); //Bottom right of the quad (Back Face)
glVertex3f(-1.0f, 1.0f, 1.0f); //Top right of the quad (Left Face)
glVertex3f(-1.0f, 1.0f,-1.0f); //Top left of the quad (Left Face)
glVertex3f(-1.0f,-1.0f,-1.0f); //Bottom left of the quad (Left Face)
glVertex3f(-1.0f,-1.0f, 1.0f); //Bottom right of the quad (Left Face)
glVertex3f( 1.0f, 1.0f,-1.0f); //Top right of the quad (Right Face)
glVertex3f( 1.0f, 1.0f, 1.0f); //Top left of the quad (Right Face)
glVertex3f( 1.0f,-1.0f, 1.0f); //Bottom left of the quad (Right Face)
glVertex3f( 1.0f,-1.0f,-1.0f); //Bottom right of the quad (Right Face)
glEnd(); //Finished Drawing The Quad
glutSwapBuffers(); //Send the 3D scene to the screen
}
void Update(int value){ //Our update function
angle+=0.5f; //Increase the angle by 5
if(angle>360){ //If the angle is greater than 360
angle=0; //Set the angle to 0
}
glutPostRedisplay(); //Tell it that the scene has changed
glutTimerFunc(25,Update,0); //Call "Update" again in another 25ms
}
void Initialize() {
glEnable(GL_DEPTH_TEST);
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_LIGHTING); //Enable lighting
glEnable(GL_LIGHT0); //Enable light No. 0
glEnable(GL_NORMALIZE); //Automatically "normalize" normals
glShadeModel(GL_SMOOTH); //Enable smooth shading (nice effect)
glClearColor(0.0, 0.0, 0.0, 0.0); //Background RGBA
glMatrixMode(GL_MODELVIEW); //MODELVIEW view
glLoadIdentity(); //Start at origin
glOrtho(0.0, 1.0, 0.0, 1.0, -1.0, 1.0); //Set the scale
//X axis = 0 to 1. Y = 0 to 1. Z = -1 to 1.
}
int main() {
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); //The display mode
glutInitWindowSize(WINDOW_WIDTH, WINDOW_HEIGHT); //Window Size
glutInitWindowPosition(200, 200); //Window Position
glutCreateWindow("Lighting!"); //Creates a window with the name "Lighting!"
Initialize(); //Call our initialize function.
glutDisplayFunc(Draw); //"Draw" then refresh the window
glutTimerFunc(25,Update,0); //Call "Update" 25ms after program starts
glutMainLoop(); //Process events etc. Also keeps the window open.
return 0; //End the program
}
However after reading many, MANY texturing tutorials (I mean literally all of the ones I could find on the web); I'm still confused about how I would add it to this program.
I know that I have to load the texture (somehow) and then bind it using the glBindTexture function before I draw the cube, but I think there is something else I need to do in-between (and I don't know how to load the image properly either).
For loading texture files (png, jpg...) use this:
(don't forget to install SDL and SDL_image libraries)
#include <SDL/SDL.h>
#include <SDL/SDL_image.h>
GLuint texture_alloc(const char *tex_name, int alpha)
{
GLuint tex_num;
SDL_Surface *tex_img;
glGenTextures(1, &tex_num);
if(tex_img = (SDL_Surface *) IMG_Load(tex_name)) {
glBindTexture(GL_TEXTURE_2D, tex_num);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE);
if (alpha==1)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tex_img->w, tex_img->h, 0, GL_RGBA, GL_UNSIGNED_BYTE, tex_img->pixels);
else
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tex_img->w, tex_img->h, 0, GL_RGB, GL_UNSIGNED_BYTE, tex_img->pixels);
SDL_FreeSurface (tex_img);
}
return tex_num;
}
For setting texture coordinates use glTexCoord function:
glTexCoord2f(0.0f, 0.0f);
glVertex3f( 1.0f, 1.0f,-1.0f);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(-1.0f, 1.0f,-1.0f);
.
.
.
I recommend looking at SOIL, an OpenGL image loading library - its what I use.
As far as getting a texture working, its pretty much:
Unsigned int texture = SOIL_load_OGL_texture(imagePath.c_str(), SOIL_CREATE_NEW_ID, SOIL_LOAD_AUTO, SOIL_FLAG_MIPMAPS);
glBindTexture(texture);
You do however have to use texture co-ordinates so that opengl knows how to wrap your texture. Heres some sample calls that render cube.
int size = 1;
// Begin Rending
glBegin(GL_QUADS);
{
// Face 1
glNormal3f( 0.0f, 0.0f, 1.0f);
glTexCoord2f(0.0f, 0.0f);
glVertex3f( m_size, m_size,-m_size);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(-m_size, m_size,-m_size);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(-m_size, m_size, m_size);
glTexCoord2f(0.0f, 1.0f);
glVertex3f( m_size, m_size, m_size);
// Face 2
glNormal3f( 0.0f, 0.0f,-1.0f);
glTexCoord2f(0.0f, 0.0f);
glVertex3f( m_size,-m_size, m_size);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(-m_size,-m_size, m_size);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(-m_size,-m_size,-m_size);
glTexCoord2f(0.0f, 1.0f);
glVertex3f( m_size,-m_size,-m_size);
// Face 3
glTexCoord2f(0.0f, 0.0f);
glVertex3f( m_size, m_size, m_size);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(-m_size, m_size, m_size);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(-m_size,-m_size, m_size);
glTexCoord2f(0.0f, 1.0f);
glVertex3f( m_size,-m_size, m_size);
// Face 4
glNormal3f( 0.0f,-1.0f, 0.0f);
glTexCoord2f(0.0f, 0.0f);
glVertex3f( m_size,-m_size,-m_size);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(-m_size,-m_size,-m_size);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(-m_size, m_size,-m_size);
glTexCoord2f(0.0f, 1.0f);
glVertex3f( m_size, m_size,-m_size);
// Face 5
glNormal3f( 1.0f, 0.0f, 0.0f);
glTexCoord2f(0.0f, 0.0f);
glVertex3f(-m_size, m_size, m_size);
glTexCoord2f(1.0f, 0.0f);
glVertex3f(-m_size, m_size,-m_size);
glTexCoord2f(1.0f, 1.0f);
glVertex3f(-m_size,-m_size,-m_size);
glTexCoord2f(0.0f, 1.0f);
glVertex3f(-m_size,-m_size, m_size);
// Face 6
glNormal3f( 1.0f, 0.0f, 0.0f);
glTexCoord2f(0.0f, 0.0f);
glVertex3f( m_size, m_size,-m_size);
glTexCoord2f(1.0f, 0.0f);
glVertex3f( m_size, m_size, m_size);
glTexCoord2f(1.0f, 1.0f);
glVertex3f( m_size,-m_size, m_size);
glTexCoord2f(0.0f, 1.0f);
glVertex3f( m_size,-m_size,-m_size);
}
glEnd();
I think the part your missing is the concept of texture coordinates.
Textures are 2d right? So, when you're pushing the 3d points of your object to the graphics card, if you want it textured you also need to associate that 3d point with a position in a texture image which you do with a pair of texture coordinates (pair because textures are 2d)... Take a look at glTexCoord2f()... If you make a call to that right before a call to glVertex3f() you'll associate that 3d point with a point in the image... If you do that for all the points that make up a primitive opengl can interpolate for all the points in between...
For image loading you can also use the SDL-image Library:
http://www.libsdl.org/projects/SDL_image/
It is very handy.
You can use the glaux code from the http://nehe.gamedeve.net/ this also is pretty simple.
AUX_RGBImageRec* LoadBMP(char* Filename)
{
FILE *File = NULL;
if ( !Filename )
return NULL;
File = fopen(Filename,"r");
if ( !Filename )
return NULL;
fclose(File);
return auxDIBImageLoad(Filename);
}
int LoadTextures()
{
int Status = FALSE;
AUX_RGBImageRec *TextureImage[1];
memset(TextureImage,0,sizeof(void*)*1);
if ( TextureImage[0] = LoadBMP("GoneFishing.bmp") )
{
Status = true;
if(texture[0] == -1)
glGenTextures(1,&texture[0]);
glBindTexture(GL_TEXTURE_2D,texture[0]);
// Generate The Texture
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // Linear Filtering
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // Linear Filtering
}
if (TextureImage[0]) // If Texture Exists
{
if (TextureImage[0]->data) // If Texture Image Exists
{
free(TextureImage[0]->data); // Free The Texture Image Memory
}
free(TextureImage[0]); // Free The Image Structure
}
return Status;
}