Search code examples
iphoneiosopengl-esshaderblending

Earth Day/Night side shaders in OpneGL ES 2.0


I have an iPhone application what models the Planet Earth! I would like to make it realistic: There is a sphere object, and a Nightside and Dayside texture and shader, but it doesn't work!

My Sphere object's draw method:

    -(bool)execute:(GLuint)texture;
 {    
    glBindTexture(GL_TEXTURE_2D, texture);
    glBindVertexArrayOES(m_VertexArrayName);
    glDrawArrays(GL_TRIANGLE_STRIP, 0, m_NumVertices);
    glBindTexture(GL_TEXTURE_2D, 0);
    return true;
 }

My ViewController call method:

- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
{
    glClearColor(0.3f, 0.3f, 0.3f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    glUniformMatrix4fv(uniforms[UNIFORM_MODELVIEWPROJECTION_MATRIX], 1, 0, _modelViewProjectionMatrix.m);
    glUniformMatrix3fv(uniforms[UNIFORM_NORMAL_MATRIX], 1, 0, _normalMatrix.m);

    glUseProgram(m_NightsideProgram);
    [m_Sphere setBlendMode:0];
    [m_Sphere execute:m_EarthNightTexture.name];

    glUseProgram(m_DaysideProgram);
    [m_Sphere setBlendMode:1];
    [m_Sphere execute:m_EarthDayTexture.name];

    glCullFace(GL_FRONT);
    glEnable(GL_CULL_FACE);
    glFrontFace(GL_CW);

}

Blendmodes: 0: glBlendFunc(GL_SRC_COLOR, GL_DST_COLOR); 1: glBlendFunc(GL_ONE, GL_CONSTANT_COLOR); //The constant is a mid blue, for make it lighter a bit

Nightside fragmentshader:

precision mediump float;

varying lowp vec4 colorVarying;
varying vec2 v_texCoord;

uniform sampler2D s_texture;


void main() {
    vec4 newColor;

    newColor=1.0-colorVarying;

    gl_FragColor = texture2D(s_texture, v_texCoord)*newColor;                    
}

DaySide Fragmentshader:

precision mediump float;

varying lowp vec4 colorVarying;
varying lowp vec4 specularColorVarying;
varying vec2 v_texCoord;

uniform sampler2D s_texture;


void main() {
    vec4 finalSpecular=vec4(0,0,0,1);
    vec4 surfaceColor;
    float halfBlue;

    surfaceColor=texture2D(s_texture,v_texCoord);

    halfBlue=0.5*surfaceColor[2];

    if(halfBlue>1.0)
        halfBlue=1.0;

    if((surfaceColor[0]<halfBlue) && (surfaceColor[1]<halfBlue))
        finalSpecular=specularColorVarying;

    gl_FragColor = surfaceColor*colorVarying+colorVarying*finalSpecular;                    
}

If I use the only one of the shaders, it seens to be good, but it won't work together!


Solution

  • For a glUniform... call to take effect, there has to be a valid program bound/used, and even then it only changes the uniform value for that specific program's uniform (indentified by the uniform location). So you have to call your glUniform... functions for each program after the respective glUseProgram.

    This is why it works with only one shader program, because you don't ever bind any other program. But it is still conceptually wrong, as in this case you are relying on a specific program being already bound, which is always a source for errors (like when adding a second program), as OpenGL is a state machine.

    On the other hand a uniform variable keeps its value even when its corresponding program gets unbound (glUseProgram(0_or_any_other_program)).