glsl - 避免在圆角几何着色器中分支
问题描述
我制作了一个 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();
}
解决方案
推荐阅读
- html - 使用网格或弹性框对齐标题、按钮和图像
- excel - 为什么我无法正确读取 .csv 数据,我如何编写代码只是为了选择特定的列?
- laravel - 是否有必要保护 Laravel 中的 destroy 方法免受未创建帖子的经过身份验证的用户的影响
- bash - 用 sed 替换 yaml 配置
- python - 复制没有缓存的 Keras 教程
- numpy - 将科学计数法日志文件输入到 python numpy
- android - Android Firebase:比较设备上的值还是使用`equalTo`方法?
- mysql - Zabbix:无法打开 PID 文件
- node.js - 节点 mssql 在一个 BEGIN TRANSACTION 中进行多个查询
- mysql - RDS mysql 数据库可用存储空间小于实例分配