I am making a rollercoaster inside of a skybox in OpenGL, and without much background on it's functions or computer graphics it is proving to be very difficult. I drew a rollercoaster using Catmull-Rom spline interpolation, and drew each point with glVertex3f. Now I want to call an update() function every 50ms to move the camera around the track. gluLookAt() is producing weird results, either removing the track from the screen, producing a black screen, etc. I think I need to move some of the matrix functions around but I am not sure where to put each one. Here is my code so far:
int main(int argc, char** argc)
{
// ... load track, etc ...
// Init currpos, nextpos, iter, up
currpos = Vec3f(0, 0, 0);
nextpos = currpos;
iter = 0;
up = Vec3f(0, 1, 0);
deque<Vec3f> points;
Vec3f newpt;
// Loop through the points and interpolate
for (pointVectorIter pv = g_Track.points().begin(); pv != g_Track.points().end(); pv++)
{
Vec3f curr(*pv); // Initialize the current point and a new point (to be drawn)
points.push_back(curr); // Push the current point onto the stack
allpoints.push_back(curr); // Add current point to the total stack
if (points.size() == 4) // Check if there are 4 points in the stack, if so interpolate
{
for (float u = 0.0f; u < 1.0f; u += 0.01f)
{
newpt = interpolate(points[0], points[1], points[2], points[3], u);
glColor3f(1, 1, 1);
glVertex3f(newpt.x(), newpt.y(), newpt.z());
allpoints.push_back(newpt);
}
points.pop_front();
}
}
// glutInit, InitGL(), etc...
}
void InitGL(GLvoid)
{
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(100.0, (GLfloat)WINDOW_WIDTH / (GLfloat)WINDOW_HEIGHT, .0001, 999999);
glMatrixMode(GL_MODELVIEW);
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
}
void display (void)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(currpos.x(), currpos.y(), currpos.z(), nextpos.x(), nextpos.y(), nextpos.z(), up.x(), up.y(), up.z());
glPushMatrix();
glEnable(GL_TEXTURE_2D); // Enable texturing from now on
/* draw skybox, this was from previous assignment and renders correctly */
glPopMatrix();
// now draw rollercoaster ...
glPushMatrix();
glBegin(GL_LINE_STRIP);
deque<Vec3f> points;
Vec3f newpt;
for each (Vec3f pt in allpoints)
{
glColor3f(1, 1, 1);
glVertex3f(pt.x(), pt.y(), pt.z());
}
glutTimerFunc(50, update, 1);
glEnd();
glPopMatrix();
// Swap buffers, so one we just drew is displayed
glutSwapBuffers();
}
void update(int a)
{
if (iter < allpoints.size())
{
currpos = allpoints[iter];
nextpos = allpoints[iter + 1];
gaze = nextpos - currpos;
gaze.Normalize();
Vec3f::Cross3(binorm, gaze, up);
binorm.Normalize();
Vec3f::Cross3(up, binorm, gaze);
up.Normalize();
glutPostRedisplay();
}
iter++;
}
The idea is that I am keeping a global deque allpoints that includes the control points of the spline and the interpolated points. Once that is complete, I call update() every 50ms, and move the camera along each point in allpoints. In a previous version of the project, I could see that the rollercoaster was being drawn correctly. It is gluLookAt() that doesn't seem to work how I want it to. With the code above, the program starts with the camera looking at one side of the skybox with a part of the rollercoaster, and then when update() is called, the rollercoaster disappears but the camera does not move. I have been messing around with where I am putting the OpenGL matrix functions, and depending on where they are sometimes update() will cause a blank screen as well.
Besides the absence of glPopMatrix (which user971377 already spotted), you call glLoadIdentity in your drawing routine, which of course overwrites any changes you did on the modelview matrix in the update method (using gluLookAt).
Always keep in mind: gluLookAt, glOrtho, gluPerspective, glTranslate, glRotate, and all other matrix and transformation functions always work on the top element (changed by glPush/PopMatrix) of the currently selected matrix stack (changed by glMatrixMode). And they always multiply the current matrix, istead of replacing it. So like for gluPerspective, you should call glLoadIdentity before calling gluLookAt. And the whole camera change should be done in the rendering routine, istead of the update routine.
Instead of doing any GL transformations in update you should rather change the variables on which the camera depends and set the camera (gluLookAt on the modelview matrix) in the display method. To demonstrate the standard use of these functions, your code should be something like:
void display()
{
<general state setup (glClear, ...)>
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glLookAt(camera); //view transformation (camera)
//object 1
glPushMatrix(); //save modelview
glTranslate/glRotate/glScale; //local model transformations
<draw object 1>
glPopMatrix();
...
//object n
glPushMatrix(); //save modelview
glTranslate/glRotate/glScale; //local model transformations
<draw object n>
glPopMatrix();
gluSwapBuffers();
}
void update()
{
camera = ...;
}
}
Noticed in your code glPushMatrix(); is called with no glPopMatrix();
Just a thought, this might have something to do with you issue.
gluLookAt always applies its result to current matrix, which in your case is GL_MODEL_VIEW. But when you render your roller coaster, you load identity in that matrix, which erase the value you put using gluLookAt.
In this case, you don't need to touch the model view. In fact, GL_MODEL_VIEW stands for model matrix multiply by view matrix. In this case, you can glPushMatrix() followed by glMulMatrix( myModelMatrix ) and after rendering glPopMatrix(). With this, you can keep your view matrix inside the GL_MODEL_VIEW and still use a different model matrix for each object
I also suggest you only change projection matrix once a frame, and not each frame.
It's been a long time since I touched OpenGL, but here are a few things to consider:
With each call to display(), you are drawing the skybox with the current matrix then loading the identity matrix to draw the roller coaster. Perhaps load the identity within the push/pop so that the skybox is constant, but your prevailing tranformations on the roller coaster are applied.
Do you need to call gluPerspective and glMatrixMode with every call to display()?
Repeatedly calculating binorm from up and then up from binorm will probably give you unexpected results in terms of rotation of the camera around the screen's z axis.
The call to gluLookAt appears to have nextpos and currpos reversed, pointing the camera in the opposite direction.
(Opinion only) It may still look wierd with a completely stationary skybox. Matching camera rotation (but not translation) when drawing the skybox and roller coaster may look better.
Related
I want to draw an oval by projection the sphere on the screen (like rasterize). Here is my code but it doesn't show anything on the screen. Should I use more functions to initialize the projection? Is this way possible to draw oval on screen by using sphere?
GLfloat xRotated, yRotated, zRotated;
GLdouble radius=1;
void display(void);
void reshape(int x, int y);
int main (int argc, char **argv)
{
glutInit(&argc, argv);
glutInitWindowSize(800,800);
glutCreateWindow("OVAL");
zRotated = 30.0;
xRotated=43;
yRotated=50;
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMainLoop();
return 0;
}
void display(void)
{
glMatrixMode(GL_PROJECTION);
glOrtho(0.1, 1.0, 0.1, 1.0, -1.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glLoadIdentity();
glTranslatef(0.0,0.0,-5.0);
glColor3f(0.9, 0.3, 0.2);
glRotatef(xRotated,1.0,0.0,0.0);
glRotatef(yRotated,0.0,1.0,0.0);
glRotatef(zRotated,0.0,0.0,1.0);
glScalef(1.0,1.0,1.0);glutSolidSphere(radius,20,20);
glFlush();
}
void reshape(int x, int y)
{
if (y == 0 || x == 0) return;
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(39.0,(GLdouble)x/(GLdouble)y,0.6,21.0);
glMatrixMode(GL_MODELVIEW);
glViewport(0,0,x,y);
}
You are drawing a sphere compltely outside of the viewing volume, so it should be no surprise that it can't be seen.
There are a couple of issues with your code:
All OpenGL matrix functions besides glLoadIndentity and glLoadMatrix always post-multiply a matrix to the current top element of the current matrix stack. In your display function, you call glOrtho without resetting the projection matrix to identity before. This will result in totally weird - and different - results if the display callback is called more than once.
You should add a call to glLoadIdentity() right before calling glOrtho.
You set up the model view transformations so that the sphere's center will always end up at (0,0,-5) in eye space. However, you set a projectiom matrix which defines a viewing volume which goes from z=1 (near plane) to z=-1 (far plane) in eye space, so your spehre is actually behind the far plane.
There are several ways this could be fixed. Changing the viewing frustum by modifying the parameters of glOrtho might be the easisest. You could for example try (-2, 2, -2, 2, 1, 10) to be able to see the sphere.
It is not really clear what
I want to draw an oval by projection the sphere on the screen (like rasterize).
exactly means. If you just want the sphere to be distorted to an ellipsoid, you could just apply some non-uniform scaling. This in principle could be done in the projection matrix (if no other objects are to be shown), but this would make much more sense to apply it to the model matrix of the sphere - you already have the glScale call there, you could try something like glScalef(1.0f, 0.5f, 1.0f);.
Also note that the ortho parameters I suggested previously will result in some distortion if your viewport is not exactly square. In a real world, one wants to incorporate the aspect ratio of the viewport into the projection matrix.
If you want to see the sphere deformed as by a perspective projection, you would have to skip the glOrtho altogheter and switch to a perspective projection matrix.
The code you are using is totally outdated. The OpenGL matrix stack has been deprecated in OpenGL 3.0 (2008) and is not available in core profiles of modern OpenGL. The same applies for builtin vertex attributes like glColor or immediate mode drawing and client-side vertex arrays. As a result, GLUT's drawing functions can also not be used with modern GL any more.
If you really intend learning OpenGL nowadays, I stronly advise you to ignore this old cruft and star learning the modern way.
here is my code:
void drawClock(void)
{
glClearColor(1.0,1.0,1.0,1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glColor3f(0.0,0.0,0.0);
for(int i=0; i<12; i++){
glRotatef(30,0,0,1);
glTranslatef(5.2,0.0,0.0);
glutWireCube(2.0);
}
glFlush();
}
Here is my reshape function (without it I don't see anything, although I'm not sure how it really works)
void changeSize(int w, int h) {
// Prevent a divide by zero, when window is too short
// (you cant make a window of zero width).
if(h == 0) h = 1;
float ratio = 1.0* w / h;
// Reset the coordinate system before modifying
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
// Set the viewport to be the entire window
glViewport(0, 0, w, h);
// Set the correct perspective.
gluPerspective(45,ratio,1,1000);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0,0.0,70.0,
0.0,0.0,-1.0,
0.0f,1.0f,0.0f);
}
So I am trying to draw the hour marks of a clock using wire cubes of size 2.0, and each of these cubes must be 5.2 units away from the center. This is for an assignment, I know it is probably very simple but I just can't manage to make it work properly. My problem is that the cubes appear in 3D, but I want them to appear in 2D, as in I would only see one face. Also, the circle is not centered, I do not understand why. I know I am supposed to use pushMatrix and popMatrix but no matter how I use it it doesn't work.
3d Problem
gluPerspective makes a perspective projection. To accomplish what you need you should apply an orthographic projection instead.
The best way to do this with your current code is to us glOrtho to provide a left, right, bottom, top, far,near box within which everything will 'appear' 2D. Try the code below in place of gluPerspective.
glOrtho(10.0f,-10.0f,10.0f,-10.0f,10.0f,-10.0f,10.0f);
Position problem
I'm not so sure about the transformation as I've not used immediate mode for a while. Note the order of operations makes a difference.
As for the push/pop of matrices, it is basically a stack of 4x4 matrices detailing the transformations. I'm sure it works along the lines of
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslate(x,y,z);// Where x,y,z is the starting point of your clock
for(int i=0; i<12; i++){
glPushMatrix();
glRotatef(i * 30,0,0,1);
glTranslatef(5.2,0.0,0.0);
glutWireCube(2.0);
glPopMatrix();
}
I have a wall pattern DrawWall and an airplane DrawAirplane in my OpenGL game. How can I push and pop the current matrix and translate only the wall in the scene?
I expect the airplane to be fixed.
private: void DrawWall(){
glPushMatrix();
glBegin(GL_POLYGON);
LeftWallPattern();
glEnd();
glBegin(GL_POLYGON);
RightWallPattern();
glEnd();
glPopMatrix();
}
private: void DrawAirplane(){
glPushMatrix();
glBegin(GL_LINE_LOOP);
//...
glEnd();
glPopMatrix();
}
public: void Display(){
glClear(GL_COLOR_BUFFER_BIT);
glTranslatef(0, -0.02, 0);
DrawWall();
DrawAirplane();
glFlush();
}
Use glPushMatrix() to push the current matrix, do glTranslate and draw the wall, then glPopMatrix() and draw the plane. This should only translate the wall. The problem is you seem to be doing the translate in display instead of in DrawWall where it should be.
A few things to expand on what Jesus was saying.
When drawing the airplane you don't want to apply any transformations to it, so you need to have the identity matrix loaded:
Push the current modelview matrix
Load the identity matrix <=== this is the step you're missing
Draw the airplane
Pop the modelview matrix
When drawing the wall you want the current transformations to apply, so you do not push the current matrix or else you've wiped out all of the translations you've built up.
Remove the Push/Pop operations from DrawWall()
At some point in your initialization, before Display is called for the first time, you need to set the modelview matrix to the identity matrix. For each subsequent call to Display, -0.02 will then be added to your translation in the y-direction.
I need to change my object's position with respect to camera position. I mean, my object should always be just in front of the camera. It should follow camera movements.What do I need to add my object drawing function?
If you're using old-style fixed function matrices, the easiest way to position an object relative to the camera is to do it after removing the camera matrix from the stack. For example, you might have a matrix setup like this:
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(...); //Or other camera functions.
for(/*each object*/)
{
glPushMatrix();
//Setup object matrices.
glTranslatef();
glRotatef();
//Setup object rendering.
glDrawElements(); //Draw the object
glPopMatrix();
}
Then switch it around into this:
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glPushMatrix();
gluLookAt(...); //Or other camera functions.
for(/*each object*/)
{
glPushMatrix();
//Setup object matrices.
glTranslatef();
glRotatef();
//Setup object rendering.
glDrawElements(); //Draw the object
glPopMatrix();
}
glPopMatrix(); //Remove camera matrix. MODELVIEW is now identity.
for(/*each camera-relative object*/)
{
glPushMatrix();
//Setup object matrices.
glTranslatef();
glRotatef();
//Setup object rendering.
glDrawElements(); //Draw the object
glPopMatrix();
}
If you're using shaders, this is even easier. If you have a matrix stack, just do the same stack-based operation as you see here. If you're doing something else to build your matrices, just don't factor the camera matrix into the transform.
Your object position should then always be at:
desiredPosition = cameraPosition + cameraDirection * offset;
I am trying to modify this Digiben sample in order to get the effect of particles that generate from a spot (impact point) and float upwards kind of like the sparks of a fire. The sample has the particles rotating in a circle... I have tried removing the cosine/sine functions and replace them with a normal glTranslate with increasing Y value but I just can't get any real results... could anyone please point out roughly where I should add/modify the translation in this code to obtain that result?
void ParticleMgr::init(){
tex.Load("part.bmp");
GLfloat angle = 0; // A particle's angle
GLfloat speed = 0; // A particle's speed
// Create all the particles
for(int i = 0; i < P_MAX; i++)
{
speed = float(rand()%50 + 450); // Make a random speed
// Init the particle with a random speed
InitParticle(particle[i],speed,angle);
angle += 360 / (float)P_MAX; // Increment the angle so when all the particles are
// initialized they will be equally positioned in a
// circular fashion
}
}
void ParticleMgr::InitParticle(PARTICLE &particle, GLfloat sss, GLfloat aaa)
{
particle.speed = sss; // Set the particle's speed
particle.angle = aaa; // Set the particle's current angle of rotation
// Randomly set the particles color
particle.red = rand()%255;
particle.green = rand()%255;
particle.blue = rand()%255;
}
void ParticleMgr::DrawParticle(const PARTICLE &particle)
{
tex.Use();
// Calculate the current x any y positions of the particle based on the particle's
// current angle -- This will make the particles move in a "circular pattern"
GLfloat xPos = sinf(particle.angle);
GLfloat yPos = cosf(particle.angle);
// Translate to the x and y position and the #defined PDEPTH (particle depth)
glTranslatef(xPos,yPos,PDEPTH);
// Draw the first quad
glBegin(GL_QUADS);
glTexCoord2f(0,0);
glVertex3f(-5, 5, 0);
glTexCoord2f(1,0);
glVertex3f(5, 5, 0);
glTexCoord2f(1,1);
glVertex3f(5, -5, 0);
glTexCoord2f(0,1);
glVertex3f(-5, -5, 0);
glEnd(); // Done drawing quad
// Draw the SECOND part of our particle
tex.Use();
glRotatef(particle.angle,0,0,1); // Rotate around the z-axis (depth axis)
//glTranslatef(0, particle.angle, 0);
// Draw the second quad
glBegin(GL_QUADS);
glTexCoord2f(0,0);
glVertex3f(-4, 4, 0);
glTexCoord2f(1,0);
glVertex3f(4, 4, 0);
glTexCoord2f(1,1);
glVertex3f(4, -4, 0);
glTexCoord2f(0,1);
glVertex3f(-4, -4, 0);
glEnd(); // Done drawing quad
// Translate back to where we began
glTranslatef(-xPos,-yPos,-PDEPTH);
}
void ParticleMgr::run(){
for(int i = 0; i < P_MAX; i++)
{
DrawParticle(particle[i]);
// Increment the particle's angle
particle[i].angle += ANGLE_INC;
}
}
For now I am adding a glPushMatrix(), glTranslate(x, y, z) in the run() function above, right before the loop, with x,y,z as the position of the enemy for placing them on top of the enemy....is that the best place for that?
Thanks for any input!
Using glTranslate and glRotate that way will in fact decrease your program's performance. OpenGL is not a scene graph, so the matrix manipulation functions directly influence the drawing process, i.e. they don't set "object state". The issue you're running into is, that a 4×4 matrix-matrix multiplication involves 64 multiplications and 16 additions. So you're spending 96 times the computing power for moving a particle, than simply update the vertex position directly.
Now to your problem: Like I already told you, glTranslate operates on (a global) matrix state of one of 4 selectable matrices. And the effects accumulate, i.e. each glTranslate will start from the matrix the previous glTranslate left. OpenGL provides a matrix stack, where one can push a copy of the current matrix to work with, then pop to revert to the state before.
However: Matrix manipulation has been removed from OpenGL-3 core and later entirely. OpenGL matrix manipulation never was accelerated (except on one particular graphics workstation made by SGI around 1996). Today it is a anachronism, as every respectable program working with 3D geometry used much more sophisticated matrix manipulation by either own implementation or 3rd party library. OpenGL's matrix stack was just redundant. So I strongly suggest you forget about OpenGL's matrix manipulation functionality and roll your own.