Related
I am not able to create a simple 3D sphere using the OpenGL library function glutSolidSphere() in C++.
Here's what I tried:
#include<GL/glu.h>
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0,0.0,0.0);
glLoadIdentity();
glutSolidSphere( 5.0, 20.0, 20.0);
glFlush();
}
void myInit()
{
glClearColor(1.0,1.0,1.0,1.0);
glColor3f(1.0,0.0,0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0,499.0,0.0,499.0);
glMatrixMode(GL_MODELVIEW);
}
void main(int argc,char **argv)
{
qobj = gluNewQuadric();
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
glutInitWindowSize(500,500);
glutCreateWindow("pendulum");
glutDisplayFunc(display);
myInit();
glutMainLoop();
}
In OpenGL you don't create objects, you just draw them. Once they are drawn, OpenGL no longer cares about what geometry you sent it.
glutSolidSphere is just sending drawing commands to OpenGL. However there's nothing special in and about it. And since it's tied to GLUT I'd not use it. Instead, if you really need some sphere in your code, how about create if for yourself?
#define _USE_MATH_DEFINES
#include <GL/gl.h>
#include <GL/glu.h>
#include <vector>
#include <cmath>
// your framework of choice here
class SolidSphere
{
protected:
std::vector<GLfloat> vertices;
std::vector<GLfloat> normals;
std::vector<GLfloat> texcoords;
std::vector<GLushort> indices;
public:
SolidSphere(float radius, unsigned int rings, unsigned int sectors)
{
float const R = 1./(float)(rings-1);
float const S = 1./(float)(sectors-1);
int r, s;
vertices.resize(rings * sectors * 3);
normals.resize(rings * sectors * 3);
texcoords.resize(rings * sectors * 2);
std::vector<GLfloat>::iterator v = vertices.begin();
std::vector<GLfloat>::iterator n = normals.begin();
std::vector<GLfloat>::iterator t = texcoords.begin();
for(r = 0; r < rings; r++) for(s = 0; s < sectors; s++) {
float const y = sin( -M_PI_2 + M_PI * r * R );
float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );
float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );
*t++ = s*S;
*t++ = r*R;
*v++ = x * radius;
*v++ = y * radius;
*v++ = z * radius;
*n++ = x;
*n++ = y;
*n++ = z;
}
indices.resize(rings * sectors * 4);
std::vector<GLushort>::iterator i = indices.begin();
for(r = 0; r < rings; r++) for(s = 0; s < sectors; s++) {
*i++ = r * sectors + s;
*i++ = r * sectors + (s+1);
*i++ = (r+1) * sectors + (s+1);
*i++ = (r+1) * sectors + s;
}
}
void draw(GLfloat x, GLfloat y, GLfloat z)
{
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef(x,y,z);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, &vertices[0]);
glNormalPointer(GL_FLOAT, 0, &normals[0]);
glTexCoordPointer(2, GL_FLOAT, 0, &texcoords[0]);
glDrawElements(GL_QUADS, indices.size(), GL_UNSIGNED_SHORT, &indices[0]);
glPopMatrix();
}
};
SolidSphere sphere(1, 12, 24);
void display()
{
int const win_width = …; // retrieve window dimensions from
int const win_height = …; // framework of choice here
float const win_aspect = (float)win_width / (float)win_height;
glViewport(0, 0, win_width, win_height);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, win_aspect, 1, 10);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
#ifdef DRAW_WIREFRAME
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
#endif
sphere.draw(0, 0, -5);
swapBuffers();
}
int main(int argc, char *argv[])
{
// initialize and register your framework of choice here
return 0;
}
It doesn't seem like anyone so far has addressed the actual problem with your original code, so I thought I would do that even though the question is quite old at this point.
The problem originally had to do with the projection in relation to the radius and position of the sphere. I think you'll find that the problem isn't too complicated. The program actually works correctly, it's just that what is being drawn is very hard to see.
First, an orthogonal projection was created using the call
gluOrtho2D(0.0, 499.0, 0.0, 499.0);
which "is equivalent to calling glOrtho with near = -1 and far = 1." This means that the viewing frustum has a depth of 2. So a sphere with a radius of anything greater than 1 (diameter = 2) will not fit entirely within the viewing frustum.
Then the calls
glLoadIdentity();
glutSolidSphere(5.0, 20.0, 20.0);
are used, which loads the identity matrix of the model-view matrix and then "[r]enders a sphere centered at the modeling coordinates origin of the specified radius." Meaning, the sphere is rendered at the origin, (x, y, z) = (0, 0, 0), and with a radius of 5.
Now, the issue is three-fold:
Since the window is 500x500 pixels and the width and height of the viewing frustum is almost 500 (499.0), the small radius of the sphere (5.0) makes its projected area only slightly over one fiftieth (2*5/499) of the size of the window in each dimension. This means that the apparent size of the sphere would be roughly 1/2,500th (actually pi*5^2/499^2, which is closer to about 1/3170th) of the entire window, so it might be difficult to see. This is assuming the entire circle is drawn within the area of the window. It is not, however, as we will see in point 2.
Since the viewing frustum has it's left plane at x = 0 and bottom plane at y = 0, the sphere will be rendered with its geometric center in the very bottom left corner of the window so that only one quadrant of the projected sphere will be visible! This means that what would be seen is even smaller, about 1/10,000th (actually pi*5^2/(4*499^2), which is closer to 1/12,682nd) of the window size. This would make it even more difficult to see. Especially since the sphere is rendered so close to the edges/corner of the screen where you might not think to look.
Since the depth of the viewing frustum is significantly smaller than the diameter of the sphere (less than half), only a sliver of the sphere will be within the viewing frustum, rendering only that part. So you will get more like a hollow circle on the screen than a solid sphere/circle. As it happens, the thickness of that sliver might represent less than 1 pixel on the screen which means we might even see nothing on the screen, even if part of the sphere is indeed within the viewing frustum.
The solution is simply to change the viewing frustum and radius of the sphere. For instance,
gluOrtho2D(-5.0, 5.0, -5.0, 5.0);
glutSolidSphere(5.0, 20, 20);
renders the following image.
As you can see, only a small part is visible around the "equator", of the sphere with a radius of 5. (I changed the projection to fill the window with the sphere.) Another example,
gluOrtho2D(-1.1, 1.1, -1.1, 1.1);
glutSolidSphere(1.1, 20, 20);
renders the following image.
The image above shows more of the sphere inside of the viewing frustum, but still the sphere is 0.2 depth units larger than the viewing frustum. As you can see, the "ice caps" of the sphere are missing, both the north and the south. So, if we want the entire sphere to fit within the viewing frustum which has depth 2, we must make the radius less than or equal to 1.
gluOrtho2D(-1.0, 1.0, -1.0, 1.0);
glutSolidSphere(1.0, 20, 20);
renders the following image.
I hope this has helped someone. Take care!
I don't understand how can datenwolf`s index generation can be correct. But still I find his solution rather clear. This is what I get after some thinking:
inline void push_indices(vector<GLushort>& indices, int sectors, int r, int s) {
int curRow = r * sectors;
int nextRow = (r+1) * sectors;
indices.push_back(curRow + s);
indices.push_back(nextRow + s);
indices.push_back(nextRow + (s+1));
indices.push_back(curRow + s);
indices.push_back(nextRow + (s+1));
indices.push_back(curRow + (s+1));
}
void createSphere(vector<vec3>& vertices, vector<GLushort>& indices, vector<vec2>& texcoords,
float radius, unsigned int rings, unsigned int sectors)
{
float const R = 1./(float)(rings-1);
float const S = 1./(float)(sectors-1);
for(int r = 0; r < rings; ++r) {
for(int s = 0; s < sectors; ++s) {
float const y = sin( -M_PI_2 + M_PI * r * R );
float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );
float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );
texcoords.push_back(vec2(s*S, r*R));
vertices.push_back(vec3(x,y,z) * radius);
push_indices(indices, sectors, r, s);
}
}
}
Here's the code:
glPushMatrix();
glTranslatef(18,2,0);
glRotatef(angle, 0, 0, 0.7);
glColor3ub(0,255,255);
glutWireSphere(3,10,10);
glPopMatrix();
I like the answer of coin. It's simple to understand and works with triangles. However the indexes of his program are sometimes over the bounds. So I post here his code with two tiny corrections:
inline void push_indices(vector<GLushort>& indices, int sectors, int r, int s) {
int curRow = r * sectors;
int nextRow = (r+1) * sectors;
int nextS = (s+1) % sectors;
indices.push_back(curRow + s);
indices.push_back(nextRow + s);
indices.push_back(nextRow + nextS);
indices.push_back(curRow + s);
indices.push_back(nextRow + nextS);
indices.push_back(curRow + nextS);
}
void createSphere(vector<vec3>& vertices, vector<GLushort>& indices, vector<vec2>& texcoords,
float radius, unsigned int rings, unsigned int sectors)
{
float const R = 1./(float)(rings-1);
float const S = 1./(float)(sectors-1);
for(int r = 0; r < rings; ++r) {
for(int s = 0; s < sectors; ++s) {
float const y = sin( -M_PI_2 + M_PI * r * R );
float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );
float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );
texcoords.push_back(vec2(s*S, r*R));
vertices.push_back(vec3(x,y,z) * radius);
if(r < rings-1)
push_indices(indices, sectors, r, s);
}
}
}
Datanewolf's code is ALMOST right. I had to reverse both the winding and the normals to make it work properly with the fixed pipeline. The below works correctly with cull on or off for me:
std::vector<GLfloat> vertices;
std::vector<GLfloat> normals;
std::vector<GLfloat> texcoords;
std::vector<GLushort> indices;
float const R = 1./(float)(rings-1);
float const S = 1./(float)(sectors-1);
int r, s;
vertices.resize(rings * sectors * 3);
normals.resize(rings * sectors * 3);
texcoords.resize(rings * sectors * 2);
std::vector<GLfloat>::iterator v = vertices.begin();
std::vector<GLfloat>::iterator n = normals.begin();
std::vector<GLfloat>::iterator t = texcoords.begin();
for(r = 0; r < rings; r++) for(s = 0; s < sectors; s++) {
float const y = sin( -M_PI_2 + M_PI * r * R );
float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );
float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );
*t++ = s*S;
*t++ = r*R;
*v++ = x * radius;
*v++ = y * radius;
*v++ = z * radius;
*n++ = -x;
*n++ = -y;
*n++ = -z;
}
indices.resize(rings * sectors * 4);
std::vector<GLushort>::iterator i = indices.begin();
for(r = 0; r < rings-1; r++)
for(s = 0; s < sectors-1; s++) {
/*
*i++ = r * sectors + s;
*i++ = r * sectors + (s+1);
*i++ = (r+1) * sectors + (s+1);
*i++ = (r+1) * sectors + s;
*/
*i++ = (r+1) * sectors + s;
*i++ = (r+1) * sectors + (s+1);
*i++ = r * sectors + (s+1);
*i++ = r * sectors + s;
}
Edit: There was a question on how to draw this... in my code I encapsulate these values in a G3DModel class. This is my code to setup the frame, draw the model, and end it:
void GraphicsProvider3DPriv::BeginFrame()const{
int win_width;
int win_height;// framework of choice here
glfwGetWindowSize(window, &win_width, &win_height); // retrieve window
float const win_aspect = (float)win_width / (float)win_height;
// set lighting
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
GLfloat lightpos[] = {0, 0.0, 0, 0.};
glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
GLfloat lmodel_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
// set up world transform
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT|GL_ACCUM_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, win_aspect, 1, 10);
glMatrixMode(GL_MODELVIEW);
}
void GraphicsProvider3DPriv::DrawModel(const G3DModel* model, const Transform3D transform)const{
G3DModelPriv* privModel = (G3DModelPriv *)model;
glPushMatrix();
glLoadMatrixf(transform.GetOGLData());
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, &privModel->vertices[0]);
glNormalPointer(GL_FLOAT, 0, &privModel->normals[0]);
glTexCoordPointer(2, GL_FLOAT, 0, &privModel->texcoords[0]);
glEnable(GL_TEXTURE_2D);
//glFrontFace(GL_CCW);
glEnable(GL_CULL_FACE);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, privModel->texname);
glDrawElements(GL_QUADS, privModel->indices.size(), GL_UNSIGNED_SHORT, &privModel->indices[0]);
glPopMatrix();
glDisable(GL_TEXTURE_2D);
}
void GraphicsProvider3DPriv::EndFrame()const{
/* Swap front and back buffers */
glDisable(GL_LIGHTING);
glDisable(GL_LIGHT0);
glDisable(GL_CULL_FACE);
glfwSwapBuffers(window);
/* Poll for and process events */
glfwPollEvents();
}
I am trying to make a simple 4x4 matrix class.
The data (float) is a single dimension array, and I use this code to store numbers as if it were a grid.
const inline int ind1(short x, short y) { // Convert coords to spot on linear array, uses SIZE
return x * (SIZE >> 2) + y;
}
This part is in the .h file
float *data;
These are in the .cpp
Mat::Mat() {
define();
diagDefine(1.0f);
}
void Mat::define() {
data = new float[SIZE];
for (int x = 0; x < SIZE >> 2; x++) {
for (int y = 0; y < SIZE >> 2; y++) {
data[ind1(x, y)] = 0;
}
}
}
void Mat::diagDefine(float nval) {
data[ind1(0, 0)] = nval;
data[ind1(1, 1)] = nval;
data[ind1(2, 2)] = nval;
data[ind1(3, 3)] = nval;
}
The problem is that when I try to multiply the matrix to my position in the vertex shader, the triangle or whatever I am drawing disappears.
My class has orthographic, perspective, translation, rotation, and scaling.
Mat Mat::getOrthographic(float left, float right, float top, float bottom, float near, float far) {
Mat newmat;
newmat.data[ind1(0, 0)] = 2.0f / (right - left);
newmat.data[ind1(1, 1)] = 2.0f / (top - bottom);
newmat.data[ind1(2, 2)] = 2.0f / (near - far);
newmat.data[ind1(0, 3)] = (left + right) / (left - right);
newmat.data[ind1(1, 3)] = (bottom + top) / (bottom - top);
newmat.data[ind1(2, 3)] = (far + near) / (far - near);
return newmat;
}
Mat Mat::getPerspective(float fov, float aspectratio, float near, float far) {
Mat newmat;
newmat.data[ind1(0, 0)] = (1.0f / tan((0.5f * fov) * (3.141519 / 180.0f))) / aspectratio;
newmat.data[ind1(1, 1)] = 1.0f / tan((0.5f * fov) * (3.141519 / 180.0f));
newmat.data[ind1(2, 2)] = (near + far) / (near - far);
newmat.data[ind1(3, 2)] = -1.0f;
newmat.data[ind1(2, 3)] = (2.0f * near * far) / (near - far);
return newmat;
}
Mat Mat::getTranslation(Vec3f &vec) {
Mat newmat;
newmat.data[ind1(0, 3)] = vec.x;
newmat.data[ind1(1, 3)] = vec.y;
newmat.data[ind1(2, 3)] = vec.z;
return newmat;
}
Mat Mat::getRotation(double angle, Vec3f &vec) {
Mat newmat;
float s = sin(angle);
float c = cos(angle);
newmat.data[ind1(0, 0)] = vec.x * (1.0f - c) + c;
newmat.data[ind1(1, 0)] = vec.y * vec.x * (1.0f - c) + vec.z * s;
newmat.data[ind1(2, 0)] = vec.x * vec.z * (1.0f - c) - vec.y * s;
newmat.data[ind1(0, 1)] = vec.x * vec.y * (1.0f - c) - vec.z * s;
newmat.data[ind1(1, 1)] = vec.y * (1.0f - c) + c;
newmat.data[ind1(2, 1)] = vec.y * vec.z * (1.0f - c) + vec.x * s;
newmat.data[ind1(0, 2)] = vec.x * vec.z * (1.0f - c) + vec.y * s;
newmat.data[ind1(1, 2)] = vec.y * vec.z * (1.0f - c) - vec.x * s;
newmat.data[ind1(2, 2)] = vec.z * (1.0f - c) + c;
return newmat;
}
Mat Mat::getScale(Vec3f &vec) {
Mat newmat;
newmat.data[ind1(0, 0)] = vec.x;
newmat.data[ind1(1, 1)] = vec.y;
newmat.data[ind1(2, 2)] = vec.z;
return newmat;
}
Vertex code
#version 330
layout(location = 0) in vec3 pos;
uniform mat4 view_mat;
void main() {
gl_Position = view_mat * vec4(pos, 1.0);
}
Finally, here is how I send the data to the shader.
// In the matrix file
float *getRawDataAsArray() { return data; }
// In the shader.h file
void Shader::GL_SET_UNIFORM_MATRIX(const char *name, Mat matrix) {
GLint location = glGetUniformLocation(program, name);
if(location != -1) {
glUniformMatrix4x2fv(location, 1, GL_FALSE, matrix.getRawDataAsArray());
}
}
// In the main.cpp (sh is shader object that contained the GET_UNIFORM_MATRIX
sh.GL_SET_UNIFORM_MATRIX("view_mat", sod2::Mat::getRotation(3.141519 / 2, 0, 0, 1));
sh.GL_SET_UNIFORM_4f("color", 0.0, 1.0, 0.0, 1.0);
Final note: My shaders do compile perfectly. When I run it without anything to do with matrices it works perfectly. (Dealing with color or modifying position).
Thanks
There are 2 issues in your code:
You use the wrong glUniform* function to set the view_mat uniform in your function Shader::GL_SET_UNIFORM_MATRIX. While glUniformMatrix4fv commits 16 floats for a 4*4* matrix, glUniformMatrix4x2fv commits 8 floats for a 4*2 matrix. See glUniform.
Further See The OpenGL Shading Language 4.6, 5.4.2 Vector and Matrix Constructors, page 101:
To initialize a matrix by specifying vectors or scalars, the components are assigned to the matrix elements in column-major order.
mat4(float, float, float, float, // first column
float, float, float, float, // second column
float, float, float, float, // third column
float, float, float, float); // fourth column
But your matrix is set up in row-major order:
const inline int ind1(short x, short y) {
return x * (SIZE >> 2) + y;
}
Either the ind1 function has to be changed to fix this issue:
const inline int ind1(short x, short y) {
return y * (SIZE >> 2) + x;
}
Or the matrix has to be transposed, when it is set to the uniform variable:
glUniformMatrix4fv(
location,
1,
GL_TRUE, // <----------------- transpose
matrix.getRawDataAsArray());
Or the vector has to be multiplied to the matrix from the left:
gl_Position = vec4(pos, 1.0) * view_mat;
I have an object that contains my view state, and I wish to be able to convert from world to screen coordinates, which works. However I also want to be able to convert from screen to world coordinates, I have the following code:
#include <glm/glm.hpp>
#include <gmock/gmock.h>
class CoordinateSpace
{
public:
CoordinateSpace(int w, int h)
{
mW = w;
mH = h;
}
glm::vec2 WorldToScreen(const glm::vec2& worldPos)
{
return ((mProjection * mView) * glm::vec4(worldPos, 1, 1)) * glm::vec4(mW / 2, -mH / 2, 1, 1) + glm::vec4(mW / 2, mH / 2, 0, 0);
}
glm::vec2 ScreenToWorld(const glm::vec2& screenPos)
{
return (glm::inverse(mProjection * mView) * glm::vec4(screenPos, 1, 1)) * glm::vec4(mW / 2, -mH / 2, 1, 1) + glm::vec4(mW / 2, mH / 2, 0, 0);
}
void UpdateCamera()
{
glm::mat4 target_projection = glm::ortho(
-mScreenSize.x / 2.0f,
mScreenSize.x / 2.0f,
mScreenSize.y / 2.0f,
-mScreenSize.y / 2.0f,
-1.0f,
1.0f);
glm::mat4 camMat = glm::translate(glm::mat4(1.0f), glm::vec3(-mCameraPosition, 0));
mView = camMat;
mProjection = target_projection;
}
glm::vec2 mScreenSize = glm::vec2;
glm::vec2 mCameraPosition = glm::vec2;
protected:
int mW = 0;
int mH = 0;
// 2d ortho projection
glm::mat4 mProjection;
// camera location into the world
glm::mat4 mView;
};
TEST(CoordinateSpace, Conversion)
{
CoordinateSpace coords(640, 480);
coords.mCameraPosition = { 0.0f, 0.0f };
coords.mScreenSize = { 640.0f, 480.0f };
coords.UpdateCamera();
const glm::vec2 actual1 = coords.WorldToScreen({ 50.0f, 100.0f });
ASSERT_EQ(glm::round((640.0f/2)+50.0f), glm::round(actual1.x));
ASSERT_EQ(glm::round((480.0f/2)+100.0f), glm::round(actual1.y));
const glm::vec2 actual2 = coords.ScreenToWorld(actual1);
ASSERT_EQ(glm::round(50.0f), glm::round(actual2.x));
ASSERT_EQ(glm::round(100.0f), glm::round(actual2.y));
}
Instead of 50.0f I get 5.12032e+06, how do I correctly calculate ScreenToWorld?
Given that you have:
SCREENPOS = ((PROJ * VIEW) * WORLDPOS) * A + B;
Using algebra to isolate WORLDPOS, I'd presume this would work:
WORLDPOS = ((SCREENPOS - B) / A) * INV(PROJ*VIEW)
SO:
return glm::inverse(mProjection * mView) * ((screenPos - glm::vec4(mW / 2, mH / 2, 0, 0)) / glm::vec4(mW / 2, -mH / 2, 1, 1));
I can already rotate point sprite on 0, 90, 180, 270 degrees
Fragment shader
precision lowp float;
uniform sampler2D us_tex;
uniform mat3 um_tex;
void main ()
{
vec2 tex_coords = (um_tex * vec3(gl_PointCoord, 1.0)).xy;
gl_FragColor = texture2D(us_tex, tex_coords);
}
2*2 Matrix operations (i know about GLM - it's great, academic purpose to handle matrix on your own)
typedef GLfloat m3[9]//3*3 matrix
#define DEG_TO_RAD(x) (x * M_PI/180.0f)
void ident_m3(m3 res)
{
memset(res, 0, sizeof(m3));
res[0] = res[4] = res[8] = 1.0f;
}
void trans_m3(m3 res, const p2* pos)
{
ident_m3(res);
res[7] = pos->x;
res[8] = pos->y;
}
void mult_m3(m3 res, const m3 m1, const m3 m2)
{
res[0] = m1[0] * m2[0] + m1[3] * m2[1] + m1[6] * m2[2];
res[1] = m1[1] * m2[0] + m1[4] * m2[1] + m1[7] * m2[2];
res[2] = m1[2] * m2[0] + m1[5] * m2[1] + m1[8] * m2[2];
res[3] = m1[0] * m2[3] + m1[3] * m2[4] + m1[6] * m2[5];
res[4] = m1[1] * m2[3] + m1[4] * m2[4] + m1[7] * m2[5];
res[5] = m1[2] * m2[3] + m1[5] * m2[4] + m1[8] * m2[5];
res[6] = m1[0] * m2[6] + m1[3] * m2[7] + m1[6] * m2[8];
res[7] = m1[1] * m2[6] + m1[4] * m2[7] + m1[7] * m2[8];
res[8] = m1[2] * m2[6] + m1[5] * m2[7] + m1[8] * m2[8];
}
in ParticlesDraw()
m3 r;
rot_m3(r, 90.0f);
...
glUniformMatrix3fv(/*um_tex uniform*/, 1, GL_FALSE, res);
glDrawArrays(GL_POINTS, 0, /*particles count*/);
...
Also i know how rotate ordinary sprite around pos(x,y,z)
Translate to pos(-x,-y,-z)
Rotate
Translate to pos(x,y,z)
Result Matrix = (Rot Matrix * Translate Matrix) * Anti-Traslate Matrix.
I want to rotate point sprite to 45, 32,64,72 e.g any degree (now it rotates not right, last frame 45 deg)
But in this case, i can translate to center of tex (0.5, 0.5), but what will be anti translate - (0.0, 0.0)?
I try something like this, but it does not work for example for 30, 45 rotation, also if my texture is 64*64, do i need to set gl_PointSize to 64.0 for rotation?
This:
Translate to pos(-x,-y,-z)
Rotate
Translate to pos(x,y,z)
Is not the same thing as this:
Result Matrix = (Rot Matrix * Translate Matrix) * Anti-Traslate Matrix.
If you wish to rotate around the point (x,y,z), then you need to do this:
Matrix T1 = Translate(x, y, z);
Matrix R1 = Rotate();
Matrix T2 = Translate(-x, -y, -z);
Which is the same thing as:
Result Matrix = T1 * R1 * T2
I am not able to create a simple 3D sphere using the OpenGL library function glutSolidSphere() in C++.
Here's what I tried:
#include<GL/glu.h>
void display()
{
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0,0.0,0.0);
glLoadIdentity();
glutSolidSphere( 5.0, 20.0, 20.0);
glFlush();
}
void myInit()
{
glClearColor(1.0,1.0,1.0,1.0);
glColor3f(1.0,0.0,0.0);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluOrtho2D(0.0,499.0,0.0,499.0);
glMatrixMode(GL_MODELVIEW);
}
void main(int argc,char **argv)
{
qobj = gluNewQuadric();
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
glutInitWindowSize(500,500);
glutCreateWindow("pendulum");
glutDisplayFunc(display);
myInit();
glutMainLoop();
}
In OpenGL you don't create objects, you just draw them. Once they are drawn, OpenGL no longer cares about what geometry you sent it.
glutSolidSphere is just sending drawing commands to OpenGL. However there's nothing special in and about it. And since it's tied to GLUT I'd not use it. Instead, if you really need some sphere in your code, how about create if for yourself?
#define _USE_MATH_DEFINES
#include <GL/gl.h>
#include <GL/glu.h>
#include <vector>
#include <cmath>
// your framework of choice here
class SolidSphere
{
protected:
std::vector<GLfloat> vertices;
std::vector<GLfloat> normals;
std::vector<GLfloat> texcoords;
std::vector<GLushort> indices;
public:
SolidSphere(float radius, unsigned int rings, unsigned int sectors)
{
float const R = 1./(float)(rings-1);
float const S = 1./(float)(sectors-1);
int r, s;
vertices.resize(rings * sectors * 3);
normals.resize(rings * sectors * 3);
texcoords.resize(rings * sectors * 2);
std::vector<GLfloat>::iterator v = vertices.begin();
std::vector<GLfloat>::iterator n = normals.begin();
std::vector<GLfloat>::iterator t = texcoords.begin();
for(r = 0; r < rings; r++) for(s = 0; s < sectors; s++) {
float const y = sin( -M_PI_2 + M_PI * r * R );
float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );
float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );
*t++ = s*S;
*t++ = r*R;
*v++ = x * radius;
*v++ = y * radius;
*v++ = z * radius;
*n++ = x;
*n++ = y;
*n++ = z;
}
indices.resize(rings * sectors * 4);
std::vector<GLushort>::iterator i = indices.begin();
for(r = 0; r < rings; r++) for(s = 0; s < sectors; s++) {
*i++ = r * sectors + s;
*i++ = r * sectors + (s+1);
*i++ = (r+1) * sectors + (s+1);
*i++ = (r+1) * sectors + s;
}
}
void draw(GLfloat x, GLfloat y, GLfloat z)
{
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef(x,y,z);
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, &vertices[0]);
glNormalPointer(GL_FLOAT, 0, &normals[0]);
glTexCoordPointer(2, GL_FLOAT, 0, &texcoords[0]);
glDrawElements(GL_QUADS, indices.size(), GL_UNSIGNED_SHORT, &indices[0]);
glPopMatrix();
}
};
SolidSphere sphere(1, 12, 24);
void display()
{
int const win_width = …; // retrieve window dimensions from
int const win_height = …; // framework of choice here
float const win_aspect = (float)win_width / (float)win_height;
glViewport(0, 0, win_width, win_height);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, win_aspect, 1, 10);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
#ifdef DRAW_WIREFRAME
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
#endif
sphere.draw(0, 0, -5);
swapBuffers();
}
int main(int argc, char *argv[])
{
// initialize and register your framework of choice here
return 0;
}
It doesn't seem like anyone so far has addressed the actual problem with your original code, so I thought I would do that even though the question is quite old at this point.
The problem originally had to do with the projection in relation to the radius and position of the sphere. I think you'll find that the problem isn't too complicated. The program actually works correctly, it's just that what is being drawn is very hard to see.
First, an orthogonal projection was created using the call
gluOrtho2D(0.0, 499.0, 0.0, 499.0);
which "is equivalent to calling glOrtho with near = -1 and far = 1." This means that the viewing frustum has a depth of 2. So a sphere with a radius of anything greater than 1 (diameter = 2) will not fit entirely within the viewing frustum.
Then the calls
glLoadIdentity();
glutSolidSphere(5.0, 20.0, 20.0);
are used, which loads the identity matrix of the model-view matrix and then "[r]enders a sphere centered at the modeling coordinates origin of the specified radius." Meaning, the sphere is rendered at the origin, (x, y, z) = (0, 0, 0), and with a radius of 5.
Now, the issue is three-fold:
Since the window is 500x500 pixels and the width and height of the viewing frustum is almost 500 (499.0), the small radius of the sphere (5.0) makes its projected area only slightly over one fiftieth (2*5/499) of the size of the window in each dimension. This means that the apparent size of the sphere would be roughly 1/2,500th (actually pi*5^2/499^2, which is closer to about 1/3170th) of the entire window, so it might be difficult to see. This is assuming the entire circle is drawn within the area of the window. It is not, however, as we will see in point 2.
Since the viewing frustum has it's left plane at x = 0 and bottom plane at y = 0, the sphere will be rendered with its geometric center in the very bottom left corner of the window so that only one quadrant of the projected sphere will be visible! This means that what would be seen is even smaller, about 1/10,000th (actually pi*5^2/(4*499^2), which is closer to 1/12,682nd) of the window size. This would make it even more difficult to see. Especially since the sphere is rendered so close to the edges/corner of the screen where you might not think to look.
Since the depth of the viewing frustum is significantly smaller than the diameter of the sphere (less than half), only a sliver of the sphere will be within the viewing frustum, rendering only that part. So you will get more like a hollow circle on the screen than a solid sphere/circle. As it happens, the thickness of that sliver might represent less than 1 pixel on the screen which means we might even see nothing on the screen, even if part of the sphere is indeed within the viewing frustum.
The solution is simply to change the viewing frustum and radius of the sphere. For instance,
gluOrtho2D(-5.0, 5.0, -5.0, 5.0);
glutSolidSphere(5.0, 20, 20);
renders the following image.
As you can see, only a small part is visible around the "equator", of the sphere with a radius of 5. (I changed the projection to fill the window with the sphere.) Another example,
gluOrtho2D(-1.1, 1.1, -1.1, 1.1);
glutSolidSphere(1.1, 20, 20);
renders the following image.
The image above shows more of the sphere inside of the viewing frustum, but still the sphere is 0.2 depth units larger than the viewing frustum. As you can see, the "ice caps" of the sphere are missing, both the north and the south. So, if we want the entire sphere to fit within the viewing frustum which has depth 2, we must make the radius less than or equal to 1.
gluOrtho2D(-1.0, 1.0, -1.0, 1.0);
glutSolidSphere(1.0, 20, 20);
renders the following image.
I hope this has helped someone. Take care!
I don't understand how can datenwolf`s index generation can be correct. But still I find his solution rather clear. This is what I get after some thinking:
inline void push_indices(vector<GLushort>& indices, int sectors, int r, int s) {
int curRow = r * sectors;
int nextRow = (r+1) * sectors;
indices.push_back(curRow + s);
indices.push_back(nextRow + s);
indices.push_back(nextRow + (s+1));
indices.push_back(curRow + s);
indices.push_back(nextRow + (s+1));
indices.push_back(curRow + (s+1));
}
void createSphere(vector<vec3>& vertices, vector<GLushort>& indices, vector<vec2>& texcoords,
float radius, unsigned int rings, unsigned int sectors)
{
float const R = 1./(float)(rings-1);
float const S = 1./(float)(sectors-1);
for(int r = 0; r < rings; ++r) {
for(int s = 0; s < sectors; ++s) {
float const y = sin( -M_PI_2 + M_PI * r * R );
float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );
float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );
texcoords.push_back(vec2(s*S, r*R));
vertices.push_back(vec3(x,y,z) * radius);
push_indices(indices, sectors, r, s);
}
}
}
Here's the code:
glPushMatrix();
glTranslatef(18,2,0);
glRotatef(angle, 0, 0, 0.7);
glColor3ub(0,255,255);
glutWireSphere(3,10,10);
glPopMatrix();
I like the answer of coin. It's simple to understand and works with triangles. However the indexes of his program are sometimes over the bounds. So I post here his code with two tiny corrections:
inline void push_indices(vector<GLushort>& indices, int sectors, int r, int s) {
int curRow = r * sectors;
int nextRow = (r+1) * sectors;
int nextS = (s+1) % sectors;
indices.push_back(curRow + s);
indices.push_back(nextRow + s);
indices.push_back(nextRow + nextS);
indices.push_back(curRow + s);
indices.push_back(nextRow + nextS);
indices.push_back(curRow + nextS);
}
void createSphere(vector<vec3>& vertices, vector<GLushort>& indices, vector<vec2>& texcoords,
float radius, unsigned int rings, unsigned int sectors)
{
float const R = 1./(float)(rings-1);
float const S = 1./(float)(sectors-1);
for(int r = 0; r < rings; ++r) {
for(int s = 0; s < sectors; ++s) {
float const y = sin( -M_PI_2 + M_PI * r * R );
float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );
float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );
texcoords.push_back(vec2(s*S, r*R));
vertices.push_back(vec3(x,y,z) * radius);
if(r < rings-1)
push_indices(indices, sectors, r, s);
}
}
}
Datanewolf's code is ALMOST right. I had to reverse both the winding and the normals to make it work properly with the fixed pipeline. The below works correctly with cull on or off for me:
std::vector<GLfloat> vertices;
std::vector<GLfloat> normals;
std::vector<GLfloat> texcoords;
std::vector<GLushort> indices;
float const R = 1./(float)(rings-1);
float const S = 1./(float)(sectors-1);
int r, s;
vertices.resize(rings * sectors * 3);
normals.resize(rings * sectors * 3);
texcoords.resize(rings * sectors * 2);
std::vector<GLfloat>::iterator v = vertices.begin();
std::vector<GLfloat>::iterator n = normals.begin();
std::vector<GLfloat>::iterator t = texcoords.begin();
for(r = 0; r < rings; r++) for(s = 0; s < sectors; s++) {
float const y = sin( -M_PI_2 + M_PI * r * R );
float const x = cos(2*M_PI * s * S) * sin( M_PI * r * R );
float const z = sin(2*M_PI * s * S) * sin( M_PI * r * R );
*t++ = s*S;
*t++ = r*R;
*v++ = x * radius;
*v++ = y * radius;
*v++ = z * radius;
*n++ = -x;
*n++ = -y;
*n++ = -z;
}
indices.resize(rings * sectors * 4);
std::vector<GLushort>::iterator i = indices.begin();
for(r = 0; r < rings-1; r++)
for(s = 0; s < sectors-1; s++) {
/*
*i++ = r * sectors + s;
*i++ = r * sectors + (s+1);
*i++ = (r+1) * sectors + (s+1);
*i++ = (r+1) * sectors + s;
*/
*i++ = (r+1) * sectors + s;
*i++ = (r+1) * sectors + (s+1);
*i++ = r * sectors + (s+1);
*i++ = r * sectors + s;
}
Edit: There was a question on how to draw this... in my code I encapsulate these values in a G3DModel class. This is my code to setup the frame, draw the model, and end it:
void GraphicsProvider3DPriv::BeginFrame()const{
int win_width;
int win_height;// framework of choice here
glfwGetWindowSize(window, &win_width, &win_height); // retrieve window
float const win_aspect = (float)win_width / (float)win_height;
// set lighting
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glEnable(GL_DEPTH_TEST);
GLfloat lightpos[] = {0, 0.0, 0, 0.};
glLightfv(GL_LIGHT0, GL_POSITION, lightpos);
GLfloat lmodel_ambient[] = { 0.2, 0.2, 0.2, 1.0 };
glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient);
glLightModeli(GL_LIGHT_MODEL_TWO_SIDE, GL_TRUE);
// set up world transform
glClearColor(0.f, 0.f, 0.f, 1.f);
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT|GL_ACCUM_BUFFER_BIT);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45, win_aspect, 1, 10);
glMatrixMode(GL_MODELVIEW);
}
void GraphicsProvider3DPriv::DrawModel(const G3DModel* model, const Transform3D transform)const{
G3DModelPriv* privModel = (G3DModelPriv *)model;
glPushMatrix();
glLoadMatrixf(transform.GetOGLData());
glEnableClientState(GL_VERTEX_ARRAY);
glEnableClientState(GL_NORMAL_ARRAY);
glEnableClientState(GL_TEXTURE_COORD_ARRAY);
glVertexPointer(3, GL_FLOAT, 0, &privModel->vertices[0]);
glNormalPointer(GL_FLOAT, 0, &privModel->normals[0]);
glTexCoordPointer(2, GL_FLOAT, 0, &privModel->texcoords[0]);
glEnable(GL_TEXTURE_2D);
//glFrontFace(GL_CCW);
glEnable(GL_CULL_FACE);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, privModel->texname);
glDrawElements(GL_QUADS, privModel->indices.size(), GL_UNSIGNED_SHORT, &privModel->indices[0]);
glPopMatrix();
glDisable(GL_TEXTURE_2D);
}
void GraphicsProvider3DPriv::EndFrame()const{
/* Swap front and back buffers */
glDisable(GL_LIGHTING);
glDisable(GL_LIGHT0);
glDisable(GL_CULL_FACE);
glfwSwapBuffers(window);
/* Poll for and process events */
glfwPollEvents();
}