GLSL Shader Crash bei Array Nutzung

Für Fragen zu Grafik APIs wie DirectX und OpenGL sowie Shaderprogrammierung.
Antworten
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

GLSL Shader Crash bei Array Nutzung

Beitrag von snoob741 »

Ich entwickel zur Zeit einen "Übershader" zur Behandlung von Lichtquellen. Für Direktionale-Lichtquellen läuft alles hervorragend. Nun bin ich dabei das ganze um Punkt-Lichtquellen zu erweitern und habe hier ein Fehlerverhalten beim Zugriff auf Array Werte, den ich mir absolut nicht erklären kann. Hier ersteinmal der gesamte Shader Quellcode:

Vertexshader:

Code: Alles auswählen

#version 130

uniform mat4 MVPMatrix;  // combined model/view/projection matrix.  
uniform mat4 NMatrix;    // normal matrix.
uniform mat4 MVMatrix;   // model/view matrix.

// Per-Vertex informations we will pass in
in vec3 vertex;  
in vec2 texCoord0;
in vec3 normal;     

// Those values will be passed into the fragment shader. 
out vec2 vTexCoord0;
out vec3 vNormal;

#ifndef MAX_LIGHT_COUNT
#define MAX_LIGHT_COUNT 8

// The number of lights which should be created
uniform int  uCountDirectionalLights;  
uniform int  uCountPointLights; 

// The directional light value arrays
uniform vec3 uDirectionalLightDirection[MAX_LIGHT_COUNT];  
out vec3 vDirectionalLightDirection[MAX_LIGHT_COUNT];
 
// The point light value arrays
uniform vec3 uPointLightPosition[MAX_LIGHT_COUNT];
out vec3 vPointLightDirection[MAX_LIGHT_COUNT];   
                                 
uniform vec3 uCameraPosition;    // Position of the camera in view space.

out vec3 vCameraDirection;
flat out int vCountDirectionalLights;
flat out int vCountPointLights;

out vec3 vTest2[MAX_LIGHT_COUNT]; 

void createLights(vec4 position)
{
    for(int i=0; i<uCountDirectionalLights; i++){
        vDirectionalLightDirection[i] = uDirectionalLightDirection[i];
    }
    
    for(int i=0; i<uCountPointLights; i++){
        vTest2[i] = uPointLightPosition[i] - position.xyz;
    }
    
    // Compute camera direction for specular lighting
    vCameraDirection = uCameraPosition - position.xyz;
    vCountDirectionalLights = uCountDirectionalLights;
    vCountPointLights = uCountPointLights;
}

void main( void )
{        
    //transform vertex position to clip space. 
    gl_Position = MVPMatrix * vec4(vertex, 1.0);
       
    // transform normal to view space.
    vec3 eNormal = vec3(NMatrix * vec4(normal, 0.0));
    eNormal = eNormal / length(eNormal);
    vNormal = eNormal;
    
    // create lights using world view position values.
    createLights(MVMatrix * vec4(vertex, 1.0));
   
    vTexCoord0 = texCoord0;    
}
Fragmentshader:

Code: Alles auswählen

#version 130

#ifdef GLES2
//Set the default precision to medium. We don't need as high of a  precision in the fragment shader.
precision mediump float;
#endif

uniform sampler2D texture; 
 
in vec3 vNormal;      	
in vec2 vTexCoord0; 

out vec4 outColor; 

#ifndef MAX_LIGHT_COUNT
#define MAX_LIGHT_COUNT 8

struct Material {
   vec4  ambient;
   vec4  diffuse;                      
   vec4  specular;
   float shininess;
};
                  
uniform Material uMaterial;

uniform bool uUseLighting;      
uniform vec4 uSceneAmbientColor;

uniform vec4 uDirectionalLightDiffuseColor[MAX_LIGHT_COUNT];
uniform vec4 uDirectionalLightSpecularColor[MAX_LIGHT_COUNT];

flat in int  vCountDirectionalLights;
in vec3 vDirectionalLightDirection[MAX_LIGHT_COUNT];

uniform vec4 uPointLightDiffuseColor[MAX_LIGHT_COUNT];
uniform vec4 uPointLightSpecularColor[MAX_LIGHT_COUNT];
uniform float uPointLightInverseRange[MAX_LIGHT_COUNT];

flat in int  vCountPointLights;
in vec3 vPointLightDirection[MAX_LIGHT_COUNT];   

in vec3 vCameraDirection;
in vec3 vTest;
in vec3 vTest2[MAX_LIGHT_COUNT];   

