• 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 [Solved] Can arguments be passed to shaders?

D

dannyjenn

Guest
I'm new to shaders, but I'm trying to make a simple palette swapper (for a 4-color retro game). So far it works, but my palettes are all hard-coded into the shader. I was wondering whether there was any way I could make it so that it pulls the color data from in-game variables? e.g.
Code:
varying vec2 v_vTexcoord;
varying vec4 v_vColour;

vec4 white = vec4(1.0,1.0,1.0,1.0);
vec4 lgray = vec4((208.0/255.0),(208.0/255.0),(208.0/255.0),1.0);
vec4 dgray = vec4((168.0/255.0),(168.0/255.0),(168.0/255.0),1.0);
vec4 black = vec4(0.0,0.0,0.0,1.0);
vec4 new_white = vec4((247.0/255.0),(229.0/255.0),(247.0/255.0),1.0);
vec4 new_lgray = vec4((193.0/255.0),(219.0/255.0),(211.0/255.0),1.0);
vec4 new_dgray = vec4((150.0/255.0),(202.0/255.0),(247.0/255.0),1.0);
vec4 new_black = vec4((23.0/255.0),(17.0/255.0),(17.0/255.0),1.0);
vec4 colorless = vec4(0.0,0.0,0.0,0.0);
vec4 error = vec4(1.0,0.0,0.5,1.0);

void main(){
    gl_FragColor = v_vColour*texture2D(gm_BaseTexture,v_vTexcoord);
    if(gl_FragColor.a<1.0){
        gl_FragColor = colorless;
    }
    else if(gl_FragColor==white){
        gl_FragColor = new_white;
    }
    else if(gl_FragColor==lgray){
        gl_FragColor = new_lgray;
    }
    else if(gl_FragColor==dgray){
        gl_FragColor = new_dgray;
    }
    else if(gl_FragColor==black){
        gl_FragColor = new_black;
    }
    else{
        gl_FragColor = error;
    }
}
I want to be able to change new_white, new_lgray, new_dgray, and new_black at runtime. Perhaps by setting their values to the values of some in-game global variables

edit - e.g.
Code:
global.new_white = $F7E5F7;
global.new_lgray = $D3DBC1;
global.new_dgray = $F7CA96;
global.new_black = $111117;
Somehow pass those values into the shader, and then convert them to vec4. Is this sort of thing possible?
 
Last edited by a moderator:
D

dannyjenn

Guest
All right, I've looked into uniforms and I think I get it, but I must be doing something wrong. Can somebody please fill me in?

Here's what I've got:
Code:
// create event:

// These are the four colors in the sprite (24-bit bgr-formatted color values):
#macro RAW_COLOR0 $FFFFFF
#macro RAW_COLOR1 $D0D0D0
#macro RAW_COLOR2 $A8A8A8
#macro RAW_COLOR3 $000000

// Get the shader handles:
shader_color_0 = shader_get_uniform(shd_palette_swap,"new_color_0");
shader_color_1 = shader_get_uniform(shd_palette_swap,"new_color_1");
shader_color_2 = shader_get_uniform(shd_palette_swap,"new_color_2");
shader_color_3 = shader_get_uniform(shd_palette_swap,"new_color_3");

// These are the four colors we want to use (15-bit bgr-formatted color values)
new_color_0 = $001F; // pure red
new_color_1 = $03E0; // pure green
new_color_2 = $7C00; // pure blue
new_color_3 = $3DEF; // some gray color
Code:
// draw event:

shader_set(shd_palette_swap);
shader_set_uniform_i(shader_color_0,new_color_0);
shader_set_uniform_i(shader_color_1,new_color_1);
shader_set_uniform_i(shader_color_2,new_color_2);
shader_set_uniform_i(shader_color_3,new_color_3);
draw_sprite(spr_opening_1,0,0,0); // spr_opening_1 is a sprite with only four colors in it (RAW_COLOR0, RAW_COLOR1, RAW_COLOR2, and RAW_COLOR3)
shader_reset();
Code:
// shd_palette_swap fragment shader:

varying vec2 v_vTexcoord;
varying vec4 v_vColour;

// These are our four original color values before applying the shader:
vec4 color_0 = vec4(1.0,1.0,1.0,1.0); // the same color as RAW_COLOR0 (hardcoded as vec4)
vec4 color_1 = vec4((208.0/255.0),(208.0/255.0),(208.0/255.0),1.0); // the same color as RAW_COLOR1 (hardcoded as vec4)
vec4 color_2 = vec4((168.0/255.0),(168.0/255.0),(168.0/255.0),1.0); // the same color as RAW_COLOR2 (hardcoded as vec4)
vec4 color_3 = vec4(0.0,0.0,0.0,1.0); // the same color as RAW_COLOR3 (hardcoded as vec4)

// These are our four new colors (which we pass into the shader from the game)
uniform int new_color_0;// = $7FFF; // 15-bit color value
uniform int new_color_1;// = $6B5A; // 15-bit color value
uniform int new_color_2;// = $56B5; // 15-bit color value
uniform int new_color_3;// = $0000; // 15-bit color value

