Custom GLSL Materials with Shadows

Okay I have tested it on a old geforce 9400 and it works fine so theoretical it shouldn’t be a problem for my Geforce Gt 540M but now I confused after I deinstalled and installed different drivers and make some other tests without a solution and I don’t where I can search to fix the problem.

The only thing that I notice is that I don’t get an error when the for loop runs only one time. So without for loop is works also for more than one shadowmap. Something like this…

  
    int i = 0; 
    shadows[i] = mainShadows(shadowMap[i], ShadowCoord[i], shadowColor[i], shadowmaptype[i], shadowactive[i], shadowbias[i], bleedbias[i]);
    i = 1;
    shadows[i] = mainShadows(shadowMap[i], ShadowCoord[i], shadowColor[i], shadowmaptype[i], shadowactive[i], shadowbias[i], bleedbias[i]);

Thanks for information guys! :slight_smile:

@Maujoe: Glad you found a workaround. What if you declare vec4 shadows outside the main function (with the uniforms)?.. I’m sorry I can’t make some tests (because I can’t reproduce your bug) so my propositions may be irrelevant.

vec4 shadows outside the main function don’t help, maybe it’s a problem with the unrolling of the for loop or something like that, I don’t know.
But thanks for your help maybe someone else know this error and can help me.
At the moment the workaround works and it’s easier than add or delete different uniforms.

Another funny workaround for the problem ^^


    vec4 shadows = vec4(1.0);
    
    int i[3];
    for (int n = 0; n < 3; n++)
        i[n]=n;
    
    for (int a = 0; a < 3; a++)
        shadows *= mainShadows(shadowMap[i[a]], ShadowCoord[i[a]], shadowColor[i[a]], shadowmaptype[i[a]], shadowactive[i[a]], shadowbias[i[a]], bleedbias[i[a]]);

But together with a #define statement its easy to change the number of shadowmaps.

Oh I notice that the shadow map is moving with the edge of the camera and also when the camera or the object moves fast there is a plane visible. Seems like there is something not correct because I test it on different computers:


I can’t reproduce the bug with green square. However, I have a problem with camera (shadows are not updated with the camera movement)… I didn’t realized. I update once I find a solution (the problem only occurs with multilamps)

Maujoe: Hi, I changed the way the uniforms are passed to the shader with the help of Kupoman (using struct Light instead of variables arrays): https://drive.google.com/file/d/0B3GouQIyoCmrbE9zUUtZZDJwd1E/view?usp=sharing
This solved my problem. I hope this solve your issues too… Please tell me if this works for you :slight_smile:
This still needs to be optimized and cleaned up…
(I updated the summary page 1)

:+1

Nice work gentleman !

Okay, It’s could be a little bit complicated

So I use blender 2.77 RC2 and my computer is Windows 7 with Geforce GT 540M, I also have tested the files on two other computers: apple with Mac os and Geforce 8600M GT and an apple with Mac os/Win 7 and Geforce 9400GT.

On all computers/every os the example for one shadowmap works but with the same bugs that I descriped in my last post.

Now to the several shadowmaps examples:

On my computer with struct I get the same errer as with the arrays:

0(95) : error C5208: Sampler needs to be a uniform (global or parameter to main)
, need to inline function or resolve conditional expression

The same message I got on the apple computer in win7 when I using struct but arrays work except the bugs that I descripted.

On Mac os blender closed on both computers with error:

fatal error C9999
Array access to non array

Maybe an apple /-driver problem I don’t know. I don’t use them often and it also are not the newest GPUs but in blender Materials the shadows work fine.

So yeah, it’s seems not so easy.
Maybe the problem with the arrays/struct is some kind of unroll problem because unrolling the for loop fix the problem on my computer (I don’t test the others).
You have a newer GPU maybe this kind of GPUs haven’t problem with that… I don’t know…

If necessary we can use single uniforms for every shadowmap because that seems to work on every system (beside the other bugs). I also don’t think that we will use so many shadowmaps so there is no big problem.

But It would nice when the bug with the flickering of the shadowmaps could be fixed.
I can’t find a solution and if you haven’t this bug it’s hard to do something.

This doesn’t seem to be working for me with the second example .blend (pass via struct):


Blender Game Engine Started
---- GLSL Program ----
Fragment info
-------------
0(99)  : error C5208: Sampler needs to be a uniform (global or parameter to  main), need to inline function or resolve conditional expression
0(99)  : error C5208: Sampler needs to be a uniform (global or parameter to  main), need to inline function or resolve conditional expression

