OpenGL Shader-圆形绘制

2,749 阅读1分钟

「这是我参与2022首次更文挑战的第3天,活动详情查看:2022首次更文挑战

前期练习

在开始绘制2D形状前,先绘制简单内容。如下shader展示绘制一半黑色一半白色,默认col赋值为0.0,通过if语句对x轴做判断,当x>0.5时col向量赋值为1.0。

void main() {
    vec2 uv = gl_FragCoord.xy / iResolution.xy;
    vec3 col = vec3(0.0);
    if(uv.x > 0.5) col = vec3(1.0);
    gl_FragColor = vec4(col,1.0);
}

image.png 但对于glsl来说if-else条件判断并不友好,尽量减少对if-else语句的使用。推荐使用glsl内置函数可能高效一些。类似如上代码可以采用内置函数step替代。改造后的代码如下:

void main() {
    vec2 uv = gl_FragCoord.xy / iResolution.xy;
    vec3 col = vec3(step(0.5,uv.x));
    gl_FragColor = vec4(col,1.0);
}

step函数等价于uv.x > 0.5 ? 1.0 : 0.0;

方法函数step(a, x) :如果 x<a,返回 0;否则,返回 1

绘制圆

圆的计算公式:{x}^2+{y}^2={r}^2x是X坐标值,y是Y坐标值,r是圆半径值。 image.pngOpenGL脚本中有length(v)内置函数可以实现圆的计算公式,对应的数学公式是sqrt(dot(v,v)),也叫取向量的模。

value = x[0]2+x[1]2+\sqrt { { x[0] }^2 + { x[1] }^2 + \dots }.

如下代码展示效果并不能达到预期效果,圆会出现在画布左下角的位置。需要知道在glsl中x,y坐标的(0,0)在画布的左下角位置,也正式圆绘制在画布位置。如果需要让圆能够绘制在画布的中心位置上,则需要调整绘制的整体坐标位置。

vec3 sdfCircle(vec2 uv,float r){
    float d = length(uv) - r;
    return d > 0. ? vec3(1.) : vec3(0., 0., 1.); // 大于0超出画圆范围,小于0在画圆范围内
}
void main() {
    vec2 uv = gl_FragCoord.xy / iResolution.xy;
    gl_FragColor = vec4(sdfCircle(uv,0.4),1.0);
}

因为glsl中顶点范围是在[0,1]区间上的,所以画布原点位置坐标是(0,0)。在绘制圆之前先对uv减0.5操作,那么原点位置坐标就变为(0.5,0.5)。绘制圆的位置坐标就偏移到画布中心点的位置,然后iResolutionx坐标和y坐标相除获取到宽高比比例。最后uv.x和宽高比相乘后赋值解决了圆被拉伸的问题。

vec3 sdfCircle(vec2 uv,float r){
    float d = length(uv) - r;
    return d > 0. ? vec3(1.) : vec3(0., 0., 1.); // 大于0超出画圆范围,小于0在画圆范围内
}
void main() {
    vec2 uv = gl_FragCoord.xy / iResolution.xy;
    uv -= 0.5; // x: <-0.5, 0.5>, y: <-0.5, 0.5>
    uv.x *= iResolution.x/iResolution.y; // x: <-0.5, 0.5> * aspect ratio, y: <-0.5, 0.5>
    gl_FragColor = vec4(sdfCircle(uv,0.4),1.0);
}
默认uv调整后的uv
image.pngimage.png