Opengl Surface normals in Chromadepth terrain

Hi All,

I am creating a chromadepth terrain using Opengl. I am loading a PGM(Portable grey map) file and then I am creating vertices for that. But I am not able to understand how would I get surface normals for the terrain? Here is my code snippet. I am using triangle strip Please help me. I am using VBO to load my buffers.



double getNormalizedX(unsigned long x){
	return ((double)x/((width-1)) * 2.0 - 1.0);
  }

  double getNormalizedY(unsigned long y){
	return ((double)(height - 1 - y) /( (height - 1))* 2.0 - 1.0);
  }

  double getNormalizedZ(unsigned long x,unsigned long y){
	unsigned long yPrime = (height - 1) - y;
	return   (0.25 * gridData[yPrime][x]/ (double)maxVal);	
  }

 void loadPGMFile(){
    FILE *pgmFile;
    char pgm_name[10000];
    size_t x,y;
    unsigned long vertexIndex = 0;


    pgmFile = fopen("sample.pgm","r");

    fgets(pgm_name,10000,pgmFile);

    fscanf(pgmFile,"%d", &width);
    fscanf(pgmFile,"%d", &height);
    fscanf(pgmFile,"%d", &maxVal);

    gridData = malloc(height * sizeof(int *));

    for( x = 0; x < height; x++){
       gridData[x] = malloc(width * sizeof(int));
         for(y = 0; y < width; y++){
            fscanf(pgmFile, "%d", &gridData[x][y]);
         }

    }

// XXX Change this allocation once everything is finished.

    indicesArray = malloc(width * height * 6 * sizeof(GLuint));
    verticesArray = malloc(width * height * 3 * sizeof(GLfloat));
    normalsArray = malloc(width * height * 3 * sizeof(GLfloat));

    int vertsI = 0, indicesI=0, normalsI = 0;
    unsigned long i=0,j=0;
    for(i = 0; i < width; i++){
      if(i > 1){
	indicesArray[indicesI++] = vertexIndex;
	indicesArray[indicesI++] = vertexIndex;
      } 

      for(j = 0; j < height; j++){
	verticesArray[vertsI++] = getNormalizedX(i);
	verticesArray[vertsI++] = getNormalizedY(j);
	verticesArray[vertsI++] = getNormalizedZ(i,j);
	if(i > 0){
	  indicesArray[indicesI++] = vertexIndex;
	  indicesArray[indicesI++] = vertexIndex - height;
	}
	vertexIndex++;

      } 

      if(i > 0){
	indicesArray[indicesI++] = vertexIndex - height -1;
	indicesArray[indicesI++] = vertexIndex - height -1;
      }
/**
** Trying to create Normals*
**/
    }

void generateTerrain(){
 static GLuint indexBuffer;
 static GLuint vertexBuffer;
    static GLuint normalsBuffer;

 static GLboolean first = GL_TRUE;


if(first){ 
glGenBuffers(1, &vertexBuffer);
 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
 glBufferData(GL_ARRAY_BUFFER, totalVertices * sizeof(GLfloat),
&verticesArray[0], GL_STATIC_DRAW);

 glGenBuffers(1, &indexBuffer);
 glBindBuffer(GL_ARRAY_BUFFER,indexBuffer);
 glBufferData(GL_ARRAY_BUFFER, totalIndices * sizeof(GLuint),
&indicesArray[0], GL_STATIC_DRAW);
first = GL_FALSE;

 }

// loadUniforms();
 glEnableVertexAttribArray(vertexPositionAttr);
glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
glVertexAttribPointer(vertexPositionAttr,3, GL_FLOAT,GL_FALSE, 0, (GLvoid*) 0);

glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, indexBuffer);

glDrawElements(GL_TRIANGLE_STRIP, totalIndices, GL_UNSIGNED_INT, (GLvoid*) 0);

 }


You have to compute yourself normals using verticesArray[] and computing/averaging dots products between edges AB and AC for alls triangles ABC that share the same vertex A
(I see that you use triangle strips so a lot of computations can be shared between connected triangles for to minimize computations)

Have you please a more complete basis of your code source for that I can test to add the necessary code to it ?
(if not, I think to can complete your code sample for to have a full fonctionnal OpenGL program code source but this can really economize a lot of time …)

I have begin to convert a simple NeHe tutorial for to make the base of the chromadepth program


// YLP 12/12/12 : transformation of a basic NeHe OpenGL code source sample (http://nehe.gamedev.net/tutorial/3d_shapes/10035/) 
// for to handle chromadepth PGM pictures

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include <GL/glut.h>    // Header File For The GLUT Library 
#include <GL/gl.h>    // Header File For The OpenGL32 Library
#include <GL/glu.h>    // Header File For The GLu32 Library
#include <unistd.h>     // needed to sleep

