@@ -33,13 +33,13 @@ layout(location = 0) in vec3 v_previous;
3333layout (location = 1 ) in vec3 v_position;
3434layout (location = 2 ) in vec3 v_next;
3535layout (location = 3 ) in float v_luminance;
36- layout (location = 4 ) in int v_direction_start_end;
36+ layout (location = 4 ) in int v_direction_start_end; // flag defining which part of a light path is currently drawn.
3737layout (location = 5 ) in vec3 v_color;
3838layout (location = 6 ) in vec3 v_surface_normal;
3939
4040uniform mat4 u_proj;
4141uniform mat4 u_view;
42- uniform vec2 u_res;
42+ uniform vec2 u_res; // resolution of the frame.
4343uniform float u_max_luminance;
4444uniform float u_max_thickness;
4545uniform float u_min_thickness;
@@ -53,66 +53,108 @@ flat out float f_thickness;
5353flat out float f_total_thickness;
5454flat out float f_aspect_expansion_len;
5555
56- const float CLIPPING_PREVENTION_FACTOR = 0.01 ;
56+ const float CLIPPING_PREVENTION_FACTOR = 0.05 ;
5757
58- void main() {
59- float aspect = u_res.x / u_res.y;
60- vec2 aspect_vec = vec2 (aspect, 1.0 );
61- mat4 vp = u_proj * u_view;
62- vec4 prev_proj = vp * vec4 (v_previous, 1.0 );
63- vec4 curr_proj = vp * vec4 (v_position + v_surface_normal * CLIPPING_PREVENTION_FACTOR, 1.0 );
64- vec4 next_proj = vp * vec4 (v_next, 1.0 );
65-
66- // get 2D screen space with W divide and aspect correction
67- vec2 curr_screen = curr_proj.xy / curr_proj.w * aspect_vec;
68- vec2 prev_screen = prev_proj.xy / prev_proj.w * aspect_vec;
69- vec2 next_screen = next_proj.xy / next_proj.w * aspect_vec;
58+ //
59+ // Reference:
60+ // - https://mattdesl.svbtle.com/drawing-lines-is-hard#screenspace-projected-lines_2
61+ //
7062
71- float orientation = 1.0 ;
72- if ((v_direction_start_end & 1 ) == 1 )
73- {
74- orientation = - 1.0 ;
75- }
63+ #define DRAW_START_PATH 1 // Drawing the start of the light path
64+ #define DRAW_MIDDLE_PATH 2 // Drawing a point in the middle of the light path
65+ #define DRAW_END_PATH 3 // Drawing the end of the light path
7666
77- vec2 dir = vec2 (0.0 );
67+ int get_drawing_mode()
68+ {
7869 if ((v_direction_start_end & 2 ) == 2 )
7970 {
8071 // starting point uses (next - current)
81- dir = normalize (next_screen - curr_screen) ;
72+ return DRAW_START_PATH ;
8273 }
8374 else if ((v_direction_start_end & 4 ) == 4 )
8475 {
8576 // ending point uses (current - previous)
86- dir = normalize (curr_screen - prev_screen);
87- } else {
77+ return DRAW_END_PATH;
78+ }
79+ else
80+ {
8881 // middle point uses (next - current)
89- dir = normalize (next_screen - curr_screen) ;
82+ return DRAW_MIDDLE_PATH ;
9083 }
91- vec2 perp_dir = vec2 (- dir.y, dir.x);
84+ }
85+
86+ // Each point on the light path is duplicated to render a real line.
87+ // The duplicated vertex of each light path point is flagged.
88+ bool is_second_point()
89+ {
90+ return ((v_direction_start_end & 1 ) == 1 );
91+ }
92+
93+ void main() {
94+ // Aspect ratio correction is applied on
95+ // screen points (only on the X axis).
96+ float aspect = u_res.x / u_res.y;
97+ vec2 aspect_correction = vec2 (aspect, 1.0 );
98+
99+ // Project points.
100+ // The currently drawn point is offset
101+ // from the surface to ensure path
102+ // doesn't go through it.
103+ mat4 vp = u_proj * u_view;
104+ vec4 curr_proj = vp * vec4 (v_position + v_surface_normal * CLIPPING_PREVENTION_FACTOR, 1.0 );
105+ vec4 prev_proj = vp * vec4 (v_previous, 1.0 );
106+ vec4 next_proj = vp * vec4 (v_next, 1.0 );
107+
108+ // Project points in screenspace.
109+ vec2 curr_screen = curr_proj.xy / curr_proj.w;
110+ vec2 prev_screen = prev_proj.xy / prev_proj.w;
111+ vec2 next_screen = next_proj.xy / next_proj.w;
112+
113+ // Apply aspect ratio correction.
114+ curr_screen *= aspect_correction;
115+ prev_screen *= aspect_correction;
116+ next_screen *= aspect_correction;
117+
118+ int drawing_mode = get_drawing_mode();
119+ bool is_second_point = is_second_point();
120+
121+ // Compute current line directionection.
122+ // Depending on which part of the path we
123+ // are drawing (start, middle or end), we
124+ // compute it differently.
125+ vec2 line_direction =
126+ drawing_mode == DRAW_START_PATH ? normalize (next_screen - curr_screen) :
127+ drawing_mode == DRAW_END_PATH ? normalize (curr_screen - prev_screen) :
128+ normalize (next_screen - curr_screen);
129+
130+ // Compute the normal of the line.
131+ vec2 line_normal = vec2 (- line_direction.y, line_direction.x);
92132
93133 vec4 normal_clip = vp * vec4 (v_surface_normal, 0.0 );
94- normal_clip.xy *= aspect_vec ;
134+ normal_clip.xy *= aspect_correction ;
95135 normal_clip = normalize (normal_clip);
96136 vec2 tang_clip = vec2 (- normal_clip.y, normal_clip.x);
97137
98- float tdp = dot (tang_clip, perp_dir );
99- vec2 expansion = perp_dir ;
138+ float tdp = dot (tang_clip, line_normal );
139+ vec2 expansion = line_normal ;
100140 if (tdp > 0.05 )
101141 expansion = tang_clip / tdp;
102142
103143 vec2 norm_exp = normalize (expansion);
104- vec2 res_exp_dir = vec2 (norm_exp.x * u_res.x, norm_exp.y * u_res.y);
105- f_aspect_expansion_len = length (res_exp_dir );
144+ vec2 res_exp_line_direction = vec2 (norm_exp.x * u_res.x, norm_exp.y * u_res.y);
145+ f_aspect_expansion_len = length (res_exp_line_direction );
106146
107147 f_thickness = (max (max (min (v_luminance / u_max_luminance, 1.0 ), 0.0 ) * u_max_thickness, u_min_thickness) / 2.0 ) / f_aspect_expansion_len;
108148
109149 f_total_thickness = f_thickness + AA_BUFFER_SIZE / f_aspect_expansion_len;
110150
111151 expansion *= f_total_thickness;
112- expansion *= orientation;
113152
114- gl_Position = vec4 ((curr_screen + expansion) / aspect_vec, curr_proj.z / curr_proj.w, 1.0 );
115- f_aa_norm = orientation;
153+ // Reverse expansion line_directionection for the duplicated point.
154+ if (is_second_point) expansion *= - 1.0 ;
155+
156+ gl_Position = vec4 ((curr_screen + expansion) / aspect_correction, curr_proj.z / curr_proj.w, 1.0 );
157+ f_aa_norm = is_second_point ? 1.0 : - 1.0 ;
116158
117159 bool is_selected = gl_VertexID >= u_first_selected && gl_VertexID < u_last_selected;
118160 float a = is_selected ? 1.0 : 0.05 ;
0 commit comments