Skip to content

Instantly share code, notes, and snippets.

@Andrej730
Created April 27, 2023 12:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Andrej730/93dd9411620a4e4880b228aef71095ed to your computer and use it in GitHub Desktop.
Save Andrej730/93dd9411620a4e4880b228aef71095ed to your computer and use it in GitHub Desktop.
# minimal shader example to create smoothed polylines on Metal
import bpy
import gpu
from gpu_extras.batch import batch_for_shader
polyline_vert_glsl = """
// emits 6 vertex per each line
// see: https://projects.blender.org/blender/blender/issues/107332
#pragma USE_SSBO_VERTEX_FETCH(TriangleList, 6)
/* Clips point to near clip plane before perspective divide. */
vec4 clip_line_point_homogeneous_space(vec4 p, vec4 q)
{
if (p.z < -p.w) {
/* Just solves p + (q - p) * A; for A when p.z / p.w = -1.0. */
float denom = q.z - p.z + q.w - p.w;
if (denom == 0.0) {
/* No solution. */
return p;
}
float A = (-p.z - p.w) / denom;
p = p + (q - p) * A;
}
return p;
}
void do_vertex(int index, vec4 pos, vec2 ofs, float flip)
{
smoothline = flip * (lineWidth + SMOOTH_WIDTH * float(lineSmooth)) * 0.5;
gl_Position = pos;
gl_Position.xy += flip * ofs * pos.w;
}
void main()
{
/** Determine output quad primitive structure. */
/* Index of the quad primitive. Each quad corresponds to one line in the input primitive. */
int quad_id = gl_VertexID / 6;
/* Determine vertex within the quad (A, B, C)(A, C, D). */
int quad_vertex_id = gl_VertexID % 6;
uint src_index_a;
uint src_index_b;
if (vertex_fetch_get_input_prim_type() == GPU_PRIM_LINE_STRIP) {
src_index_a = quad_id;
src_index_b = quad_id + 1;
}
else if (vertex_fetch_get_input_prim_type() == GPU_PRIM_LINES) {
src_index_a = quad_id * 2;
src_index_b = quad_id * 2 + 1;
}
else if (vertex_fetch_get_input_prim_type() == GPU_PRIM_LINE_LOOP) {
src_index_a = quad_id;
src_index_b = quad_id + 1;
if (quad_id == vertex_fetch_get_input_vert_count() - 1) {
src_index_b = 0;
}
}
else {
src_index_a = 0;
src_index_b = 0;
}
/* Fetch input attributes for line prims -- either provided as vec2 or vec3 -- So we need to
* query the type. */
vec3 in_pos0, in_pos1;
in_pos0 = vec3(0.0);
in_pos1 = vec3(0.0);
if (vertex_fetch_get_attr_type(pos) == GPU_SHADER_ATTR_TYPE_VEC4) {
in_pos0 = vertex_fetch_attribute(src_index_a, pos, vec4).xyz;
in_pos1 = vertex_fetch_attribute(src_index_b, pos, vec4).xyz;
}
else if (vertex_fetch_get_attr_type(pos) == GPU_SHADER_ATTR_TYPE_VEC3) {
in_pos0 = vertex_fetch_attribute(src_index_a, pos, vec3);
in_pos1 = vertex_fetch_attribute(src_index_b, pos, vec3);
}
else if (vertex_fetch_get_attr_type(pos) == GPU_SHADER_ATTR_TYPE_VEC2) {
in_pos0 = vec3(vertex_fetch_attribute(src_index_a, pos, vec2), 0.0);
in_pos1 = vec3(vertex_fetch_attribute(src_index_b, pos, vec2), 0.0);
}
/* Calculate Vertex shader for both points in Line. */
vec4 out_pos0 = ModelViewProjectionMatrix * vec4(in_pos0, 1.0);
vec4 out_pos1 = ModelViewProjectionMatrix * vec4(in_pos1, 1.0);
/*** Geometry Shader Alternative. ***/
vec4 p0 = clip_line_point_homogeneous_space(out_pos0, out_pos1);
vec4 p1 = clip_line_point_homogeneous_space(out_pos1, out_pos0);
vec2 e = normalize(((p1.xy / p1.w) - (p0.xy / p0.w)) * viewportSize.xy);
vec2 ofs = vec2(-e.y, e.x);
ofs /= viewportSize.xy;
ofs *= lineWidth + SMOOTH_WIDTH * float(lineSmooth);
if (quad_vertex_id == 0) {
do_vertex(0, p0, ofs, 1.0);
}
else if (quad_vertex_id == 1 || quad_vertex_id == 3) {
do_vertex(0, p0, ofs, -1.0);
}
else if (quad_vertex_id == 2 || quad_vertex_id == 5) {
do_vertex(1, p1, ofs, 1.0);
}
else if (quad_vertex_id == 4) {
do_vertex(1, p1, ofs, -1.0);
}
}
"""
polyline_frag_glsl = """
void main()
{
fragColor = vec4(1.0, 1.0, 0.0, 1.0);
if (lineSmooth) {
fragColor.a *= clamp((lineWidth + SMOOTH_WIDTH) * 0.5 - abs(smoothline), 0.0, 1.0);
}
}
"""
from mathutils import Vector
coords = [(1, 1, 1), (-2, 0, 0), (-2, -1, 3), (0, 1, 1)]
offset_vector = Vector([0,0,1])
coords = [Vector(c) - offset_vector for c in coords]
def draw():
print('DRAW------------------')
vert_out = gpu.types.GPUStageInterfaceInfo("vert_interface")
vert_out.smooth('FLOAT', "smoothline")
shader_info = gpu.types.GPUShaderCreateInfo()
shader_info.define("SMOOTH_WIDTH", "2.0")
shader_info.push_constant('MAT4', "ModelViewProjectionMatrix")
shader_info.push_constant("FLOAT", "lineWidth")
shader_info.push_constant("INT", "lineSmooth")
shader_info.push_constant("VEC2", "viewportSize")
shader_info.vertex_in(0, 'VEC3', 'pos')
shader_info.vertex_out(vert_out)
shader_info.fragment_out(0, 'VEC4', 'fragColor')
shader_info.vertex_source(polyline_vert_glsl)
shader_info.fragment_source(polyline_frag_glsl)
shader = gpu.shader.create_from_info(shader_info)
batch = batch_for_shader(shader, 'LINES', {"pos": coords})
shader.bind()
shader.uniform_float("lineWidth", 5.0)
shader.uniform_int("lineSmooth", 1)
region = bpy.context.area
shader.uniform_float("viewportSize", [region.width, region.height])
gpu.state.blend_set("ALPHA")
batch.draw(shader)
bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_VIEW')
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment