Hjälp med height maps i OpenGL GLUT

Permalänk
Medlem

Hjälp med height maps i OpenGL GLUT

Jag försöker få en height map att fungera i OpenGL GLUT i ett program i C, men jag vet inte vad jag gör för fel.

Här är koden:

#include <GL/glut.h> #include <math.h> #include "helpers.h" GLuint groundTexture; int height, width; unsigned char *imagedata; void renderGround() { glPushAttrib(GL_ALL_ATTRIB_BITS); // Your terrain should match this flat quad glBegin(GL_POLYGON); glNormal3f(0, 1, 0); glTexCoord2f(0, 10); glVertex3f(0, -0.01, 200); glTexCoord2f(0, 0); glVertex3f(200, -0.01, 200); glTexCoord2f(10, 0); glVertex3f(200, -0.01, 0); glTexCoord2f(10, 10); glVertex3f(0, -0.01, 0); glEnd(); glPopAttrib(); } void renderTerrain() { int row, col; int x, y, z; int step = 16; int steps; steps = step * 3; glBegin( GL_QUADS ); for (row = 0; row < width-step; row += step) for (col = 0; col < width-step; col += step) { x = row; y = (int)imagedata[row + col*3] + (int)imagedata[row + col*3 + 1] + (int)imagedata[row + col*3 + 2]; z = col; glVertex3i(x, y/100, x); x = row; y = (int)imagedata[row + (col + steps)*3] + (int)imagedata[row + (col + steps)*3+1] + (int)imagedata[row + (col + steps)*3+2]; z = col + steps; glVertex3i(x, y/100, z); x = row + steps; y = (int)imagedata[row + steps + (col + steps)*3] + (int)imagedata[row + steps + (col + steps)*3+1] + (int)imagedata[row + steps + (col + steps)*3+2]; z = col + steps; glVertex3i(x, y/100, z); x = row + steps; y = (int)imagedata[row + steps + (col * 3)] + (int)imagedata[row + steps + (col * 3)+1] + (int)imagedata[row + steps + (col * 3)+2]; z = col; glVertex3i(x, y/100, z); } glEnd(); } // Light and materials GLfloat light_position[] = { 0.0, 1.0, 0.0, 0.0 }; // Directional from above GLfloat mat_shininess[] = { 50.0 }; GLfloat mat_diffuseColor[] = { 1.0, 1.0, 1.0, 1.0 }; GLfloat mat_specularColor[] = { 1.0, 1.0, 1.0, 1.0 }; void display() { // This function is called whenever it is time to render // a new frame; due to the idle()-function below, this // function will get called several times per second // Clear framebuffer & zbuffer glClearColor(0.3, 0, 0, 1); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // Set current material glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuseColor); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specularColor); // Place camera and light glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glLoadMatrixd(getCameraMatrix()); glLightfv(GL_LIGHT0, GL_POSITION, light_position); // Render the scene! renderGround(); renderTerrain(); // Swap front- and backbuffers glutSwapBuffers(); } void init() { int row, col; // An ordinary texture groundTexture = loadTexture("../textures/TropicalFoliage0025_1_S.jpg"); // A heightmap image // imagedata = readppm("../heightmaps/44-terrain.ppm", &height, &width); imagedata = readppm("../heightmaps/fft-terrain.ppm", &height, &width); // Print out image contents. // Warning! Only do this for small images or you will get incredible amounts of data! if (width < 16) for (row = 0; row < height; row++) for (col = 0; col < width; col++) { printf("(%d, %d): R = %d, G = %d, B = %d\n", row, col, (int)imagedata[(row*width + col)*3], (int)imagedata[(row*width + col)*3+1], (int)imagedata[(row*width + col)*3+2]); } // Use the imagedata array for heights in the terrain! // GL inits glDisable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_NORMALIZE); glEnable(GL_DEPTH_TEST); glShadeModel(GL_SMOOTH); // Enable Gouraud shading glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, groundTexture); } void timer(int i) { glutTimerFunc(20, timer, i); glutPostRedisplay(); } int main(int argc, char **argv) { glutInit(&argc, argv); // Configure GLUT: // - framebuffer with RGB + Alpha values per pixel // - Z-buffer // - two sets of above mentioned buffers, so that // doublebuffering is possible // // Initial window size 800x800 glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE); glutInitWindowSize(800, 800); glutCreateWindow("Heightmap"); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(90, 1, 0.01, 1000); glMatrixMode(GL_MODELVIEW); initHelperLibrary(); init(); // Register our display- and idle-functions with GLUT glutDisplayFunc(display); glutTimerFunc(20, timer, 0); // Enter GLUT's main loop; this function will never return glutMainLoop(); return 0; }

