Improve Wire Rendering

Post your ideas and suggestions how to improve the game.

Moderator: ickputzdirwech

User avatar
y.petremann
Filter Inserter
Filter Inserter
Posts: 428
Joined: Mon Mar 17, 2014 4:24 pm
Contact:

Improve Wire Rendering

Post by y.petremann »

As for now, wires are rendered as sprites that get transformed.
Because of this, wire strictly vertical and wire shadow strictly horizontal get 1 pixel wide and not antialiased :
- Wires can't be made thicker verticaly
- Wires hide when big poles are out of view but wire still goes in the view
- Wire gets ugly because no antialiasing is done on sprite transformation.

What I would propose is to render wire using shaders.
- It would make wire width consistent
- It doesn't need a perfect bezier curve, 32 8 point makes a good aproximation for a wire going from a pole to another.
- It doesn't need transformation antialias
Last edited by y.petremann on Sun Sep 28, 2025 12:41 pm, edited 1 time in total.
User avatar
y.petremann
Filter Inserter
Filter Inserter
Posts: 428
Joined: Mon Mar 17, 2014 4:24 pm
Contact:

Re: Improve Wire Rendering

Post by y.petremann »

I know it's been near one year, I've got some update about that.

I tried with my humble programming background to write my first ever shader for wire rendering demonstration

https://www.shadertoy.com/view/3cBBDV

Code: Select all

// Example: draw one wire
struct Wire {
    vec3 from;
    vec3 to;
    vec4 color;
    float thickness;
    float tension;
    bool shadow;
};

struct PowerPole {
    vec3 wire;
    vec3 shadow;
};


float sdSegment(vec2 p, vec2 A, vec2 B) {
    vec2 pa = p - A;
    vec2 ba = B - A;
    float h = clamp(dot(pa, ba) / dot(ba, ba), 0.0, 1.0);
    return length(pa - ba * h);
}

float sdBezier(vec2 p, vec2 A, vec2 B, vec2 C) {
    const int N = 6; // number of segments (fewer = faster, more = smoother)
    vec2 prev = A;
    float minDist = 1e9;

    for (int i = 1; i <= N; i++) {
        float t = float(i) / float(N);
        vec2 curr = (1.0 - t) * (1.0 - t) * A +
                    2.0 * (1.0 - t) * t * B +
                    t * t * C;
        minDist = min(minDist, sdSegment(p, prev, curr));
        prev = curr;
    }

    return minDist;
}

vec4 renderWire(Wire w, vec2 uv) {
    vec2 A = w.from.xy;
    vec2 C = w.to.xy;

    float dz = w.to.z - w.from.z;
    vec2 gravity = w.shadow ? vec2(-1.0, 0.0) : vec2(0.0, -1.0);

    // Sag amount scaled by elevation difference
    float sag = (1.0 - w.tension) * (0.5 + abs(dz) * 0.5);

    vec2 B = (A + C) * 0.5 + gravity * sag * 50.0; // scale for visibility

    float d = sdBezier(uv, A, B, C);
    float alpha = smoothstep(w.thickness, w.thickness - 1.0, d);

    return w.color.rgba * alpha;
}

vec4 blend(vec4 col, vec4 c) {
    return mix(col,c,c.a);
}

// colors
const vec4 red_wire     = vec4(0.8, 0.1, 0.1, 1.0);
const vec4 green_wire   = vec4(0.8, 0.1, 0.1, 1.0);
const vec4 copper_wire  = vec4(0.8, 0.1, 0.1, 1.0);
const vec4 shadow_wire  = vec4(0.0, 0.0, 0.0, 1.0);
const vec4 shadow_alpha = vec4(1.0, 1.0, 1.0, 0.5);

void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
    vec2 uv = fragCoord;
    
    
    //power poles
    PowerPole pp1 = PowerPole(vec3(100, 150, 50), vec3(150, 100, 50));
    PowerPole pp2 = PowerPole(vec3(100, 250, 50), vec3(150, 200, 50));
    PowerPole pp3 = PowerPole(vec3(410, 150, 50), vec3(460, 100, 50));

    vec4 shadow = vec4(0.0,0.0,0.0,0.0);
    shadow = blend(shadow, renderWire(Wire(pp1.shadow, pp2.shadow, shadow_wire, 2.0, 0.0, true), uv));
    shadow = blend(shadow, renderWire(Wire(pp1.shadow, pp3.shadow, shadow_wire, 2.0, 0.0, true), uv));
    shadow = blend(shadow, renderWire(Wire(pp2.shadow, pp3.shadow, shadow_wire, 2.0, 0.0, true), uv));
    shadow = shadow * shadow_alpha;
    
    vec4 col = vec4(0.0,0.3,0.0,1.0);
    col = blend(col, shadow);

    col = blend(col, renderWire(Wire(pp1.wire, pp2.wire, red_wire, 2.0, 0.0, false), uv));
    col = blend(col, renderWire(Wire(pp1.wire, pp3.wire, green_wire, 2.0, 0.0, false), uv));
    col = blend(col, renderWire(Wire(pp2.wire, pp3.wire, copper_wire, 2.0, 0.0, false), uv));

    fragColor = col;
}
I give factorio devs the right to use this code and modify as they like
Post Reply

Return to “Ideas and Suggestions”