Skip to content

Commit

Permalink
Merge polygon and circle symbol shaders
Browse files Browse the repository at this point in the history
  • Loading branch information
jonmmease committed Jan 11, 2024
1 parent 75d6a6c commit d6d6094
Show file tree
Hide file tree
Showing 4 changed files with 154 additions and 183 deletions.
96 changes: 0 additions & 96 deletions sg2d-wgpu/src/marks/circle.wgsl

This file was deleted.

84 changes: 0 additions & 84 deletions sg2d-wgpu/src/marks/polygon_symbol.wgsl

This file was deleted.

7 changes: 4 additions & 3 deletions sg2d-wgpu/src/marks/symbol.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use wgpu::VertexBufferLayout;

const FILL_KIND: u32 = 0;
const STROKE_KIND: u32 = 1;
const CIRCLE_KIND: u32 = 2;

#[repr(C)]
#[derive(Copy, Clone, Debug, bytemuck::Pod, bytemuck::Zeroable)]
Expand Down Expand Up @@ -98,7 +99,7 @@ impl SymbolShader {
SymbolShape::Circle => {
let r = if has_stroke { 0.9 } else { 0.6 };
let normal: [f32; 2] = [0.0, 0.0];
let kind = FILL_KIND;
let kind = CIRCLE_KIND;
Self {
verts: vec![
SymbolVertex {
Expand All @@ -123,7 +124,7 @@ impl SymbolShader {
},
],
indices: vec![0, 1, 2, 0, 2, 3],
shader: include_str!("circle.wgsl").to_string(),
shader: include_str!("symbol.wgsl").to_string(),
vertex_entry_point: "vs_main".to_string(),
fragment_entry_point: "fs_main".to_string(),
}
Expand All @@ -149,7 +150,7 @@ impl SymbolShader {
Self {
verts: buffers.vertices,
indices: buffers.indices,
shader: include_str!("polygon_symbol.wgsl").to_string(),
shader: include_str!("symbol.wgsl").to_string(),
vertex_entry_point: "vs_main".to_string(),
fragment_entry_point: "fs_main".to_string(),
}
Expand Down
150 changes: 150 additions & 0 deletions sg2d-wgpu/src/marks/symbol.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
// Vertex shader
struct ChartUniform {
size: vec2<f32>,
scale: f32,
_pad: f32, // for 16 byte alignment
};

@group(0) @binding(0)
var<uniform> chart_uniforms: ChartUniform;

struct VertexInput {
@location(0) position: vec2<f32>,
@location(1) normal: vec2<f32>,
@location(2) kind: u32,
};

struct InstanceInput {
@location(3) position: vec2<f32>,
@location(4) fill_color: vec4<f32>,
@location(5) stroke_color: vec4<f32>,
@location(6) stroke_width: f32,
@location(7) size: f32,
@location(8) angle: f32,
};

struct VertexOutput {
@builtin(position) clip_position: vec4<f32>,

// If 1.0, the vertex is part of the bounding box of a circle symbol
// otherwise, vertex is part of a geometric (path-based) symbol
@location(0) is_circle: f32,

// Color of vertex when drawing geometric symbol based on a path
@location(1) geom_color: vec4<f32>,

// Properties of circle symbol. Circles are drawn in the fragment shader,
// so more info must be passed through
@location(2) circle_center: vec2<f32>,
@location(3) circle_radius: f32,
@location(4) circle_fill_color: vec4<f32>,
@location(5) circle_stroke_color: vec4<f32>,
@location(6) circle_stroke_width: f32,
};

const PI = 3.14159265359;

@vertex
fn vs_main(
model: VertexInput,
instance: InstanceInput,
) -> VertexOutput {
var out: VertexOutput;
let size_scale = sqrt(instance.size);

// Compute scenegraph x and y coordinates
let angle_rad = PI * instance.angle / 180.0;
let rot = mat2x2(cos(angle_rad), -sin(angle_rad), sin(angle_rad), cos(angle_rad));
let rotated_pos = rot * model.position;
let sg_x = rotated_pos[0] * size_scale + instance.position[0];
let sg_y = rotated_pos[1] * size_scale + (chart_uniforms.size[1] - instance.position[1]);
let pos = vec2(sg_x, sg_y);

if (model.kind == 0u) {
// fill vertex
out.geom_color = instance.fill_color;

let normalized_pos = 2.0 * pos / chart_uniforms.size - 1.0;
out.clip_position = vec4<f32>(normalized_pos, 0.0, 1.0);
out.is_circle = 0.0;
} else if (model.kind == 1u) {
// stroke vertex
out.geom_color = instance.stroke_color;

// Compute scaled stroke width.
// The 0.1 here is the width that lyon used to compute the stroke tesselation
let scaled_stroke_width = 0.1 * size_scale;

// Adjust vertex along normal to achieve desired line width
// The factor of 2.0 here is because the normal vector that lyon
// returns has length such that moving all stroke vertices by the length
// of the "normal" vector will increase the line width by 2.
let normal = rot * model.normal;
var diff = scaled_stroke_width - instance.stroke_width;
let adjusted_pos = pos - diff * normal / 2.0;

let normalized_pos = 2.0 * adjusted_pos / chart_uniforms.size - 1.0;
out.clip_position = vec4<f32>(normalized_pos, 0.0, 1.0);
out.is_circle = 0.0;
} else if (model.kind == 2u) {
// circle symbol. Circles are drawn in the fragment shader, so
// we compute the center and radius and pass through the stroke and
// fill specifications.
out.is_circle = 1.0;

// Pass through colors and stroke_width
out.circle_fill_color = instance.fill_color;
out.circle_stroke_color = instance.stroke_color;
out.circle_stroke_width = instance.stroke_width;

// Compute normalized position
let normalized_pos = 2.0 * pos / chart_uniforms.size - 1.0;
out.clip_position = vec4<f32>(normalized_pos, 0.0, 1.0);

// Compute circle center in fragment shader coordinates
out.circle_center = vec2<f32>(
instance.position[0] * chart_uniforms.scale,
instance.position[1] * chart_uniforms.scale,
);

// Compute radius in fragment shader coordinates
out.circle_radius = size_scale * chart_uniforms.scale / 2.0;
}

return out;
}

// Fragment shader
@fragment
fn fs_main(in: VertexOutput) -> @location(0) vec4<f32> {
if (in.is_circle == 1.0) {
// Draw anti-aliased circle
let buffer = 0.5 * chart_uniforms.scale;
let dist = length(in.circle_center - vec2<f32>(in.clip_position[0], in.clip_position[1]));

if (in.circle_stroke_width > 0.0) {
let inner_radius = in.circle_radius - in.circle_stroke_width * chart_uniforms.scale / 2.0;
let outer_radius = in.circle_radius + in.circle_stroke_width * chart_uniforms.scale / 2.0;
if (dist > outer_radius + buffer * 2.0) {
discard;
} else {
let alpha_factor = 1.0 - smoothstep(outer_radius - buffer, outer_radius + buffer, dist);
let mix_factor = 1.0 - smoothstep(inner_radius - buffer, inner_radius + buffer, dist);
var mixed_color: vec4<f32> = mix(in.circle_stroke_color, in.circle_fill_color, mix_factor);
mixed_color[3] *= alpha_factor;
return mixed_color;
}
} else {
let alpha_factor = 1.0 - smoothstep(in.circle_radius - buffer, in.circle_radius + buffer, dist);
var mixed_color: vec4<f32> = in.circle_fill_color;
mixed_color[3] *= alpha_factor;
if (dist > in.circle_radius + buffer) {
discard;
} else {
return mixed_color;
}
}
} else {
return in.geom_color;
}
}

0 comments on commit d6d6094

Please sign in to comment.