I am attempting to draw a sphere from scratch using OpenGL. The function must be defined as void drawSphere(float radius, int nSegments, int nSlices), must be centred at the (0, 0, 0) origin and must be created using GL_QUADS.
Firstly, are the "slices" the sort of tapered cylinder shapes that are stacked on top of each other to create the sphere, and the "segments" are the quads that are generated in a circle to generate the wall/side of each of these tapered cylinder slices?
Secondly, I cannot seem to find any algorithms or examples of how to make the calculations to generate this sphere using quadrilaterals - most example seem to be generated from triangles instead.
EDIT
Here is what I have just tried, which is definitely in the right direction, but my coordinate calculations are off somewhere:
void drawSphere(float radius, int nSegments, int nSlices) {
/*
* TODO
* Draw sphere centered at the origin using GL_QUADS
* Compute and set normal vectors for each vertex to ensure proper shading
* Set texture coordinates
*/
for (float slice = 0.0; slice < nSlices; slice += 1.0) {
float lat0 = M_PI * (((slice - 1) / nSlices) - 0.5);
float z0 = sin(lat0);
float zr0 = cos(lat0);
float lat1 = M_PI * ((slice / nSlices) - 0.5);
float z1 = sin(lat1);
float zr1 = cos(lat1);
glBegin(GL_QUADS);
for (float segment = 0.0; segment < nSegments; segment += 1.0) {
float long0 = 2 * M_PI * ((segment -1 ) / nSegments);
float x0 = cos(long0);
float y0 = sin(long0);
float long1 = 2 * M_PI * (segment / nSegments);
float x1 = cos(long1);
float y1 = sin(long1);
glVertex3f(x0 * zr0, y0 * zr0, z0);
glVertex3f(x1 * zr1, y1 * zr1, z0);
glVertex3f(x0 * zr0, y0 * zr0, z1);
glVertex3f(x1 * zr1, y1 * zr1, z1);
}
glEnd();
}
}
I'm not seeing radius being used. Probably a simple omission. Let's assume that for the rest of your computation the radius is 1. You should generate your 4 values by using 0-1 on both (x, y), and (z, zr), but not mix within those tuples.
So x1 * zr1, y1 * zr1, z0 is not right because you're mixing zr1 and z0. You can see that the norm of this vector is not 1 anymore. Your 4 values should be
x0 * zr0, y0 * zr0, z0
x1 * zr0, y1 * zr0, z0
x0 * zr1, y0 * zr1, z1
x1 * zr1, y1 * zr1, z1
I'm not too sure about the order since I don't use Quads but triangles.
Related
I've been working on a project for some time and Needed something that could from a Vector3 representing rotation in the XYZ axis make Forward, Right and Up vectors. I was looking through a lot of stuff and after some time I figured out I had to implement Quaternions (I have my own Math Libary but this same thing happened with glm) and Here is the code for calculating the Forward Vector: (quaternion is the rotation Quaternion member in my class and Quaternion::Euler is a static function that returns a Quaternion from Euler Angles)
quaternion = Quaternion::Euler(rotation);
Vector3 ret = quaternion * Vector3(0.0f, 0.0f, 1.0f);
when the rotation is 0, 0, 0 the function returns 0, 0, 1 as it should, but if I try something like 0, 180, 0 it should return 0, 0, -1, but instead I get -8.74228e-08, 0, -1. After some investigation I figured out that the Quaternion::Euler function returns a Quaternion where the w part is messed up. In the case where the rotation is 0, 180, 0 the Quaternion the Quaternion::Euler function returns is 0, 1, 0, -4.37113883e-08 which is almost exactly half of the random number the Forward functions returns. Here is Quaternion::Euler:
float x = Radians(euler.x);
float y = Radians(euler.y);
float z = Radians(euler.z);
x = x / 2;
y = y / 2;
z = z / 2;
return Quaternion(cos(z) * cos(y) * sin(x) - sin(z) * sin(y) * cos(x), //X
cos(z) * sin(y) * cos(x) + sin(z) * cos(y) * sin(x), //Y
sin(z) * cos(y) * cos(x) - cos(z) * sin(y) * sin(x), //Z
cos(z) * cos(y) * cos(x) + sin(z) * sin(y) * sin(x));//W
and Honestly, I stole this function from an article of a guy that was making his own Math Engine, in his case this seemed to work. Here is the Quaternion Vector Multiplication function, that I "borrowed" from the Unity Implementation: (in this case it's inside the Quaternion struct so this is a pointer to the quaternion from the Quaternion Vector multiplication)
inline Vector3 operator*(const Vector3& other) {
float x = this->x * 2.0f;
float y = this->y * 2.0f;
float z = this->z * 2.0f;
float xx = this->x * x;
float yy = this->y * y;
float zz = this->z * z;
float xy = this->x * y;
float xz = this->x * z;
float yz = this->y * z;
float wx = this->w * x;
float wy = this->w * y;
float wz = this->w * z;
Vector3 ret;
ret.x = (1.0f - (yy + zz)) * other.x + (xy - wz) * other.y + (xz + wy) * other.z;
ret.y = (xy + wz) * other.x + (1.0f - (xx + zz)) * other.y + (yz - wx) * other.z;
ret.z = (xz - wy) * other.x + (yz + wx) * other.y + (1.0f - (xx + yy)) * other.z;
return ret;
}
Does anyone know what might be wrong ? I tried to do this with glm:
glm::quat quat(glm::vec3(glm::radians(rotation.x), glm::radians(rotation.y), glm::radians(rotation.z)));
glm::vec3 v = quat * glm::vec3(0.0f, 0.0f, 1.0f);
but it's the same thing, the vector is the same and the quaternion is the same too, I've been reading into things a lot about this and couldn't find a fix, always when I tried to search implementation for the Quaternio::Euler function it just came up with how to use a math library. It would be best if the solution wouldn't require me to use glm, because I have to use my own Math Library, but honestly I will try anything to at least understand what is wrong.
My plan:
1. Calculate mouse direction [x, y] [success]
I my Mouse Move event:
int directionX = lastPosition.x - position.x;
int directionY = lastPosition.y - position.y;
2. Calculate angles [theta, phi] [success]
float theta = fmod(lastTheta + sensibility * directionY, M_PI);
float phi = fmod(lastPhi + sensibility * directionX * -1, M_PI * 2);
Edit {
bug fix:
float theta = lastTheta + sensibility * directionY * -1;
if (theta < M_PI / -2)theta = M_PI / -2;
else if (theta > M_PI / 2)theta = M_PI / 2;
float phi = fmod(lastPhi + sensibility * directionX * -1, M_PI * 2);
}
Now I have given theta, phi, the centerpoint and the radius and I want to calculate the position and the rotation [that the camera look at the centerpoint]
3. Calculate position coordinates [X,Y,Z] [failed]
float newX = radius * sin(phi) * cos(theta);
float newY = radius * sin(phi) * sin(theta);
float newZ = radius * cos(phi);
Solution [by meowgoesthedog]:
float newX = radius * cos(theta) * cos(phi);
float newY = radius * sin(theta);
float newZ = radius * cos(theta) * sin(phi);
4. Calculate rotation [failed]
float pitch = ?;
float yaw = ?;
Solution [by meowgoesthedog]:
float pitch = -theta;
float yaw = -phi;
Thanks for your solutions!
Your attempt was almost (kinda) correct:
As the diagram shows, in OpenGL the "vertical" direction is conventionally taken to be Y, whereas your formulas assume it is Z
phi and theta are in the wrong order
Very simple conversion: yaw = -phi, pitch = -theta (from the perspective of the camera)
Fixed formulas:
float position_X = radius * cos(theta) * cos(phi);
float position_Y = radius * sin(theta);
float position_Z = radius * cos(theta) * sin(phi);
(There may also be some sign issues with the mouse deltas but they should be easy to fix.)
I am given a Line PQ and a boundary. I have to find two parallel lines to the given line but lines should intersect the boundary. Also i know the distance between the parallel lines to the given line. I need to find the P'Q' and P"Q".
Please any one give a simple solution.
float vx = x2 - x1;
float vy = y2 - y1;
float mag = sqrt(vx * vx + vy * vy);
float t = (mag / 2.0) / mag;
float px = (1 - t) * x1 + t * x2;
float py = (1 - t) * y1 + t * y2;
I just found the centre point of PQ by the above code. Further i planned to draw a perpendicular line thru (px, py) with the known distance, then drawing lines perpendicular to that new line(those lines will be parallel to PQ), thru the end points of that new line. But i could not achieve it. can anyone help me or suggest me a way who know maths
Finally i got the solution.
The steps are.
First i am getting the center point of PQ.
POINT find_a_point_in_distance(float x1, float y1, float x2, float y2, float len = 0) {
float vx = x2 - x1;
float vy = y2 - y1;
float mag = sqrt(vx * vx + vy * vy);
float t = len == 0 ? ((mag / 2.0) / mag) : (len / mag);
float px = (1 - t) * x1 + t * x2;
float py = (1 - t) * y1 + t * y2;
POINT res = { px, py };
return res;
}
here (px, py) is center point of PQ.
Then i am finding the perpendicular line through (px, py).
Already i have mentioned in the question that i know distance between PQ and P'Q' also PQ and P"Q". So i am getting two points in that perpendicular line using that distance. Finally i know the angle of the line PQ, so P'Q' and P"Q" should be in that same angle, using these details i can get the lines P'Q' and P"Q" what ever length i want. Here in below code i am getting the line P'Q' and P"Q" with the length of the diagonal of the rectangular box.
POINT res = find_a_point_in_distance(x1, y1, x2, y2);
POINT res2 = find_a_point_in_distance(res.x, res.y, x2, y2, halflen);
float cosA = acos((res2.x - res.x) / halflen) * 180 / PI;
float sinA = asin((res2.y - res.y) / halflen) * 180 / PI;
float cosAngle = cos((cosA + 90.0) * PI / 180.0);
float sinAngle = sin((sinA + 90.0) * PI / 180.0);
float cx1 = res.x + halflen * cosAngle;
float cy1 = res.y + halflen * sinAngle;
float cosAngle2 = cos((cosA - 90.0) * PI / 180.0);
float sinAngle2 = sin((sinA - 90.0) * PI / 180.0);
float cx2 = res.x + halflen * cosAngle2;
float cy2 = res.y + halflen * sinAngle2;
float diagonal = sqrt(width * width + height * height);
float halfdiagonal = diagonal / 2.0;
float cosAngleT = cos(cosA * PI / 180.0);
float sinAngleT = sin(sinA * PI / 180.0);
float cosAngleTD = cos((cosA + 180) * PI / 180.0);
float sinAngleTD = sin((sinA + 180) * PI / 180.0);
float cx10 = cx1 + halfdiagonal * cosAngleT;
float cy10 = cy1 + halfdiagonal * sinAngleT;
float cx11 = cx1 + halfdiagonal * cosAngleTD;
float cy11 = cy1 + halfdiagonal * sinAngleTD;
float cx20 = cx2 + halfdiagonal * cosAngleT;
float cy20 = cy2 + halfdiagonal * sinAngleT;
float cx21 = cx2 + halfdiagonal * cosAngleTD;
float cy21 = cy2 + halfdiagonal * sinAngleTD;
here (cx10, cy10) and (cx11, cy11) is line P'Q' and
(cx20, cy20) and (cx21, cy21) is line P"Q".
then finally im finding the intersect point of P'Q' and P"Q" with all sides of rectangle
I want to draw a sphere using VBO for vertex, color and UV coordinates for texture. My problem is that the sphere is not 'closed', there is a hole in the origin. I know that this is because my code depends on (1/segments) distance between each vertex; I am working with segments = 40.
I know that, if I rise that value, the hole will be lower, but program is slower. I don't know if there's a way to eliminate the hole without rise the variable.
Here's the code:
for(int i = 0; i <= segments; i++){
double lat0 = pi * (-0.5 + (double)(i - 1) / segments);
double z0 = sin(lat0);
double zr0 = cos(lat0);
// lat1 = [-pi/2..pi/2]
double lat1 = pi * (-0.5 + (double)i / segments);
double z1 = sin(lat1);
double zr1 = cos(lat1);
for (int j = 0; j <= segments; j++){ // Longitud
// lng = [0..2*pi]
double lng = 2 * pi * (double)(j - 1) / segments;
double x = cos(lng);
double y = sin(lng);
//glNormal3f(x * zr0, y * zr0, z0); // Normals
ballVerts.push_back(x * zr0); //X
ballVerts.push_back(y * zr0); //Y
ballVerts.push_back(z0); //Z
ballVerts.push_back(0.0f);
ballVerts.push_back(0.0f);
ballVerts.push_back(0.0f);
ballVerts.push_back(1.0f); //R,G,B,A
texX = abs(1 - (0.5f + atan2(z0, x * zr0) / (2.0 * pi)));
texY = 0.5f - asin(y * zr0) / pi;
ballVerts.push_back(texX); // Texture coords
ballVerts.push_back(texY); // U, V
//glNormal3f(x * zr1, y * zr1, z1); //Normals
ballVerts.push_back(x * zr1); //X
ballVerts.push_back(y * zr1); //Y
ballVerts.push_back(z1); //Z
ballVerts.push_back(0.0f);
ballVerts.push_back(0.0f);
ballVerts.push_back(1.0f);
ballVerts.push_back(1.0f); //R,G,B,A
texX = abs(1 - (0.5f + atan2(z1, x * zr1) / (2.0 * pi)));
texY = 0.5f - asin(y * zr1) / pi;
ballVerts.push_back(texX); // Texture coords
ballVerts.push_back(texY);
}
}
// Create VBO....
And this is the output I have:
I don't think that's a hole. You're drawing one segment too many, and causing it to draw additional triangles at the south pole, with the texture wrapped around:
for(int i = 0; i <= segments; i++){
double lat0 = pi * (-0.5 + (double)(i - 1) / segments);
In the first loop iteration, with i = 0, the angle will be less than -0.5 * pi, resulting in the extra triangles shown in your picture.
If you want to split the latitude range into segments pieces, you only need to run through the outer loop segments times. With the code above, with the loop from 0 up to and including segments, you're iterating segments + 1 times.
The easiest way to fix this is to start the loop at 1:
for(int i = 1; i <= segments; i++){
double lat0 = pi * (-0.5 + (double)(i - 1) / segments);
I would probably loop from 0 and make the end exclusive, and change the angle calculations. But that's really equivalent:
for(int i = 0; i < segments; i++){
double lat0 = pi * (-0.5 + (double) / segments);
...
double lat1 = pi * (-0.5 + (double)(i + 1) / segments);
I need to have a hemisphere in opengl. I found a drawSphere function which I modified to draw half the lats (which ends up drawing half of the sphere) which is what I wanted. It does this correctly.
However, I don't know what i should do with glTexCoordf to get the textures to map properly onto this half sphere. I'm really not great with opengl, and I've tried countless variations but I just can't get the textures to appear properly on it.
void drawHemisphere(double r, int lats, int longs)
{
int i, j;
int halfLats = lats / 2;
for(i = 0; i <= halfLats; i++)
{
double lat0 = M_PI * (-0.5 + (double) (i - 1) / lats);
double z0 = sin(lat0);
double zr0 = cos(lat0);
double lat1 = M_PI * (-0.5 + (double) i / lats);
double z1 = sin(lat1);
double zr1 = cos(lat1);
glBegin(GL_QUAD_STRIP);
for(j = 0; j <= longs; j++)
{
double lng = 2 * M_PI * (double) (j - 1) / longs;
double x = cos(lng);
double y = sin(lng);
// glTexCoordf()
glNormal3f(x * zr0, y * zr0, z0);
glVertex3f(x * zr0, y * zr0, z0);
// glTexCoordf()
glNormal3f(x * zr1, y * zr1, z1);
glVertex3f(x * zr1, y * zr1, z1);
}
glEnd();
}
}
Does anyone have any idea of what values I should be putting in? Or what I need to calculate for it?
Thanks!
Basically, you shouldn't need anything fancy there. The texture coordinate space ranges from zero to one. So pick some intermediate values for the vertices in between. I can't explain it more thoroughly without image, so the best I can do is to point You to this article: UV mapping, it's a good starting point. Hope this helps as a starter.
Here's my guess:
{
double lng = 2 * M_PI * (double) (j - 1) / longs;
double x = cos(lng);
double y = sin(lng);
double s1, s2, t;
s1 = ((double) i) / halfLats;
s2 = ((double) i + 1) / halfLats;
t = ((double) j) / longs;
glTexCoord2d(s1, t);
glNormal3d(x * zr0, y * zr0, z0);
glVertex3d(x * zr0, y * zr0, z0);
glTexCoord2d(s2, t);
glNormal3d(x * zr1, y * zr1, z1);
glVertex3d(x * zr1, y * zr1, z1);
}
Remember to properly set texture in OpenGL. An example call with texture:
glActiveTexture(GL_TEXTURE0);
glMatrixMode(GL_TEXTURE);
glLoadIdentity();
glMatrixMode(GL_MODELVIEW);
// texture must be bound & enabled
texture.bind();
texture.enable();
drawHemisphere(1, 40, 40);
texture.disable();
I used Java + JOGL to test it, so it's not one-to-one C++ solution, but conceptually it should be the same. At least You have proper glTexCoord2d() calls.