---- GLSL program failed to link ----

Blender 2.78a, Nvidia GTX 460, archlinux

Hello, I can’t reproduce your bug but maybe this: https://www.opengl.org/discussion_boards/showthread.php/168302-New-NV-driver-breaks-my-app-(190-38) can help?

Curious. If I manually inline the function and unroll the loop, then the first shadow (the blue one) works. The rest still fail for some reason:


Blender Game Engine Started
Invalid uniform value: lights[1].shadowMap.
Invalid uniform value: lights[1].shadowColor.
Invalid uniform value: lights[1].shadowbias.
Invalid uniform value: lights[1].shadowmaptype.
Invalid uniform value: lights[1].shadowactive.
Invalid uniform value: lights[2].shadowMap.
Invalid uniform value: lights[2].shadowColor.
Invalid uniform value: lights[2].shadowbias.
Invalid uniform value: lights[2].shadowmaptype.
Invalid uniform value: lights[2].shadowactive.

Here’s the inlined and unrolled .blend:
CustomGLSLMaterialsWithShadowsMultiLampsarrayOfStruct_unrolled.blend (172 KB)

Can anyone confirm if it works as expected on a machine on which the loop was working correctly? (to check if I made a mistake when unrolling)

Oh yes, and I’m using nvidia’s proprietary drivers, v367.35

I have the same output. Invalid uniform values. I’ll maybe try to make the file with struct working on my laptop if I have time and motivation… Else maybe there are other answers on the net about “error C5208: Sampler needs to be a uniform (global or parameter to main)”

This fault is caused by line 198 for (int i = 0; i < 1; i++) because only one light is calculated in the shader so only lights[0] is existing. But the python codes trys to write with shader.setUniformXX to light lights[1] and lights[2] which is now not existing. So use --> for (int i = 0; i < numlights; i++)

For all that have problems with this shader.
Actual shader compilers supports loop unrolling (update your driver). Sometimes the problem is also caused by the structure.
So if you have problems like this then, first update your driver and then,

  • Try to replace the structure with arrays (most compilers supports it).
  • If you have an old shader compiler, you need to unroll all loops.

Code structures replaced with arrays and all loops unrolled.


VertexShader = """
////////////////////Shadows////////////////////////

uniform mat4 m_c2l[10];
varying vec4 ShadowCoord[3];
////////////////////Shadows////////////////////////

void main(void)
{
    /////////////////Shadows///////////////////////
    vec4 position = gl_ModelViewMatrix * gl_Vertex;
//    for (int i = 0; i &lt; numlights; i++)

    ShadowCoord[0] = m_c2l[0] * position;
    ShadowCoord[1] = m_c2l[1] * position;
    ShadowCoord[2] = m_c2l[2] * position;

    gl_Position = gl_ProjectionMatrix * position;
    gl_FrontColor = gl_Color;
    ////////////////Shadows//////////////////////// 
}
"""

