• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Shaders Completely Noob Shader Question

obscene

Member
Somehow I've managed to implement a half dozen shaders or so in my without learning jack squat about how they work and just messing with numbers til I get the result I want.

Well I need to make a quality setting that affects this shader by multiplying the number of samples by a global multiplier. Can someone give me a "for dummy's" crash course on how I could do this?

See image...
 

Attachments

I

icuurd12b42

Guest
ugh. too bad you did not provide the code. the most compatible method I found was to convert the loop into a bunch of ifs as no matter what I tried, using a for loop would result in pathetically slow code when it worked or failure to compile because it would try to unroll the loop which is not unrollable... or simply crash...
Code:
uniform float quality = 4.0; //default is 4. you must use a float since gm cant pass an int, max is 4.
...
vec4 func(params)
{
    return vec4 color from params
}
main()
{
    float q = quality;
    //if (q <= 0.0)
    //{
    //    return
    //}
    //else
    //{
    frag_color = func(params);
    //}
    q -= 1.0;
    if (q <= 0.0)
    {
        return;
    }
    else
    {
        frag_color += func(params);
    }
    q -= 1.0;
    if (q <= 0.0)
    {
        return;
    }
    else
    {
        frag_color += func(params);
    }
    q -= 1.0;
    if (q <= 0.0)
    {
        return;
    }
    else
    {
        frag_color += func(params);
    }

}
 

obscene

Member
I'm even more confused. Not sure you replied to the right topic lol. I just need to know how to change the value of a single variable in a shader...

const int NUM_SAMPLES = 20;
 
I

icuurd12b42

Guest
I thought you wanted to pass the quality setting to your shader... pretty sure that is what you mean still
you want to change NUM_SAMPLES from a constant to a unifor so you have control over the number of times the loop loops...
So the problem I tried to put to light is
1) You can't pass an int to the shader
2) for Loops don't work well with passed values
the shader compiler takes the loop and unrolls it. the final code is not a loop but something like my manually unrolled example. a bunch of ifs.


you could also keep the loop you have but set the count to the maximum possible loop value and pass a loop stopped as a float.

const int MAX_NUM_SAMPLES = 20;
uniform float num_samples= 4.0;
...
main()
{
float q = num_samples;
for (i = 0, i<MAX_NUM_SAMPLES, i++)
{
...
fragcolor+=sample;
q-=1.0;
if(q<1.0) return;
}
}

This will unroll properly BUT there is a good chance it will be slow compared to the code I provided before. I encountered this doing my LUX engine. final solution was to manually unroll the loop myself
 

obscene

Member
OK I follow you now... my bad. This morning I managed to figure out how to set that value as a uniform and passing it in and was just scratching my head about why it still don't work. Now I see... :p
 
I

icuurd12b42

Guest
In LUX I finally decided to look at the MaxRange for each light as opposed to passing a light count and use an extra light array item setting it's max range to 0 to toggle a stop...
Below is my manually unrolled call set... 20 tedious copy paste. 10 in the code below but the final has 20
Another oddity I found is the if somehow can't deal with checking if the range was <=0.0 which would have simplified the code. I guess in the example I gave above has a potential to be wrong, you should check if it works for yourself

Took me days to figure out how to have the darn thing work FAST with a varying number of lights
Code:
//This is called by main. all variables and arrays are globals and getLighting add to the global color variable
void DoAllLights()
{
    int i = 0;
    //do it, we got here because at lease light 0 in on
    getLighting(LightPos[i], LightColor[i], Falloff[i],MaxRange[i],ConeSpecs[i]);
    i++;
    //do the rest
    if(MaxRange[i]>0.0) //Checking if MaxRange is <0.0 fails so the else
    {
        getLighting(LightPos[i], LightColor[i], Falloff[i],MaxRange[i],ConeSpecs[i]);
    }
    else
    {
        return;
    }
    i++;
    if(MaxRange[i]>0.0) //Checking if MaxRange is <0.0 fails so the else
    {
        getLighting(LightPos[i], LightColor[i], Falloff[i],MaxRange[i],ConeSpecs[i]);
    }
    else
    {
        return;
    }
    i++;
    if(MaxRange[i]>0.0) //Checking if MaxRange is <0.0 fails so the else
    {
       getLighting(LightPos[i], LightColor[i], Falloff[i],MaxRange[i],ConeSpecs[i]);
    }
    else
    {
        return;
    }
    i++;
    if(MaxRange[i]>0.0) //Checking if MaxRange is <0.0 fails so the else
    {
       getLighting(LightPos[i], LightColor[i], Falloff[i],MaxRange[i],ConeSpecs[i]);
    }
    else
    {
        return;
    }
    i++;
    if(MaxRange[i]>0.0) //Checking if MaxRange is <0.0 fails so the else
    {
       getLighting(LightPos[i], LightColor[i], Falloff[i],MaxRange[i],ConeSpecs[i]);
    }
    else
    {
        return;
    }
    i++;
    if(MaxRange[i]>0.0) //Checking if MaxRange is <0.0 fails so the else
    {
       getLighting(LightPos[i], LightColor[i], Falloff[i],MaxRange[i],ConeSpecs[i]);
    }
    else
    {
        return;
    }
    i++;
    if(MaxRange[i]>0.0) //Checking if MaxRange is <0.0 fails so the else
    {
       getLighting(LightPos[i], LightColor[i], Falloff[i],MaxRange[i],ConeSpecs[i]);
    }
    else
    {
        return;
    }
    i++;
    if(MaxRange[i]>0.0) //Checking if MaxRange is <0.0 fails so the else
    {
       getLighting(LightPos[i], LightColor[i], Falloff[i],MaxRange[i],ConeSpecs[i]);
    }
    else
    {
        return;
    }
    i++;
    if(MaxRange[i]>0.0) //Checking if MaxRange is <0.0 fails so the else
    {
       getLighting(LightPos[i], LightColor[i], Falloff[i],MaxRange[i],ConeSpecs[i]);
    }
    else
    {
        return;
    }
    i++;
 
}
 

