• 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 Need some help with shaders in 3D

BenRK

Member
So I'm trying to come up with things like material shaders and outlines and 3D lighting. I am NOT asking for someone to make these for me, I more want a point in the right direction. I have several questions.

First thing I've noticed is shaders don't seem to apply to 3D models correctly when I use matrices to move them around. I've tried following tutorials, and they work without the translations/rotations/scaling, but not with them. Is there something I'm missing here? I assume I need to pass the matrix to the shader somehow, I was under the impression the vertex shader picked up that info automatically.

This is my current draw code for translating 3D models:
GML:
//shader stuff out here?
var mat = matrix_build(x,y,z,0,0,direction,1,1,-1)
matrix_set(matrix_world,mat)
       
//shader stuff in here?

vertex_submit(model,pr_trianglelist,-1)

matrix_set(matrix_world,matrix_build_identity())
Another thing I'm having a hard time wrapping my head around is making an outline shader in 3D. I absolutely understand it in 2D, but I'm not sure how to go about this in 3D. One way I was thinking was drawing the 3D scene to a surface (at least the objects I want to have outlines) but with the models in a solid color with no shading of any kind, and then going over this surface with a 2D outline shader before drawing the surface. Is there a better way of doing this? I have to imagine this method would leave me with plenty of depth issues to work out.

Anyway, again, not looking for anyone to make this stuff for me. I'm just needing a kick in the right direction.
 
Last edited:

Bart

WiseBart
Could you post the shader code as well? It's a bit hard to tell what's going on without seeing that.

How do shaders not apply correctly to 3D models? How do you notice?
Is there something that you expect to see that you're not seeing? Or the other way round?

C:
gl_Position = gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * vec4(in_Position, 1.0);
The above code is what you should have in the vertex shader. in_Position comes from the vertex data and gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] is a combination (the product) of the world, view and projection matrices.
You're setting the world matrix to a valid value, I'd say.

One thing that may give unexpected results is that scale by -1 along the z axis.
Does changing the cullmode using gpu_set_cullmode give a different result?

Not sure if I can help with the second question. I haven't done an outline shader yet.
 

BenRK

Member
Vertex
Code:
//
// Simple passthrough vertex shader
//
attribute vec3 in_Position;                  // (x,y,z)
//attribute vec3 in_Normal;                  // (x,y,z)     unused in this shader.
attribute vec4 in_Colour;                    // (r,g,b,a)
attribute vec2 in_TextureCoord;              // (u,v)

varying vec2 v_vTexcoord;
varying vec3 v_vPosition;
varying vec4 v_vColour;

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] * vec4(in_Position, 1.0);
    
    v_vColour = in_Colour;
    v_vPosition = in_Position;
    v_vTexcoord = in_TextureCoord;
}
Fragment
Code:
varying vec2 v_vTexcoord;
varying vec3 v_vPosition;
varying vec4 v_vColour;

void main()
{
    gl_FragColor = v_vColour * texture2D( gm_BaseTexture, v_vTexcoord );
    if (v_vPosition.x > 0.0)
    {
        gl_FragColor = vec4(0.0,0.0,1.0,1.0);
    }
}
I had made some progress since last night and figured out that the shader uses local coordinates, but even then it is still not applying how I would expect it to. This is just a basic shader I'm using to learn how to do this stuff. I guess at this point I'm just asking how one would get the translation/rotation matrices into the shader.

1584291893294.png1584291917036.png

In this case, what I'm trying to do is draw the right half of the model a solid color that would change relative to the rotation of the object. However I can't figure out how to get the shader to read the rotation from the matrix used above.

As for the -1 in the z scaling, that is to fix an issue in the modeling program I used. It's the first thing I tested and yes it isn't being read correctly either.
 

Yal

šŸ§ *penguin noises*
GMC Elder
I guess at this point I'm just asking how one would get the translation/rotation matrices into the shader.
Define a mat4 uniform and set it to the values of the matrix using shader_set_uniform_f or shader_est_uniform_f_array (if you give multiple arguments to the former, you can set array variables using a non-array source).

I'm pretty sure the transformation matrix you set in the GML code is accessible with gm_Matrices[MATRIX_WORLD], WORLD_VIEW_PROJECTION is the result from multiplying the world, view and projection matrices together (creating a matrix that takes model-space coordinates and translates them into screen-space coordinates).

