drawing millions of sphere

Hi,

I’ve been studying openGL for more than 2 months now. This week i’m trying to generate a 3D view of more than 1 millions of sphere. Actually, i’m trying to do something like this video (http://www.youtube.com/watch?v=Tnc_IoXwH24), but i don’t want the spheres to move or to do collisions, i want the spheres to be fixed in space. I tried some basic For loop and it was quite slow.

I read about point sprites, shaders, opencl and i’m having a hard time understanding how to use it in order to make the visualization quicker.

So my question is, if you were to generate alot of sphere(>1Mills), how would you code it?

I would go with the point sprite idea and do some fragment shader magic to get the sphere shape and proper lighting.
If your spheres may intersect then you’ll need to update the depth value of the fragments as well to avoid artifacts. However, this comes at a cost as you’ll loose early-Z (which may be not that huge problem in this particular situation).
Then you simply need the sphere center coordinates put into a VBO and issue a draw call to render the 1M points (that should be handled quite quickly by today’s hardware).

Fragment shader? do you mean pixel shader? if not, do you know a tutorial of fragment shader?

Fragment shader? do you mean pixel shader?

No he means fragment shader. “Pixel” shader is what Direct3D decided to call the equivalent functionality.

Aside: “Pixel” shader is a misnomer for several reasons.

First, they aren’t pixels. Pixels are things that are stored in images. Until the shader completes and the blend phase writes the output color(s) to the image buffer(s), it isn’t a pixel. And the blending phase can alter the color before writing, so the results from the “pixel” shader isn’t enough to know what the final color will be. In short, all of the pixel shading operations don’t happen entirely within the “pixel” shader.

Second, thanks to multisampling and supersampling, “pixel” shaders are executed on samples. Multiple samples become a single “pixel”.

Third, “pixel” shaders are quite capable of writing nothing at all to the color buffer, using “discard.” So they may not shade pixels at all.

Right now i’m quite confused. I’ve read on point sprites, fragment shading, tessellation, and i don’t know where to start. Should i used the sphere generator from glu.h to generate all the spheres and aply a fragment shader or should i do a billboard of point sprites and aply a fragment shader?

The second option.

Ok, i read the tutorial on http://www.lighthouse3d.com/opengl/billboarding/.

I ran the project on my pc, and modified it to show more than 100k object. The fps was not bad for 64k object (30 fps). But anything over that would make the fps go below 20 fps.

The part of the code generating the object from the .tga texture look like this :


glAlphaFunc(GL_GREATER, 0);
	glBindTexture(GL_TEXTURE_2D,texName[0]);
	for( i = -20; i < 20; i++)
		for(int k = -20; k < 20; k++)
			for(int j = 0; j < 40; j++) {
				glPushMatrix();
				glTranslatef(i*1.0,k*1.0,j*1.0);

				if (type == 3) {
					l3dBillboardGetRightVector(right);
					up[0] = 0;up[1] = 1;up[2]=0;
				}
				if (type == 2) {
					l3dBillboardGetUpRightVector(up,right);
				}


				pos[0] = i*1.0; pos[1] = k*1.0; pos[2] = j*1.0;
				if (type == 0)
					l3dBillboardCheatSphericalBegin();
				else if (type == 4)
					l3dBillboardSphericalBegin(cam,pos);
				else if (type == 5)
					l3dBillboardCylindricalBegin(cam,pos);
				else if (type == 1)
					l3dBillboardCheatCylindricalBegin();
				if (type == 2 || type == 3) {
					glBegin(GL_QUADS);
						glTexCoord2f(0,0);glVertex3f(-3.0f * right[0],             0.0f,                       -3.0 * right[2]);
						glTexCoord2f(1,0);glVertex3f( 3.0f * right[0],             0.0f,					    3.0 * right[2]);
						glTexCoord2f(1,1);glVertex3f( 3.0f * right[0] + 6 * up[0], 6.0f * up[1] + 3 * right[1], 3.0 * right[2] + 6 * up[2]);
						glTexCoord2f(0,1);glVertex3f(-3.0f * right[0] + 6 * up[0], 6.0f * up[1] - 3 * right[1],-3.0 * right[2] + 6 * up[2]);
					glEnd();
				}
				else {
					glBegin(GL_QUADS);
						glTexCoord2f(0,0);glVertex3f(-1.0f, 0.0f, 0.0f);
						glTexCoord2f(1,0);glVertex3f(1.0f, 0.0f, 0.0f);
						glTexCoord2f(1,1);glVertex3f(1.0f, 2.0f,  0.0f);
						glTexCoord2f(0,1);glVertex3f(-1.0f, 2.0f,  0.0f);
					glEnd();
				}
				if (type != 2 && type != 3 && type != 6) // restore matrix
			    	glPopMatrix();
					//l3dBillboardEnd();
				glPopMatrix();

			}
	glBindTexture(GL_TEXTURE_2D,0);
	glDisable(GL_BLEND);
    glDisable(GL_ALPHA_TEST);

now, how could i make this code go faster in order to be able to show over 1Mill object?

First of all, don’t use immediate mode and quads. Put your sphere center positions in a VBO and render the point sprites with a single DrawArrays command.
In the vertex shader setup the size of the point sprites based on distance from camera.
In the fragment shader shade the fragments of the point sprites with whatever lighting equation and discard fragments not part of the sphere.
Optionally you can modify the depth values if you want to correctly render overlapping spheres.

I really don’t know how to use VBO and point sprites together! I’m able to apply the shaders on a set of glutSphereSolid(over 64k it is laggy), but i don’t know how to do it with point sprite.

should the DrawArray code look like this?


glEnableClientState(GL_COLOR_POINTER????); 
glEnableClientState(GL_VERTEX_POINTER????); 
glVertexPointer(2, GL_INT, 0, points????); 
glColorPointer(4,GL_FLOAT,0,0); 
glDrawArrays(GL_POINTS, 0, num of points); 
glDisableClientState(GL_VERTEX_POINTER);
glDisableClientState(GL_COLOR_POINTER); 

I didn’t find anything about GL_COLOR_POINTER and GL_VERTEX_POINTER. But why would i need a GL_COLOR_ARRAY and GL_VERTEX_ARRAY for a point sprite? NEED HELP LOL!

If you don’t use VBOs but immediate mode then rendering 1M points would be very inefficient. Using glutSphereSolid is even worse as a sphere is represented by several hundreds or thousands of triangles rather than a single point (that’s why it’s laggy).
About color pointer, actually you don’t need any colors only if you need your spheres to have different color.
You should first learn how to use VBOs and render point sprites. As a result, you’ll have a bunch of screen-aligned textured squares rendered.
After this, learn how to use gl_PointSize GLSL built-in in the vertex shader to calculate the screen-space sphere sizes based on distance from camera.
Finally, you’ll have to write the fragment shader that makes an illuminated sphere out of the screen-aligned square.

Actually, if you don’t want to mess with shaders you can achieve a limited version of the same thing by using the ARB_point_parameters extension and binding an illuminated sphere’s image as texture for the point sprites.

I was able to draw many point sprites, and a shader was applied, but the point sprites stayed square. Also i really need some hints on the way of using VBO with my code. That would be appreciated.

Here my code, everything is compiling, no errors, but it crash right away (with no errors code, just asking for debugging). If you find what is wrong with my code, let me know! Everything was working correctly with glbegin();…glend();. WHat i changed and added are located in GLHandleARB(for the vbo) and the code in renderScene(where i had the immediate mode drawing before).é


#include <stdio.h>
#include <stdlib.h>
#include <windows.h>
#include <GL/glew.h>
#include <GL/glut.h>
#include "textfile.h"
#include <math.h>

float xpos = 0, ypos = 0, zpos = 0, xrot = 0, yrot = 0, angle=0.0;

float lastx, lasty;

GLuint v,f,f2,p;
GLfloat colors[]={1,1,1};
float lpos[4] = {1,0.5,1,0};

GLhandleARB InitShaders(const GLchar *p_vertex_shader_src,const GLchar *p_fragment_shader_src)
{
  GLint l_size;
  // create vertex-shader object
  GLhandleARB l_vertex_shader_handle=glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
  // assign source code
  l_size=::strlen(p_vertex_shader_src);
  glShaderSourceARB(l_vertex_shader_handle,1,&p_vertex_shader_src,&l_size);
  // compile vertex shader
  glCompileShaderARB(l_vertex_shader_handle);
  // create fragment-shader object
  GLhandleARB l_fragment_shader_handle=glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
  // assign source code
  l_size=::strlen(p_fragment_shader_src);
  glShaderSourceARB(l_fragment_shader_handle,1,&p_fragment_shader_src,&l_size);
  // compile fragment shader
  glCompileShaderARB(l_fragment_shader_handle);
	
  // create a GLSL program (a combination of vertex and fragment shader)
  GLhandleARB l_program_handle=glCreateProgramObjectARB();
  // attach the previously create vertex- and fragment-shaders
  glAttachObjectARB(l_program_handle,l_vertex_shader_handle);
  glAttachObjectARB(l_program_handle,l_fragment_shader_handle);
  // link to get a functional GPU program
  glLinkProgramARB(l_program_handle);
 
  // delete vertex- and fragment-shader objects.
  // They were only needed for setup.
  glDeleteObjectARB(l_fragment_shader_handle);
  glDeleteObjectARB(l_vertex_shader_handle);

  return l_program_handle;
}

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);
}

