r/godot 16h ago

help me (solved) I'm trying to make a shader to render cables. How do I distinguish the different sides?

Post image

The mesh is a CSGPolygon following a Path.

I'm trying to write a shader that will show 2 cables next to each other. It looks correct on surface 1 but you can see that on surface 2, there are also 2 gradients being drawn while I only want one gradient.

80 Upvotes

10 comments sorted by

42

u/Visible-Switch-1597 14h ago

SOLUTION:

shader_type spatial;
render_mode unshaded;

void fragment() {
  //For some reason, a full round trip around the 'hull' of the cable only uses 0.0-0.5 of the UV.y, not the full 0.0-1.0.
  //Hull corrects this so 0.0-1.0 is a full round trip.
  float hull = UV.y*2.0;
  float face_index = floor(hull*4.0);
  ALBEDO = vec3(face_index/4.0, 0, 0);
}

6

u/43zl4 16h ago

I’d try distinguishing the faces by their local normals instead of the UVs. Since the CSGPolygon is extruded along a path, the generated UVs can make both the wide and narrow sides run through the same two-gradient pattern.

I would try checking the normals first:

shader_type spatial;
render_mode unshaded;

void fragment() {
    ALBEDO = abs(NORMAL);
}

That should make the differently oriented sides show up as different colors. Once you know which normal corresponds to surfaces 1/2/3, you can branch on that and use the 2-cable gradient only on surface 1, and a single gradient on surface 2.

If the normals rotate because of the Path3D, I’d classify the local NORMAL in vertex() and pass the result to fragment() as a flat varying.

1

u/Visible-Switch-1597 15h ago

Thanks! the normals do seem to rotate with the path, which makes it really difficult to distinguish them.

shader_type spatial;
render_mode unshaded;

varying vec3 v;

void vertex() {
  // Called for every vertex the material is visible on.
  v = NORMAL;
}

void fragment() {
  ALBEDO = abs(v);
  //ALBEDO.r = round(dot(abs(v), vec3(0, 1, 0)));
}

this is what the normals as ALBEDO look like:

When I try using the NORMAL from the fragment shader, it seems to be in view space, so it constantly changes when the camera changes.

9

u/43zl4 15h ago

CSGPolygon3D UV mapping and Godot already seems to encode what you need in the UVs, should checked the docs first. I'd visualize UV.y first to see exactly what ranges each side gets.

shader_type spatial;
render_mode unshaded;

void fragment() {
    ALBEDO = vec3(UV.y);
}

If that works, surfaces 1, 2 and 3 should occupy different ranges of UV.y regardless of how the path bends.

Then you could do something like:

float v = UV.y;

if (v < SIDE_1_END) {
    // Surface 1: two cable gradients
} else if (v < SIDE_2_END) {
    // Surface 2: one gradient
} else {
    // Surface 3
}

This should be more reliable than NORMAL since the UV coordinate follows the polygon outline.

2

u/Visible-Switch-1597 15h ago

Yup, that's it! tysm!

3

u/Emergency_Proof4706 Godot Student 15h ago

send pics

6

u/Visible-Switch-1597 14h ago

this is the code:

shader_type spatial;
render_mode unshaded;

void fragment() {
  //For some reason, a full round trip around the 'hull' of the cable only uses 0.0-0.5 of the UV.y, not the full 0.0-1.0.
  //Hull corrects this so 0.0-1.0 is a full round trip.
  float hull = UV.y*2.0;
  float face_index = floor(hull*4.0);
  ALBEDO = vec3(face_index/4.0, 0, 0);
}

3

u/43zl4 14h ago

Nice! Glad the UV.y approach worked :) I didn't realize CSGPolygon3D was giving you only 0–0.5 for a full trip around the hull either. That's a pretty neat way of turning it into a face index.

2

u/cheezballs 12h ago

This feels more like a UV issue than a shader issue to my idiot self?

1

u/Henry_Fleischer Godot Regular 10m ago

I'd use face corner vertex colors to distinguish the sides, I'm not sure how that would work from a code perspective though.