PDA

View Full Version : Modelling the earth



Mukund
12-01-2010, 02:00 PM
Hello,

i am trying to model the Earth. i am using a .bmp file that consists of the image of the earth, which i am applying onto the
surface of the sphere.

i tried two methods:

1) Just plot each vertex on the sphere using the pixel values.
X_SIZE, Y_SIZE are the width and height of the image.


double x, y, z;
int i = 0, j = 0;

for (double theta = 0; theta <= 360.0; theta += (360.0 / X_SIZE)) {
for (double phi = -90; phi <= 90.0; phi += (180.0 / Y_SIZE)) {
x = sin((3.14 / 180) * theta) * cos((3.14 / 180) * phi);
y = cos((3.14 / 180) * theta) * cos((3.14 / 180) * phi);
z = sin((3.14 / 180) * phi);

glColor3d(buff1[j+2] / 255.0, buff1[j+1] / 255.0, buff1[j+2] / 255.0);
// buff has the raw pixel data
glPointSize(2);
glBegin(GL_POINTS);
glVertex3d(x, y, z);
glEnd();
}
}



2) A texture based method. The sphere is approximated using triangles. i found this on the internet.

TWOPI = 2 * 3.14 and PID2 = 3.14 / 2


// some texture initializations here.
gluBuild2DMipmaps( GL_TEXTURE_2D, 3, width, height,
GL_BGR_EXT, GL_UNSIGNED_BYTE, buff );

// buff has the raw pixel data

int i,j;
double theta1,theta2,theta3;
XYZ e,p;

/* XYZ is a tydefed struct:
typedef struct {
double x;
double y;
double z;
}
*/


for (j=0;j<n/2;j++) {
theta1 = j * TWOPI / n - PID2;
theta2 = (j + 1) * TWOPI / n - PID2;

glBegin(GL_QUAD_STRIP);
for (i=0;i<=n;i++) {
theta3 = i * TWOPI / n;

e.x = cos(theta2) * cos(theta3);
e.y = sin(theta2);
e.z = cos(theta2) * sin(theta3);
p.x = c.x + r * e.x;
p.y = c.y + r * e.y;
p.z = c.z + r * e.z;

glNormal3f(e.x,e.y,e.z);
glTexCoord2f(i/(double)n,2*(j+1)/(double)n);
glVertex3f(p.x,p.y,p.z);

e.x = cos(theta1) * cos(theta3);
e.y = sin(theta1);
e.z = cos(theta1) * sin(theta3);
p.x = c.x + r * e.x;
p.y = c.y + r * e.y;
p.z = c.z + r * e.z;

glNormal3f(e.x,e.y,e.z);
glTexCoord2f(i/(double)n,2*j/(double)n);
glVertex3f(p.x,p.y,p.z);
}
glEnd();
}



Now, i find both methods the same as far as the performance is concerned. But, shouldn't the texture based method be more efficient?

Can anyone please comment as to which method is better and why isn't there any observable difference in the performance?

Aleksandar
12-01-2010, 02:10 PM
It is quite obvious why both methods are slow.
You have a plenty of trigonometric functions in each vertex drawing, and also to many function calls (using immediate mode drawing). My advice is to calculate vertex-positions just once, and store them into VBO (or VA).

Reading the title, I thought you were about to render real model of Earth. But I was wrong. Believe me, rendering Earth is much much complicated than drawing a sphere with the texture. :)

Mukund
12-01-2010, 02:19 PM
Thanks for the reply Aleksandar. Hmm, i did not quite get the second part of the reply. What did you mean by real model of the earth? Did you mean the entire earth with topography data (as in mountains etc)?

Aleksandar
12-01-2010, 03:32 PM
Yes, I meant something like Google Earth. :)
In that case there are so many problems to be solved.

ugluk
12-01-2010, 07:58 PM
Ah, why don't you just import some model with texture coordinates already present, so you won't need to calculate them. It'll be faster I bet.

Mukund
12-02-2010, 12:25 AM
@Aleksandar

Yeah that would be pretty complicated. :)

@ugluk

Nice idea, well instead of searching for one model, i though i can create a file myself having those coordinates, then read them in the program and display, rather than calculating the coordinates each time.

Nice idea, i will try it out.

Mukund
12-02-2010, 01:28 AM
@ugluk

i tried the method suggested by you.
This is what i did:

File that has the pre-calculated data:


void CreateSphereFile(XYZ c,double r,int n)
{
FILE *fp = fopen("file_data", "wb");
int i,j;
double theta1,theta2,theta3;
XYZ e,p;


for (j=0;j<n/2;j++) {
theta1 = j * TWOPI / n - PID2;
theta2 = (j + 1) * TWOPI / n - PID2;

// glBegin(GL_QUAD_STRIP);
for (i=0;i<=n;i++) {
theta3 = i * TWOPI / n;

e.x = cos(theta2) * cos(theta3);
e.y = sin(theta2);
e.z = cos(theta2) * sin(theta3);
p.x = c.x + r * e.x;
p.y = c.y + r * e.y;
p.z = c.z + r * e.z;

// Data1
fwrite(&amp;e, 8, 3, fp);
fwrite(&amp;p, 8, 3, fp);

e.x = cos(theta1) * cos(theta3);
e.y = sin(theta1);
e.z = cos(theta1) * sin(theta3);
p.x = c.x + r * e.x;
p.y = c.y + r * e.y;
p.z = c.z + r * e.z;

fwrite(&amp;e, 8, 3, fp);
fwrite(&amp;p, 8, 3, fp);

}
}
// finish writing
fclose(fp);
}

Now read contents into a vector:


void CreateSphereFileRead()
{
FILE *fp = fopen("file_data", "rb");
int i,j, n = 50;
double theta1,theta2,theta3;
Data temp;

for (j=0;j<n/2;j++) {
theta1 = j * TWOPI / n - PID2;
theta2 = (j + 1) * TWOPI / n - PID2;

glBegin(GL_QUAD_STRIP);
for (i=0;i<=n;i++) {
theta3 = i * TWOPI / n;

// read 12 elements
fread(&amp;temp, 8, 12, fp);

// push it into the vector
file_data.push_back(temp);
}
glEnd();
}
fclose(fp);
}

file_data is a global vector:


typedef struct {
double data1;
double data2;
double data3;
double data4;
double data5;
double data6;
double data7;
double data8;
double data9;
double data10;
double data11;
double data12;
} Data;
std :: vector<Data> file_data;

This is where the sphere is drawn:


void CreateSphereRender()
{

int i,j, count, n = 50;
double theta1,theta2,theta3;

for (j=0, count = 0; j < n / 2; j++) {
theta1 = j * TWOPI / n - PID2;
theta2 = (j + 1) * TWOPI / n - PID2;

glBegin(GL_QUAD_STRIP);
for (i=0;i<=n;i++) {
theta3 = i * TWOPI / n;

glNormal3f(file_data[count].data1, file_data[count].data2, file_data[count].data3);
glTexCoord2f(i/(double)n, 2 * (j+1)/(double)n);

glVertex3f(file_data[count].data4, file_data[count].data5, file_data[count].data6);

glNormal3f(file_data[count].data7, file_data[count].data8, file_data[count].data9);
glTexCoord2f(i/(double)n,2*j/(double)n);

glVertex3f(file_data[count].data10, file_data[count].data11, file_data[count].data12);

// cout << "\n" << file_data[count].data1;
count++;
}
glEnd();
}
}

So, the only calculations done are some simple division and multiplications.

But the performance still remains the same.

i read the file and created the vector only once. So, after that it is just reading from the vector, but why is it still slow?

Any suggestions?

ZbuffeR
12-02-2010, 01:37 AM
You have a plenty of trigonometric functions in each vertex drawing, and also to many function calls (using immediate mode drawing). My advice is to calculate vertex-positions just once, and store them into VBO (or VA).
You did the first part.
Do the second, and it should be better.

With your current implementation, is it faster if the window is small ? In this case you are limited by rendering, and you will need other solutions to make it faster.

Mukund
12-02-2010, 01:48 AM
With your current implementation, is it faster if the window is small ? In this case you are limited by rendering, and you will need other solutions to make it faster.