void camera (void) {
    glRotatef(xrot,1.0,0.0,0.0);  //rotate our camera on teh x-axis (left and right)
    glRotatef(yrot,0.0,1.0,0.0);  //rotate our camera on the y-axis (up and down)
    glTranslated(-xpos,-ypos,-zpos); //translate the screen to the position of our camera
}

void renderScene(void) {
	// activate transparency
	glEnable(GL_BLEND);
	// activate texturing. Not a must, since our shader overrides this settings anyway.
	// But don't forget to bind your texture!
	glEnable(GL_TEXTURE_2D);
	// no depth test, particles should always be visible
	glDisable(GL_DEPTH_TEST);
	glDepthMask(GL_FALSE);

	// activate point-sprites
	glEnable(GL_POINT_SPRITE_ARB);

	// setup the desired particle size.
	// this value should depend on your window-size.
	glPointSize(10.0f);

	// particle coordinates and colors,
	glEnableClientState(GL_VERTEX_ARRAY);
	glEnableClientState(GL_COLOR_ARRAY);

	// tell OpenGL where the coordinates are.
	glVertexPointer(4,GL_FLOAT,0,0);

	// and here is the RGBA float color info.
	glColorPointer(4,GL_FLOAT,0,0);

	// activate the shaders
	glUseProgramObjectARB(p);

	// draw all particles at once
	glDrawArrays(GL_POINTS,0,10);

	// deactivate our shaders
	glUseProgramObjectARB(0);
}

void keyboard (unsigned char key, int x, int y) {
    if (key=='q')
    {
    xrot += 1;
    if (xrot >360) xrot -= 360;
    }

    if (key=='z')
    {
    xrot -= 1;
    if (xrot < -360) xrot += 360;
    }

    if (key=='w')
    {
    float xrotrad, yrotrad;
    yrotrad = (yrot / 180 * 3.141592654f);
    xrotrad = (xrot / 180 * 3.141592654f); 
    xpos += float(sin(yrotrad)) ;
    zpos -= float(cos(yrotrad)) ;
    ypos -= float(sin(xrotrad)) ;
    }

    if (key=='s')
    {
    float xrotrad, yrotrad;
    yrotrad = (yrot / 180 * 3.141592654f);
    xrotrad = (xrot / 180 * 3.141592654f); 
    xpos -= float(sin(yrotrad));
    zpos += float(cos(yrotrad)) ;
    ypos += float(sin(xrotrad));
    }

    if (key=='d')
    {
    float yrotrad;
    yrotrad = (yrot / 180 * 3.141592654f);
    xpos += float(cos(yrotrad)) * 0.2;
    zpos += float(sin(yrotrad)) * 0.2;
    }

    if (key=='a')
    {
    float yrotrad;
    yrotrad = (yrot / 180 * 3.141592654f);
    xpos -= float(cos(yrotrad)) * 0.2;
    zpos -= float(sin(yrotrad)) * 0.2;
    }

    if (key==27)
    {
    exit(0);
    }
}

void mouseMovement(int x, int y) {
    int diffx=x-lastx; //check the difference between the current x and the last x position
    int diffy=y-lasty; //check the difference between the current y and the last y position
    lastx=x; //set lastx to the current x position
    lasty=y; //set lasty to the current y position
    xrot += (float) diffy; //set the xrot to xrot with the addition of the difference in the y position
    yrot += (float) diffx;    //set the xrot to yrot with the addition of the difference in the x position
}

//void processNormalKeys(unsigned char key, int x, int y) {

//	if (key == 27) 
//		exit(0);
//}


void setShaders() {

	char *vs = NULL,*fs = NULL,*fs2 = NULL;

	v = glCreateShader(GL_VERTEX_SHADER);
	f = glCreateShader(GL_FRAGMENT_SHADER);
	f2 = glCreateShader(GL_FRAGMENT_SHADER);


	vs = textFileRead("shader.vert");
	fs = textFileRead("shader.frag");
	fs2 = textFileRead("toon2.frag");

	const char * ff = fs;
	const char * ff2 = fs2;
	const char * vv = vs;

	glShaderSource(v, 1, &vv,NULL);
	glShaderSource(f, 1, &ff,NULL);
	glShaderSource(f2, 1, &ff2,NULL);

	free(vs);free(fs);

	glCompileShader(v);
	glCompileShader(f);
	glCompileShader(f2);

	p = glCreateProgram();
	glAttachShader(p,f);
	glAttachShader(p,f2);
	glAttachShader(p,v);

	glLinkProgram(p);
	glUseProgram(p);
}


