How to translate this function in patch editor?

So, I found this on shadertoy. A really neat shader by Fabrice Neyret (https://www.shadertoy.com/user/FabriceNeyret2). but i don’t understand what line 11 to 16 means. It’s still new to me, because from what i learned so far from the book of shader, there is always a return color, and the uv.


#define L 8.  // interline distance
#define A 4.  // amplification factor
#define P 6.  // thickness

void mainImage( out vec4 o,  vec2 uv )
{
    o -= o;
    uv /= L;
    vec2  p = floor(uv+.5);

    #define T(x,y) texture(iChannel0,L*vec2(x,y)/iResolution.xy).g   // add .g or nothing 

    #define M(c,T) o += pow(.5+.5*cos( 6.28*(uv-p).c + A*(2.*T-1.) ),P)

    M( y, T( uv.x, p.y ) );   // modulates  y offset
    M( x, T( p.x, uv.y ) );   // modulates  y offset
    
}

the questions:

  1. why o -= o while variable o never declared before? the previous line says that o is vec4 output.
  2. How to implement line 11, 13, 15 and 16?
  3. for line 11, is that T(x,y) means pack vector 2 where the x is RGB and the y is the Alpha?
  4. for the line 15 and 16, is that a texture sampler/composition? what i don’t get is that in line 13, the M(c, T) has (uv-p).c in it. c is the input right? but why it gets swizzled just like an xyzw or rgba?

sorry for asking a lot of questions.

Can you link to the shadertoy? I’m interested in seeing what this is actually doing.

  1. No idea what o-=o is doing. I think it can be ignored. The only input here is a uv.

  2. You can think of the function #defines as patch groups. When they are called, it’s the same as passing variables to a patch group and getting some result from them. The difference with the M function is that it’s modifying the input instead of returning a result. You will need to return the result and add it to the input after it goes through the M patches

  3. The T function accepts (x, y) which are both floats. It returns a float too (the green channel of the sampled texture)

  4. These functions are running and modifying the o output. It’s a really odd way of doing things in my opinion, but maybe there’s a reason for it. The .c looks like a typo, but maybe it could be shorthand for multiplication

Not sure what it’s supposed to do, but I made it do something


unknown.arexport (53.5 KB)

1 Like

here is the link: Shader - Shadertoy BETA
What makes me interested with this shader is the code itself to be honest. because if just looking at the result, it looks like we can just translate the texture to a normal and then displace a grid to make it loo like its wrapping or distorting the lines but clamped down to certain depth. the code is a bit different than what i used to see in the book of shader format.

hmmm interesting. it looks so different than the shadertoy look. is it because the texture coordinate in spark is inverted in the y axis? cuz if i’m not mistaken, shadertoy, unity, unreal and blender has the 0,0 poin on the bottom left while spark 0,0 is on top left corner?

  1. about it, my guesstimation was that it suppose to invert the color, because i thought to displace the emboss look, he was using something similar like normal map. so he might want to flipped it.
  2. right… i thought so myself. so i think i was right about it.
  3. this is a bit weird to me especially the L*vec2(x,y). what is his vec2(x, y)? is it the original UV before divided by the L?
  4. i’m still totally lost here. if i commented the line i know what it’s doing, it just to display the line. line 15 is to for the horizontal line, and line 16 is for the vertical line. but the code looks so unfamiliar to me. and yeah, the .c is also still a mystery to me. because i can change it to .x or .r and .y or .g to only swizzle 1 channel, but this .c is like .xy but when i type .xy or .rg it shows and error

Yeah the coordinates are inverted in spark, but I’m not sure if that would matter for this shader. TBH I’m not sure where the x and y values are coming from in these lines. I think that’s where my attempt falls apart.

 M( y, T( uv.x, p.y ) );   // modulates  y offset
 M( x, T( p.x, uv.y ) );   // modulates  y offset

1. I think the o-=o is some hacky type conversion or initialization thing because the shader doesn’t work without it, but it’s unclear what it’s actually doing.

3. The xy is coming from the T( uv.x, p.y ). The uv is modified in place with uv /= L; so that is the value being used. The original uv reference is lost after that line.

4. The .c is really a mystery because if I remove it, nothing changes. This is a weird shader in part because it looks like it was written specifically to fit within two tweets.

This behaved differently today. The .c is still a mystery, but it seems like a combination of x and y, but it’s still a single channel.



Hi Josh,
so I contacted the Shader Lord Himself (Fabrice Neyret) on Facebook for guidance, but seems like he got some problem to register here.

This is his facebook profile:

This is the exact post from the screenshot:
https://www.facebook.com/fabrice.neyret/posts/10159546173726382

Hmm I’m not sure why it wouldn’t let him register. The auth is standard firebase oauth. He should be able to log in with email, google, or facebook. I would need more info to debug it, but it seems like he prefers to use the shadertoy facebook group

Hello Adi,

I am not an expert on this subject, but i tried to implement and answer the questions.

  1. Not sure why o-=o, but it is declared as a vec4 output in the mainImage function argument.
  2. To implement line number 11,13, 15, 16 : i took them outside the main function.

For 11: #define T(x,y) texture(iChannel0,Lvec2(x,y)/iResolution.xy).g*

float T(float x, float y, vec2 iResolution, Texture2d iChannel0) {
  float tOut = iChannel0.sample(L*vec2(x, y)/iResolution.xy).g;

  return tOut;
}

for 13: #define M(c,T) o += pow(.5+.5cos( 6.28*(uv-p).c + A*(2.T-1.) ),P)

auto M(float a, float tOut1, vec2 uv, vec2 p,  float x, float y, vec2 iResolution, Texture2d iChannel0) {
  float mOut ;

  if(a==0.0) mOut = pow(.5+.5*cos( 6.28*(uv-p).x + A*(2.*tOut1-1.) ),P);
  else       mOut = pow(.5+.5*cos( 6.28*(uv-p).y + A*(2.*tOut1-1.) ),P);
  return mOut;

}

*for 15: M( y, T( uv.x, p.y ) ); *

o+=  M(1.0, T(uv.x, p.y, iResolution, iChannel0), uv, p, uv.x, p.y, iResolution, iChannel0);

for 17: M( x, T( p.x, uv.y ) );

o+=  M(0.0, T(p.x, uv.y, iResolution, iChannel0), uv, p, p.x, uv.y, iResolution, iChannel0);
  1. For line number 11, T(x,y) means x is either uv.x or uv.y and y means p.x or p.y i.e x and y are float variables.

  2. The way it has been used in the #define function, the input c works as a string ( i assume). So c would represent the target swizzle string(xyzw), which is x and y in this shader.

To implement this, we could use an if else condition based on some float values to apply the swizzle string.
for example 0.0 → x ; 1.0 ->y

using namespace std;

#define L 8.  // interline distance
#define A 4.  // amplification factor
#define P 6.  // thickness

float T(float x, float y, vec2 iResolution, Texture2d iChannel0) {
  float tOut = iChannel0.sample(L*vec2(x, y)/iResolution.xy).g;

  return tOut;
}

auto M(float a, float tOut1, vec2 uv, vec2 p,  float x, float y, vec2 iResolution, Texture2d iChannel0) {
  float mOut ;

  if(a==0.0) mOut = pow(.5+.5*cos( 6.28*(uv-p).x + A*(2.*tOut1-1.) ),P);
  else       mOut = pow(.5+.5*cos( 6.28*(uv-p).y + A*(2.*tOut1-1.) ),P);
  return mOut;

}

vec4 mainImage( Texture2d iChannel0, vec2 iResolution )
{
    vec4 o;
    vec2 uv = fragment(getVertexTexCoord());
    o -= o;
    uv /= L;
    vec2  p = floor(uv+.5);

    o+=  M(1.0, T(uv.x, p.y, iResolution, iChannel0), uv, p, uv.x, p.y, iResolution, iChannel0);
    o+=  M(0.0, T(p.x, uv.y, iResolution, iChannel0), uv, p, p.x, uv.y, iResolution, iChannel0);


    return o;
    
}

Thanks! Unfortunately that doesn’t work, but I managed to make it work in patch editor after trying to understand the code that you share! You’re right about the c. the c means the swizzle slot. and the rest of them are just regular function, with 2 M for x and y axis, and 2 T function to feed the M. Also, the resolution is simply to make line thickness for x and axis the same size, in spark we can just multiply it by aspect ratio from screensize. also the o literally does notthing at all in my interpretation. it’s more like place holder for the background color, but i ended up using mix patch to select colors for the lines and background.
this is the result:

*sorry for the bad video quality, i got to compress it below 4MB due to video size limit in this forum.

2 Likes

Once again, thanks to Mr Fabrice Neyret for sharing the original code on shadertoy.
If someone find this thread helpful please share some love to him.
This is the link for the original code:

Fabrice Neyret Shadertoy profile:
https://www.shadertoy.com/user/FabriceNeyret2

This is the setup I ended up using:

Patch: image
Main:


T:
M:

I hope this post is helpful to future readers cuz It’s definitely super helpful to me. I learned a lot while trying to translate this shader in spark.

This is the test link for the effect:

4 Likes