3

Does anyone know how to make rounded corners for a 2d rectangle with a custom shader?

enter image description here

import bpy
import gpu
from gpu_extras.batch import batch_for_shader

vertices = ( (100, 100), (300, 100), (100, 200), (300, 200))

indices = ( (0, 1, 2), (2, 1, 3))

shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR') batch = batch_for_shader(shader, 'TRIS', {"pos": vertices}, indices=indices)

def draw(): shader.bind() shader.uniform_float("color", (0, 0.5, 0.5, 1.0)) batch.draw(shader)

bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL')

Karan
  • 1,984
  • 5
  • 21

2 Answers2

6

Make a mesh.-

Your example has 4 vertices and two faces making a rectangle of two triangles, going to need a bit more geometry than that to have rounded corners. The same method applies here to making a mesh.

Given blender has a built in tool to make meshes, let's make a plane with bmesh, bevel the corners, triangulate it, and scale to suit.

Bevel & triangulate.

enter image description here

Test script, makes a 2 x 1 rounded corner rectangle. The corner radius is 0.2 and 5 segments create the bevel. The bmesh is later translated and scaled to match our bgl coordinates. Moved to 100, 100 and scaled by 100 (Could also be saved to a mesh. See other bgl manual examples re using mesh coordinates)

import bmesh
import bpy
from mathutils import Matrix

dim = (2, 1) cnr = 0.2