int main(int argc, char **argv) {
	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DEPTH | GLUT_DOUBLE | GLUT_RGBA);
	glutInitWindowPosition(200,200);
	glutInitWindowSize(800,600);
	glutCreateWindow("OpenGL");

	glutDisplayFunc(renderScene);
	glutIdleFunc(renderScene);
	//glutKeyboardFunc(processNormalKeys);
	glutReshapeFunc(changeSize);

	glEnable(GL_DEPTH_TEST);
	glClearColor(1.0,1.0,1.0,1.0);
//	glEnable(GL_CULL_FACE);

	glewInit();
	if (glewIsSupported("GL_VERSION_2_0"))
		printf("Ready for OpenGL 2.0
");
	else {
		printf("OpenGL 2.0 not supported
");
		exit(1);
	}
	setShaders();

    glutKeyboardFunc (keyboard); 

	glutMainLoop();

	return 0;
}

I don’t see it anywhere that you have created a VBO. Without a VBO you are actually using client memory vertex arrays and there a NULL pointer specified with glVertexPointer(4,GL_FLOAT,0,0) will cause your application to crash.

Try to develop under linux next time, then use gdb to backtrace to the place of your crash. Under Windblows you could also develop in cygwin to debug with gdb. Of course, aqnuep already found the bug:

// tell OpenGL where the coordinates are.
glVertexPointer(4,GL_FLOAT,0,0);

// and here is the RGBA float color info.
glColorPointer(4,GL_FLOAT,0,0);

It’s odd you use shaders and yet, use these 2 functions. You ought to be using glVertexAttribPointer().

So what should be the last 2 values : glVertexPointer(4,GL_FLOAT,?,?).
I had to put zeros there because i would get compilating errors.

You could use null values just you have to setup your VBOs as you don’t have any buffer objects created.

Where do you think you get the geometry data based on your code?

I think you should first learn how to use VBOs. After a bit googling I found these to start from:
http://www.songho.ca/opengl/gl_vbo.html
http://developer.nvidia.com/object/using_vbos.html
http://www.opengl.org/wiki/Vertex_Buffer_Object

I don’t know how good these articles are but most probably they will do the job.

The first link is the one i’m working on today, but it is drawing a cube, not point sprites. The fact is, i really don’t know how to use point sprites instead of cube. I know how to draw point sprites using GL_Point, but using them in a vbo doesnt seems different.
Here the code i modified from the songho website. I’m really not sure about the “for” loop for generating alot of objects. Anyway it is already alot faster since i’m able to generate 1Mil cube like these with less lag.


void displayCB()
{
    // clear buffer
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    // save the initial ModelView matrix before modifying ModelView matrix
    glPushMatrix();

    // tramsform camera
    glTranslatef(0, 0, cameraDistance);
    glRotatef(cameraAngleX, 1, 0, 0);   // pitch
    glRotatef(cameraAngleY, 0, 1, 0);   // heading

    if(vboUsed) // draw cube using VBO
    {
        // bind VBOs with IDs and set the buffer offsets of the bound VBOs
        // When buffer object is bound with its ID, all pointers in gl*Pointer()
        // are treated as offset instead of real pointer.
        glBindBufferARB(GL_ARRAY_BUFFER_ARB, vboId);

        // enable vertex arrays
        glEnableClientState(GL_NORMAL_ARRAY);
        glEnableClientState(GL_COLOR_ARRAY);
        glEnableClientState(GL_VERTEX_ARRAY);

        // before draw, specify vertex and index arrays with their offsets
        glNormalPointer(GL_FLOAT, 0, (void*)sizeof(vertices));
        glColorPointer(3, GL_FLOAT, 0, (void*)(sizeof(vertices)+sizeof(normals)));
        glVertexPointer(3, GL_FLOAT, 0, 0);
	  for(int i=-10;i<10;i++)
		for(int j=-10;j<10;j++)
			for(k=0;k<20;k++)
			{
			glPushMatrix();
			glTranslatef(i*4,k*4,j*4);
			glDrawArrays(GL_QUADS, 0, 24);
			glPopMatrix();
			}

        glDisableClientState(GL_VERTEX_ARRAY);  // disable vertex arrays
        glDisableClientState(GL_COLOR_ARRAY);
        glDisableClientState(GL_NORMAL_ARRAY);

        // it is good idea to release VBOs with ID 0 after use.
        // Once bound with 0, all pointers in gl*Pointer() behave as real
        // pointer, so, normal vertex array operations are re-activated
        glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
    }