首页 > 解决方案 > 避免在圆角几何着色器中分支

问题描述

我制作了一个 glsl 几何着色器,它可以使 2D 线带的角变圆。着色器在每个角处插入圆形圆角。

当两个相邻线段共线且无法创建圆角时,存在一种特殊情况。在这种情况下,只有一个原始顶点通过。

这需要为每个输入顶点执行一个 if 语句。在这种特殊情况下,有没有一种巧妙的方法可以避免分支?如果每帧处理的顶点总数通常在几百个左右,这甚至是一个问题吗?

这是完整的着色器代码:

#version 400

layout(lines_adjacency) in;
layout(line_strip, max_vertices=25) out;

uniform mat4 modelMatrix;
uniform mat4 viewMatrix;
uniform mat4 projectionMatrix;
uniform float radius = 160.0;
uniform int steps = 10;

in VS_OUT {
    vec2 position;
} gsIn[];


bool arc(vec2 p0, vec2 p1, vec2 p2, out vec2 arcCenter, out vec2 arcStartPoint, out vec2 arcMidPoint, out vec2 arcEndPoint){
    vec2 t0 = normalize(p0 - p1);
    vec2 t1 = normalize(p2 - p1);   
    
    // segments are colinear, exit
    if(abs(dot(t0, t1)) > .9999f){
        return false;
    }

    vec2 h = normalize((t0 + t1) /2.0);
    float cosa = abs(dot(h, vec2(-t0.y, t0.x)));
    float hlen = radius/cosa;

    arcCenter = p1 + h*hlen;     
    
    float d = sqrt(hlen*hlen - radius*radius);  

    arcStartPoint  = p1 + t0 * d;
    arcEndPoint = p1 + t1 *d;
    arcMidPoint = arcCenter - h * radius;
    return true;
} 

float stepangle(vec2 center, vec2 s, vec2 e){
    vec2 rv1 = s-center;
    vec2 rv2 = e-center;
    float angle = acos( dot(normalize(rv1), normalize(rv2)) ) / steps;
    if( dot(rv1, vec2(-rv2.y, rv2.x))<0 ){
        angle = -angle;
    }
    return angle;
}

mat2 rotationMatrix(float angle){
    float cosa = cos(angle);
    float sina = sin(angle);
    return mat2(cosa, -sina, sina, cosa);
}

void emitFillet(vec2 center, vec2 arcStartPoint, vec2 arcEndPoint, mat4 mvpMatrix){
    float a = stepangle(center, arcStartPoint, arcEndPoint);
    mat2 rotMat = rotationMatrix(a);
    vec2 radVec = arcStartPoint-center;

    for(int i=0; i <=steps ; ++i){
        gl_Position = mvpMatrix * vec4(center + radVec, 0.0, 1.0);
        EmitVertex();
        radVec = rotMat * radVec;
    }
}

void emitSingleVertex(vec2 vert, mat4 mvpMatrix){
    gl_Position = mvpMatrix * vec4(vert, 0.0, 1.0);
    EmitVertex();
}

void main(){    

    mat4 mvMatrix = viewMatrix * modelMatrix;
    mat4 mvpMatrix = projectionMatrix * mvMatrix;

    vec2 p0 = gsIn[0].position;
    vec2 p1 = gsIn[1].position;
    vec2 p2 = gsIn[2].position;
    vec2 p3 = gsIn[3].position;

    vec2 center, s, m, e;
    mat2 rotMat;
    vec2 radVec;
    float a;
    bool canMakeFillet;

    // first corner half-fillet
    canMakeFillet = arc(p0, p1, p2, center, s, m, e);
    if(canMakeFillet){
        emitFillet(center, m, e, mvpMatrix);
    }
    else{
        emitSingleVertex(p1, mvpMatrix);
    }
    
    // scond corner half-fillet
    canMakeFillet = arc(p1, p2, p3, center, s, m, e);
    if(canMakeFillet){
        emitFillet(center, s, m, mvpMatrix);
    }
    else{
        emitSingleVertex(p2, mvpMatrix);
    }
    
    EndPrimitive();
}

标签: glslgeometry-shader

解决方案


推荐阅读