FragmentShader = """
///////////////////Shadows/////////////////////////
uniform sampler2DShadow shadowMap[3];
uniform float shadowbias[3];
uniform int shadowmaptype[3];
uniform vec3 shadowColor[3];
uniform int shadowactive[3];

varying vec4 ShadowCoord[3];

//////////////////Shadows//////////////////////////


//////////////////Shadows///////////////////////////
float shadowbuf(sampler2DShadow shadowmap, vec4 co, float shadowbias)
{
    float result = 0.0;

    //vec4 co = shadowpersmat*vec4(rco, 1.0); //from original Blender code
    //co.z -= shadowbias*co.w; //from original Blender code ???
    co.z -= shadowbias/co.w;
    
    if (co.w &gt; 0.0 && co.x &gt; 0.0 && co.x/co.w &lt; 1.0 && co.y &gt; 0.0 && co.y/co.w &lt; 1.0)
        result = shadow2DProj(shadowmap, co).x;
    else
        result = 1.0;

    return result;
}

float shadowbuf_vsm(sampler2DShadow shadowmap, vec4 co, float shadowbias)
{
    float result = 0.0;

    if (co.w &gt; 0.0 && co.x &gt; 0.0 && co.x/co.w &lt; 1.0 && co.y &gt; 0.0 && co.y/co.w &lt; 1.0)
    {
        //vec2 moments = texture2DProj(shadowmap, co).rg;
        vec2 moments = shadow2DProj(shadowmap, co).rg;
        float dist = co.z/co.w;
        float p = 0.0;
        
        //if(dist &lt;= moments.x)
        //  p = 1.0;

        float variance = moments.y - (moments.x*moments.x);
        variance = max(variance, shadowbias/10.0);

        float d = moments.x - dist;
        float p_max = variance / (variance + d*d);

        // Now reduce light-bleeding by removing the [0, x] tail and linearly rescaling (x, 1]
        p_max = clamp((p_max)/(1.0), 0.0, 1.0);

        result = max(p, p_max);
    }
    else
    {
        result = 1.0;
    }

    return result;
}

vec4 mainShadows(sampler2DShadow shadowmap, vec4 ShadowCoord, vec3 shadowColor, int shadowmaptype, int shadowactive, float shadowbias)
{
    vec4 result = vec4(1.0);
    float bias;
    float shadfac;
    if (bool(shadowactive)) {
        if (shadowmaptype == 1) // variance
        {
            shadfac = shadowbuf_vsm(shadowmap, ShadowCoord, shadowbias);
        }
        else                    // simple
        {
            shadfac = shadowbuf(shadowmap, ShadowCoord, shadowbias);
        }   
        
        result -= (1.0 - shadfac) * (vec4(1.0) - vec4(shadowColor, 0.0));
    }
    else {
        result = vec4(0.0);
    }
    return result;
}
//////////////////////Shadows////////////////////////
void main()
{


/////////////////////Shadows/////////////////////////
    vec4 shadows[10];
    vec4 result = vec4(1.0);
    
    float bias = 0.0;
    float shadfac = 0.0;

    if (bool(shadowactive[0])) {
        if (shadowmaptype[0] == 1) // variance
        {
            shadfac = shadowbuf_vsm(shadowMap[0], ShadowCoord[0], shadowbias[0]);
        }
        else                    // simple
        {
            shadfac = shadowbuf(shadowMap[0], ShadowCoord[0], shadowbias[0]);
        }   
        
        result -= (1.0 - shadfac) * (vec4(1.0) - vec4(shadowColor[0], 0.0));
    }
    else {
        result = vec4(0.0);
    }
    shadows[0] = result;


    if (bool(shadowactive[1])) {
        if (shadowmaptype[1] == 1) // variance
        {
            shadfac = shadowbuf_vsm(shadowMap[1], ShadowCoord[1], shadowbias[1]);
        }
        else                    // simple
        {
            shadfac = shadowbuf(shadowMap[1], ShadowCoord[1], shadowbias[1]);
        }   
        
        result -= (1.0 - shadfac) * (vec4(1.0) - vec4(shadowColor[1], 0.0));
    }
    else {
        result = vec4(0.0);
    }
    shadows[1] = result;

    if (bool(shadowactive[2])) {
        if (shadowmaptype[2] == 1) // variance
        {
            shadfac = shadowbuf_vsm(shadowMap[2], ShadowCoord[2], shadowbias[2]);
        }
        else                    // simple
        {
            shadfac = shadowbuf(shadowMap[2], ShadowCoord[2], shadowbias[2]);
        }   
        
        result -= (1.0 - shadfac) * (vec4(1.0) - vec4(shadowColor[2], 0.0));
    }
    else {
        result = vec4(0.0);
    }
    shadows[2] = result;


/////////////////////Shadows/////////////////////////

    /////Write your own material shader here/////////
    vec4 materialColor = vec4(0.5, 0.7, 0.9, 1.0);
    
    materialColor *= shadows[0];
    materialColor *= shadows[1];
    materialColor *= shadows[2];


    gl_FragColor = materialColor;
}
"""

Ah right, I had a feeling I had forgotten something, thanks.

I can’t update my driver due to an unrelated bug in the latest version, but it’s not that outdated (released in july of this year).
Indeed, it doesn’t seem like loops themselves are the issue; if I just put “numlights” back in place of “1” in the forloop of my unrolled version, all works as expected (albeit with some warnings):


0(97) : warning C7050: "shadows[3..9]" might be used before being initialized

When I tried the fully unrolled version you provided, I got this:


