My question is simple, how do I draw a cylinder in modern OpenGL? I'm using GLFW together with OpenGL 3.x. My thought at first was to create a function that computes the vertex positions at the bottom and at the top as circles and then draw lines between these vertices. But I have no idea how to implement this.. Does anyone have a good solution?
You can do that with a triangle strip and generate a vertex at the bottom then one at the top. That should generate the sides easily. Then just generate the caps with a triangle fan and you are don. To simplify things you can use the modelview matrix to move the cylinder into position where you want. This way you only need to have a circle in the x/y plane or similar so the math is very simple.
For performance consider using precompiled objects and/or vertex arrays.
I have been using this for a while now and I hope it will help people in the future.
struct {
GLfloat x,z, y_start, y_end;
}each_pole; // struct
std::vector<each_pole> each_pole_vector; // vector of structs
//Cylinder with y axis up
GLfloat cylinder_height = 1.0f,
cylinder_radius = 0.5f,
nr_of_points_cylinder = 360.f;
for (int i = 0; i < nr_of_points_cylinder; ++i)
{
GLfloat u = i / (GLfloat)nr_of_points_cylinder;
//Where the cylinder is in the x and z positions (3D space)
each_pole.x = center.x
+ cylinder_radius*cos(2*M_PI*u);
each_pole.z = center.z
+ cylinder_radius*sin(2*M_PI*u);
each_pole.y_start = 0.0f;
each_pole.y_end = cylinder_height;
each_pole_vector.push_back(each_pole);
}
return each_pole_vector;
Related
This question already has an answer here:
OpenGL stretched shapes - aspect ratio
(1 answer)
Closed 7 years ago.
I have been trying to generate an ellipse using OpenGL and I have a feeling I have got something very wrong. I am trying to use an ellipse generating code but for simplicity, I have set the length of the major and minor axes equal. This should give me a circle but somehow that is not what is rendered with OpenGL and I am not sure what is wrong.
So the code is as follows:
glPushAttrib(GL_CURRENT_BIT);
glColor3f(1.0f, 0.0f, 0.0f);
glLineWidth(2.0);
// Draw center
glBegin(GL_POINTS);
glVertex2d(0, 0);
glEnd();
glBegin(GL_LINE_LOOP);
// This should generate a circle
for (GLfloat i = 0; i < 360; i++)
{
float x = cos(i*M_PI/180.f) * 0.5; // keep the axes radius same
float y = sin(i*M_PI/180.f) * 0.5;
glVertex2f(x, y);
}
glEnd();
glPopAttrib();
This should generate a circle as far as I can think. However. I get something like the attached image, which is not a circle. I am not sure what I am doing wrong.
It is a circle in clip space. Note that the horizontal extent is half the screen's width and the vertical extent is half the screen's height. The viewport transformation that maps clip space (-1 to 1 on both axes) to screen space basically performs a scaling and translation, which causes the deformation of the circle.
To prevent this from happening, you need to set up an appropriate projection transform, e.g. with glOrtho.
I'm trying to draw a circle in opengl, but i can't seem to get the right coordinates, so i always get an ellipsis no matter what method i use. The current code is as follows:
void Ball::Render() {
float center_position_x = _body->GetWorldCenter().x;
float center_position_y = _body->GetWorldCenter().y;
float radius = static_cast<b2CircleShape*>(_body->GetFixtureList()->GetShape())->m_radius;
glBegin(GL_LINE_STRIP);
for(float angle = 0; angle <= 360; ++angle) {
float angleInRadians = glm::radians(angle);
float x = glm::cos(angleInRadians);
float y = glm::sin(angleInRadians);
glVertex3f(x,y,0);
}
glEnd();
}
( I know that code should draw the circle at the origin, but even then it's not a perfect circle, if i understand correctly that should draw a circle at the origin with radius=1 )
The other methods i used were:
http://slabode.exofire.net/circle_draw.shtml
http://www.opengl.org/discussion_boards/showthread.php/167955-drawing-a-smooth-circle
This is my OpenGL window setup code (It's a stub program so i'm starting with hardcoded values):
gluOrtho2D(-10, 15, -10, 15);
Use gluOrtho2D(-WIDOWS_WIDTH, WIDOWS_WIDTH, -WINDOWS_HEIGHT, WINDOWS_HEIGHT);
By the way your are using fixed pipeline in 2013. With a vertex shader this would be much easy to understand.
The aspect ratio of your 2D projection matrix defined by gluOrtho2d must be same as the viewport/window on which you are rendering otherwise you'll notice distortions in the figures you are rendering. You can use the above gluOrtho2d statement or other way of writing it is -
float ar = (float)WindowWidth/WindowHeight;
gluOrtho2D(-1*ar, ar, -1, 1)
I am having trouble with a camera class I am trying to use in my program. When I change the camera_target of the gluLookAt call, my whole terrain is rotating instead of just the camera rotating like it should.
Here is some code from my render method:
camera->Place();
ofSetColor(255, 255, 255, 255);
//draw axis lines
//x-axis
glBegin(GL_LINES);
glColor3f(1.0f,0.0f,0.0f);
glVertex3f(0.0f,0.0f,0.0f);
glVertex3f(100.0f, 0.0f,0.0f);
glEnd();
//y-axis
glBegin(GL_LINES);
glColor3f(0.0f,1.0f,0.0f);
glVertex3f(0.0f,0.0f,0.0f);
glVertex3f(0.0f, 100.0f,0.0f);
glEnd();
//z-axis
glBegin(GL_LINES);
glColor3f(0.0f,0.0f,1.0f);
glVertex3f(0.0f,0.0f,0.0f);
glVertex3f(0.0f, 0.0f,100.0f);
glEnd();
glColor3f(1,1,1);
terrain->Draw();
And the rotate and place methods from my camera class:
void Camera::RotateCamera(float h, float v){
hRadians += h;
vRadians += v;
cam_target.y = cam_position.y+(float)(radius*sin(vRadians));
cam_target.x = cam_position.x+(float)(radius*cos(vRadians)*cos(hRadians));
cam_target.z = cam_position.z+(float)(radius*cos(vRadians)*sin(hRadians));
cam_up.x = cam_position.x-cam_target.x;
cam_up.y = ABS(cam_position.y+(float)(radius*sin(vRadians+PI/2))) ;
cam_up.z = cam_position.z-cam_target.z;
}
void Camera::Place() {
//position, camera target, up vector
gluLookAt(cam_position.x, cam_position.y, cam_position.z, cam_target.x, cam_target.y, cam_target.z, cam_up.x, cam_up.y, cam_up.z);
}
The problem is that the whole terrain is moving around the camera, whereas the camera should just be rotating.
Thanks for any help!
EDIT - Found some great tutorials and taking into account the answers on here, I make a better camera class. Thanks guys
From the POV of the terrain, yes, the camera is rotating. But, since your view is from the POV of the camera, when you rotate the camera, it appears that the terrain is rotating. This is the behavior that gluLookAt() is intended to produce. If there is something else that you expected, you will need to rotate only the geometry that you want rotated, and not try to rotate using gluLookAt().
Update 1: Based on the discussion below, try this:
void Camera::RotateCamera(float h, float v)
{
hRadians += h;
vRadians += v;
cam_norm.x = cos(vRadians) * sin(hRadians);
cam_norm.y = -sin(vRadians);
cam_norm.z = cos(vRadians) * sin(hRadians);
cam_up.x = sin(vRadians) * sin(hRadians);
cam_up.y = cos(vRadians);
cam_up.z = sin(vRadians) * cos(hRadians);
}
void Camera::Place()
{
//position, camera target, up vector
gluLookAt(cam_pos.x, cam_pos.y, cam_pos.z,
cam_pos.x+cam_norm.x, cam+pos.y+cam_norm.y, camp_pos.z+cam_norm.z,
cam_up.x, cam_up.y, cam_up.z);
}
Separating the camera normal (the direction the camera is looking) from the position allows you to independently change the position, pan and tilt... that is, you can change the position without having to recompute the normal and up vectors.
Disclaimer: This is untested, just what I could do on the back of a sheet of paper. It assumes a right handed coordinate system and that pan rotation is applied before tilt.
Update 2: Derived using linear algebra rather than geometry... again, untested, but I have more confidence in this.
Well, rotating the camera or orbiting the terrain around the camera looks essentially the same.
If you want to orbit the camera around a fixed terrain point you have to modify the camera position, not the target.
Should it not be?:
cam_up.x = cam_target.x - cam_position.x;
cam_up.y = ABS(cam_position.y+(float)(radius*sin(vRadians+PI/2))) ;
cam_up.z = cam_target.z - cam_position.z;
Perhaps you should normalize cam_up as well.
HTH
I am having a simple program, it draws a circle :/
This works fine...
for (k = 1; k < n+1+1; k++){
vertices[k].color = GU_COLOR( 0.0f, 0.0f, 1.0f, 0.0f );
vertices[k].x = cos_d( 360 - ((k-1) * dstep) );
vertices[k].y = sin_d( 360 - ((k-1) * dstep) );
vertices[k].z = 0.0f;
}
...
//Now draw it
sceGumDrawArray(GU_TRIANGLE_FAN, GU_COLOR_8888|GU_VERTEX_32BITF|GU_TRANSFORM_3D, n+1+1, 0, vertices);
But this doesn't:
for (k = 1; k < n+1+1; k++){
vertices[k].color = GU_COLOR( 0.0f, 0.0f, 1.0f, 0.0f );
vertices[k].x = cos_d( ((k-1) * dstep) );
vertices[k].y = sin_d( ((k-1) * dstep) );
vertices[k].z = 0.0f;
}
But shouldn't this be the same - just with difference that the first loop calculates the vertices clockwise and the second anti-clockwise?
It's just awkward that it just doesn't draw the vertices using the second example...
Or am I stupid?
It may not be drawn because of back-face culling.
"If the user has specified that front-facing polygons have a clockwise winding, if the polygon projected on the screen has a counter-clockwise winding it has been rotated to face away from the camera and will not be drawn."
Not really sure how you are drawing the circle (I see you creating a list of vertices, but know nothing about the rendering of those), but:
Usually, when you invert from clockwise to counter-clockwise, you end up getting the normal inverted, which means that you are looking at the back of your circle. And, as is the case with most APIs, when looking at the back of something (that doesn't have volume), it isn't rendered.
This is called backface culling.
I don't know the library you are using, but are you sure you aren't mixing radians and degrees in your code? Do the sin and cos functions take radians or degrees?
Presumably dstep is 360 / n?
As an aesthetic point, with GL_TRIANGLE_FAN you probably want your first point to be the center of the circle.
I want to know how to draw a spiral.
I wrote this code:
void RenderScene(void)
{
glClear(GL_COLOR_BUFFER_BIT);
GLfloat x,y,z = -50,angle;
glBegin(GL_POINTS);
for(angle = 0; angle < 360; angle += 1)
{
x = 50 * cos(angle);
y = 50 * sin(angle);
glVertex3f(x,y,z);
z+=1;
}
glEnd();
glutSwapBuffers();
}
If I don't include the z terms I get a perfect circle but when I include z, then I get 3 dots that's it. What might have happened?
I set the viewport using glviewport(0,0,w,h)
To include z should i do anything to set viewport in z direction?
You see points because you are drawing points with glBegin(GL_POINTS).
Try replacing it by glBegin(GL_LINE_STRIP).
NOTE: when you saw the circle you also drew only points, but drawn close enough to appear as a connected circle.
Also, you may have not setup the depth buffer to accept values in the range z = [-50, 310] that you use. These arguments should be provided as zNear and zFar clipping planes in your gluPerspective, glOrtho() or glFrustum() call.
NOTE: this would explain why with z value you only see a few points: the other points are clipped because they are outside the z-buffer range.
UPDATE AFTER YOU HAVE SHOWN YOUR CODE:
glOrtho(-100*aspectratio,100*aspectratio,-100,100,1,-1); would only allow z-values in the [-1, 1] range, which is why only the three points with z = -1, z = 0 and z = 1 will be drawn (thus 3 points).
Finally, you're probably viewing the spiral from the top, looking directly in the direction of the rotation axis. If you are not using a perspective projection (but an isometric one), the spiral will still show up as a circle. You might want to change your view with gluLookAt().
EXAMPLE OF SETTING UP PERSPECTIVE
The following code is taken from the excellent OpenGL tutorials by NeHe:
glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix
// Calculate The Aspect Ratio Of The Window
gluPerspective(45.0f,(GLfloat)width/(GLfloat)height,0.1f,100.0f);
glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
glLoadIdentity(); // Reset The Modelview Matrix
Then, in your draw loop would look something like this:
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Clear The Screen And The Depth Buffer
glLoadIdentity();
glTranslatef(-1.5f,0.0f,-6.0f); // Move Left 1.5 Units And Into The Screen 6.0
glBegin(GL_TRIANGLES); // Drawing Using Triangles
glVertex3f( 0.0f, 1.0f, 0.0f); // Top
glVertex3f(-1.0f,-1.0f, 0.0f); // Bottom Left
glVertex3f( 1.0f,-1.0f, 0.0f); // Bottom Right
glEnd();
Of course, you should alter this example code your needs.
catchmeifyoutry provides a perfectly capable method, but will not draw a spatially accurate 3D spiral, as any render call using a GL_LINE primitive type will rasterize to fixed pixel width. This means that as you change your perspective / view, the lines will not change width. In order to accomplish this, use a geometry shader in combination with GL_LINE_STRIP_ADJACENCY to create 3D geometry that can be rasterized like any other 3D geometry. (This does require that you use the post fixed-function pipeline however)
I recommended you to try catchmeifyoutry's method first as it will be much simpler. If you are not satisfied, try the method I described. You can use the following post as guidance:
http://prideout.net/blog/?tag=opengl-tron
Here is my Spiral function in C. The points are saved into a list which can be easily drawn by OpenGL (e.g. connect adjacent points in list with GL_LINES).
cx,cy ... spiral centre x and y coordinates
r ... max spiral radius
num_segments ... number of segments the spiral will have
SOME_LIST* UniformSpiralPoints(float cx, float cy, float r, int num_segments)
{
SOME_LIST *sl = newSomeList();
int i;
for(i = 0; i < num_segments; i++)
{
float theta = 2.0f * 3.1415926f * i / num_segments; //the current angle
float x = (r/num_segments)*i * cosf(theta); //the x component
float y = (r/num_segments)*i * sinf(theta); //the y component
//add (x + cx, y + cy) to list sl
}
return sl;
}
An example image with r = 1, num_segments = 1024:
P.S. There is difference in using cos(double) and cosf(float).
You use a float variable for a double function cos.