OpenGL ES Shader 怎样绘制一颗“心”

共 4597字,需浏览 10分钟

 ·

2023-08-28 09:49

今天讲一下绘制心形的两种方式,主要是为了扩展一下绘制复杂形状的思路,为后面讲特效做一些简单的铺垫。

心形绘制可以参考 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.00.00.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.010.01, s-r);//边缘过渡
  vec3 color = vec3(col) * vec3(1.00.00.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.010.01, e-r);
  vec3 color = vec3(col) * vec3(1.00.00.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.010.01, d);
  vec3 color = vec3(col) * vec3(1.00.00.0);

  fragColor = vec4(color,1.0);
}

函数曲线实际上是把整个画布当做一个原点位于中心坐标系,利用函数特效构建一个闭合的图形。你要问这个函数怎么得出来的,这个当然是图形大佬调出来的。

效果如下:



-- END --

AI 问答助手




进技术交流群,扫码添加我的微信:Byte-Flow



获取相关资料和源码



推荐:

Android FFmpeg 实现带滤镜的微信小视频录制功能

全网最全的 Android 音视频和 OpenGL ES 干货,都在这了

一文掌握 YUV 图像的基本处理

抖音传送带特效是怎么实现的?

所有你想要的图片转场效果,都在这了

面试官:如何利用 Shader 实现 RGBA 到 NV21 图像格式转换?

我用 OpenGL ES 给小姐姐做了几个抖音滤镜

Shader 编程:只用一个函数就能生成三角形、矩形等所有的正多边形

别人用 Shader 画了个圆,你却只能画椭圆?

浏览 637
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

分享
举报