vec4 computeLighting(vec3 normalVector, vec3 lightDirection, float attenuation, vec3 cameraDirection, vec4 diffuseColor, vec4 specularColor)
{
    vec4 LightWeighting = vec4(1.0, 1.0, 1.0, 1.0);  
    if(uUseLighting)
    {
       // do light calculations
       float dotNormalLightDirection = max(dot(normalVector, lightDirection), 0.0);
          
       vec4 ambientLight = uSceneAmbientColor * uMaterial.ambient;
       vec4 diffuseLight = dotNormalLightDirection * diffuseColor * uMaterial.diffuse; 
      
       // Specular
       vec3 halfVector = normalize(lightDirection + cameraDirection);
       float dotHalfLightDirection = max(dot(normalVector, halfVector), 0.0);

       vec4 specularLight = vec4(0.0);
       if(dotHalfLightDirection > 0.0){
          specularLight = pow(dotHalfLightDirection, uMaterial.shininess)* specularColor * uMaterial.specular;    
       }       
       LightWeighting = ambientLight + diffuseLight + specularLight;
    }
    return LightWeighting;
}

vec4 computeLights(vec3 normal)
{
    vec4 combinedLightWeights = vec4(0.0, 0.0, 0.0, 0.0);
    if(uUseLighting)
    {
         // normalize camer a for specular lighting
         vec3 cameraDirection = normalize(vCameraDirection);

         for(int i=0; i<vCountDirectionalLights; i++){
             vec3 lightDirection = normalize(vDirectionalLightDirection[i]);      
             combinedLightWeights += computeLighting(normal, -lightDirection, 1.0, cameraDirection, uDirectionalLightDiffuseColor[i], uDirectionalLightSpecularColor[i]); 
         }  

         for(int n=0; n<1; n++){          
             /* Calculate the light attenuation based on light radius algorithm.
                @see http://www.ozone3d.net/tutorials/glsl_lighting_phong_p4.php  
             */
             //vec3 ldir = vPointLightDirection[i] * uPointLightInverseRange[i];
             //float attenuation = clamp(1.0 - dot(ldir, ldir), 0.0, 1.0);         

             vec4 color = vec4(1.0, 1.0, 0.0, 1.0);
             if(n==0){
                  color = vec4(1.0, 0.0, 0.0, 1.0);
             }
             if(n>0){
                  color = vec4(0.0, 1.0, 0.0, 1.0);
             }

             int k = 0;
             vec3 dir = vTest2[k];

             //combinedLightWeights += computeLighting(normal, normalize(vPointLightDirection[i]), attenuation, cameraDirection, uPointLightDiffuseColor[i], uPointLightSpecularColor[i]);
             combinedLightWeights += computeLighting(normal, normalize(dir), 1.0, cameraDirection, color, uPointLightSpecularColor[n]); 
         }
    }
    return combinedLightWeights;
}

void main(void) 
{    
    // Normalize the vectors.
    vec3 normal = normalize(vNormal);
    vec4 lightWeighting = computeLights(normal);                                                                                                                                            
                                      
    vec4 textureColor = texture2D(texture, vTexCoord0);                                         
    outColor = vec4(textureColor.xyz * lightWeighting.xyz, textureColor.a);
}
Ich habe inzwischen 2 Tage mit Debuggen verbracht und alle Werte zum Shader überprüft. Sie sind in Ordnung und werden korrekt übergeben. Beim Testen im Shader bin ich auf folgende Stelle gestoßen, die zum Fehlverhalten führt:

Code: Alles auswählen

int k = 0;
vec3 dir = vTest2[k];

