Difficulty Implementing World Size and Screen Size Toggle in GL Shader

I’ve been thoroughly enjoying and, at times, finding it challenging to work with GL Shader. I’m currently stumped on how to implement a feature that toggles between world size and screen size. The example that I’ve been trying to emulate is the “symbols” shader.

I noticed that on the McNeel GitHub page, there is a shader example called “sprite,” which includes this toggling option. However, my attempts to integrate this feature into my work have been unsuccessful.

Despite my best efforts and even after spending hours consulting with ChatGPT, I haven’t been able to successfully incorporate it into my project. It seems human expertise is still a critical piece of the puzzle. :laughing:

@stevebaer, I would be very grateful if you could take a moment to look at my code. I’ve cleared out all my previous attempts that were leading nowhere. Your guidance would be invaluable to me.

dot_gradient
shader.gh (44.8 KB)

Hi @crz_06 , have you found a solution? I was trying to do the same couple weeks back but failed, but still interested in a solution.

Maybe one of our local glsl heros @mrhe , @stevebaer , @kitjmv , can you share some light on this?

Can you elaborate on what exactly you are trying to achieve?

Hi @mrhe , thanks for the reply.

My aim is the following:

if you look at the second shader node, it has a toggle “world_size” that allows the rendered vertices to switch between real (in millimeters) dimensions, and dimensions proportional to screensize.

The same feature I would like to add the to top shader, so it can use be used with real world dimensions.

I’d thought to just take the sample code from the bottom shader as guidance, and implement it to the top shader, assuming it should be relatively easy to implementation, but not a skilled programmer, i cannot pinpoint exactly where my code breaks.

PS: both code are from mcneal github repro, with the top one with some additions i added

Attached a working example:
shader_worldsize.gh (46.2 KB)

The key was to move the calculation of the screen position from the Vertex Shader to Geometry shader:

Vertex Shader:

/*
  vec4 clip = _worldToClip * vec4(world_vertex, 1);
  // center points on pixels so they get drawn as 'clean' as possible
  clip.x = (int(clip.x * _viewportSize.x) + 0.5) / _viewportSize.x;
  clip.y = (int(clip.y * _viewportSize.y) + 0.5) / _viewportSize.y;
  gl_Position = clip;
  */
  gl_Position = vec4(world_vertex, 1.0);

Geometry Shader

void main()
{
  Out.stroke_color = In[0].stroke_color;
  Out.fill_color = In[0].fill_color;
  Out.linewidth = In[0].linewidth;
  Out.diameter = In[0].diameter;
  Out.inner_diameter = In[0].inner_diameter;
  Out.style = In[0].style;
  Out.full_diameter = In[0].full_diameter;


  
  if (world_size==1) {
    vec4 center =  _worldToCamera * gl_in[0].gl_Position;

    if( In[0].style==PIN )
    {
      center.y += In[0].diameter * 0.5 + In[0].linewidth;
    }
    if( In[0].style==ARROW_TAIL )
    {
      center.x += In[0].diameter * 0.5;
      center.x += In[0].inner_diameter;
    }
    if( In[0].style==ARROW_TIP )
    {
      center.x -= In[0].diameter * 0.5;
      center.x -= In[0].inner_diameter;
    }
    
    Out.texture_coordinate = vec2(-0.5,0.5);
    vec4 corner = _cameraToClip * vec4(center.x - radius, center.y - radius, center.z, 1);
    Emit(corner);

    Out.texture_coordinate = vec2(0.5,0.5);
    corner = _cameraToClip * vec4(center.x + radius, center.y - radius, center.z, 1);
    Emit(corner);

    Out.texture_coordinate = vec2(-0.5,-0.5);
    corner = _cameraToClip * vec4(center.x - radius, center.y + radius, center.z, 1);
    Emit(corner);

    Out.texture_coordinate = vec2(0.5,-0.5);
    corner = _cameraToClip * vec4(center.x + radius, center.y + radius, center.z, 1);
    Emit(corner);
  }
  else
  {
    vec4 clip = _worldToClip * gl_in[0].gl_Position;

    // center points on pixels so they get drawn as 'clean' as possible
    clip.x = (int(clip.x * _viewportSize.x) + 0.5) / _viewportSize.x;
    clip.y = (int(clip.y * _viewportSize.y) + 0.5) / _viewportSize.y;

    vec2 screen = ClipToScreen(clip, _viewportSize);
    float radius = In[0].full_diameter * 0.5;

    vec2 center = screen;
    if( In[0].style==PIN )
    {
      screen.y += In[0].diameter * 0.5 + In[0].linewidth;
    }
    if( In[0].style==ARROW_TAIL )
    {
      screen.x += In[0].diameter * 0.5;
      screen.x += In[0].inner_diameter;
    }
    if( In[0].style==ARROW_TIP )
    {
      screen.x -= In[0].diameter * 0.5;
      screen.x -= In[0].inner_diameter;
    }

    Out.texture_coordinate = vec2(-0.5,0.5);
    vec2 screen0 = screen + vec2(-radius, -radius);
    screen0 = RotatePoint(screen0, In[0].rotation, center);
    vec4 corner = ScreenToClip(screen0.xy,clip.z,clip.w, _viewportSize);
    Emit(corner);

    Out.texture_coordinate = vec2(0.5,0.5);
    vec2 screen1 = screen + vec2(radius, -radius);
    screen1 = RotatePoint(screen1, In[0].rotation, center);
    corner = ScreenToClip(screen1.xy,clip.z,clip.w, _viewportSize);
    Emit(corner);

    Out.texture_coordinate = vec2(-0.5,-0.5);
    vec2 screen2 = screen + vec2(-radius, radius);
    screen2 = RotatePoint(screen2, In[0].rotation, center);
    corner = ScreenToClip(screen2.xy,clip.z,clip.w, _viewportSize);
    Emit(corner);

    Out.texture_coordinate = vec2(0.5,-0.5);
    vec2 screen3 = screen + vec2(radius, radius);
    screen3 = RotatePoint(screen3, In[0].rotation, center);
    corner = ScreenToClip(screen3.xy,clip.z,clip.w, _viewportSize);
    Emit(corner);
  }
  EndPrimitive();
}

You’d need to rewrite the rotation part to work with world coordinates and vec4 as input but I’ll leave it up to you.

1 Like

thanks so much for this, iIll see if i can make the rotation and attractor work with world size. From the looks of it several modifications are necessary in both the vertex and geometry shaders.