Improve Wire Rendering

Post your ideas and suggestions how to improve the game.

Moderator: ickputzdirwech

User avatar
y.petremann
Filter Inserter
Filter Inserter
Posts: 434
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: 434
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

Go to https://www.shadertoy.com/new and replace by the code bellow.

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
User avatar
y.petremann
Filter Inserter
Filter Inserter
Posts: 434
Joined: Mon Mar 17, 2014 4:24 pm
Contact:

Re: Improve Wire Rendering

Post by y.petremann »

Sorry for unknow reason, my shadertoy demo link has problems and the account too, so simple paste the code in a new shadertoy
mmmPI
Smart Inserter
Smart Inserter
Posts: 4760
Joined: Mon Jun 20, 2016 6:10 pm
Contact:

Re: Improve Wire Rendering

Post by mmmPI »

It shows a triangle of red wire on green background. ( and their shadow )

Can you highlight which number to modify to change the position of one point of the triangle for someone with no background in programming to see how it looks when the point are placed differently ?
Check out my latest mod ! It's noisy !
User avatar
y.petremann
Filter Inserter
Filter Inserter
Posts: 434
Joined: Mon Mar 17, 2014 4:24 pm
Contact:

Re: Improve Wire Rendering

Post by y.petremann »

mmmPI wrote: Wed Oct 08, 2025 8:19 am It shows a triangle of red wire on green background. ( and their shadow )

Can you highlight which number to modify to change the position of one point of the triangle for someone with no background in programming to see how it looks when the point are placed differently ?
There is PowerPole with have two vectors which are the coordinates of the wire and it shadow

Code: Select all

PowerPole pp1 = PowerPole(vec3(100, 150, 50), vec3(150, 100, 50));
the coordinates represent x,y and elevation (changing elevation is not really interesting, keep it to 50)

then there is renderWire which take a Wire definition with start, end and, type

Code: Select all

shadow = blend(shadow, renderWire(Wire(pp1.shadow, pp2.shadow, shadow_wire, 2.0, 0.0, true), uv));

Code: Select all

col = blend(col, renderWire(Wire(pp1.wire, pp3.wire, green_wire, 2.0, 0.0, false), uv));
So you can extend by modifying the mainImage function

Code: Select all

void mainImage( out vec4 fragColor, in vec2 fragCoord ) {
    vec2 uv = fragCoord;
    
    
    // HERE you can define new powerpoles
    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);

    // HERE you add wires shadow rendering
    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);

    // HERE you add wires rendering
    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;
}
mmmPI
Smart Inserter
Smart Inserter
Posts: 4760
Joined: Mon Jun 20, 2016 6:10 pm
Contact:

Re: Improve Wire Rendering

Post by mmmPI »

Thanks for adding the comments :) I can't access the /new page from shadertoy atm, i guess they will fix this soon, and i'll try adding a power pole, that sound more interesting that just moving one around.

The arguments for the propostion are technical, i don't know how it would fit in game, it reminded me the curvy rails discussion,i wonder what is the complexity / performance tradeoff on this one.
Check out my latest mod ! It's noisy !
User avatar
y.petremann
Filter Inserter
Filter Inserter
Posts: 434
Joined: Mon Mar 17, 2014 4:24 pm
Contact:

Re: Improve Wire Rendering

Post by y.petremann »

Curved rails are managed by sprites and it seems reasonable becaus eit can became really complex because they hold a lot of details.
But the fact is that wires are monocolored and mostly empty sprite that is stretched and skewed, on some cases they are so thin that they are barely visible.

My proposal is for drawing wires using shaders so we can manage those edge cases in a better way,
I imagine that a better version could use a shader that use straight wire sprite, so modders could customise the sprite without touching the shader (this would allow to theme as christmas garland for example)

On the other hand some players could install mods that modify the shader for alternate pathing (like removing the curve or having orthogonal pathing)
mmmPI
Smart Inserter
Smart Inserter
Posts: 4760
Joined: Mon Jun 20, 2016 6:10 pm
Contact:

Re: Improve Wire Rendering

Post by mmmPI »