Invalid uniform value: lights[0].shadowMap.
Invalid uniform value: lights[0].shadowColor.
Invalid uniform value: lights[0].shadowbias.
Invalid uniform value: lights[0].shadowmaptype.
Invalid uniform value: lights[0].shadowactive.
Invalid uniform value: lights[1].shadowMap.
Invalid uniform value: lights[1].shadowColor.
Invalid uniform value: lights[1].shadowbias.
Invalid uniform value: lights[1].shadowmaptype.
Invalid uniform value: lights[1].shadowactive.
Invalid uniform value: lights[2].shadowMap.
Invalid uniform value: lights[2].shadowColor.
Invalid uniform value: lights[2].shadowbias.
Invalid uniform value: lights[2].shadowmaptype.
Invalid uniform value: lights[2].shadowactive.
Invalid uniform value: numlights.
Invalid uniform value: lights[0].m_c2l.

For the arrays you need to change the Python code too.

Here the full code with the necessary Python changes.


# Shader code implemented by HG1, taken from Blender sources
# Works since this commit https://developer.blender.org/D1690
# (works for sun/spot variance/simple shadows) 01/19/2016
# Youle with the help of Kupoman for MultiLamps

import bge
from bgl import *

VertexShader = """
////////////////////Shadows////////////////////////

uniform mat4 m_c2l[10];
varying vec4 ShadowCoord[3];
////////////////////Shadows////////////////////////

void main(void)
{
    /////////////////Shadows///////////////////////
    vec4 position = gl_ModelViewMatrix * gl_Vertex;
//    for (int i = 0; i &lt; numlights; i++)

    ShadowCoord[0] = m_c2l[0] * position;
    ShadowCoord[1] = m_c2l[1] * position;
    ShadowCoord[2] = m_c2l[2] * position;

    gl_Position = gl_ProjectionMatrix * position;
    gl_FrontColor = gl_Color;
    ////////////////Shadows//////////////////////// 
}
"""

FragmentShader = """
///////////////////Shadows/////////////////////////
uniform sampler2DShadow shadowMap[3];
uniform float shadowbias[3];
uniform int shadowmaptype[3];
uniform vec3 shadowColor[3];
uniform int shadowactive[3];

varying vec4 ShadowCoord[3];

//////////////////Shadows//////////////////////////


//////////////////Shadows///////////////////////////
float shadowbuf(sampler2DShadow shadowmap, vec4 co, float shadowbias)
{
    float result = 0.0;

    //vec4 co = shadowpersmat*vec4(rco, 1.0); //from original Blender code
    //co.z -= shadowbias*co.w; //from original Blender code ???
    co.z -= shadowbias/co.w;
    
    if (co.w &gt; 0.0 && co.x &gt; 0.0 && co.x/co.w &lt; 1.0 && co.y &gt; 0.0 && co.y/co.w &lt; 1.0)
        result = shadow2DProj(shadowmap, co).x;
    else
        result = 1.0;

    return result;
}

float shadowbuf_vsm(sampler2DShadow shadowmap, vec4 co, float shadowbias)
{
    float result = 0.0;

    if (co.w &gt; 0.0 && co.x &gt; 0.0 && co.x/co.w &lt; 1.0 && co.y &gt; 0.0 && co.y/co.w &lt; 1.0)
    {
        //vec2 moments = texture2DProj(shadowmap, co).rg;
        vec2 moments = shadow2DProj(shadowmap, co).rg;
        float dist = co.z/co.w;
        float p = 0.0;
        
        //if(dist &lt;= moments.x)
        //  p = 1.0;

        float variance = moments.y - (moments.x*moments.x);
        variance = max(variance, shadowbias/10.0);

        float d = moments.x - dist;
        float p_max = variance / (variance + d*d);

        // Now reduce light-bleeding by removing the [0, x] tail and linearly rescaling (x, 1]
        p_max = clamp((p_max)/(1.0), 0.0, 1.0);

        result = max(p, p_max);
    }
    else
    {
        result = 1.0;
    }

    return result;
}

vec4 mainShadows(sampler2DShadow shadowmap, vec4 ShadowCoord, vec3 shadowColor, int shadowmaptype, int shadowactive, float shadowbias)
{
    vec4 result = vec4(1.0);
    float bias;
    float shadfac;
    if (bool(shadowactive)) {
        if (shadowmaptype == 1) // variance
        {
            shadfac = shadowbuf_vsm(shadowmap, ShadowCoord, shadowbias);
        }
        else                    // simple
        {
            shadfac = shadowbuf(shadowmap, ShadowCoord, shadowbias);
        }   
        
        result -= (1.0 - shadfac) * (vec4(1.0) - vec4(shadowColor, 0.0));
    }
    else {
        result = vec4(0.0);
    }
    return result;
}
//////////////////////Shadows////////////////////////
void main()
{


/////////////////////Shadows/////////////////////////
    vec4 shadows[10];
    vec4 result = vec4(1.0);
    
    float bias = 0.0;
    float shadfac = 0.0;

    if (bool(shadowactive[0])) {
        if (shadowmaptype[0] == 1) // variance
        {
            shadfac = shadowbuf_vsm(shadowMap[0], ShadowCoord[0], shadowbias[0]);
        }
        else                    // simple
        {
            shadfac = shadowbuf(shadowMap[0], ShadowCoord[0], shadowbias[0]);
        }   
        
        result -= (1.0 - shadfac) * (vec4(1.0) - vec4(shadowColor[0], 0.0));
    }
    else {
        result = vec4(0.0);
    }
    shadows[0] = result;


    if (bool(shadowactive[1])) {
        if (shadowmaptype[1] == 1) // variance
        {
            shadfac = shadowbuf_vsm(shadowMap[1], ShadowCoord[1], shadowbias[1]);
        }
        else                    // simple
        {
            shadfac = shadowbuf(shadowMap[1], ShadowCoord[1], shadowbias[1]);
        }   
        
        result -= (1.0 - shadfac) * (vec4(1.0) - vec4(shadowColor[1], 0.0));
    }
    else {
        result = vec4(0.0);
    }
    shadows[1] = result;

    if (bool(shadowactive[2])) {
        if (shadowmaptype[2] == 1) // variance
        {
            shadfac = shadowbuf_vsm(shadowMap[2], ShadowCoord[2], shadowbias[2]);
        }
        else                    // simple
        {
            shadfac = shadowbuf(shadowMap[2], ShadowCoord[2], shadowbias[2]);
        }   
        
        result -= (1.0 - shadfac) * (vec4(1.0) - vec4(shadowColor[2], 0.0));
    }
    else {
        result = vec4(0.0);
    }
    shadows[2] = result;


/////////////////////Shadows/////////////////////////

    /////Write your own material shader here/////////
    vec4 materialColor = vec4(0.5, 0.7, 0.9, 1.0);
    
    materialColor *= shadows[0];
    materialColor *= shadows[1];
    materialColor *= shadows[2];


    gl_FragColor = materialColor;
}
"""