void main(){
 
    // Get the color of the current pixel:
    gl_FragColor = v_vColour*texture2D(gm_BaseTexture,v_vTexcoord);
 
    // Swap its color:
    if(gl_FragColor==color_0){
        gl_FragColor = vec4(float(new_color_0&$1F)/31.0,float((new_color_0>>5)&$1F)/31.0,float((new_color_0>>10)&$1F)/31.0,1.0); // convert new_color_0 from int to vec4 and use that as the color
    }
    else if(gl_FragColor==color_1){
        gl_FragColor = vec4(float(new_color_1&$1F)/31.0,float((new_color_1>>5)&$1F)/31.0,float((new_color_1>>10)&$1F)/31.0,1.0); // convert new_color_1 from int to vec4 and use that as the color
    }
    else if(gl_FragColor==color_2){
        gl_FragColor = vec4(float(new_color_2&$1F)/31.0,float((new_color_2>>5)&$1F)/31.0,float((new_color_2>>10)&$1F)/31.0,1.0); // convert new_color_2 from int to vec4 and use that as the color
    }
    else{// if(gl_FragColor==color_3){
        gl_FragColor = vec4(float(new_color_3&$1F)/31.0,float((new_color_3>>5)&$1F)/31.0,float((new_color_3>>10)&$1F)/31.0,1.0); // convert new_color_3 from int to vec4 and use that as the color
    }
}
I run the program and it just draws the sprite as it is (no color swapping).
 

Simon Gust

Member
Not sure you can just take a number between black and white and pass it as a color.
Shaders only have r, g, b or a for color, and these are never integers. The are always from 0 to 1.

On top of that I think shader_set_uniform_i doesn't work at all, only shader_set_uniform_f does.
In this function you can make a vec4 out of it by just passing 4 values and defining the uniform as a vec4 probably.

setting gl_FragColor might also end the shader right there but I'm not sure about that.
 
D

dannyjenn

Guest
Not sure you can just take a number between black and white and pass it as a color.
Shaders only have r, g, b or a for color, and these are never integers. The are always from 0 to 1.
If my code works as I think it does, what it should be doing is:
1. extracting the each color component from the int (as mentioned, the int contains all three color values in 15-bit bgr format),
2. converting that color component (an int) to a float, and
3. scaling the float down into the 0.0 to 1.0 range
So I don't think this is the problem, though it's possible it's trying to do these steps out of order, or maybe I'm just doing something wrong...

On top of that I think shader_set_uniform_i doesn't work at all, only shader_set_uniform_f does.
shader_set_uniform_i() is in the manual, so I'm not sure why it wouldn't work. Or do you mean there's a known bug with it?

In this function you can make a vec4 out of it by just passing 4 values and defining the uniform as a vec4 probably.
Ok, I guess I'll try that. I wanted to avoid that though, since I'd then need 16 separate variables instead of just the 4. (Well, I'd only need 12 variables, since my alpha is always going to be 1.0 for what I'm doing...)

Thank you.
 
D

dannyjenn

Guest
This may only be true for GM:S 1.4
I finally got my shader working, and I can verify, in GMS2 you can use int.

The problem (as far as I could tell) was that the bitwise operators are apparently not supported. (The modulus operator also doesn't seem to be supported...) But I rewrote my shader as follows, and it now seems to work fine:
Code:
// shd_palette_swap fragment shader:

varying vec2 v_vTexcoord;
varying vec4 v_vColour;

// These are our four original color values before applying the shader:
vec4 color_0 = vec4(1.0,1.0,1.0,1.0); // the same color as RAW_COLOR0 (hardcoded as vec4)
vec4 color_1 = vec4((208.0/255.0),(208.0/255.0),(208.0/255.0),1.0); // the same color as RAW_COLOR1 (hardcoded as vec4)
vec4 color_2 = vec4((168.0/255.0),(168.0/255.0),(168.0/255.0),1.0); // the same color as RAW_COLOR2 (hardcoded as vec4)
vec4 color_3 = vec4(0.0,0.0,0.0,1.0); // the same color as RAW_COLOR3 (hardcoded as vec4)

// These are our four new colors (which we pass into the shader from the game)
uniform int new_color_0;// = $7FFF; // 15-bit color value
uniform int new_color_1;// = $6B5A; // 15-bit color value
uniform int new_color_2;// = $56B5; // 15-bit color value
uniform int new_color_3;// = $0000; // 15-bit color value

float get_red(int color_){ // This function converts the color to a float, extracts the red value, and scales that value down to the 0.0 to 1.0 range
    return mod(float(color_),32.0)/31.0;
}
float get_green(int color_){ // This function converts the color to a float, extracts the green value, and scales that value down to the 0.0 to 1.0 range
    return mod(float(color_)/32.0,32.0)/31.0;
}
float get_blue(int color_){ // This function converts the color to a float, extracts the blue value, and scales that value down to the 0.0 to 1.0 range
    return mod(float(color_)/1024.0,32.0)/31.0;
}

void main(){

    // Get the color of the current pixel:
    gl_FragColor = v_vColour*texture2D(gm_BaseTexture,v_vTexcoord);

    // Swap its color:
    if(gl_FragColor==color_0){
        gl_FragColor = vec4(get_red(new_color_0),get_green(new_color_0),get_blue(new_color_0),1.0);
    }
    else if(gl_FragColor==color_1){
        gl_FragColor = vec4(get_red(new_color_1),get_green(new_color_1),get_blue(new_color_1),1.0);
    }
    else if(gl_FragColor==color_2){
        gl_FragColor = vec4(get_red(new_color_2),get_green(new_color_2),get_blue(new_color_2),1.0);
    }
    else{ //if(gl_FragColor==color_3){
        gl_FragColor = vec4(get_red(new_color_3),get_green(new_color_3),get_blue(new_color_3),1.0);
    }
}

edit - I decided to just go ahead and use float though, lol
 
Last edited by a moderator:
Top