y.petremann wrote: Sun Oct 12, 2025 2:14 am Curved rails are managed by sprites and it seems reasonable becaus eit can became really complex because they hold a lot of details.
But the fact is that wires are monocolored and mostly empty sprite that is stretched and skewed, on some cases they are so thin that they are barely visible.
I didn't realized wire were sprites and the implications before reading your post but i knew rails are :) The reminiscence i guess came from seeing those bezier curves applied to some game objects, the position of the train on the rail while moving should match the sprite of the rail, the calculation for this was discussed on the forum with user showing some desmos graph to present formulas that would do that depending on how the rail sprites were going to be. Maybe it's not too similar i wanted to toy around to see what i could understand.
y.petremann wrote: Wed Oct 08, 2025 8:10 am Sorry for unknow reason, my shadertoy demo link has problems and the account too, so simple paste the code in a new shadertoy
I have tried again today to access shadertoy and it still didn't work only the main page was accessible, tried 2 browsers and different settings for adblockers only to realize that i needed to be logged in to access the rest of the website. It wasn't the case for the first attempt (and others month ago) so i don't know what's going on currently and wether or not that impact the demonstration for other people than me but i wanted to let you know in case.

Eventually tho i was able to follow the instruction and add a 4rth power pole to try and make a rectangle :) . It felt a bit like magic to see the curvy shadow while the wire is straight and made more clear the differences between what describe the attach points location and what describe the existing connexions.
y.petremann wrote: Sun Oct 12, 2025 2:14 am My proposal is for drawing wires using shaders so we can manage those edge cases in a better way,
I imagine that a better version could use a shader that use straight wire sprite, so modders could customise the sprite without touching the shader (this would allow to theme as christmas garland for example)

On the other hand some players could install mods that modify the shader for alternate pathing (like removing the curve or having orthogonal pathing)
I understand the argument for visibility in those edge cases were the streching and lack of anti aliasing could hinter visibility. Your customisation example made me think of planet with lower gravity where there could be a tweak to the formula so the wires are strung up, and on space platform, how there's no gravity there, the wire could be "floating", wobbly and possibly animated, slowly ondulating , then i remembered that the first part of your code is still a bit like magic to me, but at least it i could use the demo properly this time :)
Check out my latest mod ! It's noisy !
mmmPI
Smart Inserter
Smart Inserter
Posts: 4760
Joined: Mon Jun 20, 2016 6:10 pm
Contact:

Re: Improve Wire Rendering

Post by mmmPI »

I couldn't resist toying around :


Code: Select all

// Animated wires + synchronized shadows (no phase shift)

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 = 12;
    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, float iTime, float phaseSeed) {
    vec2 A = w.from.xy;
    vec2 C = w.to.xy;

    float dz = w.to.z - w.from.z;

    // Shared phase to sync wire + shadow
    float wave = sin(phaseSeed * 0.05 + iTime * 2.0);
    vec2 gravity = vec2(0.0, wave);

    float sag = (1.0 - w.tension) * (0.5 + abs(dz) * 0.5);

    vec2 B = (A + C) * 0.5 + gravity * sag * 130.0;

    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.1, 0.8, 0.1, 1.0);
