Search across packages, versions, files, declarations, and comments. Use multiple terms to narrow results (AND), and add @vertex, @fragment, or @compute to filter by shader stage.
1import bevy_render::{view::View, maths::affine3_to_square};
3@group(0) @binding(0) var<uniform> view: View;
6struct LineGizmoUniform {
7 world_from_local: mat3x4<f32>,
8 line_width: f32,
9 depth_bias: f32,
10 resolution: u32,
11 @if(SIXTEEN_BYTE_ALIGNMENT)
12 // WebGL2 structs must be 16 byte aligned.
13 _padding: f32,
16@group(1) @binding(0) var<uniform> joints_gizmo: LineGizmoUniform;
18struct VertexInput {
19 @location(0) position_a: vec3<f32>,
20 @location(1) position_b: vec3<f32>,
21 @location(2) position_c: vec3<f32>,
22 @location(3) color: vec4<f32>,
23 @builtin(vertex_index) index: u32,
24};
26struct VertexOutput {
27 @builtin(position) clip_position: vec4<f32>,
28 @location(0) color: vec4<f32>,
29};
31const EPSILON: f32 = 4.88e-04;
33@vertex
34fn vertex_bevel(vertex: VertexInput) -> VertexOutput {
35 var positions = array<vec2<f32>, 3>(
36 vec2(0, 0),
37 vec2(0, 0.5),
38 vec2(0.5, 0),
39 );
40 var position = positions[vertex.index];
42 let world_from_local = affine3_to_square(joints_gizmo.world_from_local);
44 var clip_a = view.clip_from_world * world_from_local * vec4(vertex.position_a, 1.);
45 var clip_b = view.clip_from_world * world_from_local * vec4(vertex.position_b, 1.);
46 var clip_c = view.clip_from_world * world_from_local * vec4(vertex.position_c, 1.);
48 // Manual near plane clipping to avoid errors when doing the perspective divide inside this shader.
49 clip_a = clip_near_plane(clip_a, clip_c);
50 clip_b = clip_near_plane(clip_b, clip_a);
51 clip_c = clip_near_plane(clip_c, clip_b);
52 clip_a = clip_near_plane(clip_a, clip_c);
54 let resolution = view.viewport.zw;
55 let screen_a = resolution * (0.5 * clip_a.xy / clip_a.w + 0.5);
56 let screen_b = resolution * (0.5 * clip_b.xy / clip_b.w + 0.5);
57 let screen_c = resolution * (0.5 * clip_c.xy / clip_c.w + 0.5);
59 var color = vertex.color;
60 var line_width = joints_gizmo.line_width;
62 @if(PERSPECTIVE)
63 line_width /= clip_b.w;
65 // Line thinness fade from https://acegikmo.com/shapes/docs/#anti-aliasing
66 if line_width > 0.0 && line_width < 1. {
67 color.a *= line_width;
68 line_width = 1.;
69 }
71 let ab = normalize(screen_b - screen_a);
72 let cb = normalize(screen_b - screen_c);
73 let ab_norm = vec2(-ab.y, ab.x);
74 let cb_norm = vec2(cb.y, -cb.x);
75 let tangent = normalize(ab - cb);
76 let normal = vec2(-tangent.y, tangent.x);
77 let sigma = sign(dot(ab + cb, normal));
79 var p0 = line_width * sigma * ab_norm;
80 var p1 = line_width * sigma * cb_norm;
82 let screen = screen_b + position.x * p0 + position.y * p1;
84 let depth = depth(clip_b);
86 var clip_position = vec4(clip_b.w * ((2. * screen) / resolution - 1.), depth, clip_b.w);
87 return VertexOutput(clip_position, color);
90@vertex
91fn vertex_miter(vertex: VertexInput) -> VertexOutput {
92 var positions = array<vec3<f32>, 6>(
93 vec3(0, 0, 0),
94 vec3(0.5, 0, 0),
95 vec3(0, 0.5, 0),
96 vec3(0, 0, 0),
97 vec3(0, 0.5, 0),
98 vec3(0, 0, 0.5),
99 );
100 var position = positions[vertex.index];
102 let world_from_local = affine3_to_square(joints_gizmo.world_from_local);
104 var clip_a = view.clip_from_world * world_from_local * vec4(vertex.position_a, 1.);
105 var clip_b = view.clip_from_world * world_from_local * vec4(vertex.position_b, 1.);
106 var clip_c = view.clip_from_world * world_from_local * vec4(vertex.position_c, 1.);
108 // Manual near plane clipping to avoid errors when doing the perspective divide inside this shader.
109 clip_a = clip_near_plane(clip_a, clip_c);
110 clip_b = clip_near_plane(clip_b, clip_a);
111 clip_c = clip_near_plane(clip_c, clip_b);
112 clip_a = clip_near_plane(clip_a, clip_c);
114 let resolution = view.viewport.zw;
115 let screen_a = resolution * (0.5 * clip_a.xy / clip_a.w + 0.5);
116 let screen_b = resolution * (0.5 * clip_b.xy / clip_b.w + 0.5);
117 let screen_c = resolution * (0.5 * clip_c.xy / clip_c.w + 0.5);
119 var color = vertex.color;
120 var line_width = joints_gizmo.line_width;
122 @if(PERSPECTIVE)
123 line_width /= clip_b.w;
125 // Line thinness fade from https://acegikmo.com/shapes/docs/#anti-aliasing
126 if line_width > 0.0 && line_width < 1. {
127 color.a *= line_width;
128 line_width = 1.;
129 }
131 let ab = normalize(screen_b - screen_a);
132 let cb = normalize(screen_b - screen_c);
133 let ab_norm = vec2(-ab.y, ab.x);
134 let cb_norm = vec2(cb.y, -cb.x);
135 let tangent = normalize(ab - cb);
136 let normal = vec2(-tangent.y, tangent.x);
137 let sigma = sign(dot(ab + cb, normal));
139 var p0 = line_width * sigma * ab_norm;
140 var p1 = line_width * sigma * normal / dot(normal, ab_norm);
141 var p2 = line_width * sigma * cb_norm;
143 var screen = screen_b + position.x * p0 + position.y * p1 + position.z * p2;
145 var depth = depth(clip_b);
147 var clip_position = vec4(clip_b.w * ((2. * screen) / resolution - 1.), depth, clip_b.w);
148 return VertexOutput(clip_position, color);
151@vertex
152fn vertex_round(vertex: VertexInput) -> VertexOutput {
153 let world_from_local = affine3_to_square(joints_gizmo.world_from_local);
155 var clip_a = view.clip_from_world * world_from_local * vec4(vertex.position_a, 1.);
156 var clip_b = view.clip_from_world * world_from_local * vec4(vertex.position_b, 1.);
157 var clip_c = view.clip_from_world * world_from_local * vec4(vertex.position_c, 1.);
159 // Manual near plane clipping to avoid errors when doing the perspective divide inside this shader.
160 clip_a = clip_near_plane(clip_a, clip_c);
161 clip_b = clip_near_plane(clip_b, clip_a);
162 clip_c = clip_near_plane(clip_c, clip_b);
163 clip_a = clip_near_plane(clip_a, clip_c);
165 let resolution = view.viewport.zw;
166 let screen_a = resolution * (0.5 * clip_a.xy / clip_a.w + 0.5);
167 let screen_b = resolution * (0.5 * clip_b.xy / clip_b.w + 0.5);
168 let screen_c = resolution * (0.5 * clip_c.xy / clip_c.w + 0.5);
170 var color = vertex.color;
171 var line_width = joints_gizmo.line_width;
173 @if(PERSPECTIVE)
174 line_width /= clip_b.w;
176 // Line thinness fade from https://acegikmo.com/shapes/docs/#anti-aliasing
177 if line_width > 0.0 && line_width < 1. {
178 color.a *= line_width;
179 line_width = 1.;
180 }
182 let ab = normalize(screen_b - screen_a);
183 let cb = normalize(screen_b - screen_c);
184 let ab_norm = vec2(-ab.y, ab.x);
185 let cb_norm = vec2(cb.y, -cb.x);
187 // We render `joints_gizmo.resolution`triangles. The vertices in each triangle are ordered as follows:
188 // - 0: The 'center' vertex at `screen_b`.
189 // - 1: The vertex closer to the ab line.
190 // - 2: The vertex closer to the cb line.
191 var in_triangle_index = f32(vertex.index) % 3.0;
192 var tri_index = floor(f32(vertex.index) / 3.0);
193 var radius = sign(in_triangle_index) * 0.5 * line_width;
194 var theta = acos(dot(ab_norm, cb_norm));
195 let sigma = sign(dot(ab_norm, cb));
196 var angle = theta * (tri_index + in_triangle_index - 1) / f32(joints_gizmo.resolution);
197 var position_x = sigma * radius * cos(angle);
198 var position_y = radius * sin(angle);
200 var screen = screen_b + position_x * ab_norm + position_y * ab;
202 var depth = depth(clip_b);
204 var clip_position = vec4(clip_b.w * ((2. * screen) / resolution - 1.), depth, clip_b.w);
205 return VertexOutput(clip_position, color);
208fn clip_near_plane(a: vec4<f32>, b: vec4<f32>) -> vec4<f32> {
209 // Move a if a is behind the near plane and b is in front.
210 if a.z > a.w && b.z <= b.w {
211 // Interpolate a towards b until it's at the near plane.
212 let distance_a = a.z - a.w;
213 let distance_b = b.z - b.w;
214 // Add an epsilon to the interpolator to ensure that the point is
215 // not just behind the clip plane due to floating-point imprecision.
216 let t = distance_a / (distance_a - distance_b) + EPSILON;
217 return mix(a, b, t);
218 }
219 return a;
222fn depth(clip: vec4<f32>) -> f32 {
223 var depth: f32;
224 if joints_gizmo.depth_bias >= 0. {
225 depth = clip.z * (1. - joints_gizmo.depth_bias);
226 } else {
227 // depth * (clip.w / depth)^-depth_bias. So that when -depth_bias is 1.0, this is equal to clip.w
228 // and when equal to 0.0, it is exactly equal to depth.
229 // the epsilon is here to prevent the depth from exceeding clip.w when -depth_bias = 1.0
230 // clip.w represents the near plane in homogeneous clip space in bevy, having a depth
231 // of this value means nothing can be in front of this
232 // The reason this uses an exponential function is that it makes it much easier for the
233 // user to chose a value that is convenient for them
234 depth = clip.z * exp2(-joints_gizmo.depth_bias * log2(clip.w / clip.z - EPSILON));
235 }
236 return depth;
239struct FragmentInput {
240 @location(0) color: vec4<f32>,
241};
243struct FragmentOutput {
244 @location(0) color: vec4<f32>,
245};
247@fragment
248fn fragment(in: FragmentInput) -> FragmentOutput {
249 // return FragmentOutput(vec4(1, 1, 1, 1));
250 return FragmentOutput(in.color);
Contents