• There is NO official Otland's Discord server and NO official Otland's server list. The Otland's Staff does not manage any Discord server or server list. Moderators or administrator of any Discord server or server lists have NO connection to the Otland's Staff. Do not get scammed!

[FIX] SHADERS "Jumping when player move" (Include: rewritten Shaders Module + few examples) [Default/MehahOTC]


Oct 21, 2009
Reaction score
Hello dear OTLanders!
As you know or maybe not - Shaders not work properly in default OTClient - so I got frustrated about it and wanted to fix that.
There is an issue with "jumping" shaders while LocalPlayer is moving. It's works like that because default OTClient (created by Edubart) not supporting shaders in 100% by default.
Ofcourse, there are pieces of shaders code and handling in OTC but they're missing few functions and variables like - to fix this issue for example - passing player offset into Uniform from OTC into Shader (GLSL).

To see what it means, here is comparision between old (default) and after fix, videos:
( just try look at the "clouds" when LocalPlayer moves, the "clouds" - shader - jumping )

With a little tips from @Shadowsong I managed to fix this "jumping" glitch issue. After his agreement I decided to share this solution to public on OTLand.
WARNING : Tested on Mehah OTClient v1.0

Let's get started!

1. Let's begin with /framework/ , so:
-> src/framework/graphics/paintershaderprogram.h
Add in includes :
#include <client/position.h>
After that, add in public section:
void setPosition(const Position& position){m_startPos = position; };
Position getPosition(){ return m_startPos;};
and at the end, in private section:
Position m_startPos;

2. Now (probably if I not forgot anything else from /framework/) you can move directly to client sources (src/client/)
-> src/client/luafunctions.cpp
Register those functions (find similar looking functions and paste it between/at the end of them)
g_lua.bindSingletonFunction("g_shaders", "setupMapShader", &ShaderManager::setupMapShader, &g_shaders);
g_lua.bindSingletonFunction("g_shaders", "registerShader", &ShaderManager::registerShader, &g_shaders);
-> src/client/mapview.cpp
Inside MapView::MapView() register those walkOffsets:
    m_walkDirs[ Otc::North] = PointF(0,1);
    m_walkDirs[ Otc::East] = PointF(1, 0);
    m_walkDirs[ Otc::South] = PointF(0, -1);
    m_walkDirs[ Otc::West] = PointF(1, 0);
    m_walkDirs[ Otc::NorthEast] = PointF(1, 1);
    m_walkDirs[ Otc::SouthEast] = PointF(1, -1);
    m_walkDirs[ Otc::SouthWest] = PointF( -1, -1);
    m_walkDirs[ Otc::NorthWest] = PointF(-1, 1);