const vec4 copper_wire  = vec4(0.72, 0.45, 0.2, 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.35);

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = fragCoord;
    float iTime = iTime;

    // 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));
    PowerPole pp4 = PowerPole(vec3(410, 250, 50), vec3(460, 200, 50));

    vec4 shadow = vec4(0.0);

    // Helper: compute phase seed from wire midpoint (wire positions)
    #define MIDPOINT(p1, p2) (((p1).x + (p2).x) * 0.5)

    // Shadows (use wire positions for phase seed to sync)
    shadow = blend(shadow, renderWire(Wire(pp1.shadow, pp2.shadow, shadow_wire, 2.0, 0.0, true), uv, iTime, MIDPOINT(pp1.wire, pp2.wire)));
    shadow = blend(shadow, renderWire(Wire(pp1.shadow, pp3.shadow, shadow_wire, 2.0, 0.0, true), uv, iTime, MIDPOINT(pp1.wire, pp3.wire)));
    shadow = blend(shadow, renderWire(Wire(pp2.shadow, pp3.shadow, shadow_wire, 2.0, 0.0, true), uv, iTime, MIDPOINT(pp2.wire, pp3.wire)));
    shadow = blend(shadow, renderWire(Wire(pp3.shadow, pp4.shadow, shadow_wire, 2.0, 0.0, true), uv, iTime, MIDPOINT(pp3.wire, pp4.wire)));
    shadow = blend(shadow, renderWire(Wire(pp2.shadow, pp4.shadow, shadow_wire, 2.0, 0.0, true), uv, iTime, MIDPOINT(pp2.wire, pp4.wire)));
    shadow = blend(shadow, renderWire(Wire(pp1.shadow, pp4.shadow, shadow_wire, 2.0, 0.0, true), uv, iTime, MIDPOINT(pp1.wire, pp4.wire)));

    shadow *= shadow_alpha;

    vec4 col = vec4(0.0, 0.3, 0.0, 1.0); // Background
    col = blend(col, shadow);

    // Wires (use same phase seed)
    col = blend(col, renderWire(Wire(pp1.wire, pp2.wire, red_wire,    2.0, 0.0, false), uv, iTime, MIDPOINT(pp1.wire, pp2.wire)));
    col = blend(col, renderWire(Wire(pp1.wire, pp3.wire, green_wire,  2.0, 0.0, false), uv, iTime, MIDPOINT(pp1.wire, pp3.wire)));
    col = blend(col, renderWire(Wire(pp2.wire, pp3.wire, copper_wire, 2.0, 0.0, false), uv, iTime, MIDPOINT(pp2.wire, pp3.wire)));
    col = blend(col, renderWire(Wire(pp3.wire, pp4.wire, copper_wire, 2.0, 0.0, false), uv, iTime, MIDPOINT(pp3.wire, pp4.wire)));
    col = blend(col, renderWire(Wire(pp2.wire, pp4.wire, copper_wire, 2.0, 0.0, false), uv, iTime, MIDPOINT(pp2.wire, pp4.wire)));
    col = blend(col, renderWire(Wire(pp1.wire, pp4.wire, copper_wire, 2.0, 0.0, false), uv, iTime, MIDPOINT(pp1.wire, pp4.wire)));

    fragColor = col;
}

Code: Select all

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);
}

// Wobbly wire between two points, with fixed endpoints
float sdWobblyWire(vec2 p, vec2 A, vec2 C, float iTime, float phaseSeed, float tension) {
    const int N = 20;
    float minDist = 1e9;

    vec2 dir = normalize(C - A);
    vec2 perp = vec2(-dir.y, dir.x);
    float len = length(C - A);

    float amp = mix(0.0, 15.0, 1.0 - tension); // tension affects wobble size
    float freq = 10.0;

    for (int i = 0; i < N; i++) {
        float t0 = float(i) / float(N);
        float t1 = float(i + 1) / float(N);

        vec2 p0 = A + dir * (t0 * len);
        vec2 p1 = A + dir * (t1 * len);

        // Taper curve to zero at ends: sin(PI * t)
        float taper0 = sin(3.14159 * t0);
        float taper1 = sin(3.14159 * t1);

        float w0 = sin(t0 * freq + iTime * 2.0 + phaseSeed) * amp * taper0;
        float w1 = sin(t1 * freq + iTime * 2.0 + phaseSeed) * amp * taper1;

        p0 += perp * w0;
        p1 += perp * w1;

        minDist = min(minDist, sdSegment(p, p0, p1));
    }

    return minDist;
}

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

    float d = sdWobblyWire(uv, A, C, iTime, phaseSeed, w.tension);
    float alpha = smoothstep(w.thickness, w.thickness - 1.0, d);

    return w.color * 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.1, 0.8, 0.1, 1.0);
const vec4 copper_wire  = vec4(0.72, 0.45, 0.2, 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.35);

