【问题标题】:GLSL, SDF based Rounding RectangleGLSL,基于 SDF 的圆角矩形
【发布时间】:2021-09-15 09:23:17
【问题描述】:

该应用基于 PyOpenGL(核心配置文件)并使用正交投影。我必须在四边形(2 个三角形)上绘制几个不同的 2d 形状。

我发现了一个非常棒的article 使用 SDF 渲染 2d/3d 形状。我尝试的第一个形状是带边框的圆角矩形。这个Shadertoy 示例完全符合我的要求。这是我的两个着色器:

顶点着色器

#version 330 core
// VERTEX SHADER
layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;

out vec2 tex_coord;
uniform mat4 mvp;

void main()
{
    gl_Position = mvp * vec4(aPos, 1.0);
    tex_coord = aTexCoord;
}

片段着色器

#version 330 core
// FRAGMENT SHADER 

uniform vec4 in_color;
in vec2 tex_coord;
vec2 resolution = vec2(800, 600);
float aspect = resolution.x / resolution.y;
const float borderThickness = 0.01;
const vec4 borderColor = vec4(1.0, 1.0, 0.0, 1.0);
const vec4 fillColor = vec4(1.0, 0.0, 0.0, 1.0);
const float radius = 0.05;

float RectSDF(vec2 p, vec2 b, float r)
{
    vec2 d = abs(p) - b + vec2(r);
    return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - r;   
}

void main() {
    
    // https://www.shadertoy.com/view/ltS3zW
    vec2 centerPos = tex_coord - vec2(0.5, 0.5); // <-0.5,0.5>
    //vec2 centerPos = (tex_coord/resolution - vec2(0.5)) * 2.0;
    //centerPos *= aspect; // fix aspect ratio
    //centerPos = (centerPos - resolution.xy) * 2.0;
    
    float fDist = RectSDF(centerPos, vec2(0.5, 0.5), radius);
    
    vec4 v4FromColor = borderColor; // Always the border color. If no border, this still should be set
    vec4 v4ToColor = vec4(0.0, 0.0, 1.0, 1.0); // Outside color
    
    if (borderThickness > 0.0)
    {
        if (fDist < 0.0)
        {
            v4ToColor = fillColor;   
        } 
        
        fDist = abs(fDist) - borderThickness;
    }
    
    float fBlendAmount = smoothstep(-0.01, 0.01, fDist);
    
    // final color
    gl_FragColor = mix(v4FromColor, v4ToColor, fBlendAmount);
}

以及两个输出的区别:

问题 1 在 Shadertoy 示例中,边框整齐且没有模糊,我的是模糊的。

问题 2 我使用 ndc 坐标来指定 borderThicknessradius,因此我没有得到一致的边框。如果您在图像中看到,水平边框比垂直边框稍宽。我更喜欢在像素大小中使用 borderThicknessradius。这个想法是在矩形周围获得一致的边框,而与屏幕尺寸无关。

问题 3 使外面的蓝色透明。

问题 4 正如我所提到的,我最近开始学习 GLSL,在某些地方我读到过多的“if”条件会极大地影响着色器的性能,并且您可能会不必要地使用它们。此代码中已经存在两个“if”条件,我不确定它们是否可以省略。