bm = bmesh.new() bmesh.ops.create_grid( bm, size=0.5, matrix=Matrix.Diagonal((dim[0], dim[1], 1, 1)) @
Matrix.Translation((0.5, 0.5, 0)), ) bmesh.ops.bevel( bm, geom=bm.verts, #loop_slide=True, affect='VERTICES', profile=0.5, # round offset=cnr, segments=5, )

bmesh.ops.triangulate( bm, faces=bm.faces, )

scale by 100

bm.transform( Matrix.Diagonal([100] * 4) )

translate by 100, 100

bm.transform( Matrix.Translation((100, 100, 0)) )

print("vertices = (") for v in bm.verts: print(f"\t{v.co.xy[:]},") print(")") print("faces = (") for f in bm.faces: print(f"\t{[v.index for v in f.verts]},") bm.to_mesh(bpy.context.object.data) print(")")

Produces

enter image description here Color (0,1, 0.5, 1.0) rounded corner result of using data below (faces for indices), overlayed on pre-run rectangle of question script

vertices = (
    (120.0, 100.0),
    (100.0, 120.0),
    (113.81965637207031, 100.97886657714844),
    (108.24429321289062, 103.81965637207031),
    (103.81965637207031, 108.24429321289062),
    (100.97886657714844, 113.81965637207031),
    (300.0, 120.0),
    (280.0, 100.0),
    (299.0211181640625, 113.81965637207031),
    (296.18035888671875, 108.24429321289062),
    (291.7557067871094, 103.81965637207031),
    (286.18035888671875, 100.97886657714844),
    (120.0, 200.0),
    (100.0, 180.0),
    (113.81965637207031, 199.0211181640625),
    (108.24429321289062, 196.18032836914062),
    (103.81965637207031, 191.75570678710938),
    (100.97886657714844, 186.18032836914062),
    (280.0, 200.0),
    (300.0, 180.0),
    (286.18035888671875, 199.0211181640625),
    (291.7557067871094, 196.18032836914062),
    (296.18035888671875, 191.75570678710938),
    (299.0211181640625, 186.18032836914062),
)
faces = (
    [13, 1, 0],
    [19, 21, 18],
    [1, 5, 0],
    [5, 4, 3],
    [3, 2, 0],
    [0, 7, 18],
    [7, 11, 6],
    [18, 7, 6],
    [11, 10, 9],
    [9, 8, 11],
    [8, 6, 11],
    [6, 19, 18],
    [19, 23, 21],
    [23, 22, 21],
    [21, 20, 18],
    [18, 12, 0],
    [12, 14, 13],
    [0, 12, 13],
    [14, 15, 13],
    [15, 16, 13],
    [16, 17, 13],
    [5, 3, 0],
)

Triangulate with Poke instead

enter image description here

Result of replacing bmesh triangulate operator with poke

bmesh.ops.poke(
        bm,
        faces=bm.faces,
        )

Block with rounded edges in python

batFINGER
  • 84,216
  • 10
  • 108
  • 233
3

enter image description here

It works, but you need to find a way to do the Anti-Aliasing

import bpy, gpu
from mathutils import Matrix
from gpu_extras.batch import batch_for_shader
from bgl import glEnable, GL_BLEND

vertex_shader = ''' uniform mat4 winMat;

in vec2 position;
out vec2 pos;

void main(){
    pos = position;
    gl_Position = winMat * vec4(position, 0.0, 1.0);
}

'''

fragment_shader = ''' uniform vec4 color; uniform vec4 LRBT; uniform float d;

in vec2 pos;
out vec4 FragColor;

float c;
float dL;
float dR;
float dB;
float dT;

void main()
{
    c = color[3];

    if (pos[0] < LRBT[0] + d){
        if (pos[1] > LRBT[3] - d){

        }
    }
    dL = 1 - (LRBT[0] + d - pos[0]) / d;
    dT = 1 - (pos[1] - LRBT[3] + d ) / d;
    dB = 1 - (LRBT[2] + d - pos[1]) / d;
    dR = 1 - (pos[0] - LRBT[1] + d) / d;
    c *= dL * dT * dR * dT * dR * dB * dL * dB;

    if (c > 0.1){c = 1.0;} else {c = 0.0;}

    if (pos[0] <= LRBT[0] + 1){c = 0.0;
    } else {
        if (pos[0] >= LRBT[1] - 1){c = 0.0;
        } else {
            if (pos[1] <= LRBT[2] + 1){c = 0.0;
            } else {
                if (pos[1] >= LRBT[3] - 1){c = 0.0;
                }
            }
        }
    }


    FragColor = vec4(color[0], color[1], color[2], c);
}

'''

vertices = ( (100, 100), (300, 100), (100, 200), (300, 200))

indices = ( (0, 1, 2), (2, 1, 3))

shader = gpu.types.GPUShader(vertex_shader, fragment_shader) batch = batch_for_shader(shader, 'TRIS', {"position": vertices}, indices=indices) shader.uniform_float("color", (0.5, 0.5, 0.5, 1.0)) v = vertices shader.uniform_float("LRBT", (v[0][0], v[1][0], v[0][1], v[2][1])) shader.uniform_float("d", 30)

def draw(): glEnable(GL_BLEND) shader.bind() shader.uniform_float("winMat", gpu.matrix.get_projection_matrix()) batch.draw(shader)

bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL')

Make a class

import bpy, gpu
from mathutils import Matrix
from gpu_extras.batch import batch_for_shader
from bgl import glEnable, GL_BLEND
from gpu.types import GPUShader
from gpu.matrix import get_projection_matrix

vertex_shader = ''' uniform mat4 winMat;

in vec2 position;
out vec2 pos;

void main(){
    pos = position;
    gl_Position = winMat * vec4(position, 0.0, 1.0);
}

'''

fragment_shader = ''' uniform vec4 color; uniform vec4 LRBT; uniform float d;

in vec2 pos;
out vec4 FragColor;

float c;
float dL;
float dR;
float dB;
float dT;

void main()
{
    c = color[3];

    if (pos[0] < LRBT[0] + d){
        if (pos[1] > LRBT[3] - d){

        }
    }
    dL = 1 - (LRBT[0] + d - pos[0]) / d;
    dT = 1 - (pos[1] - LRBT[3] + d ) / d;
    dB = 1 - (LRBT[2] + d - pos[1]) / d;
    dR = 1 - (pos[0] - LRBT[1] + d) / d;
    c *= dL * dT * dR * dT * dR * dB * dL * dB;

    if (c > 0.1){c = 1.0;} else {c = 0.0;}

    if (pos[0] <= LRBT[0] + 1){c = 0.0;
    } else {
        if (pos[0] >= LRBT[1] - 1){c = 0.0;
        } else {
            if (pos[1] <= LRBT[2] + 1){c = 0.0;
            } else {
                if (pos[1] >= LRBT[3] - 1){c = 0.0;
                }
            }
        }
    }


    FragColor = vec4(color[0], color[1], color[2], c);
}

'''

box_indices = ((0, 1, 2), (2, 1, 3))

class Rbox: slots = "L", "R", "B", "T", "d", "color", "batch", "shader" def init(self, L, R, B, T, d, color): # left, right, bottom, top, depth self.L = L self.R = R self.B = B self.T = T self.d = d self.color = color self.shader = GPUShader(vertex_shader, fragment_shader) def update(self): s = self.shader self.batch = batch_for_shader(s, 'TRIS', {"position": ( (self.L, self.B), (self.R, self.B), (self.L, self.T), (self.R, self.T))}, indices=box_indices) def draw(self): s = self.shader s.bind() s.uniform_float("color", self.color) s.uniform_float("LRBT", (self.L, self.R, self.B, self.T)) s.uniform_float("d", self.d) s.uniform_float("winMat", get_projection_matrix()) self.batch.draw(s) def move(self, dx, dy): self.L += dx self.R += dx self.B += dy self.T += dy def move_update(self, dx, dy): self.move(dx, dy) self.update()

Rboxes = [ Rbox(100, 300, 100, 200, 30, (0.9, 0.9, 0.9, 1.0)), Rbox(100, 300, 300, 400, 30, (0.1, 0.1, 0.1, 1.0)), ] for r in Rboxes: r.update()

def draw(): glEnable(GL_BLEND) for r in Rboxes: r.draw()

bpy.types.SpaceView3D.draw_handler_add(draw, (), 'WINDOW', 'POST_PIXEL')

X Y
  • 5,234
  • 1
  • 6
  • 20