/* ASCII code for the escape key. */
#define ESCAPE 27

/* The number of our GLUT window */
int window; 

/* specifics variables used for the PGM chromadepth */
int    width = 0, height = 0, maxVal = 0;
int  **gridData = NULL;

int   *indicesArray = NULL;
float *verticesArray = NULL;
float *normalsArray = NULL;

int    totalVertices = 0;
int    totalIndices = 0;
int    totalNormals = 0;

int vertexPositionAttr = 0;

GLuint indexBuffer = 0;
GLuint vertexBuffer = 0;
GLuint normalsBuffer = 0;

float rarray = 0;


/* A general OpenGL initialization function.  Sets all of the initial parameters. */
void InitGL(int Width, int Height)            // We call this right after our OpenGL window is created.
{
  glClearColor(0.0f, 0.0f, 0.0f, 0.0f);        // This Will Clear The Background Color To Black
  glClearDepth(1.0);                // Enables Clearing Of The Depth Buffer
  glDepthFunc(GL_LESS);                    // The Type Of Depth Test To Do
  glEnable(GL_DEPTH_TEST);                // Enables Depth Testing
  glShadeModel(GL_SMOOTH);            // Enables Smooth Color Shading

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();                // Reset The Projection Matrix

  gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f);    // Calculate The Aspect Ratio Of The Window

  glMatrixMode(GL_MODELVIEW);
}

/* The function called when our window is resized (which shouldn't happen, because we're fullscreen) */
void ReSizeGLScene(int Width, int Height)
{
  if (Height==0)                // Prevent A Divide By Zero If The Window Is Too Small
    Height=1;

  glViewport(0, 0, Width, Height);        // Reset The Current Viewport And Perspective Transformation

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f);
  glMatrixMode(GL_MODELVIEW);
}

/*  Init into the drawing function. */
void InitGLScene()
{
  glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);    // Clear The Screen And The Depth Buffer
  glLoadIdentity();                // Reset The View
}

/* The function called whenever a key is pressed. */
void keyPressed(unsigned char key, int x, int y) 
{
    /* avoid thrashing this call */
    usleep(100);

    /* If escape is pressed, kill everything. */
    if (key == ESCAPE) 
    { 
      /* shut down our window */
      glutDestroyWindow(window); 
      
      /* exit the program...normal termination. */
      exit(0);                   
    }
}


// Here are located specifics Chromadepth funcs

double getNormalizedX(unsigned long x)
{
    return ((double)x/((width-1)) * 2.0 - 1.0);
}
 
double getNormalizedY(unsigned long y)
{
    return ((double)(height - 1 - y) /( (height - 1))* 2.0 - 1.0);
}
 
double getNormalizedZ(unsigned long x,unsigned long y)
{
    unsigned long yPrime = (height - 1) - y;

    // return   (0.25 * gridData[yPrime][x]) / (double)maxVal;
    return -fabs(gridData[yPrime][x]) / 20.0f;
}
 