Still inside - src/client/mapview.cpp - now, kinda tricky part, find an fragment of code which is:
if(m_shader && g_painter->hasShaders()
and compare this IF with mine (you need to compare because you can have it in different place or already changed, so I just paste there my "IF" and try compare it with yours)
    if(m_shader && g_painter->hasShaders() && g_graphics.shouldUseShaders()) {
        Rect framebufferRect = Rect(0,0, m_drawDimension * m_tileSize);
        const Point center = m_rectCache.srcRect.center();
        const Point globalCoord = Point(cameraPosition.x - m_drawDimension.width()/2, -(cameraPosition.y - m_drawDimension.height()/2)) * m_tileSize;
        m_shader->setUniformValue(ShaderManager::MAP_CENTER_COORD, center.x / (float)m_rectDimension.width(), 1.0f - center.y / (float)m_rectDimension.height());
        m_shader->setUniformValue(ShaderManager::MAP_GLOBAL_COORD, globalCoord.x / (float)m_rectDimension.height(), globalCoord.y / (float)m_rectDimension.height());
        m_shader->setUniformValue(ShaderManager::MAP_ZOOM, m_scaleFactor);

        Point last = transformPositionTo2D( player->getPosition(),m_shader->getPosition());
        //Reverse vertical axis.
        last.y = -last.y;

        m_shader->setUniformValue(ShaderManager::MAP_WALKOFFSET, last.x / (float)m_rectDimension.width(), last.y / (float)m_rectDimension.height());

Little tip while comparing:
- You need to bind uniformValue to the center and globalCoord of game screen dimension. Center should be as the name saying - center and globalCoord should be entire "game screen" (rectangle where game is drawing on).

Now replace function void MapView::setShader( , with it :
void MapView::setShader(const PainterShaderProgramPtr& shader, float fadein, float fadeout)
    if((m_shader == shader ))

    if(fadeout > 0.0f && m_shader) {
        m_nextShader = shader;
        m_shaderSwitchDone = false;
    } else {
        m_shader = shader;
        m_nextShader = nullptr;
        m_shaderSwitchDone = true;
    m_fadeInTime = fadein;
    m_fadeOutTime = fadeout;

    LocalPlayerPtr player = g_game.getLocalPlayer();
    if (player && shader ){

And in mapview.h , add in the end of file before "};" (in private variables, register):
std::map<Otc::Direction, PointF> m_walkDirs ;

In src/client/shadermanager.cpp in function:
PainterShaderProgramPtr ShaderManager::createFragmentShader(
before return shader;
m_shaders[name] = shader;
registerShader(name, shader);
Add function, somewhere between other "void" functions:
void ShaderManager::registerShader(const std::string& name, const PainterShaderProgramPtr& shader){
    m_shaders[name] = shader;
And in function void ShaderManager::setupMapShader( , you need to add extra Uniform (that passing walkOffset from Client to Shader and vice-versa):
shader->bindUniformLocation(MAP_WALKOFFSET, "u_WalkOffset");
In src/client/shadermanager.h , in public (at beginning of file), replace or add extra enums (just compare them with):
enum {
        ITEM_ID_UNIFORM = 10,
        SHADER_ID_UNIFORM = 10,
        MAP_CENTER_COORD = 10,
        MAP_GLOBAL_COORD = 11,
        MAP_ZOOM = 12,
        MAP_WALKOFFSET = 13,
And register in public those functions that you added few steps before, so:
void registerShader(const std::string& name, const PainterShaderProgramPtr& shader);
void setupMapShader(const PainterShaderProgramPtr& shader);
also not forget to remove them from private (that's because we want Shader to communicate with OTClient too (sending info from shader - not only OTClient to Shader) so removing it from private should do it [I don't know if it's good but works])

Register function in public in uimap.h
void setMapShader(const PainterShaderProgramPtr& shader, float fadein, float fadeout) { m_mapView->setShader(shader, fadein, fadeout); }

So, basically that's all from C++ (Sources) modifications.
If I forget something, feel free to post what I forget and I will add it to this main post.

3. Now you have to download attached to this thread file that contains game_mapshaders module, paste it in /modules/ folder and add in load-later somewhere that you load the modules (probably /modules/client/client.otmod) by default.

To test those shaders that I gave you in this package just after succesfully implement all of those things that I wrote above and module folder, simply click "CTRL+Y" while in-game and should combo-box appear in top-left of your gamescreen. Choose one of 3 shaders that I put in pack and see if it works.


Little Understanding Tutorial
When you start dealing with shaders (for future writing shaders) that you need to know:
Inside the shadername.frag file (the shader script file) you need to mention the walkOffset and use it correctly.
( Without mentioning walkOffset the shader will still be buggy - because it should "read" the offset from Uniform that you sending to him from OTClient Source (C++) after changes that you added before )

I'm not the specialist in writing shaders, but that I learnt is you have to declare it before main shader function by get Uniform from server, so:
uniform vec2 u_WalkOffset;
after that in main function of shader you need to add this offset, so you have to get base TexCoord's which are the game screen Coords that you sent from server and add to it the walkOffset that u declare, something like:
vec2 mixingScreenWithOffset = v_TexCoord + vec2(u_WalkOffset.x,u_WalkOffset.y);

tldr; Just open any shader file that are in this package that I attached to this thread and read the .frag shader code.
Probably you will understand what's going on because those shaders are simple ones.


Hope it helps a little bit and grow you in creating custom shaders on the game-screen!
Enjoy and have a nice day,



  • game_mapshaders.rar
    122 KB · Views: 37 · VirusTotal
Last edited:


Nov 18, 2015
Reaction score
Hey, thanks for sharing it with us!
When we need to search for:
if(m_shader && g_painter->hasShaders() && g_graphics.shouldUseShaders())
A piece of the code calls for "player" that wasn't declared before:
Point last = transformPositionTo2D( player->getPosition(),m_shader->getPosition());

jestem pro

That is the question
Apr 20, 2013
Reaction score
There's an error while loading the rain.frag.

around 35 line:
vec4 Game = texture2D(u_Tex0, v_TexCoord);
Should be:
vec3 Game = texture2D(u_Tex0, v_TexCoord).xyz;