返回

梦回雷达图,步履从容蹈雷阵

前端

刚开始学shader的时候,每个优秀的shader例子都是值得分析的对象。毕竟这种东西光是看是没用的,就像学开车,光看教程肯定不行,必须实际去开几圈才能掌握车感。

我是一个自学写shader的,ShaderToy是我经常去看的地方,上面有非常多优秀的shader,我们还可以通过修改别人的shader来快速学习一些新技巧。

写shader有一个好处就是,你可以非常快速地看到你做的改动所带来的效果,所以,上手起来非常快。

如果你在shadertoy上见过这个作品,那么你应该知道这个图叫雷达图(Radar Chart),它是用来多维数据的,每个维度的值用一个点表示,然后连接这些点形成一个封闭的图形。

雷达图的绘制原理很简单,就是先计算出每个维度的值在雷达图上的位置,然后连成线。

shader实现原理也很好理解,它和我们熟悉的极坐标系有很大关系,因为雷达图本身就是用极坐标系来表示的。极坐标系不同于直角坐标系,它使用极角和极径两个参数来确定一个点的坐标,其中极角表示该点与极轴的夹角,极径表示该点到极点的距离。

好了,我们来看看shader代码,我把每个维度的值都定义成了变量,这些变量可以接受外部传入的值。

uniform vec3 color;
uniform float width;
uniform float maxValue;
uniform float startAngle;
uniform float data[];

首先是color,它是一个三维向量,用于控制雷达图的颜色;width是雷达图的线宽;maxValue是雷达图的最大值,用于控制雷达图的尺寸;startAngle是雷达图的起始角度;data是一个数组,用于存储每个维度的值。

接下来是vertex shader,这个shader的作用是把每个维度的值转换成对应的极坐标系坐标。

varying vec3 vPosition;
varying vec4 vColor;
void main() {
    float angle = startAngle + data[gl_VertexID] / maxValue * 2.0 * 3.1415926;
    vec2 position = vec2(cos(angle), sin(angle)) * width;
    vPosition = vec3(position, 0.0);
    vColor = vec4(color, 1.0);
    gl_Position = projectionMatrix * modelViewMatrix * vec4(vPosition, 1.0);
}

vertex shader的输入是每个维度的值,输出是对应的极坐标系坐标。

首先,我们计算每个维度的值对应的角度,角度的范围是0到2π,然后,我们根据角度计算对应的极坐标系坐标。

接下来是fragment shader,这个shader的作用是把极坐标系坐标转换成屏幕坐标,并把颜色输出到屏幕上。

varying vec3 vPosition;
varying vec4 vColor;
void main() {
    vec2 position = vec2(vPosition.x, -vPosition.y);
    gl_FragColor = vColor;
}

fragment shader的输入是极坐标系坐标,输出是屏幕坐标和颜色。

我们首先把极坐标系坐标转换成屏幕坐标,然后把颜色输出到屏幕上。

好了,这就是雷达图shader的全部代码了。

我们可以通过修改color、width、maxValue、startAngle和data这几个变量来控制雷达图的外观。

例如,我们可以修改color来改变雷达图的颜色,我们可以修改width来改变雷达图的线宽,我们可以修改maxValue来改变雷达图的尺寸,我们可以修改startAngle来改变雷达图的起始角度,我们可以修改data来改变每个维度的值。

我们可以通过修改这些变量来创建各种各样的雷达图。