void mainImage(out vec4 fragColor, in vec2 fragCoord) {
    vec2 uv = fragCoord;
    float time = iTime;

    // Define 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));
    PowerPole pp4 = PowerPole(vec3(410, 250, 50), vec3(460, 200, 50));

    vec4 shadow = vec4(0.0);

    // Midpoint as phase seed (shared by wire and shadow)
    #define MIDPOINT(p1, p2) (((p1).x + (p2).x) * 0.5)

    // Shadows
    shadow = blend(shadow, renderWire(Wire(pp1.shadow, pp2.shadow, shadow_wire, 2.0, 0.2, true), uv, time, MIDPOINT(pp1.wire, pp2.wire)));
    shadow = blend(shadow, renderWire(Wire(pp1.shadow, pp3.shadow, shadow_wire, 2.0, 0.2, true), uv, time, MIDPOINT(pp1.wire, pp3.wire)));
    shadow = blend(shadow, renderWire(Wire(pp2.shadow, pp3.shadow, shadow_wire, 2.0, 0.2, true), uv, time, MIDPOINT(pp2.wire, pp3.wire)));
    shadow = blend(shadow, renderWire(Wire(pp3.shadow, pp4.shadow, shadow_wire, 2.0, 0.2, true), uv, time, MIDPOINT(pp3.wire, pp4.wire)));
    shadow = blend(shadow, renderWire(Wire(pp2.shadow, pp4.shadow, shadow_wire, 2.0, 0.2, true), uv, time, MIDPOINT(pp2.wire, pp4.wire)));
    shadow = blend(shadow, renderWire(Wire(pp1.shadow, pp4.shadow, shadow_wire, 2.0, 0.2, true), uv, time, MIDPOINT(pp1.wire, pp4.wire)));

    shadow *= shadow_alpha;

    // Restore original background (dark green)
    vec4 col = vec4(0.0, 0.3, 0.0, 1.0);
    col = blend(col, shadow);

    // Wires
    col = blend(col, renderWire(Wire(pp1.wire, pp2.wire, red_wire,    2.0, 0.2, false), uv, time, MIDPOINT(pp1.wire, pp2.wire)));
    col = blend(col, renderWire(Wire(pp1.wire, pp3.wire, green_wire,  2.0, 0.2, false), uv, time, MIDPOINT(pp1.wire, pp3.wire)));
    col = blend(col, renderWire(Wire(pp2.wire, pp3.wire, copper_wire, 2.0, 0.2, false), uv, time, MIDPOINT(pp2.wire, pp3.wire)));
    col = blend(col, renderWire(Wire(pp3.wire, pp4.wire, copper_wire, 2.0, 0.2, false), uv, time, MIDPOINT(pp3.wire, pp4.wire)));
    col = blend(col, renderWire(Wire(pp2.wire, pp4.wire, copper_wire, 2.0, 0.2, false), uv, time, MIDPOINT(pp2.wire, pp4.wire)));
    col = blend(col, renderWire(Wire(pp1.wire, pp4.wire, copper_wire, 2.0, 0.2, false), uv, time, MIDPOINT(pp1.wire, pp4.wire)));

    fragColor = col;
}
It's still a bit like magic but also AI are magic, and you can solve magic with magic !
The side effects are i'm not sure how it works, and i don't know what's wrong with the wire color, i only used AI to modfiy the code with my silly idea after i made the rectangle to see if it would work, that's why the comment in the text.
Your setup looks more realistic :)
Attachments
spacy wire.mp4
(82.84 KiB) Downloaded 7 times
bouncy.mp4
(59.08 KiB) Downloaded 7 times
Check out my latest mod ! It's noisy !
User avatar
y.petremann
Filter Inserter
Filter Inserter
Posts: 434
Joined: Mon Mar 17, 2014 4:24 pm
Contact:

Re: Improve Wire Rendering

Post by y.petremann »

Here it's totally unoptimised, consider that on every pixel, it will get to check if it's near a cable:
1) we have sdSegment that return the distance of any point to a segment defined by two points.
2) we have sdBezier which does a 12 point interpolation of a bezier curve, use sdSegment on each part of the segment, then use the minimum value
3) finaly we have renderwire that use sdBezier to check the distance of a point to the bezier curve (the wire) and use smoothstep so that wire edge is "antialiased"

