OpenGL ES Shader 怎样绘制一颗“心”
今天讲一下绘制心形的两种方式,主要是为了扩展一下绘制复杂形状的思路,为后面讲特效做一些简单的铺垫。
心形绘制可以参考 ShaderToy 上的代码:
https://www.shadertoy.com/view/XsfGRn
上述代码绘制心形,首先将原点从左下角移至坐标系中央,这样所有片元的向量均以屏幕中心为起点,则向量 uv 就是画布中心与像素点坐标之间的方向向量。
vec2 uv = (2.0 * fragCoord - iResolution.xy) / min(iResolution.y,iResolution.x); //坐标原点设置到中心位置
然后利用反正切函数值和当前片元(像素)与屏幕中心点的距离相比较,来确定心形状的边界。
GL ES 中的反正切函数 atan(p.x,p.y) 取值范围是[-π, π],然后除以 PI 后,取值范围变成了 [-1, 1] 。
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = (2.0 * fragCoord - iResolution.xy) / min(iResolution.y,iResolution.x); //坐标原点设置到中心位置
float r = length(uv);
float d = atan(uv.x,uv.y)/PI; //[-1.0,1.0]
float s = abs(d);
vec3 color = vec3(s) * vec3(1.0, 0.0, 0.0);
fragColor = vec4(color,1.0);
}
我们把反正切的结果取绝对值,渲染出来:
可以看到,从中心向外辐射,每条直线上的值都是一样的,即每条直线上像素点得到的 s 值都是相同的。
我们用黄点表示距离屏幕中心的远近 length(uv),然后通过 s-r 的值来确定心形的边界,得到一个最简单的心形。
再贴一下代码:
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = (2.0 * fragCoord - iResolution.xy) / min(iResolution.y,iResolution.x);
uv.y -= 0.3;// 向上移动一些
float r = length(uv);
float d = atan(uv.x,uv.y)/PI; //[-1.0,1.0]
float s = abs(d);
float col = smoothstep(-0.01, 0.01, s-r);//边缘过渡
vec3 color = vec3(col) * vec3(1.0, 0.0, 0.0);
fragColor = vec4(color,1.0);
}
增加一个函数使心的形状更加扁平化 float e = (13.0*s - 22.0*s*s + 10.0*s*s*s)/(6.0-5.0*s);
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = (2.0 * fragCoord - iResolution.xy) / min(iResolution.y,iResolution.x);
uv.y -= 0.3;
float r = length(uv);
float d = atan(uv.x,uv.y)/PI; //[-1.0,1.0]
float s = abs(d);
float e = (13.0*s - 22.0*s*s + 10.0*s*s*s)/(6.0-5.0*s);
float col = smoothstep(-0.01, 0.01, e-r);
vec3 color = vec3(col) * vec3(1.0, 0.0, 0.0);
fragColor = vec4(color,1.0);
}
增加扁平化函数之后的心形:
上述反正切和距离判断绘制心形,实际上是有向距离场(SDF)算法思路,除此之外还有一种直接通过函数曲线绘制的思路。
float heart(vec2 p) {
p.y += 0.3;
float offset = 0.3;
// (x^2+(1.2*y-sqrt(abs(x)))^2−1)
float k = 1.2 * p.y - sqrt(abs(p.x)*1.2 + offset);
return 1.0 - p.x * p.x - k * k;
}
void mainImage(out vec4 fragColor, in vec2 fragCoord)
{
vec2 uv = (2.0 * fragCoord - iResolution.xy) / min(iResolution.y,iResolution.x);
uv.y += 0.3;
float d = heart(uv);
float col = smoothstep(-0.01, 0.01, d);
vec3 color = vec3(col) * vec3(1.0, 0.0, 0.0);
fragColor = vec4(color,1.0);
}
函数曲线实际上是把整个画布当做一个原点位于中心坐标系,利用函数特效构建一个闭合的图形。你要问这个函数怎么得出来的,这个当然是图形大佬调出来的。
效果如下:
-- END --
AI 问答助手
进技术交流群,扫码添加我的微信:Byte-Flow
获取相关资料和源码
推荐:
全网最全的 Android 音视频和 OpenGL ES 干货,都在这了
面试官:如何利用 Shader 实现 RGBA 到 NV21 图像格式转换?