• 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!

Legacy GM [SOLVED] Shader makes everything invisible in 3D game

F

Freddie The Potato

Guest
Heya, I've run into a problem whilst developing a shader for a 3D game.
The shader is intended to create a kind of "colored glass" effect, wherein everything viewed through the object using the shader will be colorized with the specified color.
However, calling this shader via shader_set(); results in all objects and primitives becoming completely invisible, even the objects which do not use the shader. Esentially it leaves the screen blank.

Vertex Shader:
Code:
attribute vec3 in_Position;

varying vec4 v_Position;

void main()
{
    vec4 object_space_pos = vec4( in_Position.x, in_Position.y, in_Position.z, 1.0);
    gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * object_space_pos;
 
    v_Position = gl_Position;
}
Fragment Shader:
Code:
varying vec4 v_Position;

uniform sampler2D u_Tex;
uniform vec3 u_Color;

void main()
{
    //Pull original color from surface texture
    vec4 originalColor = texture2D(u_Tex, vec2(
        ((v_Position.x / v_Position.w) + 1.0) * 0.5,
        (-(v_Position.y / v_Position.w) + 1.0) * 0.5
        ));
 
    //Calculate greyscale value from original color
    float greyscale = ((originalColor.r * 0.2) + (originalColor.g * 0.7) + (originalColor.b * 0.1));
 
    //Get final color by multiplying the greyscale by the colorize color.
    gl_FragColor = vec4(
        u_Color.r * greyscale,
        u_Color.g * greyscale,
        u_Color.b * greyscale,
        1.0);
}
Draw Code:
Code:
    //Temporary variables
    var
        _shader = shadColorize,
        _col = make_color_rgb(150, 75, 100),
        _tex = surface_get_texture(application_surface);
    
    //Get shader uniform handles
    var
        _uTex = shader_get_sampler_index(_shader, "u_Tex"),
        _uColor = shader_get_uniform(_shader, "u_Color");
    
    shader_set(_shader);
    
        //Assign shader uniforms
        texture_set_stage(_uTex, _tex);
        shader_set_uniform_f(_uColor, color_get_red(_col) / 255, color_get_green(_col) / 255, color_get_blue(_col) / 255);
    
        //Draw the box
        d3d_draw_block(x, y, z, x + 32, y + 32, z + 32, sprite_get_texture(sprTex, 0), 1, 1);

    shader_reset();
Interestingly, if I remove the texture2D() function from the shader and set originalColor to some arbitrary value - for example: originalColor = vec4(1.0, 1.0, 1.0, 1.0) - then everything becomes visible once more.
Manually specifying texture coordinates to the texture2D() function - for example: texture2D(u_Tex, vec2(0.5, 0.5)) - has no effect; everything remains invisible.

I've tested this shader in a 2D game (albeit with a different method of calculating texture coordinates) and it worked fine with no problems.

Any help of advice would be greatly appreciated :)
Thank you.
 
Last edited by a moderator:
The only thing that I can think of that would explain this is that you are drawing something with the shader that takes up the whole view, and it ends up being black in color.
 
F

Freddie The Potato

Guest
The only thing that I can think of that would explain this is that you are drawing something with the shader that takes up the whole view, and it ends up being black in color.
Thank you for the response :)
When any object uses this shader, I'm left with a blank screen with the room background color (in this case a dark purple). Moving or rotating the camera in-game also does not reveal anything.
 
F

Freddie The Potato

Guest
I seem to have found a solution.

I'm not sure why, but simply drawing the application_surface directly into another surface somewhere else in the program prevents the issue completely and perfectly creates the desired effect.
I'm not actually using this new surface as the texture for the shader, I'm still passing the application_surface directly into the shader. In fact, passing this new second surface into the shader fails to create the desired effect (however, everything still remains visible).

Is there something obscure about how the application_surface works, or how surfaces are stored as textures that I'm not understanding?
It seems like a waste of memory to solve this issue by creating an arbitrary surface.
 
Last edited by a moderator:
F

Fishman1175

Guest
If I’m not mistaken surface_set_target() resets the view matrix sent to the shader.

I read your post several times and couldn’t see a specific issue with the code you posted.

Edit: but I’m nowhere near an expert in these things
 
F

Freddie The Potato

Guest
If I’m not mistaken surface_set_target() resets the view matrix sent to the shader.

I read your post several times and couldn’t see a specific issue with the code you posted.

Edit: but I’m nowhere near an expert in these things
Hmmm, interesting...
Thanks for the response! I'll need to look into this and see what I can find.
 

NightFrost

Member
I recall I ran into this... feature?... when I was testing out making some effects with shaders. Using application_surface in this manner made all the objects disappear (but background image was still visible). I simplified my test code to the point where I realized the shader didn't even need to touch the texture to make it happen. It was when I called texture_set_stage that objects disappeared. Commenting out that single line fixed it, which of course meant the shader could no longer use the app surface. That made me go "huh, interesting" but I never looked into why that happened and how to make it not happen.

EDIT: the offending code fragment was
Code:
var Texture = surface_get_texture(application_surface);
...
shader_set(sdr_test_3);
texture_set_stage(U_Sampler, Texture);
...
 
Ah, I didn't notice you were draing the application surface. If you are drawing the application surface onto itself, that will usually cause problems. You'll need to either copy the application surface to another surface, or else draw the application surface (with the shader) onto a different surface. Probably the best thing to do would be to turn off automatic drawing of the application surface, and draw it in the gui event using your shader.
 
Top