YouTube 又可以用了,我就捡起了好久没搞的shader(数学)绘图 。
看视频还是有不便之处的(代码不太容易看清),幸好站上也有代码。
本文,跟着视频,看着代码,一起来绘制一棵椰子树。
准备好shader编程环境。
一个渐变色的背景没啥好说的。
vec2 st = v_Uv ;
vec3 color = mix(vec3(1.,.3,.3), vec3(1.,.8,.3) ,st.y) ;
树叶
绘制之前,再啰嗦一下,源代码是没有处理xy分量的差异的,它的绘图尺寸是个矩形,所以绘制出来是这个效果。
这个树叶是由椭圆变化而来的,先画一个(椭)圆。 简单的smoothstep的应用, 圆心在(.33, .7) ,半径为r, 因为后面还会用到以圆心为原点的坐标进行计算, 所以这里声明了 c1
vec2 c1 = st - vec2 (.33, .7) ;//
float c1s = dot(c1,c1);
float len =sqrt(c1s);
float r = .1 ;
color *= smoothstep(r, r+ .01, len) ;
fragColor = vec4(color, 1.);
叶子
让这个圆长出叶子, 其实就是让这个圆的半径不再是恒定的,而是周期变化的。
r+= .04 * cos(atan(c1.y,c1.x) * 10.) ;
这里在原本r的基础上又加了一个余弦值, 这个值得范围是【-0.04,0.04】, 结果就是r的范围成了【0.06,0.14】, 周期性变化。 从距离场的角度来理解可能更好一些。 这个图形就是到圆心距离小于r的集合, 而r的值和其以圆心为极点的 极坐标的角度有关。
乘以10是为了增加叶子的数目, 一个周期一个叶子,现在角度的值域范围扩大至原本的10倍,可以表现出10个周期了。 你可以自行调整。
再次强调, 这里之所以是椭圆,是因为没处理。 这里把画布尺寸调整为正方形给大家看看。
强调这个,是因为要说明,这里的角度本来是(一圈)均匀分布的。 现在要加一点东西,让它不再均匀分布,因为椰子树叶显然不可能这么匀称。
r+= .04 * cos(atan(c1.y,c1.x) * 10. + 20.*st.x ) ;
效果就是这样了, 现在是关于y轴对称, 如果你不希望这样,可以加点别的干扰,或者改变这个三角函数的初相。 下面会修改初相,目的是为了和树干配合。
树干
先来个简单的矩形,然后这个矩形超出上面圆心的部分要被砍掉。可以看到,一个电线杆似的树干,太直了。 d是树干的宽度 ,后面还要继续处理。
float d = .02 ;
float trunck = smoothstep( d,d -0.01 ,abs(c1.x ))* step(c1.y,0.) ;
color = mix( color, vec3(0), trunck);
这个树干太直了,太假了,搞弯一点。 如果是我来写,我肯定就来一个曲线函数,替换d,放到smoothstep里, 这是典型的造型函数绘制曲线的方法。
不过他这里,要巧妙一些。用sin(y)影响了其正直。虽然很巧妙,但是总觉得这东西不能复用。
float trunck = smoothstep( d,d -0.01 ,abs(c1.x -.2* sin(c1.y*2.)))* step(c1.y,0.) ;
还有,这个树干表面太光滑了,我想来点凹凸不平。 这里就用上造型函数了。为了让宽度不至于为零这里系数比较小,诸君可以自行调整 , 还有波纹的数量。
d += .005*sin(c1.y *100.) ;
地形
虽然,这里还有一个地形,但是在代码上这个地形是树干的一部分。 这个地形,就是一个土包,希望就是树底下是这个土包的最高点。
这里使用指数函数, 指数爆炸, 当y = 0的时候,结果是1 ,1就是铺满整个横向了。 当y=某个值,比如0.1的时候,我们希望它趋于0 , 越往上越接近0 ,无穷小, 很明显,只有负指数能做的。 所以函数如下。 后面加了一个常数,可以微调其最高点的位置。
d+= exp(-40.* (st.y -.01) );
结束
本文搬运了shaderToy站长的教程 The basics of Painting with Maths,源代码。
使用了一些内置函数和图形的加减乘运算,最后绘制出一棵椰树的投影。
前面一直在说这里没处理xy的差异,会导致图形随画布而变形。如果想固定形状的,可以先在一定的尺寸下绘制好,记录这个尺寸, 然后在代码里校正xy的差异, 最后再缩放回那个尺寸。 大致思路就是这样了。
至于为什么不直接绘制一个椭圆,然后修改,当然是因为麻烦, 如果用椭圆方程的话,这里就不能简单的使用半径了。
void main (){
t = u_Time /1000. ;
float ratio = u_CanvasSize.x/u_CanvasSize.y;
vec2 st = v_Uv ;
st-= .5 ;
st.x*= ratio ;
st +=.5;
st*= vec2(1.,2.);
vec2 c1 = st - vec2 (.33, .7) ;//
float c1s = dot(c1,c1);
float len =sqrt(c1s);
vec3 color = mix(vec3(1.,.3,.3), vec3(1.,.8,.3) ,st.y) ;
float r = .1 ;
r+= .04 * cos(atan(c1.y,c1.x) * 10. + 20.*st.x -2.2) ;
color *= smoothstep(r, r+ .01, len) ;
float d = .02 ;
d += .005*sin(c1.y *100.) ;
d+= exp(-40.* (st.y -.01) );
float trunck = smoothstep( d,d -0.01 ,abs(c1.x -.2* sin(c1.y*2.)))* step(c1.y,0.) ;
color = mix( color, vec3(0), trunck);
fragColor = vec4(color, 1.);
}
本文的前置知识,在基础专栏里。