代码Shader画一个梵高的星星

1,404 阅读4分钟

2406230337

上面图片为梵高星空星星的局部,颜色非常奇妙 黄色,白色,深蓝。 表示星星的光,光晕,夜空。 同时笔触都是短的线段, 另外线段呈现出类似于天文中星旋的结构。 旋转真是极坐标下容易得到的结构,下图是Shader的实现,可以点击链接查看运动图。 另外抱歉作者是色弱.... 涉及到颜色说的不准确,大家看的时候如果发现文图颜色不对,那一定是作者错了...

2406233226

www.shadertoy.com/view/XX3XDn

基本结构

在上文极坐标介绍中,对极角进行 fract之后,图像呈现了了一朵画。 如果对极距也fract, 会得到一个极坐标axis图。 这里用掉了


float numberOfRings = 10.; 
float smoothFactor = 5. * numberOfRings  / iResolution.y; 
vec2 uv = (2.*fragCoord.xy - iResolution.xy) / iResolution.y;
vec2 st = vec2(atan(uv.x, uv.y), length(uv));
st.x = st.x / 2.0 / PI + 0.5;

// segment index
vec2 id = floor(st*numberOfRings);
// polar corrdinate
st = fract(st*numberOfRings);

// ray in polarCoord
float m1 = ssmoothstep(
    abs(st.x - .5), .5, smoothFactor / PI 
);
// circle in polarCoord
float m2 = ssmoothstep(
    abs(st.y - .5), .5, smoothFactor
);

vec3 col = m1 * m2 * vec3(1.0);
fragColor = vec4(col, 1.0);

上面用到了

  1. 将极坐标和笛卡尔坐标都放到[0, 1]空间
  2. 通过floor 获取 对应segment的坐标
  3. 通过frac获取 每个小segment内坐标
  4. 通过smoothstep抗锯齿 所有技巧在之前文章都提到过,最后得到图像 2406235012

在图像中所有圆弧段都可以通过 id 这个二位向量确定唯一。 这个时候如果我们将梵高星星的颜色放进去,并且增加线段数量。看看会有什么效果。颜色使用 2406235609 这个色盘是在 iquilezles.org/articles/pa… 介绍的通过三角函数产生美妙的色盘

vec3(.26,.76,.77),vec3(1,.3,1),vec3(.8,.4,.7),vec3(0,.12,.54)

按照距离去使用色盘中的颜色,

vec3 col = m1 * m2 * sp(length(uv) * .4);

最后会有 2406235958 是不是有点感觉了呢。

接近

上面的图形太规律,如果后续尝试做蒙格里安的图像,也许不需要引入随机,但是在风格派之前,绘画从写实到抽象的过程,也许在Shader中是一个随机到理性的过程。在上一节中我们已经拥有了一个极坐标的基本结构,同时知道每一段的圆弧的坐标。 接下去就想方法逼近梵高的星星

Segement

在我们上面的图形中,将图像分割为等角度不同圆弧,外侧圆弧比内侧圆弧要长,在实际会绘画中同样的线条长度差不多, 也就是想要将外侧圆多分割一些圆弧。 这里只要将 角度的分割按照距离来增加即可。 距离圆心越远,分割次数越多


    st.y = st.y * numberOfRings;
    float ringIndex = floor(st.y);
    float numberOfRingSegment = 0.6 * ringIndex + 2.0;
    st.x = st.x * numberOfRingSegment;

2406231534

看起来线段长度差不多啊, 当然也可以不断的去调节 numberOfRingSegment 参数。

Shift

虽然线段变得差不多长,但是看起来有一种规律,好像所有线都是从一个地方开始画的。实际上就是从-y轴方向开始画的... 不过我们可以给每一圈加一个旋转的随机份量

    st.x += 0.3 * (hash12(vec2(ringIndex))*.5+.25);

2406231855 这样看起来好多了

Random

线段都是固定的圆弧,看起来太假了,当然没办法通过简单的代码模仿出梵高的笔触。但是在圆与圆之间,弧与弧之间来点随机距离,看起来更加真实些. 这里可以找到一些更好的random用法,而不是直接在极坐标上random, 也许会有不错的效果。。。 需要不断试验了

    st += (noise(st*vec2(1,4)+id))*vec2(.7,.6);

2406232433

Mix

上面的黑色太突兀了, 这个时候只需要加一个底色,然后将线圈的数量增加,就可以得到不错的效果

    vec3 col = sp(sin( (length(uv)-.2) ) + 0.02);
    col = mix(
        col*.35, 
        col + vec3(vec2((hash12(id))*0.3), 0.0), 
        m1 *m2
    );  

2406232601

运动

在蒙克名作《呐喊》天空,夕阳,河流扭曲在一起,整个画面的形体,结构,边界都被打破,从而传达了强烈的情绪,也是人类绘画历史上第一次表达了眩晕的这种感觉。 而在梵高星月夜中整个画面出现轻微的扭曲, 在距离最近的树像火焰一样往上生长。 而在shader中让画面wobble太简单了... 就是将uv坐标增加一些三角函数. 之前SDF空间操纵里面有讲过

float wobble(vec2 p, float frequency, float amount)
{
    return sin(iTime*frequency + p.x * 2.0+p.y )*amount;
}

uv += wobble(uv, 4.0, 0.035) * 0.3;

当然我们可以加点旋转,极坐标的旋转其实就是简单的对angle+时间。非常非常简单. 同样不同层圈的旋转速度不一样,外层的需要慢一点. 大家可以随意试试函数达到想要的效果。 最后得到效果 2406231100

Reference