• 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 Blend Modes & Shaders

DesArts

Member
I don't know a lot about shaders - but I don't think blend modes and shaders work together, is there any way to combine them in some way?

I'm trying to draw a vertex buffer with a blend mode - (was previously a primitive with a blend mode) but it also has a shader to ensure the texture repeats without it needing to be on a separate page. Enabling the shader and blend mode together gives incorrect results, and of course so does only having one enabled.

Can you even write a shader to draw a sprite or primitive with a blend?
 

Simon Gust

Member
You probably want to take a look at the fragment shader part in this case.
What kind of blendmode is it? You may be able to replicate it via code in the shader.
also, what's your vertex format, it can also screw with things.
 

Xor

@XorDev
Some blendmodes do work with shaders. I've used bm_add without any trouble. You can always draw the shader output to a surface and then use blendmodes on that or you recreate the blendmode in the shader. If you choose the latter, you'll want to pass in a surface into the shader using uniform sampler2D and then recreate the chosen blendmode with simple math.
 

DesArts

Member
@Simon Gust
The blendmode I'm currently trying to use is subtract.
The format has position, textcoord, and then custom float4 (with usage also being textcoord) used to wrap texture coordinates in the shader.

@Xor
I am thinking of the former as a last resort (a last resort cause the thing that has this blend is animated so that's just adding to the things it has to do each frame), for the latter I'd need help because I've tried and I cannot figure it.

So to apply a colour change to things below the thing being drawn I first have to pass in a surface with all of that in there, I'm assuming application surface would work?
 

Xor

@XorDev
Yeah, you could use the application surface if you want to apply the effect over everything. The exact implementation depends on your code, but I wrote some (untested) examples of what it could look like:
Code:
///Create event:
//Prepare to set the uniform sampler.
uDestination = shader_get_uniform_sampler(shader0,"Destination");

///Draw GUI event:
//This draws the shader and inputs the destination (application) surface.
shader_set(shader0);
texture_set_stage(uDestination,surface_get_texture(application_surface));
//Draw stuff.
shader_reset()

///Near the end of the vertex shader (in main()):
//Pass the screen space coordinates.
vCoord = gl_Position.xy*.5+.5;

///Top of the fragment shader:
//Get the texture and coordinates.
uniform sampler2D Destination;
varying vec2 vCoord;

///Near the end of the fragment shader (in main()):
//Output the final color. Set PixelColor as necessary.
vec4 DestColor = texture(Destination,v_vCoord);
gl_FragColor = DestColor - PixelColor;
Please excuse any mistakes or typos. I'm in a bit of a rush.
I hope this helps.
-Xor
 
Last edited:

CMAllen

Member
Yeah, you could use the application surface if you want to apply the effect over everything. The exact implementation depends on your code, but I wrote some (untested) examples of what it could look like:
Code:
///Create event:
//Prepare to set the uniform sampler.
uDestination = shader_get_uniform_sampler(shader0,"Destination");

///Draw event:
//This draws the shader and inputs the destination (application) surface.
shader_set(shader0);
texture_set_stage(uDestination,surface_get_texture(application_surface));
//Draw stuff.
shader_reset()

///Near the end of the vertex shader (in main()):
//Pass the screen space coordinates.
vCoord = gl_Position.xy*.5+.5;

///Top of the fragment shader:
//Get the texture and coordinates.
uniform sampler2D Destination;
varying vec2 vCoord;

///Near the end of the fragment shader (in main()):
//Output the final color. Set PixelColor as necessary.
vec4 DestColor = texture(Destination,v_vCoord);
gl_FragColor = DestColor - PixelColor;
Please excuse any mistakes or typos. I'm in a bit of a rush.
I hope this helps.
-Xor
You can do this, but the functionality of reading from and writing to the same surface in the same draw call is sketchy. It might work sometimes and not others. It might not work at all. It might create visual artifacts. What will happen depends on the video card, operating system, and drivers. It's a concurrency issue, and different combinations of hardware and software deal with it in different ways. Thankfully, the solution is to to use a duplicate of the read surface instead of trying to read and write at the same time -- in this case, the application surface.
 

Xor

@XorDev
You can do this, but the functionality of reading from and writing to the same surface in the same draw call is sketchy. It might work sometimes and not others. It might not work at all. It might create visual artifacts. What will happen depends on the video card, operating system, and drivers. It's a concurrency issue, and different combinations of hardware and software deal with it in different ways. Thankfully, the solution is to to use a duplicate of the read surface instead of trying to read and write at the same time -- in this case, the application surface.
That's not what is happening here. It uses the application surface, but you can draw any sprite with the shader and it will subtract it from the application surface. It's possible that it would make a difference if it's in the Draw Event instead of the Draw GUI Event, but I don't see how that would be a problem.
 

CMAllen

Member
That's not what is happening here. It uses the application surface, but you can draw any sprite with the shader and it will subtract it from the application surface. It's possible that it would make a difference if it's in the Draw Event instead of the Draw GUI Event, but I don't see how that would be a problem.
You're passing in the id of the application surface while drawing to the application surface, too. You are reading pixel data from a surface that you're actively drawing to. That's not going to have consistent results across all hardware. I've looked into the issue of having source and destination pixel information handy in a shader before (specifically to combine aspects of multiple blend modes together). That was what OpenGL's and GLSL, etc documentation stated -- performance and results will be inconsistent because that kind of operation isn't fully supported.
 
  • Like
Reactions: Xor

Xor

@XorDev
You're passing in the id of the application surface while drawing to the application surface, too. You are reading pixel data from a surface that you're actively drawing to. That's not going to have consistent results across all hardware. I've looked into the issue of having source and destination pixel information handy in a shader before (specifically to combine aspects of multiple blend modes together). That was what OpenGL's and GLSL, etc documentation stated -- performance and results will be inconsistent because that kind of operation isn't fully supported.
Oh, I get what you mean. I suppose it won't be an issue in the Draw GUI event then?
 
First, make sure you understand exactly what the blend mode is doing.

It seems really odd to me that adding a shader would affect the blend mode. It is possible something else is going on which is affecting the results, such as overlapping primitives, or you may be doing someting strange with colors, or alpha testing may be altered somehow, etc....
 
Even if you don't set a shader yourself GMS will set a standard shader. And either way the shader outputs a pixel colour which is then passed into the blending stage on the GPU drawing pipeline:
GP-Pipeline-All.png

So I as well think shaders work fine with blend modes. The problem shouldn't be there and I doubt you'll need to simulate a blend mode inside the shader as was proposed. That's usually only needed if you want to blend by an algorithm not supported by the GPU (i.e. some fancy Photoshop blend modes) or if you don't want to blend with the destination but with something else.

I guess there's other reasons why the result is not what you expect. i.e.:
  • the shader is not outputting what you expect
  • the blend mode is not doing what you think it should
  • you're using functions that alter the standard shader and your custom shader is not replicating that
 
  • Like
Reactions: Xor
Top