Playing with Low-poly

So, I decided to explore a low-poly route for my game after being inspired by Kingdoms and Castles. I think the SSAO post process shader here:, still needs a bit of work. So I’m trying to tweak it as much as possible.

(edit, updated sampling for SSAO)


Looks nice. I like the UI styling :+1:

Thanks @bmcorser!

For anyone interested, here are the tweaks I made to the SSAO shader. They’re not crazy tweaks though, just changing the value, and removing the “depth” transition. I can’t say I fully understand the shader, however the paper referenced in it does help. The low/high sample settings can cause a performance hit.

#include "Uniforms.glsl"
#include "Samplers.glsl"
#include "Transform.glsl"
#include "ScreenPos.glsl"
varying highp vec2 vScreenPos;
void VS()
    mat4 modelMatrix = iModelMatrix;
    vec3 worldPos = GetWorldPos(modelMatrix);
    gl_Position = GetClipPos(worldPos);
    vScreenPos = GetScreenPosPreDiv(gl_Position);
uniform highp vec2 cScreenSize;
// Port from:
// see T M?ller, 1999: Efficiently building a matrix to rotate one vector to another
mat3 rotateNormalVecToAnother(vec3 f, vec3 t) {
    vec3 v = cross(f, t);
    float c = dot(f, t);
    //float h = (1.0 - c) / (1.0 - c * c);
    float h = (c) / (c * c);
    return mat3(c + h * v.x * v.x, h * v.x * v.y + v.z, h * v.x * v.z - v.y,
                h * v.x * v.y - v.z, c + h * v.y * v.y, h * v.y * v.z + v.x,
                h * v.x * v.z + v.y, h * v.y * v.z - v.x, c + h * v.z * v.z);
vec3 normal_from_depth(float depth, highp vec2 texcoords) {
    // One pixel: 0.001 = 1 / 1000 (pixels)
    const vec2 offset1 = vec2(0.0, 0.001);
    const vec2 offset2 = vec2(0.001, 0.0);
    float depth1 = DecodeDepth(texture2D(sEmissiveMap, texcoords + offset1).rgb);
    float depth2 = DecodeDepth(texture2D(sEmissiveMap, texcoords + offset2).rgb);
    vec3 p1 = vec3(offset1, depth1 - depth);
    vec3 p2 = vec3(offset2, depth2 - depth);
    highp vec3 normal = cross(p1, p2);
    normal.z = -normal.z;
    return normalize(normal);
void PS()
    const float aoStrength = 2.5;
    highp vec2 tx = vScreenPos;
    highp vec2 px = vec2(1.0 / cScreenSize.x, 1.0 / cScreenSize.y);
    float depth = DecodeDepth(texture2D(sEmissiveMap, vScreenPos).rgb);
    vec3  normal = normal_from_depth(depth, vScreenPos);
    // radius is in world space unit
    const float radius = 0.025;

    //float farClip = 1.0;
    //float nearClip = 0.1;
    //float zRange = radius / (farClip - nearClip);
    float zRange = radius / (cFarClipPS - cNearClipPS);
    // calculate inverse matrix of the normal by rotate it to identity
    mat3 InverseNormalMatrix = rotateNormalVecToAnother(normal, vec3(0.0, 0.0, 1.0));
    // result of line sampling
    // See Loos & Sloan: Volumetric Obscurance
    float hemi = 0.0;
    float maxi = 0.0;
    float screenSample = 2;
    int lowSample = -6;
    int highSample = 6;
    for (int x = lowSample; x <= highSample; ++x) {
        for (int y = lowSample; y <= highSample; ++y) {
            // make virtual sphere of unit volume, more closer to center, more ambient occlusion contributions
            float rx = 0.03 * float(x);
            float ry = 0.03 * float(y);
            float rz = sqrt(1.0 - rx * rx - ry * ry);
            highp vec3 screenCoord = vec3(float(x) * px.x, float(y) * px.y, 0.0);
            // 0.25 times smaller when farest, 5.0 times bigger when nearest.
            //highp vec2 coord = tx + (5.0 - 4.75 * depth) * screenCoord.xy;
            highp vec2 coord = tx + (screenSample * screenCoord.xy);
            // fetch depth from texture
            screenCoord.z = DecodeDepth(texture2D(sEmissiveMap, coord).rgb);
            // move to origin
            screenCoord.z -= depth;
            // ignore occluders which are too far away
            //if (screenCoord.z < -zRange) continue;

            // Transform to normal-oriented hemisphere space
            highp vec3 localCoord = InverseNormalMatrix * screenCoord;
            // ralative depth in the world space radius
            float dr = localCoord.z / zRange;
            // calculate contribution
            float v = clamp(rz + dr * aoStrength, 0.0, 2.0 * rz);

            maxi += rz;
            hemi += v;

    float ao = clamp(hemi / maxi, 0.0, 1.0);
    ao = mix(ao, 1.0, 0.4);
    vec3 finalColor = texture2D(sDiffMap, vScreenPos).rgb * ao;
    gl_FragColor = vec4(finalColor, 1.0);

Finished up a few menus, working on the “in-game” map editing.


New color correction and water shader. Also tweaked the values a bit more on the ambient occlusion shader. Wrapped up the map editing system as well, so this weekend I hope to start adding some characters onto the map.


Over the weekend I decided to use textures for the map instead of solid colors. So now the forest area has more of a dirt texture than a grass texture. However, the buildings will still remain texture-less, and I’ll just use the material colors.


Looking really nice!
Maybe cut down on the AO a bit?

Yeah, I believe you’re right! In my settings.txt file I’ve added a “high, medium, low” AO setting for anyone to adjust to their taste (or increase FPS on their machine). There is a setting to turn it off completely as well. :smiley:

I do think it adds a lot, but to me it feels wrong for the AO around the buildings to be darker than the shadows.

Ah, I see what you’re saying. Yeah, last night I did make a change to the shadow intensity, but I didn’t adjust the AO along with it. I’ll try to fix that this week.

That’s so beautiful! Is that cursor pointer a 3D model? If so, kudos for the creativity!

I cannot take credit for the 3D cursor, that along with the shore edges were inspired by Kingdom and Castles (equally beautiful game). However, my game isn’t a city builder, but rather a strategy game. Thanks for the compliment, I wish I could take 100% of the credit :smiley:

Been having a lot of fun scripting out some characters and events. This weekend I should proper cities and bandit camps in place.

The northmen know not yet how to die…

But soon they’ll know some fear


Thinks are starting to feel more like a fully playable game. This week has been redoing/finishing a lot of the UI. Icons are from which has one of the best icon repository of icons I’ve seen :slight_smile:

The city walls are still a work in progress, but I thought I’d display the progress.


That UI looks excellent! Is it using the built-in Urho UI system with different textures, or is it entirely separate?

Yes, this is using Urho’s UI :slight_smile:

It’s mostly just coloring with BorderImage for the icons. To create elements however, I actually created my own scripting engine (before I even knew about Urho) to handle building out and positioning the UI.

So my ui scripts look something like this (which is used to build out the UIElements)

I haven’t updated in a while, figured I would today. Been mostly working on the different interfaces for the game. Very much inspired by the CK2 style.


This looks like an awesome project!

Hey this looks really nice!