Comparing to your demos, I tried to be faithfull to some rules about wires in factorio, the sun is from west and you look from south, so for a wire that goes from north to south, it's straight and it's shadow is curved, and for a wire that goes from east to west, it's curved and shadow is straight. I've seen that the devs had rejected similar proposals just because it didn't respect this rule (and they are totally right about it).
Ihmemies
Burner Inserter
Burner Inserter
Posts: 6
Joined: Sun Oct 20, 2024 10:56 am
Contact:

Re: Improve Wire Rendering

Post by Ihmemies »

You really get some thicc wires with the original sprite based mod (you can select which wires you want to be thicker)

Image

Unless they are vertical in which case RIP (you can see the huge difference in readability in the screenshot below)

Image

Vertical wires being the most hard to read anyways, and the mod not working with those, I endorse a rewrite for the wire rendering. With a better rendering method you could also change the wire colors to be more readable by color blind people. Especially the red-green combo used in current wires is not helpful at all for many color blind people.

Thanks.
mmmPI
Smart Inserter
Smart Inserter
Posts: 4760
Joined: Mon Jun 20, 2016 6:10 pm
Contact:

Re: Improve Wire Rendering

Post by mmmPI »

y.petremann wrote: Sun Oct 12, 2025 6:44 pm Here it's totally unoptimised, consider that on every pixel, it will get to check if it's near a cable:
1) we have sdSegment that return the distance of any point to a segment defined by two points.
2) we have sdBezier which does a 12 point interpolation of a bezier curve, use sdSegment on each part of the segment, then use the minimum value
3) finaly we have renderwire that use sdBezier to check the distance of a point to the bezier curve (the wire) and use smoothstep so that wire edge is "antialiased"
I have no idea what i'm doing, so thanks for letting me know :)

I have changed the 6 to 12 for the smoothness of the curve myself, i tried toying around with other values to see what would happen, sometimes it broke the shadows or change orientation, in the end i wanted to see how it would look if there was "no gravity" so the wire would be straight and animating an increase of gravity, but now i realize the vertical wire aren't affected in the first animation, maybe that's why it look a bit ridiculous like a hammock.

The animations are also further modfied by the sceen recording i suppose, so for the antialising, i can't tell wether it's your original code, something the AI did that i don't get, or the screen recording that's responsible the most. If the AI version have added antialiasing, i'm not aware, if your version had it already, i couldn't recognize it was happening :)

y.petremann wrote: Sun Oct 12, 2025 6:44 pm Comparing to your demos, I tried to be faithfull to some rules about wires in factorio, the sun is from west and you look from south, so for a wire that goes from north to south, it's straight and it's shadow is curved, and for a wire that goes from east to west, it's curved and shadow is straight. I've seen that the devs had rejected similar proposals just because it didn't respect this rule (and they are totally right about it).
Now i realized i had it backward in the first animation, it's not just my vertical wires that were wrong, but also the shadow of the horizontal ones, that must participate in the silly look whereas your original proposal is strikingly similar to the game, except for the width of the wire :) For the second animation, it was supposed to be a space background, but then you couldn't see the shadows, and when considering what the shadow could look like in space i was a little puzzled and stop thinking about it x)
Also the tension is poorly defined, but when reducing the amplitude of the wooble it doesn't show as much that the wire oscillate top-bottom when horizontal and left right when vertical, which makes little to no sense, slower with a tiny bit of randomness can probably help making it look like wires are floating in 3D without gravity. ( and also kill any optimization probably :( )

I understand the main point is increased visibility and it was quite well demonstrated by @ Ihmemies, +1 for the suggestion :)

It's also fun to toy around x) thanks for sharing the code snippet and explaining a bit, now i'm thinking maybe when the space platform moves, the wire could reflect the drag by bending backward, or maybe some explosions like rockets could have their blast shakes the wires nearby, not as a suggestion, just to toy around with the shader :)
Check out my latest mod ! It's noisy !
Post Reply

Return to “Ideas and Suggestions”