1.背景绘制
这里的背景绘制比上一个要简单一些:
1 2 3 4 5 |
vec4 main(vec2 fragCoord) { vec2 p = (2.0 * fragCoord - iResolution.xy) / min(iResolution.y, iResolution.x); vec3 bcol = vec3(1.0, 0.8, 0.7 - 0.07 * p.y) * (1.0 - 0.25 * length(p)); return float4(bcol, 1); } |
首先计算了p,其中fragCoord本身就代表屏幕大小,以1920 * 1080举例,那么前半段(2.0 * fragCoord - iResolution.xy)
就是把原来的范围扩大为二倍并进行一次位移:
- x:[0, 1920] -> [-1920, 1920]
- y:[0, 1080] -> [-1080, 1080]
然后min(iResolution.y, iResolution.x)
对屏幕上距离比较小的那一侧进行归一化:
- x:[-1920, 1920] -> [-1.78, 1.78]
- y:[-1080, 1080] -> [-1, 1]
接下来计算颜色时使用了上面的结果p的Y值和距离值:
- 其中y值越大,颜色的Blue色域越小。
- P的距离越小,也就是P离原点越近,颜色就更亮(rgb都更高)。
- 0.25只是为了控制这个颜色的范围,是一个理论值,此外我们还可以通过调节p的坐标来移动原点
1 - length(p):
1 - 0.25 * length(p):
2.绘制心形
心形的绘制和我们之前绘制点和线的过程差不多:先确定心形的表达式,然后判断某个像素是否在心内,人如果在心内使用心的颜色,否则使用背景色。
这里作者提供了两种心形的绘制方法,分别为:
1 2 3 4 5 |
p.y -= 0.25; float a = atan2(p.x, p.y) / 3.141593; float r = length(p); float h = abs(a); float d = (13.0 * h - 22.0 * h * h + 10.0 * h * h * h) / (6.0 - 5.0 * h); |
和:
1 2 3 4 |
p *= 0.8; p.y = -0.1 - p.y * 1.2 + abs(p.x) * (1.0 - abs(p.x)); float r = length(p); float d = 0.5; |
说实话,两个都没看懂23333
3.填充心形颜色
经过上面的一顿心形计算,其实我已经猪脑过载了,但是人生还是要继续,代码还是要抄下去,接下来搬一下心形的颜色
1 2 3 4 5 |
float s = 0.75 + 0.75 * p.x; s *= 1.0 - 0.4 * r; s = 0.3 + 0.7 * s; s *= 0.5 + 0.5 * pow(1.0 - clamp(r / d, 0.0, 1.0), 0.1); vec3 hcol = vec3(1.0, 0.4 * r, 0.3) * s; |
上述两种计算方式最后都会声明一个r(p到中心点距离)和d(懵逼)。接下来的颜色计算可以看我从乐乐老师那偷过来的图:
从图片我们可以直观的看出,第一行根据p的x方向来得到一个在x方向上的渐变,第二行在此基础上添加了根据p的距离来产生的渐变,虽然不明显但可以看出新的渐变有了弧形,第三行则是使用类似半兰伯特的方法,增亮了左侧暗部区域,而最后一行则关键的分出了心形内外的区域颜色。
4.心的跳动
跳动相关函数也基本上直接拿过来就可以用了:
1 2 3 4 5 |
// animate float tt = mod(_Time.y, 1.5) / 1.5; float ss = pow(tt, .2) * 0.5 + 0.5; ss = 1.0 + ss * 0.5 * sin(tt * 6.2831 * 3.0 + p.y * 0.5) * exp(-tt * 4.0); p *= vec2(0.5, 1.5) + ss * vec2(0.5, -0.5); |
简简单单的四行代码,我只能看懂第一行是一个周期函数,而且周期是1.5s。(先对1.5求余,把时间限定到范围[0-1.5],然后再除1.5进行归一化,时间变成[0-1]范围)
然后接下来2、3行就是对这个周期函数一顿操作,很不幸我又猪脑过载了。但是乐乐老师总结了这是一段周期函数的动画效果,大概曲线为:
通过上面的曲线我们可以看出来,这个函数的y值范围基本都在1附近跳动。所以第4行其实就是将这个曲线和我们想要的心在y轴上拉伸,x轴上收缩进行一次加权平均。x没什么好说的,两个各取0.5,但y值因为在1附近跳动,当取值为0.5时值也在0.5左右跳动,这里取-0.5时,为了保证结果不会十分夸张,所以前面的值选择在1.5,所以最终结果会在1-5-0.5=1左右跳动。
5.结果
这一顿抄下来,猪脑疯狂过载,数学太差了啊。完整代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
Shader "Shadertoy/Heart" { Properties{ } CGINCLUDE #include "UnityCG.cginc" #pragma target 3.0 #define vec2 float2 #define vec3 float3 #define vec4 float4 #define mat2 float2x2 #define mat3 float3x3 #define mat4 float4x4 #define iGlobalTime _Time.y #define mod fmod #define mix lerp #define fract frac #define texture2D tex2D #define iResolution _ScreenParams #define gl_FragCoord ((_iParam.scrPos.xy/_iParam.scrPos.w) * _ScreenParams.xy) #define PI2 6.28318530718 #define pi 3.14159265358979 #define halfpi (pi * 0.5) #define oneoverpi (1.0 / pi) struct v2f { float4 pos : SV_POSITION; float4 scrPos : TEXCOORD0; }; v2f vert(appdata_base v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.scrPos = ComputeScreenPos(o.pos); return o; } vec4 main(vec2 fragCoord); fixed4 frag(v2f _iParam) : COLOR0{ vec2 fragCoord = gl_FragCoord; return main(gl_FragCoord); } vec4 main(vec2 fragCoord) { // background color vec2 p = (2.0 * fragCoord - iResolution.xy) / min(iResolution.y, iResolution.x); vec3 bcol = vec3(1.0, 0.8, 0.7 - 0.07 * p.y) * (1.0 - 0.25 * length(p)); // animate float tt = mod(_Time.y, 1.5) / 1.5; float ss = pow(tt, .2) * 0.5 + 0.5; ss = 1.0 + ss * 0.5 * sin(tt * 6.2831 * 3.0 + p.y * 0.5) * exp(-tt * 4.0); p *= vec2(0.5, 1.5) + ss * vec2(0.5, -0.5); // shape // p *= 0.8; // p.y = -0.1 - p.y * 1.2 + abs(p.x) * (1.0 - abs(p.x)); // float r = length(p); // float d = 0.5; // shape p.y -= 0.25; float a = atan2(p.x, p.y) / 3.141593; float r = length(p); float h = abs(a); float d = (13.0 * h - 22.0 * h * h + 10.0 * h * h * h) / (6.0 - 5.0 * h); // color float s = 0.75 + 0.75 * p.x; s *= 1.0 - 0.4 * r; s = 0.3 + 0.7 * s; s *= 0.5 + 0.5 * pow(1.0 - clamp(r / d, 0.0, 1.0), 0.1); vec3 hcol = vec3(1.0, 0.4 * r, 0.3) * s; vec3 col = mix(bcol, hcol, smoothstep(-0.01, 0.01, d - r)); return float4(col, 1); } ENDCG SubShader{ Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #pragma fragmentoption ARB_precision_hint_fastest ENDCG } } FallBack Off } |