Forum: La Femme 2


Subject: Dynamic Clothes WIPs for La Femme 2

odf opened this issue on Dec 02, 2023 ยท 356 posts


odf posted Mon, 28 October 2024 at 7:43 PM

hborre posted at 9:17 AM Mon, 28 October 2024 - #4490845

Incredible-looking procedural fabrics.  I am very interested in what the Shaders look like in the Material Room.

Well, you asked for it, so don't complain when your eyes start bleeding. :grin:

Here's the top level of the red-and-black checked fabric:

The central piece is the Weaver compound node which I made. It takes the u and v coordinates - which I'm distorting by adding some fractal noise in order to introduce irregularities - a scale to determine how many threads to draw per uv unit, a gap parameter to determine the density of the fabric, and three more that determine the type of the weaving. The main outputs are a bump map the simulates the elevation of the threads in the weaving and an opacity mask so that we can look through the gaps between the threads. Then there's some output that helps with the coloring of individual threads as shown in the lower left corner. I'm also adding a bit of extra bump after the fact.

Here's what the inside of the compound node looks like:

That goes on for quite a bit, as you can see by the sliders. How did I make this? Not by hand, obviously. Here's the script with the formulas (actually a slightly more recent version with more outputs):

from pydeltamesh.makeTexture import *


def rem(a, b):
    return (a % b + b) % b


def maprange(x, from_min, from_max, to_min, to_max):
    t = (to_max - to_min) / (from_max - from_min)
    return (x - from_min) * t + to_min


def threads(x, scale, gap):
    a = maprange(x * scale % 1, gap / 2, 1 - gap / 2, 0, 1)
    return (a * (1 - a)).sqrt()


u = Input(U(), "u")
v = Input(V(), "v")
scale = Input(10, "scale")
gap = Input(0.4, "gap")
up_count = Input(3, "up_count")
down_count = Input(2, "down_count")
shift = Input(2, "shift")

warp = threads(u, scale, gap)
weft = threads(v, scale, gap)

warp_mask_raw = warp > 0
weft_mask_raw = weft > 0

opacity = warp_mask_raw.max(weft_mask_raw)
opacity.name = "opacity"

idx_warp = (u * scale).floor()
idx_warp.name = "warp_thread_index"

idx_weft = (v * scale).floor()
idx_weft.name = "weft_thread_index"

period = up_count + down_count

next_weft_is_up = rem(idx_warp - shift * (idx_weft + 1), period) < up_count
this_weft_is_up = rem(idx_warp - shift * idx_weft, period) < up_count
previous_weft_is_up = rem(idx_warp - shift * (idx_weft - 1), period) < up_count

warp_mask = warp_mask_raw & ~(weft_mask_raw & this_weft_is_up)
warp_mask.name = "warp_mask"

weft_mask = weft_mask_raw & ~warp_mask
weft_mask.name = "weft_mask"

weft_pos = rem(u * scale - shift * idx_weft, period)
weft_height = 0.5 * (
    this_weft_is_up
    - (0.5 - weft_pos).clamp()
    + (weft_pos + 0.5 - period).clamp()
    - (weft_pos + 0.5 - up_count).clamp() * (weft_pos < up_count)
    + (up_count + 0.5 - weft_pos).clamp() * (weft_pos > up_count)
).smoothstep()

warp_pos = rem(v * scale, 1)
warp_height = 0.5 * (
    ~this_weft_is_up
    + (warp_pos - 0.5) * (
        ((warp_pos > 0.5) & this_weft_is_up & ~next_weft_is_up)
        + ((warp_pos < 0.5) & ~this_weft_is_up & previous_weft_is_up)
        - ((warp_pos > 0.5) & ~this_weft_is_up & next_weft_is_up)
        - ((warp_pos < 0.5) & this_weft_is_up & ~previous_weft_is_up)
    )
).smoothstep()

bump = (weft + weft_height) * weft_mask + (warp + warp_height) * warp_mask
bump.name = "bump"

thread_u = u * weft_mask + v * warp_mask
thread_u.name = "thread_u"

thread_v = v * weft_mask + (1 - u) * warp_mask
thread_v.name = "thread_v"

with open("weaver_mktx.mt5", "w") as fp:
    write_poser_file(
        fp,
        name="weaver",
        output_nodes=[
            opacity, bump,
            warp_mask, weft_mask,
            idx_warp, idx_weft,
            thread_u, thread_v
        ]
    )

from PIL import Image
Image.fromarray(thread_v.data * 256).show()


Together with a library I made, the script produces an .mt5 file that I can load into Poser to get the compound node. It's a bit like a poor person's matmatic and loom.


-- I'm not mad at you, just Westphalian.