scene = bge.logic.getCurrentScene() # The scene
object = scene.objects['Plane'] # The shader object
if "lightList" not in scene:
    scene["lightList"] = [lamp for lamp in scene.objects if lamp.name.startswith("Lamp")]
    scene["numlights"] = len(scene["lightList"])


for mesh in object.meshes:
    for material in mesh.materials:
        shader = material.getShader()
        if shader != None:
            if not shader.isValid():
                shader.setSource(VertexShader, FragmentShader, True)
                #######################Shadows##############################
                # bind shadowmaps
                for i in range(scene["numlights"]):
                    glActiveTexture(GL_TEXTURE0 + scene["lightList"][i].shadowBindId)
                    glBindTexture(GL_TEXTURE_2D, scene["lightList"][i].shadowBindId)
                
                for i, light in enumerate(scene["lightList"]):              
                    shader.setUniform1i("shadowMap[%s]"%i, light.shadowBindId)
                    shader.setUniform3f("shadowColor[%s]"%i, *light.shadowColor)
                    shader.setUniform1f("shadowbias[%s]"%i, light.shadowBias * 0.005)
                    #shader.setUniform1f("shadowbleedbias[%s]"%i, light.shadowBleedBias)
                    shader.setUniform1i("shadowmaptype[%s]"%i, light.shadowMapType)
                    shader.setUniform1i("shadowactive[%s]"%i, light.useShadow)
                
                #shader.setUniform1i('numlights', scene["numlights"])                 
            
            for i, light in enumerate(scene["lightList"]):
                shader.setUniformMatrix4("m_c2l[%s]"%i, light.shadowMatrix)
            #########################Shadows#################################
                
            # bind your own uniforms here

Thanks very much @HG1!

I made a .blend with HG1 code updated for people who had issues with previous code: CustomGLSLMaterialsWithShadowsHG1Update.blend (157 KB)

I update the summary on page 1.

hm I still can’t use it.:frowning:

The problem with multiple lights are fixed but the problem with the flickering and the strange behavior sometimes when the camera or the lamp move are still there also with the example that use one shadow map (UPDATE by HG1: 20/01/2016).

I tested it on different OSs and with different drivers. Does really no one else have the problem? For me it’s looks more like a general problem with the shadowmatrix or another calculation.

I made a video that shows what happening