Och här är koden som läser ppm-filer:

#include <stdio.h> #include <stdlib.h> #include <string.h> #include <GL/glut.h> char *readppm(char *filename, int *height, int *width) { FILE *fd; int k; char c; int i,j; char b[100]; int red, green, blue; long numbytes; int n; int m; char *image; fd = fopen(filename, "r"); if (fd == NULL) { printf("Could not open %s\n", filename); return NULL; } c = getc(fd); if (c=='P' || c=='p') c = getc(fd); if (c == '3') { printf("%s is a PPM file (plain text version)\n", filename); // NOTE: This is not very good PPM code! Comments are not allowed // except immediately after the magic number. c = getc(fd); if (c == '\n' || c == '\r') // Skip any line break and comments { c = getc(fd); while(c == '#') { fscanf(fd, "%[^\n\r] ", b); printf("%s\n",b); c = getc(fd); } ungetc(c,fd); } fscanf(fd, "%d %d %d", &n, &m, &k); printf("%d rows %d columns max value= %d\n",n,m,k); numbytes = n * m * 3; image = (char *) malloc(numbytes); if (image == NULL) { printf("Memory allocation failed!\n"); return NULL; } for(i=n-1;i>=0;i--) for(j=0;j<m;j++) { fscanf(fd,"%d %d %d",&red, &green, &blue ); image[(i*n+j)*3]=red * 255 / k; image[(i*n+j)*3+1]=green * 255 / k; image[(i*n+j)*3+2]=blue * 255 / k; } } else if (c == '6') { printf("%s is a PPM file (raw version)!\n", filename); c = getc(fd); if (c == '\n' || c == '\r') // Skip any line break and comments { c = getc(fd); while(c == '#') { fscanf(fd, "%[^\n\r] ", b); printf("%s\n",b); c = getc(fd); } ungetc(c,fd); } fscanf(fd, "%d %d %d", &n, &m, &k); printf("%d rows %d columns max value= %d\n",n,m,k); c = getc(fd); // Skip the last whitespace numbytes = n * m * 3; image = (char *) malloc(numbytes); if (image == NULL) { printf("Memory allocation failed!\n"); return NULL; } // Read and re-order as necessary for(i=n-1;i>=0;i--) for(j=0;j<m;j++) { image[(i*n+j)*3+0]=getc(fd); image[(i*n+j)*3+1]=getc(fd); image[(i*n+j)*3+2]=getc(fd); } } else { printf("%s is not a PPM file!\n", filename); return NULL; } printf("read image\n"); *height = n; *width = m; return image; }

Permalänk
Medlem

Väldigt utdaterad metod att göra heightfields.

Gör det med shaders istället. Då är det bara att slå upp positionen i texturen (heightmappen) från vertex shadern och flytta vertexen i normalens riktning.

GLUT är gammalt och omodernt och jag tror tom skaparen av det har bett om ursäkt för att det är så onödigt tillkrånglat.

Sedan om du verkligen vill få hjälp med att felsöka din kod behöver du nog lämna mer information än "det funkar inte". Tror ingen orkar lusläsa din kod (om det är din och inte bara kopierat rakt av) för att leta upp felet.

Permalänk
Medlem

Det är till en uppgift i en datorgrafikkurs, därför måste jag använda GLUT och inte använda shaders.
Vi får ett kodskelett att utgå ifrån.
Filer och uppgifter går att hitta här:
http://computer-graphics.se/TSBK07-files/lab4.html

Jag kan kompilera och köra programmet, men polygonerna jag renderar ser inte rätt ut. Det är svårt att förklara i ord så jag har tagit en skärmdump.

Permalänk
Medlem

Nu orkar jag inte sätta mig in i din kod

Men det känns som du antagligen går utanför din heightmap-array struktur eller att det är nått off med din "step-logik" när du ska bygga dina quads..

tips..

Gör först en getHeight-metod som tar en x och z koordinat och returnerar höjden i heightmappen..

typ:

int getHeight(int x, int z) { if (x > "bredden på heightmap") ... printa nått fel och crasha } if (z > "höjden på heightmap") { ... printa nått fel och crasha } int imageIndex = x + z * width; int rgbValue = imageData[imageIndex]; imageIndex++; rgbValue += imageData[imageIndex]; imageIndex++; rgbValue += imageData[imageIndex]; return rgbValue; }

Sen är det busenkelt att spotta ut din heightmap.