void loadPGMFile(char *filename)
{
    FILE *pgmFile;
    int x, y;
    char pgm_name[10000];
    unsigned char temp[4];
   
    // pgmFile = fopen("sample.pgm","r");
    pgmFile = fopen(filename,"r");

    if( pgmFile == NULL )
    {
    printf("Canot open the %s file :(
", filename);
    return;
    }
 
    fgets(pgm_name,10000,pgmFile);
 
    fscanf(pgmFile,"%d", &width);
    fscanf(pgmFile,"%d", &height);
    fscanf(pgmFile,"%d", &maxVal);

    printf("%s : width=%d height=%d maxVal=%d 
", filename, width, height, maxVal);
 
    gridData = (int **)malloc(height * sizeof(int *));
 
    for( y = 0; y < height; y++)
    {
    gridData[y] = malloc(width * sizeof(int));
    for(x = 0; x < width; x++)
    {
            // fscanf(pgmFile, "%d", &gridData[y][x]);
        fscanf(pgmFile, "%c", temp);
        gridData[y][x] = temp[0];
        // printf("gridData[%d][%d] = %d 
", y, x, gridData[y][x] );
        }
    }
}
 
void InitTerrain()
{
       size_t x,y;
     unsigned long vertexIndex = 0;

       // XXX Change this allocation once everything is finished.
 
        indicesArray = malloc(width * height * 6 * sizeof(GLuint));
        verticesArray = malloc(width * height * 3 * sizeof(GLfloat));
        normalsArray = malloc(width * height * 3 * sizeof(GLfloat));
 
        int vertsI = 0, indicesI=0, normalsI = 0;
        unsigned long i=0,j=0;

        for(i = 0; i < width; i++)
        {
              if(i > 1)
              {
            indicesArray[indicesI++] = vertexIndex;
            indicesArray[indicesI++] = vertexIndex;
              }     
 
              for(j = 0; j < height; j++)
              {
            verticesArray[vertsI++] = getNormalizedX(i);
            verticesArray[vertsI++] = getNormalizedY(j);
            verticesArray[vertsI++] = getNormalizedZ(i,j);
    
            if(i > 0)
                {
                  indicesArray[indicesI++] = vertexIndex;
                  indicesArray[indicesI++] = vertexIndex - height;
            }

            vertexIndex++;
              } 
 
              if(i > 0)
              {
            indicesArray[indicesI++] = vertexIndex - height -1;
            indicesArray[indicesI++] = vertexIndex - height -1;
              }


        /**
        ** Trying to create Normals*
        **/
    }

    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(3, GL_FLOAT, 0, verticesArray);

    // glEnableClientState(GL_NORMAL_ARRAY);
    // glVertexPointer(3, GL_FLOAT, 0, normalsArray);

    totalVertices = vertsI / 3;
    totalIndices = indicesI;
    totalNormals = normalsI;

    printf("%d vertices, %d indices, %d normals 
", totalVertices, totalIndices, totalNormals);

/*
    for( i = 0 ; i < totalVertices ; i++)
    {
        printf("chromadepth(%f,%f) = %f 
", verticesArray[i*3], verticesArray[i*3+1], verticesArray[i*3+2]);
    }
*/             
}

void DrawTerrain()
{ 
    glLoadIdentity();
    // glRotatef(rarray, 0.0f,1.0f,0.0f);        // Rotate The Terrain On The Y axis
    glScalef(0.02f, 0.02f, 0.02f);
    glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    glColor3f(1.0f,1.0f,1.0f);
    glDrawElements(GL_TRIANGLE_STRIP, totalIndices, GL_UNSIGNED_INT, indicesArray);

    rarray += 1.0f;
}

void DrawGLScene()
{
   InitGLScene();

   DrawTerrain();
 
   glutSwapBuffers();
}

int main(int argc, char **argv) 
{  
  /* Initialize GLUT state - glut will take any command line arguments that pertain to it or 
     X Windows - look at its documentation at http://reality.sgi.com/mjk/spec3/spec3.html */  
  glutInit(&argc, argv);  

  /* Select type of Display mode:   
     Double buffer 
     RGBA color
     Alpha components supported 
     Depth buffered for automatic clipping */  
  glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);  

  /* get a 640 x 480 window */
  glutInitWindowSize(640, 480);  

  /* the window starts at the upper left corner of the screen */
  glutInitWindowPosition(0, 0);  

  /* Open a window */  
  window = glutCreateWindow("Chromadepth PGM by YLP v0.0 (12/12/12) ");  

  /* Register the function to do all our OpenGL drawing. */
  glutDisplayFunc(&DrawGLScene);  

  /* Go fullscreen.  This is as soon as possible. */
  // glutFullScreen();

  /* Even if there are no events, redraw our gl scene. */
  glutIdleFunc(&DrawGLScene);

  /* Register the function called when our window is resized. */
  glutReshapeFunc(&ReSizeGLScene);

  /* Register the function called when the keyboard is pressed. */
  glutKeyboardFunc(&keyPressed);

  /* Initialize our window. */
  InitGL(640, 480);

  /* Chromadepth loading */
  loadPGMFile("sample.pgm");
  InitTerrain();

  
  /* Start Event Processing Engine */  
  glutMainLoop();  

  return 1;
}

I compile this using gcc on my Linux box


gcc chromadepth.c -o chromadepth -lX11 -lglut -lGL -lGLU -lm

The important functions are loadPGMFile(), getNormalized*(), InitTerrain() and DrawTerrain()
(I have a little modify your source for to can use a variable filename of the .pgm picture and read unsigned bytes instead ints [this is perhaps for this that I have a problem with the format …])

I have found a depth picture at http://dev.bukkit.org/profiles/thedeadlytao/, converted it to a grayscale picture, resample to 30x30 (for to have an array that I can speedly analyze) and save it to sample.pgm DL.FREE.FR

I have discover a problem with the reading of the pgm file because GIMP/ImageMagick store a label on the header
=> I have suppress this line and it can read the file

But I think that the data isn’t really loaded at the good format
(I read pixels data from disk as unsigned chars and have modified the getNormalizedZ() function for to have something displayed)

PS : I add the computation of normals as soon as I can properly read/decode .pgm files :slight_smile:
(and/or can properly handle the scaling of the depth)