【问题讨论】:

    标签: python opengl glsl pyopengl


    【解决方案1】:

    使用Uniform (rectSize) 来指定矩形的像素大小。纹理坐标 (tex_coord) 需要在 [0.0, 1.0] 范围内。计算矩形中的像素位置 (rectSize * tex_coord)。现在您可以以像素为单位指定半径和边缘厚度:

    in vec2 tex_coord;
    uniform vec2 rectSize;
    
    const float borderThickness = 10.0;
    const float radius = 30.0;
    
    // [...]
    
    void main() 
    {
        vec2 pos = rectSize * tex_coord;
            
        float fDist = RectSDF(pos-rectSize/2.0, rectSize/2.0 - borderThickness/2.0-1.0, radius);
        float fBlendAmount = smoothstep(-1.0, 1.0, abs(fDist) - borderThickness / 2.0);
    
        vec4 v4FromColor = borderColor;
        vec4 v4ToColor = (fDist < 0.0) ? fillColor : vec4(0.0);
        gl_FragColor = mix(v4FromColor, v4ToColor, fBlendAmount);
    }
    
    rect_loc = glGetUniformLocation(program, "rectSize")
    glUniform2f(rect_loc, width, height)
    

    使用Blending 使外部透明。为此,外部颜色的 alpha 通道必须为 0。(例如 vec4(0.0)

    glEnable(GL_BLEND)
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
    

    小例子:

    from OpenGL.GLUT import *
    from OpenGL.GLU import *
    from OpenGL.GL import *
    import OpenGL.GL.shaders
    from ctypes import c_void_p
    import glm
    
    sh_vert = """
    #version 330 core
    // VERTEX SHADER
    layout (location = 0) in vec3 aPos;
    layout (location = 1) in vec2 aTexCoord;
    
    out vec2 tex_coord;
    uniform mat4 mvp;
    
    void main()
    {
        gl_Position = mvp * vec4(aPos, 1.0);
        tex_coord = aTexCoord;
    }
    """
    
    sh_frag = """
    #version 330 core
    // FRAGMENT SHADER 
    
    in vec2 tex_coord;
    uniform vec2 rectSize;
    
    const vec4 borderColor = vec4(1.0, 1.0, 0.0, 1.0);
    const vec4 fillColor = vec4(1.0, 0.0, 0.0, 1.0);
    const float borderThickness = 10.0;
    const float radius = 30.0;
    
    float RectSDF(vec2 p, vec2 b, float r)
    {
        vec2 d = abs(p) - b + vec2(r);
        return min(max(d.x, d.y), 0.0) + length(max(d, 0.0)) - r;   
    }
    
    void main() 
    {
        vec2 pos = rectSize * tex_coord;
            
        float fDist = RectSDF(pos-rectSize/2.0, rectSize/2.0 - borderThickness/2.0-1.0, radius);
        float fBlendAmount = smoothstep(-1.0, 1.0, abs(fDist) - borderThickness / 2.0);
    
        vec4 v4FromColor = borderColor;
        vec4 v4ToColor = (fDist < 0.0) ? fillColor : vec4(0.0);
        gl_FragColor = mix(v4FromColor, v4ToColor, fBlendAmount);
    }
    """
    
    def display():
        glClear(GL_COLOR_BUFFER_BIT)
        
        glEnable(GL_BLEND)
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)
        glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, None)
    
        glutSwapBuffers()
        glutPostRedisplay()
    
    def reshape(width, height):
        glViewport(0, 0, width, height)
        
    
    resolution = (640, 480)
    rect = (50, 50, 350, 250)
    attributes = (GLfloat * 20)(*[rect[0],rect[1],0,0,1, rect[2],rect[1],0,1,1, rect[2],rect[3],0,1,0, rect[0],rect[3],0,0,0])
    indices = (GLuint * 6)(*[0,1,2, 0,2,3])
    
    glutInit(sys.argv)
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB)
    glutInitWindowSize(*resolution)
    glutCreateWindow(b"OpenGL Window")
    glutDisplayFunc(display)
    glutReshapeFunc(reshape)
    
    vao = glGenVertexArrays(1)
    vbo = glGenBuffers(1)
    ebo = glGenBuffers(1)
    glBindVertexArray(vao)
    glBindBuffer(GL_ARRAY_BUFFER, vbo)
    glBufferData(GL_ARRAY_BUFFER, attributes, GL_STATIC_DRAW)
    glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, ebo)
    glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices, GL_STATIC_DRAW)
    glVertexAttribPointer(0, 3, GL_FLOAT, False, 5 * 4, None)
    glEnableVertexAttribArray(0)
    glVertexAttribPointer(1, 2, GL_FLOAT, False, 5 * 4, c_void_p(3 * 4))
    glEnableVertexAttribArray(1)
    
    program = OpenGL.GL.shaders.compileProgram(
        OpenGL.GL.shaders.compileShader(sh_vert, GL_VERTEX_SHADER),
        OpenGL.GL.shaders.compileShader(sh_frag, GL_FRAGMENT_SHADER)
    )
    glUseProgram(program)
    mvp_loc = glGetUniformLocation(program, "mvp")
    mvp = glm.ortho(0, *resolution, 0, -1, 1)
    glUniformMatrix4fv(mvp_loc, 1, GL_FALSE, glm.value_ptr(mvp))
    rect_loc = glGetUniformLocation(program, "rectSize")
    glUniform2f(rect_loc, rect[2]-rect[0], rect[3]-rect[1])
    
    glClearColor(0.5, 0.5, 0.5, 0.0)
    glutMainLoop()
    

    【讨论】:

    • Rabbid76 float CircleSDF(vec2 p, vec2 b, float r) { float d = step(length(p), r); return clamp(d, 0.0, 1.0); } 我正在使用此代码绘制一个与具有厚度和边框的矩形完全相同的圆,但没有正确获取它。你能帮忙吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多