To summarize the different spaces:
  • Model space is the coordinates in the model. Think of it as similar to the origin of sprites: sprites have most of their contents outside the origin, but the origin defines where the sprite begins when you want to draw it somewhere.
  • World space is model space coordinates placed at a position in the world (and potentially rotated). This is the thing you usually think of as the "normal" 3D coordinates in your game.
  • View space is coordinates relative to the camera you render the scene from. You usually skip over this entirely, but it can be useful for some effects occasionally.
  • Projection space (or screen space, to use the more common name) is coordinates on your screen - 0,0 is the top left corner, 1,1 is the bottom right corner. This is the final step of a shader: figuring out where on the screen to put these pixels. For 2D, this is pretty trivial (just adjust coordinates based on view position and how big it is) but 3D has some additional steps because you have that pesky Z coordinate to flatten out.
 

BenRK

Member
Define a mat4 uniform and set it to the values of the matrix using shader_set_uniform_f or shader_est_uniform_f_array (if you give multiple arguments to the former, you can set array variables using a non-array source).

I'm pretty sure the transformation matrix you set in the GML code is accessible with gm_Matrices[MATRIX_WORLD], WORLD_VIEW_PROJECTION is the result from multiplying the world, view and projection matrices together (creating a matrix that takes model-space coordinates and translates them into screen-space coordinates).

To summarize the different spaces:
  • Model space is the coordinates in the model. Think of it as similar to the origin of sprites: sprites have most of their contents outside the origin, but the origin defines where the sprite begins when you want to draw it somewhere.
  • World space is model space coordinates placed at a position in the world (and potentially rotated). This is the thing you usually think of as the "normal" 3D coordinates in your game.
  • View space is coordinates relative to the camera you render the scene from. You usually skip over this entirely, but it can be useful for some effects occasionally.
  • Projection space (or screen space, to use the more common name) is coordinates on your screen - 0,0 is the top left corner, 1,1 is the bottom right corner. This is the final step of a shader: figuring out where on the screen to put these pixels. For 2D, this is pretty trivial (just adjust coordinates based on view position and how big it is) but 3D has some additional steps because you have that pesky Z coordinate to flatten out.
When I try to set the uniform via shader_set_uniform_f, I get an error.
Code:
############################################################################################
FATAL ERROR in
action number 1
of Draw Event
for object obj_control:

shader_set_uniform_f argument 2 incorrect type (array) expecting a Number (YYGR)
 at gml_Object_obj_control_Draw_0 (line 30) -               shader_set_uniform_f(shader_get_uniform(shader_current(),"t_Matrix"),mat)
############################################################################################
--------------------------------------------------------------------------------------------
stack frame is
gml_Object_obj_control_Draw_0 (line 30)
And when I try setting gm_Matrices to MATRIX_WORLD, the model completely disappears. Forgive me, but this stuff is still new to me.
 

Xor

@XorDev
There are separate functions for matrices. Use something like this: shader_set_uniform_matrix_array(matrix_uniform, matrix_array);
Using the world matrix should work fine too if you aren't using it for anything else.
 

BenRK

Member
How would I apply the matrix passed to the shader? Everything I've tried keeps giving me compile errors.

Code:
Vertex Shader: shd_test0 at line 17 : '[]'
Code:
//
// Simple passthrough vertex shader
//
attribute vec3 in_Position;                  // (x,y,z)
//attribute vec3 in_Normal;                  // (x,y,z)     unused in this shader.
attribute vec4 in_Colour;                    // (r,g,b,a)
attribute vec2 in_TextureCoord;              // (u,v)

varying vec2 v_vTexcoord;
varying vec3 v_vPosition;
varying vec4 v_vColour;

uniform mat4 t_Matrix;

void main()
{
    vec4 object_space_pos = vec4( in_Position.x, in_Position.y, in_Position.z, 1.0);
    gl_Position = gm_Matrices[t_Matrix] * vec4(in_Position, 1.0);//gm_Matrices[t_Matrix] * vec4(in_Position, 1.0);
    
    v_vColour = in_Colour;
    v_vPosition = in_Position;
    v_vTexcoord = in_TextureCoord;
}
Obviously I'm doing something wrong here/not understanding the concept. I really hope someone says something, it clicks in my head, and then I feel like an idiot.
 

