Graphics Multi Texture Shader for use in 3D

Kentae

Member
GM version: Game Maker Studio 1.4
Target Platform: Windows, but can probably be used for more than that.
Last Updated: 08 / 08 / 2017

For so long did I try to find a multi texture code or shader or tutorial or whatever but I could never find one for Game Maker. However, not to long ago I found a tutorial on how to create a multi texture shader on youtube. It was for use with java though so I needed to convert it for use in Game Maker and I have fianlly succeded.

Here I will show you the code needed and some images to help me explain how it all works.
(Not quite sure if this can be called a tutorial but I didn't really find any place more suited for this on the forum)

First I want to show you guys what to expect, in other words an image that shows the shader in use:


Pretty neat huh? These are just some textures I found on google for testing btw, you can of course use your own if you want. Just make sure that the resolution is a power of 2,
32x32
64x64
128x128
256x256
512x512
and so on...
and also all you textures, including the blendmap, must have the Used for 3D box checked.

What do you need:
To use this shader you need 4 textures, like grass, dirt, stone and road as in the image above or anything else you might want.
You also need a blendMap, wich looks like this:

I know this isn't the best example as all that is going on in it is consentrated up in the top-left corner but it is the one used to create the effect you see on the example-image so I thought it would be appropriate.

How does it work:
The blendmap is basically, well, a map that determines where the diffrent textures should be shown.
The diffrent textures are all linked to the RGB values of the blendmap, for example, dirt will only be drawn where the blendmap is colored RED and stone will only be drawn where the blendmap is GREEN and so on.
You will need to set one of your textures as the base texture, wich will be drawn everywhere the blendmap shows black. It is usually best to set the most common texture as your base, like in this case it is the grass-texture.

And that is a basic explaination of how it works. Please remember that I have simply converted this shader from one I found on youtube so if my explaining is a bit lacking it is because I did not come up with this whole solution, I just figured it was about time someone shared one that workes for Game Maker.

How to use/The code:
First you of course need to pass all your textures into the shader. To do that you need to get the handles of the shader samplers to use and also store your textures in variables. This is all best done in the Create Event of the terrain/world object.
Here's the code:
Code:
u_baseTex = shader_get_sampler_index( sh_multitex, "baseTex" );
u_rTex = shader_get_sampler_index( sh_multitex, "rTex" );
u_gTex = shader_get_sampler_index( sh_multitex, "gTex" );
u_bTex = shader_get_sampler_index( sh_multitex, "bTex" );
u_blendMap = shader_get_sampler_index( sh_multitex, "blendMap" );

texBase = background_get_texture( tex_grass );
texR = background_get_texture( tex_dirt );
texG = background_get_texture( tex_stone );
texB = background_get_texture( tex_road );
texBM = background_get_texture( tex_terrain_map );
Then in the Draw Event of the same object you set the shader and pass in the textures using the handles you got and the variables holding your textures.
Here's the code:
Code:
shader_set( sh_multitex );

texture_set_stage( u_baseTex, texBase );
texture_set_stage( u_rTex, texR );
texture_set_stage( u_gTex, texG );
texture_set_stage( u_bTex, texB );
texture_set_stage( u_blendMap, texBM );


d3d_set_culling( true );
terrain_draw( tex_grass );
d3d_set_culling( true );

shader_reset();
I'm actually not quite sure if you need to set a texture for your terrain or flat plane or whatever you want your textures to be drawn on like I've done here, terrain_draw( tex_grass );
But oh well, it works, so I wont question it.

The next step will of course be to give you the actual shadercode.
It's worth mentioning that you don't have to do anything in the Vertex Shader for this to work as all the calculations take place in the Fragment Shader.
Here it is:
Code:
//
// Simple passthrough fragment shader
//
varying vec2 v_vTexcoord;
varying vec4 v_vColour;

uniform sampler2D baseTex;
uniform sampler2D rTex;
uniform sampler2D gTex;
uniform sampler2D bTex;
uniform sampler2D blendMap;

void main()
{
    // Get the colour of the blendMap
    vec4 blendMapCol = texture2D( blendMap, v_vTexcoord );
    
    // Find out how much the base texture shows through the rest (base texture is usually something like grass)
    float baseTexAmount = 1.0 - ( blendMapCol.r + blendMapCol.g + blendMapCol.b );
    // How many times the texture is repeated (Can of course be passed in as a uniform)
    vec2 tiledCoords = v_vTexcoord * 60.0;
    // Calculate how much is shown of each texture based on the blendmap
    vec4 baseTexCol = texture2D( baseTex, tiledCoords ) * baseTexAmount;
    vec4 rTexCol = texture2D( rTex, tiledCoords ) * blendMapCol.r;
    vec4 gTexCol = texture2D( gTex, tiledCoords ) * blendMapCol.g;
    vec4 bTexCol = texture2D( bTex, tiledCoords ) * blendMapCol.b;
    
    // Add all the textures together
    vec4 totalCol = baseTexCol + rTexCol + gTexCol + bTexCol;

    gl_FragColor = v_vColour * totalCol;
}
Hopefully the comments in the code and the explaining I did earier will help you understand what is going on here :)

