找回密码
 会员注册
查看: 28|回复: 0

WebGL实战之绘制圆角矩形

[复制链接]

2万

主题

0

回帖

6万

积分

超级版主

积分
64449
发表于 2024-9-20 18:48:29 | 显示全部楼层 |阅读模式
随着业务发展需要,订阅号消息流中的部分卡片现已完成动态化改造,卡片UI渲染的工作由基于WebGL实现的渲染器完成。本篇文章将介绍底层的实现原理,核心就是用WebGL实现圆角矩形的绘制,基于圆角矩形我们就可以像拼图一般完成整个卡片的绘制。WebGL绘制矩形由于WebGL上手难度较高,阅读本文前假定你已了解WebGL的基础用法(注1-3)。我们知道使用WebGL绘制图形主要依赖WebGL程序(WebGLProgram)来实现,核心是着色器(Shader)的编写。创建完WebGL程序后就可以调用浏览器提供的WebGLAPI来完成WebGL上下文的创建,绘制前向WebGL程序传递好数据,最后调用绘制指令输出结果。WebGL是以顶点和图元来描述图形几何信息的。顶点就是几何图形的顶点,比如,三角形有三个顶点,四边形有四个顶点。图元是WebGL可直接处理的图形单元,由WebGL的绘图模式决定,有点、线、三角形等等。WebGL绘制一个图形的过程,一般需要用到两段着色器,一段叫顶点着色器(VertexShader)负责处理图形的顶点信息,另一段叫片元着色器(FragmentShader)负责处理图形的像素信息。一段绘制矩形的Shader代码示例://顶点着色器attributevec4a_Position;voidmain(){gl_Position=a_Position;}//片元着色器precisionmediumpfloat;uniformvec4u_FragColor;voidmain(){gl_FragColor=u_FragColor;}创建完WebGL程序后接着调用WebGLAPI传入顶点数据和颜色色值(红色为例)就可以在屏幕上输出红色矩形了:// 传入顶点坐标var vertexBuffer = gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER, vertexBuffer);gl.bufferData(gl.ARRAY_BUFFER, new Float32Array([  -0.5, 0.5, 0.5, 0.5, 0.5, -0.5, // Triangle 1  -0.5, 0.5, 0.5, -0.5, -0.5, -0.5 // Triangle 2]), gl.STATIC_DRAW);// 传入颜色值var u_FragColor = gl.getUniformLocation(gl.program, 'u_FragColor');gl.uniform4fv(u_FragColor, [1.0, 0.0, 0.0, 1.0]); // rgba// 清空屏幕gl.clearColor(0.0, 0.0, 0.0, 1.0);gl.clear(gl.COLOR_BUFFER_BIT);// 输出结果gl.drawArrays(gl.TRIANGLES, 0, 6);WebGL绘制矩形利用SDF实现圆角矩形下面进入正题,介绍如何在实现矩形的基础上绘制圆角矩形。一个思路是只保留图形边缘轮廓内的像素,轮廓外则用透明度为0的像素填充,如下图所示:绿色圆点代表画布上的像素点可以看到这个思路的核心就是判断当前绘制的像素点是否位于图形轮廓的内部,如果有一个函数能算出当前点到形状边缘的距离且距离带有符号(正或负),那么我们就能知道点是在轮廓内还是轮廓外了。而这个函数就是:符号距离函数(SDF)。符号距离函数(signeddistancefunction),简称SDF,又可以称为定向距离函数(orienteddistancefunction),用来在空间中的一个有限区域上确定一个点到区域边界的最短距离并同时对距离的符号进行定义:点在区域边界内部为负,外部为正(内部为正,外部为负亦可),位于边界上时为0。下面贴出一些基础2D图形的SDF代码:圆形/***圆形:1.p表示画布上任意一点*2.r为圆的半径*/floatsdfCircle(vec2p,floatr){//与圆心距离为r的点,在该圆上,SDF取值0returnlength(p)-r;}圆形的SDF代码比较好理解,用当前像素点距离圆心的长度减去圆的半径即可。矩形/***矩形:1.p表示画布上任意一点*2.b为圆角矩形右上角顶点*/floatsdfBox(invec2p,invec2b){//abs(p)是常用技巧,由于原点位于中心,因此四个象限都是相同的,都映射到第一象限处理//d表示长方体右上角顶点b到当前像素点p的向量vec2d=abs(p)-b;//p点在外部:length(max(d,0.0)),在内部则是min(max(d.x,d.y),0.0),这两项总至少有一项为0returnlength(max(d,0.0))+min(max(d.x,d.y),0.0);}代码详解,4种情况分别分析p表示画布上任意一点,b为圆角矩形右上角顶点,代码第一行首先通过abs(p)操作将p点映射到坐标系的第一象限(x,y分量为正),因为其他几个象限的计算方式是一样的。接着减去b得到b点到p点的向量d,也就是vec2(Px-Bx,Py-By)。接着就是这个公式:length(max(d,0.0))+min(max(d.x,d.y),0.0),看起来不好理解,为了简化分析,如上图所示我们可以将p点分四种情况代入公式计算。p点落在区域①:max(vec2(Px-Bx,Py-By),0.0) =vec2(Px-Bx,0.0);// 因为Px-Bx> 0 &y-By 0length(vec2(0.0,Py-By)) =Py-By;min(max(Px-Bx,Py-By),0.0) = 0;故最终结果就是Py-By,这也是区域②内的一点p到矩形边界的最短距离。p点落在区域③:max(vec2(Px-Bx,Py-By),0.0) =vec2(Px-Bx,Py-By);// 因为Px-Bx> 0 &y-By> 0length(vec2(Px-Bx,Py-By)) = √((𝑃𝑥−𝐵𝑥)^2+(𝑃𝑦−𝐵𝑦)^2);min(max(Px-Bx,Py-By),0.0) = 0;故最终结果就是√((𝑃𝑥−𝐵𝑥)^2+(𝑃𝑦−𝐵𝑦)^2),这也是区域③内的一点p到矩形边界的最短距离。p点落在区域④:max(vec2(Px-Bx,Py-By),0.0) =vec2(0.0,0.0);// 因为Px-Bx0.0)?r.xy:r.zw;//求得圆角矩形在当前像素点所在象限的圆角半径r.x=(p.y>0.0)?r.x:r.y;vec2d=abs(p)-b+r.x;returnlength(max(d,0.0))+min(max(d.x,d.y),0.0)-r.x;}可以看到圆角矩形的SDF代码和矩形的很像,其实就是将“内接”矩形(圆角圆心作为顶点的内部矩形)的SDF结果减去一个常数r(圆角的半径)即可,看完下图就懂了:“内接”矩形SDF减去圆角半径算出SDF距离后就能对形状内外的像素透明度做相应处理来初步实现圆角矩形的绘制:pixel_opacity=distance_to_edge
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 会员注册

本版积分规则

QQ|手机版|心飞设计-版权所有:微度网络信息技术服务中心 ( 鲁ICP备17032091号-12 )|网站地图

GMT+8, 2024-12-26 23:57 , Processed in 0.381613 second(s), 26 queries .

Powered by Discuz! X3.5

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表