Xor

@XorDev
gm_Matrices is used for GM's internal matrices. When you add your own you can simply reference it by its name.
GML:
attribute vec3 in_Position;                  // (x,y,z)
//attribute vec3 in_Normal;                  // (x,y,z)     unused in this shader.
attribute vec4 in_Colour;                    // (r,g,b,a)
attribute vec2 in_TextureCoord;              // (u,v)

varying vec2 v_vTexcoord;
varying vec3 v_vPosition;
varying vec4 v_vColour;

uniform mat4 t_Matrix;

void main()
{
    vec4 transform = t_Matrix * vec4(in_Position,1.0);

    gl_Position =  gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * transform;

    v_vColour = in_Colour;
    v_vPosition = in_Position;
    v_vTexcoord = in_TextureCoord;
}
I'm working on some shader tutorials to describe all of this. You can follow my progress here if you like.
 
Last edited:

BenRK

Member
Fixing the ) typo, that just makes the model completely vanish again.

Possibly relevant draw code
GML:
        var mat = matrix_build(x,y,z,180,0,direction,20,20,20)
        matrix_set(matrix_world,mat)
        
        shader_set(shd_test0)
        shader_set_uniform_matrix_array(shader_get_uniform(shader_current(),"t_Matrix"),mat)
        
        vertex_submit(model,pr_trianglelist,-1)
        shader_reset()
 

Xor

@XorDev
That's not enough info to tell what the problem is, but make sure you set up your GML code right:

GML:
var mat = matrix_build(x,y,z,0,0,direction,1,1,-1)
shader_set(shader);
u_Matrix = shader_get_uniform(shader, "t_Matrix");
shader_set_uniform_matrix_array(u_Matrix, mat);

//Draw code
shader_reset();
 

BenRK

Member
Oook so I think applying the matrix in the draw code was what was causing it to outright vanish. I assume because the shader would be handling that from there on out. Now to figure out how to pass the rotation to the fragment shader. Every time I try to do what I think is right I keep getting compile errors.

EDIT:
I think I got it! Changed v_vPosition = in_Position; to v_vPosition = transform.xyz;

Please tell me if this is incorrect, BUT I think I got it!
 
Last edited:

BenRK

Member
Yep that's right.
Great. Now I'm trying to adjust the normal vectors based off the rotation. Been trying to learn this stuff without asking for help, but I have no idea how to inverse a matrix or if that's even the right way to go about this.
 

Xor

@XorDev
That's a bit hard to learn on your own so here's an example (untested):
GML:
attribute vec3 in_Position;
attribute vec3 in_Normal;
attribute vec4 in_Colour;
attribute vec2 in_TextureCoord;

varying vec2 v_vTexcoord;
varying vec3 v_vPosition;
varying vec4 v_vColour;

uniform mat4 t_Matrix;

void main()
{
    vec4 transform = t_Matrix * vec4(in_Position,1.0);
    gl_Position =  gm_Matrices[MATRIX_WORLD_VIEW_PROJECTION] * transform;

    v_vColour = in_Colour;
    v_vPosition = transform.xyz;
    v_vNormal = (t_Matrix * vec4(in_Normal,0.0)).xyz;
    v_vTexcoord = in_TextureCoord;
}
You can multiply the normal by the same matrix, but to make sure it doesn't get translated (moved) you make the w-component = 0.0. I'll let you look into the cause of that on your own.
This means the normal will be rotated and scaled, but not translated. Make sure you normalize the normal in the fragment shader to avoid problems.
Hope this helps!
 

BenRK

Member
That has moved over the normals, but they are inverted. Is there a way to flip them around 180 degrees in the shader? I tried the classic value * (-1) but that didn't work (not that I was seriously expecting it to).
 

Xor

@XorDev
That isn't caused by the shader so it must be something in your setup (inverted normals or negative scaling). You can rotate it 180 degrees in the shader by multiplying only two axes by negative 1: normal = normal*vec3(-1,-1,1);
 

BenRK

Member
I would not be surprised if it was an issue with the models them selves. ANYWAY, thanks, huge help. I think I can start making more progress with shaders and 3D stuff. On to applying basic reflections! I'm always jumping into the deep end.
 
  • Like
Reactions: Xor
Top