glBegin(GL_QUADS); for (int x = 0; x < width-1; x++) { for (int z = 0; z < height-1; z++) { glVertex(x, getHeight(x, z), z); glVertex(x, getHeight(x, z+1), z+1); glVertex(x+1, getHeight(x+1, z+1), z+1); glVertex(x+1, getHeight(x+1, z), z); } } glEnd();

Sen kör du bara glScale för att få den att passa in i scenen.

Permalänk
Medlem

Jag orkar inte heller sätta mig in i koden men kan ge några felsökningstips iaf:
Det du vill göra är att hitta platsen för felet. Har du väl gjort det är det säkert ganska lätt att fixa. För att hitta felet är det lättast att dela upp problemet i mindre delar och sedan dela in dessa mindre problem ännu mer ända tills du vet vilken/vilka rader som det är fel på.

I ditt fall hade jag börjat med att använda en 2x2 heightmap som input, på så sätt blir det enklare att följa koden.
Därefter hade jag lagt in en print i renderTerrains for-loopar och printat ut det som skickas in till glVertex3i. Är det rätt är det själva anropen det är fel på, kanske kommer dom i fel ordning för att göra en quad eller något.. Är det fel är felet inte i renderTerrains-funktionen och du kan koncentrera dig på resten av koden. Och så fortsätter du tills du hittat felet/felen.

Dock råkade jag se som snabbast något som ser lite mysko ut, ska det verkligen vara x på slutet här: "glVertex3i(x, y/100, x);"

Visa signatur

AK47s for everyone! - Angry mob
Since NaN /= NaN, I think, we should decipher 'NaN' as 'Not a NaN' - Miguel Mitrofanov
(Varför är människan så benägen att tro på Gud?) Antagligen har det lönat sig och evolutionen har drivit fram sådana hjärnor. - Anon

Permalänk
Medlem
Citat:

Ursprungligen inskrivet av vb
Dock råkade jag se som snabbast något som ser lite mysko ut, ska det verkligen vara x på slutet här: "glVertex3i(x, y/100, x);"

Nej det ska det verkligen inte. Typiskt slarvfel.
Jag fixade till det och la till en glScalef och snyggade till koden som harre föreslog.

Nu ser min renderterrain funktion ut så här:

void renderTerrain() { int x, z; int step = 1; int steps; float scaley; float scalexz; steps = step * 3; scaley = (float)100/765; scalexz = (float)200/width; glPushMatrix(); glScalef(scalexz, scaley, scalexz); glBegin( GL_QUADS ); for (x = 0; x < width-step; x += step) for (z = 0; z < width-step; z += step) { glColor3f(1,0,0); glVertex3i(x, getHeight(x, z), z); glColor3f(0,1,0); glVertex3i(x, getHeight(x, z + steps), z + steps); glColor3f(0,0,1); glVertex3i(x + steps, getHeight(x + steps, z + steps), z + steps); glColor3f(0,1,1); glVertex3i(x + steps, getHeight(x + steps, z), z); } glEnd(); glPopMatrix(); }

Heightmapen är 4x4 pixlar stor och ser ut så här

Resultatet blir

Det verkar inte som att det fungerar.

Permalänk
Medlem

Du ritar varje quad med överlapp...

Om vi tänker oss att du bara ska rita en linje..

Så ska du göra nått liknande såhär..

x1-----x2------x3...

där x1, x2, och x3 är x-koordinaten..

För varje varv i for-loopen ökar du värdet med step, men när du ritar så hoppar du steps i riktningen..

steps är 3 och step är 1 så det blir lite skumt

så istället får du nån linje som är liknande detta

x1------------- x2------------ x3------------- osv..

Permalänk
Medlem
Citat:

Ursprungligen inskrivet av harre
Du ritar varje quad med överlapp...

Tack för hjälpen!
Det ser bättre ut nu, men jag är inte säker på om allt är rätt.
Bör denna heightmap generera den här terrängen?

Borde det inte vara som ett hål i mitten?

Min nuvarande renderTerrain-funktion:

void renderTerrain() { int x, y, z; int step = 16; float scalex; float scaley; float scalez; typedef GLfloat vect[3]; vect vec1, vec2, vec3; GLfloat normx, normy, normz; scalex = (float)200/height; scaley = (float)50/765; scalez = (float)200/width; glPushMatrix(); glScalef(scalex, scaley, scalez); glBegin( GL_QUADS ); for (x = 0; x < height; x += step) for (z = 0; z < width; z += step) { glTexCoord2f(0, 10); glVertex3i(x, getHeight(x, z), z); glTexCoord2f(0, 0); glVertex3i(x, getHeight(x, z + step), z + step); glTexCoord2f(10, 0); glVertex3i(x + step, getHeight(x + step, z + step), z + step); glTexCoord2f(10, 10); glVertex3i(x + step, getHeight(x + step, z), z); vec1[0] = (float)(x + step); vec1[1] = (float)getHeight(x + step, z); vec1[2] = (float)z; vec2[0] = (float)(x + step); vec2[1] = (float)getHeight(x + step, z + step); vec2[2] = (float)(z + step); vec3[0] = (float)x; vec3[1] = (float)getHeight(x, z + step); vec3[2] = (float)(z + step); calcVectNorm(vec1, vec2, vec3, &normx, &normy, &normz); glNormal3f(normx, normy, normz); } glEnd(); glPopMatrix(); }