Thanks for the reply ZBuffer. Yes, indeed. The performance is really good on small windows.

i am rotating the sphere. It rotates pretty fast on 200x200 window.

But becomes really slow on a 700x700 one.

How can i solve this?

ugluk
12-02-2010, 02:29 AM
Some graphics cards don't like quads, even quad strips. I'd convert them to triangles or tristrips first. Then I'd upload them to a VBO and use that for rendering.

ZbuffeR
12-02-2010, 02:38 AM
Ugluk, it is unlikely the primitive has an effect on rasterization speed.

What about the texture ?
Resolution, POT, filtering method ? Depending on the card it can be slow. And what is your 3d card by the way ?

Mukund
12-02-2010, 03:39 AM
@ZBuffer

Well, i do not have an external graphics card. Onboard graphics memory of 128MB and i have 1GB RAM.

The texture resolution is 256x256 (.bmp file)

Could this affect the speed by a large extent?

ugluk
12-02-2010, 07:13 AM
The only way to find out is to bench. Maybe you can resize the 256x256 down to 128x128 or somesuch and see?

Alfonse Reinheart
12-02-2010, 07:31 AM
Well, i do not have an external graphics card. Onboard graphics memory of 128MB and i have 1GB RAM.

External or integrated, your GPU must have a name. What is it? Are you sure you're getting an accelerated rendering context? What do you get when you call glGetString(GL_RENDERER)?

Mukund
12-02-2010, 09:27 AM
@Alfonse Reinheart

glGetString(GL_RENDERER) is returning "GDI Generic". Please excuse me if this sounds trivial, but i did not understand what you meant by "accelerated rendering context".

@ugluk

Yes, i tried that, it seems to be a little faster now :). Thanks.

Mukund
12-02-2010, 09:57 AM
Also, i am having a little problem with the mapping. The texture on the surface of the sphere seems to flip suddenly.

i have attached the entire code in a .txt file.
i could not attach a .bmp file using the file manager, so i just renamed the .bmp extension to .jpg

Please change the extension before reading the file from the program.


There seemed to be a problem downloading the txt file.
The entire code:



#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <math.h>
#include <vector>
#include <iostream>
#include <GL/glut.h>

using namespace std;

#define FILENAME "et.bmp"
#define TWOPI 6.2831
#define PID2 1.57079

typedef struct XYZ {
double x;
double y;
double z;
} XYZ;

XYZ c = {0, 0, 0};

void CreateSphereRender();void CreateSphere1(XYZ c,double r,int n);
void CreateSphereFile(XYZ, double, int);
void CreateSphereFileRead();
void init();

typedef struct {
double data1;
double data2;
double data3;
double data4;
double data5;
double data6;
double data7;
double data8;
double data9;
double data10;
double data11;
double data12;
} Data;
std :: vector<Data> file_data;

unsigned char buff[256 * 256 * 3];
unsigned char arr[100];


void init(void)
{
glClearColor (0.0, 0.0, 0.0, 0.0);
glShadeModel (GL_FLAT);
glEnable( GL_TEXTURE_2D );
}

void display(void)
{
FILE *fp;
cout << glGetString(GL_RENDERER);

glClearColor (0.0, 0.0, 0.0, 0.0);
glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glFlush();

glMatrixMode (GL_PROJECTION);
glLoadIdentity();
glFrustum(-2, 2, -2, 2, 2, 10);


fp = fopen(FILENAME, "rb");
fread(arr, 1, 54, fp);
fread(buff, 1, 256 * 256 * 3, fp);

GLuint texture;

glGenTextures( 1, &amp;texture );

// select texture
glBindTexture( GL_TEXTURE_2D, texture );


glTexEnvf( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );


glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
GL_LINEAR_MIPMAP_NEAREST );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );

int width, height;

// texture width and height
width = 256;
height = 256;

gluBuild2DMipmaps( GL_TEXTURE_2D, 3, width, height,
GL_BGR_EXT, GL_UNSIGNED_BYTE, buff );

CreateSphereFile(c, 1.5, 50);
CreateSphereFileRead();

glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0, 0, 5, 0, 0, 0, 0, 1, 0);