obscene

Member
Well, because there are some other comlications (it's not just about the loop but other settings need to be recalculated to in order to compensate for a night number of samples so the effect isn't too bright).... I've decided it might be simpler to just make 3 copies of the shader and then just swap out the one that gets called.

Another question if you don't mind... would it be better to just change every value into a constant (because I never change them) which eliminates all the setting uniform values in the draw event (surely that's wasteful right?) How would I do that for vec2 uniforms that have 2 values?
 
I

icuurd12b42

Guest
1-yeah, it's probably best to have dedicated shaders and swap them in GML. that is what is done in Modulus

2-yes it would be better to make const out of uniform you never use... or change. on the other hand you only need to set the value once with GML... you if it dont change throughout the game, a single set when the game start is enough. the shader remembers the value last sent to it.

3-const vec3 NORMAL = {0.0, 1.0, 0.0};
 

obscene

Member
OK... If I could bug you one last time. I'm trying to just take out all the uniforms and just hardcode these values. Amazingly when I do the shader appears to stop working (It does compile though.)

Here's how the shader was written originally:

varying vec2 v_vTexcoord;
varying vec4 v_vColour;

uniform vec2 lightPositionOnScreen;
uniform vec2 surfaceSize;
uniform float Density;
uniform float Weight;
uniform float Decay;
uniform float Exposure;
const int NUM_SAMPLES = 20;

void main()
{
vec2 deltaTextCoord = vec2( v_vTexcoord - vec2(lightPositionOnScreen.x/surfaceSize.x, lightPositionOnScreen.y/surfaceSize.y));
vec2 textCoo = v_vTexcoord;
deltaTextCoord *= 1.0 / float(NUM_SAMPLES) * Density;
float illuminationDecay = 1.0;
for(int i=0; i < NUM_SAMPLES ; i++)
{
textCoo -= deltaTextCoord;
vec4 sample = texture2D(gm_BaseTexture, textCoo );
sample *= illuminationDecay * Weight;
gl_FragColor += sample;
illuminationDecay *= Decay;
}
gl_FragColor *= Exposure;
}
And when I passed values to it...

shader_set(shd_god_rays_low_quality);
shader_set_uniform_f(light_pos, x-view_xview, y-view_yview);
shader_set_uniform_f(surface_size, 1920, 1080);
shader_set_uniform_f(Density, 1.05);
shader_set_uniform_f(Weight, .55);
shader_set_uniform_f(Decay, .98);
shader_set_uniform_f(Exposure, .25);


Now... simply hardcoding the values by replacing them with the numbers I usually passed through (haven't swapped out the vec2 and vec4 yet ...

varying vec2 v_vTexcoord;
varying vec4 v_vColour;

uniform vec2 lightPositionOnScreen;
uniform vec2 surfaceSize;

void main()
{
vec2 deltaTextCoord = vec2( v_vTexcoord - vec2(lightPositionOnScreen.x/surfaceSize.x, lightPositionOnScreen.y/surfaceSize.y));
vec2 textCoo = v_vTexcoord;
deltaTextCoord *= 1.0 / 20.0 * 1.05;
float illuminationDecay = 1.0;
for(int i=0; i < 20 ; i++)
{
textCoo -= deltaTextCoord;
vec4 sample = texture2D(gm_BaseTexture, textCoo );
sample *= illuminationDecay * (0.55 * 0.5);
gl_FragColor += sample;
illuminationDecay *= 0.98;
}
gl_FragColor *= 0.25;
}
Seems to me that should be simple but nothing happens after that change.
 
I

icuurd12b42

Guest
start again and add stuff gradually instead

Code:
varying vec2 v_vTexcoord;
varying vec4 v_vColour;

uniform vec2 lightPositionOnScreen;
uniform vec2 surfaceSize;
const float Density = 1.05; //first change
uniform float Weight;
uniform float Decay;
uniform float Exposure;
const int NUM_SAMPLES = 20;

void main()
{
vec2 deltaTextCoord = vec2( v_vTexcoord - vec2(lightPositionOnScreen.x/surfaceSize.x, lightPositionOnScreen.y/surfaceSize.y));
vec2 textCoo = v_vTexcoord;
deltaTextCoord *= 1.0 / float(NUM_SAMPLES) * Density;
float illuminationDecay = 1.0;
for(int i=0; i < NUM_SAMPLES ; i++)
{
textCoo -= deltaTextCoord;
vec4 sample = texture2D(gm_BaseTexture, textCoo );
sample *= illuminationDecay * Weight;
gl_FragColor += sample;
illuminationDecay *= Decay;
}
gl_FragColor *= Exposure;
}
 

obscene

Member
I tried that but I redid it just to be sure. You can see in the attached image I move the values directly over and that's the only thing in the entire shader I touched when I press "T" to test it all goes away like nothing is happening.

EDIT: Attached screenshot :p
 

Attachments

I

icuurd12b42

Guest
well looks OK, but what of the GML side? are you still trying to set the uniforms?
 

obscene

Member
I was setting the uniforms correctly but had forgot to change the get_uniforms in the create event the correct shader. :p

OK I think I'll have this working soon!
 
Top