Vertex Shader und Normale



  • Hallo,
    ich habe ein kleines C++ Programm mit einem Vertex Shader geschrieben, das Programm schickt ein Quadrat ( bestehend aus vielen kleinen Quadraten, damit ich viele Vertices an den Shader verfüttern kann, liegt in xz-Ebene ) in den Shader. Dieser schiebt meine Vertices mithilfe einer Sinusfunktion in Abhängigkeit vom Abstand zum Quadratmittelpunkt, entlang der yAchse hoch und runter, so das ich ein hübsches Wellenbild bekomme - sieht so aus wie wenn man einen Stein ins Wasser schmeisst. Das ganze funktioniert auch soweit, allerdings ist die Farbe leider einheitlich, da ich nicht weiss wie ich nach dem Verschieben die Normale verändern kann. Ich möchte gerne eine realistische Beleuchtung dort hineinbekommen, das geht allerdings nur wenn ich meine Normalen an den verschobenen Punkt anpassen kann, bis jetzt zeigen sie alle nach oben ( y Achse ) . Meines Wissens nach bräuchte ich doch dafür alle 4 Eckpunkte eines Unterquadrates, um dann mit einem Verktorprodukt das ganze ausrechnen zu können, die Vertices werden ja aber einzeln verfüttert. Was kann ich da machen. Ich sende euch meinen Kompletten Code mit und bin für Hilfe dankbar.

    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    #include <fcntl.h>
    
    #ifdef WIN32
    #define GLEW_STATIC 1
    #include "glew.h"
    #else
    #define GL_GLEXT_PROTOTYPES 1
    #endif
    
    #ifdef __APPLE__
    #include <GLUT/glut.h>
    #else
    #include <GL/glut.h>
    #endif
    
    GLuint quadDL;
    
    GLint loc,wave;
    GLuint v,f,f2,p;
    
    int tess = 50;
    int active_tess = -1;
    int num_waves = 10;
    float speed = 0.1;
    
    int counter = 0;
    float  a = 0;
    
    #define TIMER_WAIT 10
    
    GLfloat light1_position[] = { 10.f, 10.f, 10.f, 0.f };
    const GLfloat WATER_AMBIENT[]={0.10f, 0.19f, 0.17f, 0.8f};
    const GLfloat WATER_DIFFUSE[]={0.40f, 0.74f, 0.69f, 0.8f};
    const GLfloat WATER_SPECULAR[]={0.30f, 0.31f, 0.31f, 0.8f};
    const GLfloat WATER_SHININESS[]={51.2f};
    
    char *loadTextFile(const char *fn)
    {
        FILE *fp;
        char *content = 0;
        int count;
        fp = fopen(fn, "r");
        fseek (fp, 0, SEEK_END);
        count = ftell(fp);
        fclose(fp);
        if (fn != 0) {
            fp = fopen(fn,"r");
    
            if (fp != 0) {
                if (count > 0) {
                    content = (char *)malloc(sizeof(char) * (count+1));
                    count = fread(content, sizeof(char),count,fp);
                    content[count] = '\0';
                }
                fclose(fp);
            } else {
                printf("Can't read %s!\n", fn);
                exit(1);
            }
        }
        return content;
    }
    
    void checkError(GLuint handle)
    {
        GLsizei infologLength = 0;
        GLsizei charsWritten  = 0;
        char *infoLog;
        glGetShaderiv(handle, GL_INFO_LOG_LENGTH,
                      &infologLength);
    
        if (infologLength > 1) {
            infoLog = (char*)malloc(infologLength);
            glGetShaderInfoLog(handle, infologLength, &charsWritten, infoLog);
            fprintf(stderr, "Error: %s", infoLog);
            free(infoLog);
        }
    }
    
    void renderScene(void)
    
    {
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
        glLoadIdentity();
        gluLookAt(0.0, 2.0, 3.0,
                  0.0, 0.0, 0.0,
                  0.0, 1.0, 0.0); 
    	glUniform1fARB(loc, a);
    	glUniform1fARB(wave,num_waves);
    
    	GLfloat set[]={1,1,1,1};
    	GLfloat white1_light[] = {1.0f, 1.0f, 1.0f, 1.f};
    	glLightfv(GL_LIGHT0, GL_POSITION, light1_position);
    	glLightfv(GL_LIGHT0, GL_DIFFUSE, white1_light);
    	glLightfv(GL_LIGHT0, GL_SPECULAR, set);
    
    	glEnable(GL_LIGHT0);
    
    	glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT, WATER_AMBIENT); 
    	glMaterialfv(GL_FRONT_AND_BACK, GL_DIFFUSE, WATER_DIFFUSE);
    	glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, WATER_SPECULAR);
    	glMaterialfv(GL_FRONT_AND_BACK, GL_SHININESS, WATER_SHININESS);
    
        if (tess != active_tess) {
            int x, y;
            float fw = 1.0 / tess;
            glNewList(quadDL, GL_COMPILE);
    		glColor3f(1.0,0.0,0.0);
            glBegin(GL_QUADS);
            for (x=-tess; x<tess; x++)
                for (y=-tess; y<tess; y++) {
                    float fx = (float)x / tess;
                    float fy = (float)y / tess;
                    glVertex3f(fx, 0.0, fy); 
                    glVertex3f(fx, 0.0, fy + fw);
                    glVertex3f(fx + fw, 0.0, fy + fw);
                    glVertex3f(fx + fw, 0.0, fy);
                }
            glEnd();
            glEndList();
            active_tess = tess;
        }
    
        glCallList(quadDL);
    	a+=speed;
    
        glutSwapBuffers();
    }
    
    void initShaders() {
    
    	char *vs = NULL,*fs = NULL,*fs2 = NULL;
    
    	v = glCreateShader(GL_VERTEX_SHADER);
    	f = glCreateShader(GL_FRAGMENT_SHADER);
    	f2 = glCreateShader(GL_FRAGMENT_SHADER);
    
    	vs = loadTextFile("sin.vert");
    	fs = loadTextFile("sin.frag");
    
    	const char * vv = vs;
    	const char * ff = fs;
    
    	glShaderSource(v, 1, &vv,NULL);
    	glShaderSource(f, 1, &ff,NULL);
    
    	free(vs);free(fs);
    
    	glCompileShader(v);
    	glCompileShader(f);
    
    	p = glCreateProgram();
    	glAttachShader(p,v);
    	glAttachShader(p,f);
    
    	glLinkProgram(p);
    	glUseProgram(p);
    
    	loc = glGetUniformLocation(p,"time");
    	wave = glGetUniformLocation(p,"waves");
    
    }
    
    void printUsage()
    {
        printf("Sinushade:\n"
               "+/-\tIncrease/decrease tesselation\n"
               "W/w\tIncrease/decrease number of waves\n"
               "S/s\tIncrease/decrease speed\n"
               "q\tQuit\n");
    }
    
    void keyPressed(unsigned char key, int x, int y)
    {
        switch(key) {
        case 27:
        case 'q':
            exit(0);
            break;
        case '+':
            tess++;
            printf("Tesselation: %d\n", tess);
            break;
        case '-':
            if (tess > 1) tess--;
            printf("Tesselation: %d\n", tess);
            break;
        case 'W':
            num_waves++;
            printf("Waves: %d\n", num_waves);
            break;
        case 'w':
            if (num_waves > 1) num_waves--;
            printf("Waves: %d\n", num_waves);
            break;
        case 'S':
            speed= speed + 0.01;
            printf("Speed: %d\n", speed);
            break;
        case 's':
            if (speed > 0.01) speed=speed-0.01;
            printf("Speed: %d\n", speed);
            break;
        }
    }
    
    void timerCallback (int value)
    {
        glutTimerFunc(TIMER_WAIT, timerCallback, 0);
        counter++;
        glutPostRedisplay();
    }
    
    void reshape(int w, int h)
    {
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
    
        glViewport(0, 0, w, h);
        gluPerspective(45, 1, 1, 1000);
        glMatrixMode(GL_MODELVIEW);
    }
    
    int main(int argc, char **argv)
    {
        glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_RGB | GLUT_DEPTH | GLUT_DOUBLE);
        glutInitWindowSize(500, 500);
        glutCreateWindow("Sinushade");
    
        glutDisplayFunc(renderScene);
    	glutIdleFunc(renderScene);
        glutReshapeFunc(reshape);
        glutKeyboardFunc(keyPressed);
        glutTimerFunc(TIMER_WAIT, timerCallback, 0);
    
    #ifdef WIN32
        glewInit();
        if (!glewGetExtension("GL_ARB_fragment_program"))
            printf("WARNING! GL_ARB_vertex_program extension not available!\n");
    #endif
    
        glEnable(GL_DEPTH_TEST);
        glClearColor(0.0, 0.0, 0.0, 1.0);
        quadDL = glGenLists(1);
        initShaders();
    
        printUsage();
        glutMainLoop();
    
        return 0;
    }
    

    Und noch der Shader namens sin.vert:

    uniform float time;
    uniform float waves;
    void main(void)
    {
          vec3 normal, lightDir, viewVector, halfVector;
    	vec4 diffuse, ambient, globalAmbient, specular = vec4(0.0);
    	float NdotL,NdotHV;
    
    	/* first transform the normal into eye space and normalize the result */
    	normal = normalize(gl_NormalMatrix * gl_Normal);
    
    	/* now normalize the light's direction. Note that according to the
    	OpenGL specification, the light is stored in eye space. Also since 
    	we're talking about a directional light, the position field is actually 
    	direction */
    	lightDir = normalize(vec3(gl_LightSource[0].position));
    
    	/* compute the cos of the angle between the normal and lights direction. 
    	The light is directional so the direction is constant for every vertex.
    	Since these two are normalized the cosine is the dot product. We also 
    	need to clamp the result to the [0,1] range. */
    
    	NdotL = max(dot(normal, lightDir), 0.0);
    
    	/* Compute the diffuse, ambient and globalAmbient terms */
    	diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
    	ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
    	globalAmbient = gl_LightModel.ambient * gl_FrontMaterial.ambient;
    
    	/* compute the specular term if NdotL is  larger than zero */
    	if (NdotL > 0.0) {
    
    		NdotHV = max(dot(normal, normalize(gl_LightSource[0].halfVector.xyz)),0.0);
    		specular = gl_FrontMaterial.specular * gl_LightSource[0].specular * pow(NdotHV,gl_FrontMaterial.shininess);
    	}
    
    	gl_FrontColor = globalAmbient + NdotL * diffuse + ambient + specular;
    	gl_BackColor = globalAmbient + NdotL * diffuse + ambient + specular;
    
    	gl_Position = ftransform();
    
          vec4 v = vec4(gl_Vertex);
    
    	v.y = sin(3.14*waves*(sqrt(v.x*v.x+v.z*v.z))+time)*0.1;
    
    	gl_Position = gl_ModelViewProjectionMatrix * v;
    
    }
    

    + sin.frag :

    void main(void)
    {
    	gl_FragColor = gl_Color;
    }
    

    Kann mir bitte da wer helfen?
    MFG Jan



  • tja, das kannst du beliebig kompliziert machen...
    korrekt ist:

    alle 4 Eckpunkte eines Unterquadrates, um dann mit einem Verktorprodukt das ganze ausrechnen zu können

    tatsaechlich brauchen taetest du nur 3. die hast du aber leider nicht, da der vertex-shader nur einfachen 1:1 input-output produziert.
    dh du muesstest pro vertex auch die benachbarten positionen berechnen, aber das vervielfacht den aufwand.
    da dein mesh aber bekannt und der abstand der vertices konstant ist, kannst du das problem erheblich vereinfachen.
    oder einfach gleich nach gutduenken einen bruchteil (der faktor waere abhaengig von der kantenlaenge) der z-verschiebung auf die xy-komponente deiner normalen addieren und anschliessend neu normieren.



  • Also wenn ich das richtig verstehe und dann selbst weiterdenke : ich habe ja 4 Eckpunkte, dann muss ich in meinem c++ code einen counter machen, den ich weitergebe an den Shader, damit ich dort mit ifAbfrage testen kann an welchem der Eckpunkte ich mich grade befinde. Wenn ich dies rausgefunden habe, kann ich Vektoren zu 2 anderen Eckpunkten bilden, von denen ich vorher die y-Position berechnen muss. Dann bilde ich das Vektorprodukt und fertig ist die Sache. Einfacher wäre es ( spart Rechenaufwand ) wohl, wenn ich nur einmal pro 4 Eckpunkte die Normale berechen muss, also wenn mein Counter auf 1 ist. Kann der Shader sich diese Normale merken? Und auf die 3 Folge Eckpunkte anrechnen?
    MFG Jan



  • Kann der Shader sich diese Normale merken?

    nein, kann er nicht. denn:

    der vertex-shader produziert nur einfachen 1:1 input-output

    dh der shader kann ausschliesslich auf die daten zugreifen die in form der struktur eines vertex' reinkommen.
    wenn du es korrekt machen willst, muss jeder eckpunkt also auch die positionen der umliegenden eckpunkte beinhalten (und diese muessen entsprechend transformiert werden).
    da du aber davon ausgehen kannst, dass dein mesh fein genug tesseliert ist um die schwingung abzubilden, ist die differenz der amplitude zwischen zwei eckpunkten klein.
    entsprechend klein verschieben sich die xy-komponenten der normale: der aufwand lohnt nicht.



  • So habe nun meinen Shader ein wenig umgeschrieben, mit dem Erfolg, dass nun alles weiss wird.

    uniform float time;
    uniform float waves;
    uniform int counter;
    uniform float fx;
    void main(void)
    {
          vec3 normal, lightDir, viewVector, halfVector;
    	vec3 point2,point3;
    	vec4 diffuse, ambient, globalAmbient, specular = vec4(0.0);
    	float NdotL,NdotHV;
    
    	vec4 v = vec4(gl_Vertex);
    
    	v.y = sin(3.14*waves*(sqrt(v.x*v.x+v.z*v.z))+time)*0.1;
    
    	if (counter = 1){
    		point2[0] = v.x+fx;
    		point2[2] = v.z;
    		point2[1] = sin(3.14*waves*(sqrt(point2[0]*point2[0]+point2[2]*point2[2]))+time)*0.1;
    		point3[0] = v.x+fx;
    		point3[2] = v.z+fx;
    		point3[1] = sin(3.14*waves*(sqrt(point3[0]*point3[0]+point3[2]*point3[2]))+time)*0.1;
    	}
    	if (counter = 2){
    		point2[0] = v.x+fx;
    		point2[2] = v.z;
    		point2[1] = sin(3.14*waves*(sqrt(point2[0]*point2[0]+point2[2]*point2[2]))+time)*0.1;
    		point3[0] = v.x+fx;
    		point3[2] = v.z-fx;
    		point3[1] = sin(3.14*waves*(sqrt(point3[0]*point3[0]+point3[2]*point3[2]))+time)*0.1;
    	}
    	if (counter = 3){
    		point2[0] = v.x-fx;
    		point2[2] = v.z;
    		point2[1] = sin(3.14*waves*(sqrt(point2[0]*point2[0]+point2[2]*point2[2]))+time)*0.1;
    		point3[0] = v.x-fx;
    		point3[2] = v.z-fx;
    		point3[1] = sin(3.14*waves*(sqrt(point3[0]*point3[0]+point3[2]*point3[2]))+time)*0.1;
    	}
    	if (counter = 4){
    		point2[0] = v.x-fx;
    		point2[2] = v.z;
    		point2[1] = sin(3.14*waves*(sqrt(point2[0]*point2[0]+point2[2]*point2[2]))+time)*0.1;
    		point3[0] = v.x-fx;
    		point3[2] = v.z+fx;
    		point3[1] = sin(3.14*waves*(sqrt(point3[0]*point3[0]+point3[2]*point3[2]))+time)*0.1;
    	}
    	vec3 v1,v2;
    	v1[0] = point2[0]-v.x;
    	v1[1] = point2[1]-v.y;
    	v1[2] = point2[2]-v.z;
    
    	v2[0] = point3[0]-v.x;
    	v2[1] = point3[1]-v.y;
    	v2[2] = point3[2]-v.z;
    	vec3 norm2;
    	norm2 = cross(v1,v2);
    
    	/* first transform the normal into eye space and normalize the result */
    	normal = gl_Normal;
          normal[0] = norm2[0];
    	normal[1] = norm2[1];
    	normal[2] = norm2[2];
    
    	//normal = normalize(gl_NormalMatrix * gl_Normal);
    	normal = normalize(gl_NormalMatrix * normal);
    
    	//normal = normalize(normal);
    
    	/* now normalize the light's direction. Note that according to the
    	OpenGL specification, the light is stored in eye space. Also since 
    	we're talking about a directional light, the position field is actually 
    	direction */
    	lightDir = normalize(vec3(gl_LightSource[0].position));
    
    	/* compute the cos of the angle between the normal and lights direction. 
    	The light is directional so the direction is constant for every vertex.
    	Since these two are normalized the cosine is the dot product. We also 
    	need to clamp the result to the [0,1] range. */
    
    	NdotL = max(dot(normal, lightDir), 0.0);
    
    	/* Compute the diffuse, ambient and globalAmbient terms */
    	diffuse = gl_FrontMaterial.diffuse * gl_LightSource[0].diffuse;
    	ambient = gl_FrontMaterial.ambient * gl_LightSource[0].ambient;
    	globalAmbient = gl_LightModel.ambient * gl_FrontMaterial.ambient;
    
    	/* compute the specular term if NdotL is  larger than zero */
    	if (NdotL > 0.0) {
    
    		NdotHV = max(dot(normal, normalize(gl_LightSource[0].halfVector.xyz)),0.0);
    		specular = gl_FrontMaterial.specular * gl_LightSource[0].specular * pow(NdotHV,gl_FrontMaterial.shininess);
    	}
    
    	gl_FrontColor = globalAmbient + NdotL * diffuse + ambient + specular;
    	gl_BackColor = globalAmbient + NdotL * diffuse + ambient + specular;
    
    	//gl_Position = ftransform();
    
    	gl_Position = gl_ModelViewProjectionMatrix * v;
    
    }
    

    Was mache ich falsch?



  • du moechtest die fehlermeldung des shader-compile-vorgangs ausgeben:

    glCompileShaderARB( ... );
    GLint compileStatus;
    glGetObjectParameterivARB(shader, GL_OBJECT_COMPILE_STATUS_ARB, &compileStatus);
    if (compileStatus!=GL_TRUE)
    {
      GLcharARB *infoLog;
      GLint      infoLogLength;
    
      glGetObjectParameterivARB(shader, GL_OBJECT_INFO_LOG_LENGTH_ARB, &infoLogLength);
      if (infoLogLength)
      {
        infoLog = (GLcharARB *) malloc(infoLogLength);
        glGetInfoLogARB(shader, infoLogLength, NULL, infoLog);
        printf("%s\n", infoLog);
        free(infoLog);
      }
    }
    

Anmelden zum Antworten