// keep rotating.
for (int i = 0; ; ) {
glPushMatrix();
glTranslatef(1.5, 0, 0);
glRotatef((i += 10) % 360, 1, 1, 1);
CreateSphereRender();
glPopMatrix();

glutSwapBuffers();

Sleep(100);
glClear (GL_COLOR_BUFFER_BIT);
}

}

void CreateSphereFile(XYZ c,double r,int n)
{
FILE *fp = fopen("file_data", "wb");
int i,j;
double theta1,theta2,theta3;
XYZ e,p;


for (j=0;j<n/2;j++) {
theta1 = j * TWOPI / n - PID2;
theta2 = (j + 1) * TWOPI / n - PID2;

for (i=0;i<=n;i++) {
theta3 = i * TWOPI / n;

e.x = cos(theta2) * cos(theta3);
e.y = sin(theta2);
e.z = cos(theta2) * sin(theta3);
p.x = c.x + r * e.x;
p.y = c.y + r * e.y;
p.z = c.z + r * e.z;

fwrite(&amp;e, 8, 3, fp);
fwrite(&amp;p, 8, 3, fp);

e.x = cos(theta1) * cos(theta3);
e.y = sin(theta1);
e.z = cos(theta1) * sin(theta3);
p.x = c.x + r * e.x;
p.y = c.y + r * e.y;
p.z = c.z + r * e.z;

fwrite(&amp;e, 8, 3, fp);
fwrite(&amp;p, 8, 3, fp);

}
}
fclose(fp);
}


void CreateSphereFileRead()
{
FILE *fp = fopen("file_data", "rb");
int i,j, n = 50;
double theta1,theta2,theta3;
Data temp;


for (j=0;j<n/2;j++) {
theta1 = j * TWOPI / n - PID2;
theta2 = (j + 1) * TWOPI / n - PID2;

glBegin(GL_QUAD_STRIP);
for (i=0;i<=n;i++) {
theta3 = i * TWOPI / n;

// read 12 elements
fread(&amp;temp, 8, 12, fp);

file_data.push_back(temp);
}
glEnd();
}
fclose(fp);
}

void CreateSphereRender()
{

int i,j, count, n = 50;
double theta1,theta2,theta3;

for (j=0, count = 0; j < n / 2; j++) {
theta1 = j * TWOPI / n - PID2;
theta2 = (j + 1) * TWOPI / n - PID2;

glBegin(GL_TRIANGLE_STRIP);
for (i=0;i<=n;i++) {
theta3 = i * TWOPI / n;

glNormal3f(file_data[count].data1, file_data[count].data2, file_data[count].data3);
glTexCoord2f(i/(double)n, 2 * (j+1)/(double)n);

glVertex3f(file_data[count].data4, file_data[count].data5, file_data[count].data6);

glNormal3f(file_data[count].data7, file_data[count].data8, file_data[count].data9);
glTexCoord2f(i/(double)n,2*j/(double)n);

glVertex3f(file_data[count].data10, file_data[count].data11, file_data[count].data12);

count++;
}
glEnd();
}
}

int main(int argc, char** argv)
{
glutInit(&amp;argc, argv);
glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
glEnable(GL_DEPTH_TEST);

glutInitWindowSize (400, 400);
glutCreateWindow (argv[0]);

init ();

glutDisplayFunc(display);

glutMainLoop();
return 0;
}

Any idea where i am going wrong?

ZbuffeR
12-02-2010, 11:28 AM
"GDI Generic" means you use OpenGL in software mode, running only on your CPU, without taking advantage of any hardware. So it is normal you get crappy performance.
Try with this instead of LINEAR :
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
Should be almost usable.

Anyway, to fix the real problem, you should find what is your integrated graphic chip, and install its drivers.

Even lowest intel chips should render a textured sphere on a small window with adequate performance.

ugluk
12-02-2010, 11:42 AM
If you have an Intel chip, then it's Open GL support is problematic. The "GDI generic" thing solves the mystery.

Mukund
12-02-2010, 12:00 PM
@Aleksandar, ugluk, Alfonse Reinheart, ZbuffeR

Thanks a lot for the support. This thread was really helpful. :)

@ugluk

Yes, it is an Intel Chip.