And also I hope someone will find this usefull ^^
 

Kentae

Member
Hey! Is there a way to use more than just 3 (+ base texture).
Yes, there absolutley is :) I have been working a little with my multi-texture shader latley and found a way to add 1 additional texture without using more blendmaps.
Speaking of more blendmaps... It is possible to use 2 blendmaps and 5 textures + base texture for a total of 6 and I'm currently working on a way to add even more.
I'm not quite there yet though. I'll see to it that this thread is updated when I figure it out ;)
 

Fredrik

Member
Yes, there absolutley is :) I have been working a little with my multi-texture shader latley and found a way to add 1 additional texture without using more blendmaps.
Speaking of more blendmaps... It is possible to use 2 blendmaps and 5 textures + base texture for a total of 6 and I'm currently working on a way to add even more.
I'm not quite there yet though. I'll see to it that this thread is updated when I figure it out ;)
That's great to hear! I'm currently trying to learn more about shaders with hopes of "baking" togheter your shader with a fog shader and a light shader, if it's possible.
 

Kentae

Member
That's great to hear! I'm currently trying to learn more about shaders with hopes of "baking" togheter your shader with a fog shader and a light shader, if it's possible.
Yes, that is also very possible. I am currently doing so myself and also plan to add a shadow system in time. The shadow system was not written by me though so it will not be included here.
 

Fredrik

Member
That's cool!
I've managed to 'bake' together your shader with a fog shader following this tutorial. It was pretty straight forward after doing some research on GLSL and shaders in general :)
 

Fredrik

Member
I've been playing around abit and managed to use a total of 7 textures (6 + base), but I had to use two blendmaps tho.
Examples:
At the moment as I'm writing this I still have a few issues tho. For some reason the textures under the red value won't interpolate, and when I tried with a snow texture it also blended in with the path texture (the texture seen in screenshot 2).
 

Kentae

Member
I've been playing around abit and managed to use a total of 7 textures (6 + base), but I had to use two blendmaps tho.
Examples:
At the moment as I'm writing this I still have a few issues tho. For some reason the textures under the red value won't interpolate, and when I tried with a snow texture it also blended in with the path texture (the texture seen in screenshot 2).
Ahh yes, I had that issue too when experimenting with several blendmaps, unfortunatley I never found a good solution to this. Come to think of it though, I have not made an attempt at using more blendMaps with the new multi-texture shader I have. Might work there, I'll get back to you if I get it to work :)
 
G

Guest User

Guest
Reading project file....Error compiling vertex shader:
Error : [V]Shader_sh_multitex(32) : 'gl_FragColor' : undeclared identifier
Error : [V]Shader_sh_multitex(32) : 'assign' : cannot convert from '4-component vector of float' to 'float'
finished.
what the problem here?
 

Kentae

Member
Reading project file....Error compiling vertex shader:
Error : [V]Shader_sh_multitex(32) : 'gl_FragColor' : undeclared identifier
Error : [V]Shader_sh_multitex(32) : 'assign' : cannot convert from '4-component vector of float' to 'float'
finished.
what the problem here?
I honestly have no idea what is wrong here but that might come from me not having a look at this old code in a loooong time.
I think I actually wrote this tutorial before I even switched over to GMS 2 so there might be some issues there maybe?

On a different note:
I have since made a much better multitex shader that can use up to 16 textures with normal maps, roughness maps and metallic maps. The textures can then be painted on to the terrain procedurally, hand painted or a combination of the two. Obviously there is some different setup to the different methods.

I am planning on updating this tutorial with the new system soon. I have to finish my current project before I can do so though :) And hopefully I'll be better able to explain the whole process then.
 
G

Guest User

Guest
No, I have 1.4
GLSL ES ? Or should there be something else?
shdr_terrain -where can I get this shader?
gl_FragColor - gives an error, the project does not start
is multitexture shader a fragment shader?
everything worked out! But the problem is that the heightmap is 512 and the room size is 4096. How do I stretch to the entire map?
 
Last edited by a moderator:

Kentae

Member
No, I have 1.4
GLSL ES ? Or should there be something else?
It should be GLSL ES and GMS 1.4 is probably the best bet since that's what I used to develop it.

shdr_terrain -where can I get this shader?
The shader code needed should be in the main post of this thread.

gl_FragColor - gives an error, the project does not start
I have never encountered this error before so I'm nont able to help you with it I'm affraid.

is multitexture shader a fragment shader?
As I remember, all the code for the multitex shader is supposed to be in the fragment shader, yes.

everything worked out! But the problem is that the heightmap is 512 and the room size is 4096. How do I stretch to the entire map?
You would have to do some calculations when generating the terrain so that each quad is big enough to make the terrain stretch to the correct size.
Although room size really does not matter at all when working in 3D. Unless you plan to use the room editor to place objects ofc.
 
Top