Search code examples
opengltexturesfragment-shader

Choosing between multiple shaders based on uniform variable


I want to choose from 2 fragment shaders based on the value of an uniform variable. I want to know how to do that.

I have onSurfaceCreated function which does compile and link to create program1 and glGetAttribLocation of that program1

In my onDrawFrame I do glUseProgram(program1). This function runs for every frame.

My problem is that, in the function onDrawFrame(), I get value of my uniform variable. There I have to choose between program1 or program2. But program1 is already compiled linked and all. How to do this? How will I change my program accordingly and use that since it is already done in onSurfaceCreated.?


Solution

  • Looks like you need to prepare both programs in your onSurfaceCreated function. I'll try to illustrate that with a sample code. Please organize it in a more accurate manner in your project:

    // onSurfaceCreated function:
    glCompileShader(/*shader1 for prog1*/);
    glCompileShader(/*shader2 for prog1*/);
    //...
    glCompileShader(/*shadern for prog1*/);
    glCompileShader(/*shader1 for prog2*/);
    glCompileShader(/*shader2 for prog2*/);
    //...
    glCompileShader(/*shadern for prog2*/);
    glLinkProgram(/*prog1*/);
    glLinkProgram(/*prog2*/);
    u1 = glGetUniformLocation(/*uniform in prog1*/);
    u2 = glGetUniformLocation(/*uniform in prog2*/);
    
    // onDrawFrame
    if(I_need_prog1_condition) {
        glUseProgram(prog1);
        glUniform(/*set uniform using u1*/);
    } else {
        glUseProgram(prog2);
        glUniform(/*set uniform using u2*/);
    }
    

    If you want to use the same set of uniforms form different programs (like in the code above), there exists a more elegant and up-to-date solution: uniform buffer objects! For example, you can create a buffer object with all variables that any of your shaders may need, but each of your shader programs can use only a subset of them. Moreover, you can determine unneeded (optimized-out) uniforms using glGetActiveUniform.

    Also please note that the title of your question is a bit misleading. It looks like you want to choose an execution branch not in your host code (i.e. onDrawFrame function), but in your shader code. This approach is known as uber-shader technique. There are lots of discussions about them in the Internet like these:

    http://www.gamedev.net/topic/659145-what-is-a-uber-shader/ http://www.shawnhargreaves.com/hlsl_fragments/hlsl_fragments.html

    If you decide to do so, remember that GPU is not really good at handling if statements and other branching.