Permalänk
Medlem

Men hur kommer du fram till att step skall vara 16 och hur ser din getHeight ut..
int step = 16;

För om du vill göra uppslagningar på varje pixel så behöver du ju bara ha step=1... dvs... x går från 0 till width-1 och z från 0..height-1...

För width och height är höjd och bredd på heightmappen eller?

Skulle du även kunna zippa ihop katalogen så kan man lättare kompilera det och köra lite enklare tester..

Permalänk
Medlem

kan du inte köra fler samples på height mappen? fler vertexpunkter på din gräsmatta eller vad det är? ser ut som du tappar detaljer pga för stora polygoner.

Permalänk
Medlem

Anledningen till att jag satte steps till 16 är för att inte belasta grafikkortet, men det kanske är onödigt med ett så litet program?

Så här ser det ut när jag sätter steps till 1.
Det går långsammare att flytta kameran men så kör jag på en netbook så det är nog inte så konstigt.:D

Height och Width är höjden respektive bredden på heightmappen.

Här är katalogen zippad:
http://www.megaupload.com/?d=NC4JY5IS

Permalänk
Medlem

det ser inte korrekt ut iaf

Permalänk
Medlem

Om jag inte minns helt fel så ska tex coords vara float, dvs inte 10 tex, nått mellan 0-1.0 kanske? annars tilar den kanske, beror lite på... Och är det heltal, dvs integers du vill ha från glVertex3i? jag har inte alls kollat vad din kod gör, bara nått jag såg. Detta "scaley = (float)50/765;
" blir också ett väldigt litet tal.

Permalänk
Medlem

Får detta när jag försöker ladda ner filen:
"The file you are trying to access is temporarily unavailable."

Permalänk
Medlem

För mig går det att ladda ner. Kanske bara var tillfälligt borta?

Permalänk
Medlem

din getHeight var trasig...

Testade lägga in denna snurra på den lilla bilden så såg man det ganska snabbt..

int step = 1; for (int x = 0; x < height; x += step) for (int z = 0; z < width; z += step) printf("(%d; %d) -> %d\n", x, z, getHeight(x, z));

Sen var du one-off på din rendering av terrängen detta såg man när man la in att den kollade att du inte var out-of bounds på din array, sen så tog du inte med bredden i beräkning när du hämtade punkterna i din bild-array.

Så getHeight moddade jag till detta:

int getHeight(int x, int z) { if (x >= width || z >= height) { printf("(%d; %d)\n", x, z); exit(10); } int rgbValue = (int)imagedata[(x + z*width)*3] + (int)imagedata[(x + z*width)*3+1] + (int)imagedata[(x + z*width)*3+2]; return rgbValue; }

Hur byggde du koden förresten eftersom din makefil verkar vara trasig...
Fast det gick fint med detta
gcc *.c -o test -lglut -ljpeg -Wall -std=c99

Får några småvarningar angående att printf är implicit importerad..
Kan vara en ide att ha med
#include <stdlib.h>
så det även kan funka på andra plattformar

Sen behöver du
#include <stdio.h>
om du även vill köra med exit

Permalänk
Medlem
Citat:

Ursprungligen inskrivet av harre
din getHeight var trasig...

Tack för hjälpen! Nu börjar det faktiskt se hyffsat ut.

Citat:

Ursprungligen inskrivet av harre
Hur byggde du koden förresten eftersom din makefil verkar vara trasig...

Jag skriver bara make så fungerar det för mig.

Permalänk
Medlem
Citat:

Ursprungligen inskrivet av Snejk
Tack för hjälpen! Nu börjar det faktiskt se hyffsat ut.

Jag skriver bara make så fungerar det för mig.

Varsågod... var kul att hjälpa till
Lycka till att knyta ihop det och bli klar med uppgiften

Men är det på nån mac eller cygwin du bygger i? eftersom här i ubuntu så vill den inte..

Permalänk
Medlem

Det var konstigt, jag kör Kubuntu så det borde ju vara samma resultat tycker man.