//combinedLightWeights += computeLighting(normal, normalize(vPointLightDirection[i]), attenuation, cameraDirection, uPointLightDiffuseColor[i], uPointLightSpecularColor[i]);
combinedLightWeights += computeLighting(normal, normalize(dir), 1.0, cameraDirection, color, uPointLightSpecularColor[n]); 
Das ganze läuft korrekt und fehlerfrei, wenn ich den Zugriff auf das Array mit einem konstanten Index mache : vec3 dir = vTest2[0]; Nutze ich jedoch wie im Snipped eine Variable mit dem Laufindexwert: vec3 dir = vTest2[k]; dann tickt der Shader aus. D.h. es wird um das x-fache langsamer gerendert (Beleuchtung sieht nach wie vor korrekt aus) und ich habe im Wechsel immer ein Bild und einen schwarzen Bildschirm. Unter Linux führt das ganze z.T. zu einem so harten Crash, dass nix mehr geht. D.h. ich kann dann nicht mal mehr die Konsole öffen um einen Prozess zu killen, sondern muss durchstarten. Unter Windows habe ich nur das gleiche Fehlverhalten. Der GDB meldet keinen Fehler, Valgrind schmiert zusammen mit dem Prozess ab :o(

Für mich sieht es so aus, als ob irgendwo ein Speicherzugriff daneben geht. Aber ich verstehe nicht wie das passieren kann und sehe auch keine Möglichkeit das zu debuggen, da das bei Shadern ja nicht geht. Ich habe im Test mal den Laufindex überprüft indem ich wie man im Snipped sehen kann, den diffusen Lichtwert in Anbhängigkeit zum Index manipuliere, der scheint i.O. zu sein, es wird immer rot gerendert. D.h. der Laufindex der Schleife ist 0 bei einer Lichtquelle und somit richtig.

Hat jemand eine Idee was hier schief laufen könnte oder wie ich möglicherweise weiter die Fehlerquelle eingrenzen kann, bin inzwischen völlig ratlos und verstehe die Welt nicht mehr.
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Re: GLSL Shader Crash bei Array Nutzung

Beitrag von snoob741 »

Um der Sache mal weiter auf den Grund zu gehen habe ich unter Windows den gDEBugger angeschmissen. Die Daten die an den Shader übergeben werden sehen
gleich aus (Somit schließe ich mal einen Fehler auf Seiten der Datenübergabe aus):

http://www.file-upload.net/download-873 ... t.txt.html //Test mit Fehler
http://www.file-upload.net/download-873 ... t.txt.html //Test ohne Fehler

Zudem habe ich mal mit FRAPS zwei Videos erstellt. Eins zeigt das Sample ohne Fehler und eines mit:
http://www.file-upload.net/download-873 ... es.7z.html

Eine Anmerkung habe ich noch. Ich habe mit gDEBugger herausgefunden dass ich im Fehlerfall beim Steppen durch die Frames im jeweils 2ten Frame einen
schwarzen Bildschirm habe (die Daten die übergeben werden sind nach wie vor korrekt). Das erklärt zumindest das Flackern. Nur kann ich mir nicht erklären
wie dies zustande kommt.
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Re: GLSL Shader Crash bei Array Nutzung

Beitrag von snoob741 »

Nach dem ich nun heute weiter am Debuggen war, bin ich mir nicht mehr so sicher, ob die Datenübergabe korrekt ist.
Aktuell mache ich folgendes um einen VEC4 Array value als Farbwert zu setzen:

Code: Alles auswählen

 case brGraphics::brShaderParameter::eType::FLOAT_ARRAY_VEC4:
 {
          // perform auto update to get actual values
          std::vector<float> data;
          param->getValues(data, 4);

          // only update uniform if we got valid data and hookID
          unsigned int size =  data.size();   
          if(size>0 && hookID!= -1){
            glUniform4fv(hookID, size, &data[0]);
          }
          break;
 }
Laut dieser Doku http://www.opengl.org/wiki/GLSL_:_common_mistakes müsste ich jedoch bei der Angabe des Counts für den glUnifom4fv Aufruf Typenabhängig die Size übergeben. D.h. im Falle eines float[4] Arrays 1. Ich benutze aber einen std::vector, wenn ich hier nun statt size size/4 übergebe scheinen keine Werte im Shader anzukommen. D.h. bisher habe ich laut gDEBugger übergeben :

Code: Alles auswählen

glGetUniformLocation(5, uPointLightDiffuseColor)
glUniform4fv(19, 4, {0, 0, 1, 1})
Dass müsste entsprechend der Doku falsch sein und eigentlich lauten:

Code: Alles auswählen

glGetUniformLocation(5, uPointLightDiffuseColor)
glUniform4fv(19, 1, {0, 0, 1, 1})
Dann habe ich allerdings keinen Farbe im Shader, d.h. die übergebene Farbe wird nicht gerendert. Ist der Aufruf nun richtig oder falsch? Irgendwie verwirrt mich dies grad. Unabhängig davon besteht das Problem nach wie vor.
Benutzeravatar
Artificial Mind
Establishment
Beiträge: 802
Registriert: 17.12.2007, 17:51
Wohnort: Aachen

Re: GLSL Shader Crash bei Array Nutzung

Beitrag von Artificial Mind »

Ich möchte kurz anmerken, dass du nicht die Lights per out in den Fragmentshader übergeben musst. Alle Uniforms sind sowohl im VS als auch im FS verfügbar.
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Re: GLSL Shader Crash bei Array Nutzung

Beitrag von snoob741 »

Danke für den Hinweis mit den Lights out. Die Uniforms muss ich aber dennoch im FS deklarieren richtig?

Übrigens habe ich folgenden workarround gefunden:

Code: Alles auswählen

#define i0  0
#define i1  1
#define i2  2

vec3 lightPos = vec3(0.0, 0.0, 0.0);
if (i==i0)
     lightPos = vPointLightDirection[i0];
     if (i==i1)
         lightPos = vPointLightDirection[i1];

         combinedLightWeights = computeLighting(normal, normalize(lightPos), attenuation, cameraDirection, uPointLightDiffuseColor[0], uPointLightSpecularColor[0]);
Dann läuft das ganze. Was mich dabei irritiert ist, dass ich dieses Verhalten nur bei einem weiteren in vec3 Array habe, nutze ich mein zuvor deklariertes vDirectionalLightDirection Array, dann läuft das ganze auch mit Laufindex. Irgendwie Strange. Kann es sein das es bei der Verwendung von Arrays eine Begrenzung gibt? Sind Arrays generell ein schlechter Ansatz? Wie soll man sonst mehrere Objekte eines Typs im Shader behandeln?
Benutzeravatar
kimmi
Moderator
Beiträge: 1405
Registriert: 26.02.2009, 09:42
Echter Name: Kim Kulling
Wohnort: Luebeck
Kontaktdaten:

Re: GLSL Shader Crash bei Array Nutzung

Beitrag von kimmi »

Welche Plattform, aktueller Treiber? Ich habe s schon erlebt, dass beim Branching solche Fehler aufgetreten sind, weil der schlechte Treiber irgend etwas verschimmbessern musste. Passiert das gleiche auf anderen Plattformen / Grafikkarten?

Gruß Kimmi
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Re: GLSL Shader Crash bei Array Nutzung

Beitrag von snoob741 »

Das gleiche passiert auf Windows (7) und Linux (Xubuntu), mit dem Unterschied, dass bei Linux dann nichts mehr geht und ich nicht mal mehr die Konsole öffnen kann um den Prozess zu killen. Hardware ist eine NVIDA GeForce 260, mehr habe ich nicht zum Testen. Ich habe inzwischen gelesen, dass es solche Probleme vereinzelt mit ATI Karten gab ... Hab auch schon die Version bis 330 hochgestellt, das Problem bleibt bestehen.

Ich habe inzwischen festgestellt, dass wohl nicht das Array sonder die For-Schleife das Problem ist. Mir scheint es so, als ob er zur Laufzeit Probleme mit der Condition hat. Nutze ich für die Anzahl der Lichtquellen eine Konstante die ich per define setze, läuft es auch mit dem PointLightDirection-Array. Langsam aber sicher bekomme ich Zuckungen :evil:
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Re: GLSL Shader Crash bei Array Nutzung

Beitrag von snoob741 »

Ich habe ne ganze weile im Netzt gesucht und scheinbar scheint es bei GLSL z.T. Probleme mit For-Schleifen zu geben, wenn kein unwinding möglich ist, da der Compiler zur Laufzeit die Condition als Konstante benötigt.

Da bei mir die Verwendung einer Konstanten nicht möglich ist, da die Anzahl der Lichtquellen auf einem Culling Ergebnis basiert, habe ich mich entschieden selber ein Unwinding für die Schleife(n) über die Lichtquellen einzubauen. Ist zwar nicht schön, da ich aber aktuell von 8 maximalen Lichtquellen ausgehe, ist das ganze akzeptabel und läuft (auch wenn der Code jetzt aufgebläht ist).
Benutzeravatar
Ingrater
Establishment
Beiträge: 103
Registriert: 18.04.2007, 21:52

Re: GLSL Shader Crash bei Array Nutzung

Beitrag von Ingrater »

schon versucht deine for schleifen so zu schreiben?

for(int i=0; i< numLights && i < 8; i++) { ... }

oder wahlweise

for(int i=0; i < 8; i++)
{
if(i >= numLights)
break;
...
}

beides gibt dem compiler eine fixe Obergrenze damit er unrollen kann.
snoob741
Beiträge: 76
Registriert: 03.04.2012, 22:36

Re: GLSL Shader Crash bei Array Nutzung

Beitrag von snoob741 »

Danke Ingrater für den Hinweis. Hab das ganze ausprobiert, leider behebt dies das Problem nicht. Ich muss wirklich auf die Schleife verzichten, dann läuft es wie erwartet.
Antworten