manu
manu

Reputation: 197

Uniform int is always zero in fragment shader

I´m doing my own engine for my game and I have had a lot of problems with shaders. One of the most annoying is that I can't calculate the lights because a condition in the fragment shader is never meet, so never sums the color of the different materials/lights. The piece of code where I found the problem is, is:

// Compute lights
vec4 totalColorLighting = vec4(0.0);
for (int indexComputeLights = 0; indexComputeLights <  MAX_LIGHTS; indexComputeLights++)
{
    if (indexComputeLights < numLights) {

numLights is an uniform passed to the fragment shader. If I write totalColorLight = vec4(1,0); outside the if statement, my models get drawn in white color. If I put it inside, like I want, the screen remains black...

In short, the code that shows the white model is:

for (int indexComputeLights = 0; indexComputeLights <  MAX_LIGHTS; indexComputeLights++)                              
{
    totalColorLight = vec4(1,0); // model is white, so here it´s entering

    if (indexComputeLights < numLights) {
                // code that never get executed
            }
}

And the code is not working:

for (int indexComputeLights = 0; indexComputeLights <  MAX_LIGHTS; indexComputeLights++)    
{
    if (indexComputeLights < numLights) {
            totalColorLight = vec4(1,0); // model is black, so here it´s NOT entering
            }
}

Other ways to get a "workarround" is to change "numLights" to, ie, a constant number like 2 or 1. I revised the vars where I pass the uniform value to the shaders via eclipse debugger, and it´s 1, so it should be entering in the if statement.

The way I upload the data to this int uniform is:

int[] vecNumLights = new int[1];
vecNumLights[0] = numLights;            
GLES20.glUniform1iv(gl_numLights_Uniform_Locator, 1, vecNumLights, 0);

Giving a quick look, does somebody knows what I´m down wrong? Do you need more code to be copied ?

By the way, I´m using the 4.4.2 Api 19 SDK

EDIT 1:

I noticed that indexComputeLights is not zero from the beginning. If I write a condition like this:

 if (indexComputeLights > 0) totalColorLight = totalColorLight + vec4(0.05); 

the model is less white the higher the condition, I mean, if the condition is

 if (indexComputeLights > 6) 

the color ends being more transparent and black, just in the reverse order I figured it should be. What´s happend here with the for-unroll ?

EDIT2:

I think my problem is very similar to this: http://www.opengl.org/discussion_boards/showthread.php/171366-problem-with-uniform-int-and-for-loop

The differences is that I didn´t notice that the for loop was endless

EDIT3:

I have discovered that numLights (which is an int uniform) is always zero. I can´t understand whats failing, because all the other uniforms looks to load fine. I´m sharing the uniform name between vertex and fragment shaders, but in vertex shaders looks to be working fine.

EDIT4:

I would like to share with you the full vertex and fragment code.

Vertex shader:

#pragma glsl

attribute vec3 position;
attribute vec3 normal;
attribute vec2 texCoord;

// matrices we'll need
uniform mat4 inversedTrasposedModelViewMatrix; 
uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;

// other uniforms, constant or data we are going to use
const int MAX_LIGHTS = 8;

struct LightSourceParameters {
mediump vec3 ambient; 
mediump vec3 lightColor;
mediump vec4 position;  
mediump float spotExponent; 
mediump float spotCutoff; // (range: [0.0,90.0], 180.0)
mediump vec3 spotDirection;
mediump float constantAttenuation; 
mediump float linearAttenuation; 
mediump float quadraticAttenuation; 
};
uniform LightSourceParameters LightSource[MAX_LIGHTS];

uniform lowp int numLights;

// out parameters to fragment shader: varyings      
varying vec3 outNormal;
varying vec2 outTextCoord;
varying vec3 outViewVector;
varying vec3 outLightVector[MAX_LIGHTS];



void main(){

// Calculate view-space position coordinate
vec4 P = modelViewMatrix * vec4(position,1.0);

// Calculate the normal in view-space
outNormal = vec3(inversedTrasposedModelViewMatrix * vec4(normal ,0.0));

// Calculate the view vector in view-space space coordinate
outViewVector = -P.xyz;

// Assign the texture coordinate
outTextCoord = texCoord;

// Calculate clip-space position of each vertex
gl_Position = projectionMatrix * P;

// Calculate light vector for all light source
for (int indexLightVector = 0; indexLightVector < MAX_LIGHTS; indexLightVector++){
    if (indexLightVector < numLights) {
        /* Si no es ambiental: la unica luz que no lleva asociada vector */
        if ((length(LightSource[indexLightVector].ambient) == 0.0) /* no ambiental */
                && (LightSource[indexLightVector].position.w != 0.0)){ /* no directional */

            /* La luz es o point o spotLight */                 
            outLightVector[indexLightVector] = vec3(modelViewMatrix*LightSource[indexLightVector].position) - P.xyz;

        }
        else if (length(LightSource[indexLightVector].ambient) == 0.0) { /* no ambiental */
            /* La luz es directional: position es un vector,
             * lo transformamos con inversedTransposedModelViewMatrix
             * y lo negamos para que vaya desde el punto a la luz y no al revés
             */
            outLightVector[indexLightVector] = - vec3(inversedTrasposedModelViewMatrix*LightSource[indexLightVector].position);

        }
    }
}


} 

Fragment shader:

#pragma glsl

precision mediump float;

const int MAX_LIGHTS = 8;

struct LightSourceParameters {
vec3 ambient; 
vec3 lightColor;
vec4 position;  
float spotExponent; 
float spotCutoff; // (range: [0.0,90.0], 180.0)
vec3 spotDirection;
float constantAttenuation; 
float linearAttenuation; 
float quadraticAttenuation; 
};
uniform LightSourceParameters LightSource[MAX_LIGHTS];

struct MaterialParameters {
vec4 emission;   
vec4 ambient;    
vec4 diffuse;
sampler2D diffuseTexture;
int hasDiffuseTexture;    
vec4 specular;
sampler2D specularTexture;
int hasSpecularTexture;
float shininess; 
};  
uniform MaterialParameters Material;

uniform lowp int numLights;

varying vec3 outNormal;
varying vec2 outTextCoord;
varying vec3 outViewVector;
varying vec3 outLightVector[MAX_LIGHTS];

/* Declaramos cabecera de funcion, necesaria para que GLSL no diga que la funcion no existe, al definirse despues de main */
vec4 computeLight(in MaterialParameters material, in LightSourceParameters lightSource, in vec3 normal, in vec2 textCoord, in vec3 lightVector, in vec3 halfVector);

void main(){

    // Normalize the incoming vectors
    vec3 normal = normalize(outNormal);
    vec3 viewVector = normalize(outViewVector);
    vec3 lightVector[MAX_LIGHTS];
    vec3 halfVector[MAX_LIGHTS];    
    // normalize lightvector, compute half vectors and lights
    vec4 totalColorLighting = vec4(0.0);
    int indexComputeLights = 0;
    for (indexComputeLights; indexComputeLights <  MAX_LIGHTS; indexComputeLights++){


        if (indexComputeLights < numLights) {

            totalColorLighting = totalColorLighting + vec4(0.05); 

            if (length(LightSource[indexComputeLights].ambient) == 0.0 ){ /* no es ambiental, que no tienen vector */
                lightVector[indexComputeLights] = normalize(outLightVector[indexComputeLights]);
            }

            if (length(LightSource[indexComputeLights].ambient) == 0.0 ){ /* no es ambiental, que no tienen half vector */
                halfVector[indexComputeLights] = normalize(outLightVector[indexComputeLights] + outViewVector);
            }

            LightSourceParameters light = LightSource[indexComputeLights];
            vec3 currentLightVector = lightVector[indexComputeLights];
            vec3 currentHalfVector = halfVector[indexComputeLights];

            /* Si la luz es ambiental, halfVector y lightVector son 
             * indefinidos para esa luz, pero da igual porque no son 
             * utilizados en el algoritmo que calcula las luces
             */
            totalColorLighting = totalColorLighting + computeLight(Material, light, normal, outTextCoord, currentLightVector, currentHalfVector);
            indexComputeLights = indexComputeLights + 1;
        }

    }

    vec4 emission = Material.emission;
    // vec4 emission = vec4(0.5);
    // totalColorLighting = vec4(0.0);
    // Compute emission material
    if (length(emission) != 0.0) { /* El material tiene un termino emisivo, es decir, emite luz. Lo andimos al total de color calculado */
        totalColorLighting = totalColorLighting + emission;
    }

    /* Devolvemos el color de fragmento calculado para almacenarlo en el framebuffer */
    gl_FragColor = totalColorLighting;
    //gl_FragColor = vec4(1.0);
}

vec4 computeLight(in MaterialParameters material, in LightSourceParameters lightSource, 
                in vec3 normal, in vec2 textCoord, in vec3 lightVector, in vec3 halfVector){

    float attenuation = 1.0; // no attenuation
    vec4 totalLightingColor = vec4(0.0); // no color

    if (length(lightSource.ambient) > 0.0){ // es luz ambiente
        totalLightingColor = vec4(lightSource.ambient, 1.0) * material.ambient;
    }
    else { // Is not ambiental light
        if (lightSource.position.w == 0.0) { // es un vector, por lo tanto es una luz direccional
            attenuation = 1.0; // no attenuation
        }
        else { // Is a point light or a spot light
            float distanceToLight = length(lightVector);
            attenuation = 1.0 / (lightSource.constantAttenuation + 
                                                (lightSource.linearAttenuation * distanceToLight) + 
                                                (lightSource.quadraticAttenuation * distanceToLight * distanceToLight));

        if (lightSource.spotCutoff <= 90.0){ /* Is a spot light */
                vec3 spotDirection = normalize(lightSource.spotDirection);
                float clampedCosine = max(0.0, dot(-lightVector, spotDirection));
                if (clampedCosine < cos(radians(lightSource.spotCutoff))){ /* outside the spotlight cone */
                    attenuation = 0.0; /* full attenuation */
                }
                else { /* inside the spotlight cone */
                    attenuation = attenuation * pow(clampedCosine, lightSource.spotExponent);
                }
            }
        }

        // Calculo de los terminos de color: diffuso y especular
        vec4 diffuseMaterialTerm = vec4(0.0, 0.0, 0.0, 1.0); /* El canal difuso será opaco y negro hasta que se sobreescriban sus datos */
        if (material.hasDiffuseTexture == 0) { /* El canal difuso no tiene textura */
            diffuseMaterialTerm =  material.diffuse;
        }
        else if (material.hasDiffuseTexture == 1){
            diffuseMaterialTerm = texture2D(material.diffuseTexture, textCoord);
        }
        vec4 diffuseReflection = attenuation * vec4(lightSource.lightColor, 1.0) * diffuseMaterialTerm * max(0.0, dot(normal, lightVector));

        vec4 specularReflection = vec4(0.0);
        if (dot(normal, lightVector) < 0.0 ) { // light source in the wrong side
            specularReflection = vec4(0.0);
        }
        else { // light source in the right side
            float NdotHV = max(dot(normal, halfVector), 0.0); /* Normal-dot-halfvector */
            vec4 specularMaterialTerm = vec4 (0.0, 0.0, 0.0, 1.0); /* El canal especular será opaco y negro hasta que se sobreescriban sus datos */
            if (material.hasSpecularTexture == 0){
                specularMaterialTerm = material.specular;
            }
            else if (material.hasSpecularTexture == 1){
                specularMaterialTerm = texture2D(material.specularTexture, textCoord);
            }
            specularReflection = attenuation * pow(NdotHV, material.shininess) * vec4(lightSource.lightColor, 1.0) * specularMaterialTerm;
        }

        totalLightingColor = diffuseReflection + specularReflection;

    }
    return totalLightingColor;

}

This is the code (Java) I´m using to upload this uniform (numLights):

GLES20.glUseProgram(gl_Program.GetProgramID()); // GetProgramID() returns 3 in my case  

int numLights = iLightList.size(); // the scene I want to render has 1 light, so this value is 1

String numLightsString = "numLights";
int gl_numLights_Uniform_Locator = gl_Program.GetUniformLocation(numLightsString); // is 83 in my program

GLES20.glUniform1i(gl_numLights_Uniform_Locator, numLights);
int error = GLES20.glGetError(); // error is always 0

Any help would be very appreciated

EDIT 5:

I found something very strange. If I comment this code in the fragment shader, the uniform numLights is loaded with the correct value:

 // vec4 emission = Material.emission;
 // if (length(emission) != 0.0) { /* El material tiene un termino emisivo, es decir, emite luz. Lo añadimos al total de color calculado */
 //     totalColorLighting = totalColorLighting + emission;
 // }

But if I do so, I lost the emission light part calculation... Any ideas ?

Upvotes: 0

Views: 6462

Answers (2)

Utumno
Utumno

Reputation: 21

I am facing identical issue - i.e. passing an int uniform to a vertex shader results in unpredictable behaviour ON SOME HARDWARE: it is working perfectly fine on Android emulator and on my Kindle Fire 2nd gen tablet, but it is failing (crashing the phone!) on Galaxy S III. I discovered the phone crashes because in the vertex shader, the loop

for(int i=0; i<u_Sinks; i++)

where u_Sinks is the int uniform, appears to be infinite. But I do pass 1 to u_Sinks like this:

GLES20.glUniform1i( mSinksH  , 1);

and of course I did make sure mSinksH gets assigned a valid value>=0:

mSinksH          = GLES20.glGetUniformLocation(mProgramH, "u_Sinks");    
Log.d("distorted", "mSinksH="+mSinksH);

and the Log prints out mSinksH=4, which looks correct to me.

Upvotes: -1

jozxyqk
jozxyqk

Reputation: 17324

It looks like GLES20.glUniform1i should be used as the GLES20.glUniform1iv case would be for uniform int numLights[1];. As I'm sure you already have, GLES20.glUniform1i should be after GLES20.glUseProgram and before drawing anything.

83 is getting pretty high for a location, though not unreasonable. I don't know much about android specs but it's certainly possible to run out of uniform locations and memory. Maybe try simplifying the shader (test with less lights) or placing the uniform higher up.

[EDIT] To continue this idea, GLSL compilers can be really unintuitive when loops and functions are concerned. Also when branching starts to get quite large. You may just be hitting some weird GLSL compiler bug. You could try unrolling the loop manually, throwing indexComputeLights < MAX_LIGHTS && indexComputeLights < numLights inside the for-loop condition should cause the generated conditionals to nest (at least on my nvidia card with current drivers). Maybe replace the expensive computeLight call with a constant just for testing. I've hit cases where generated code seems to be too large and I get an invalid result rather than a compile error.

Does lowp affect the ability to set the uniform?

Another way to get the desired outcome, which may also be faster, is to compile a shader for each number of lights. This can be done easily by injecting a #define NUM_LIGHTS x at the top of your source.

Upvotes: 1

Related Questions