关于dFdxy函数

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第3天,点击查看活动详情

之所以要搞清楚这个函数,还是因为我绘制网格的时候遇到了问题。直到遇见了偏导数才得以解决。

常规的网格

先来一个普通的网格,就是利用放大坐标然后fract取小数,实现周期。

vec2 grid2 (vec2 uv , float w) { 
  uv-=vec2(.5 );
  return step(vec2(.5 -w) , abs(uv) );  
}
。。。。。。。。
   vec2 xy = grid2(st, .01);
   color = mix(color, vec4(color1,1), xy.x + xy.y);


放大了10倍,也就是10 X 10 。效果如下。

image.png
看上去还不错 ,那么来试试放大20 30倍看看。 可以看到,如果说20倍还只是有些线条的粗细不均,那么30倍的时候我们就丢失了很多线条。

image.png

image.png

线条消失术?

我们之前的判断是不是在网格上的逻辑很简单,就是判断在放大的一个[0,1]区间内,xy是否小于线宽。当然我还做了处理,处理后区间变为[-.5,.5]。 这里需要注意的是,传入的线宽w是绝对的 ,而xy实际上是放大后的。

什么意思? 比如说,原本xy的值域是[0,1] , 经过上述处理后,一个单元格内的[-.5,.5] 实际对应 原本的十分之一,相当于[-0.05, 0.05] 。 对于线宽来说,就是线宽变细了原本是0.01,现在是0.001

这么一说,是不是恍然大悟,线条太细,细的看不见了。 加粗就
完了。 加粗确实可以,但是如果我要一个确定粗细呢,是不是应该把缩放系数给线宽也乘上。 我们来试一试。

vec2 grid2 (vec2 uv , float w, float repeat) { 
  uv= fract(uv * repeat) - .5;
  w = w* repeat ;
  return step(vec2(.5 -w) , abs(uv) );  
}
.........
    vec2 xy = grid2(st, .01, 10.);

确实没问题了,并且线条宽度完全可控。 真的完全可控吗,再细点 0.001,1.0001.

image.png

image.png

image.png

可以看到,取0.0001的时候还是出问题了。 而且不是线宽细的看不见,因为仍有部分线条是可以看见的。

世界是不连续的 像素点也是

遇事不决,量子力学。 宏观连续,微观离散。 我们能从屏幕上看到色彩,实际上是有具体数目的像素点组合形成的效果,这个想必大家都知道,锯齿,像素风,就是那个意思。

所以,一个片元是不可能比实际上的一个像素点还小的。 网格之所以有些线条看不见,就是因为,那部分本该绘制的区域刚好卡在像素/片元的缝隙里,完美错过。

偏导数

dFdx dFdy 这些函数就能解决这个问题。先来看文档

image.png
fwidth = dFdx + dFdy

返回关于 x 或 y 的参数的偏导数。

dFdx 和 dFdy 只能在片段着色器中使用,它们分别返回表达式 p 在 x 和 y 中的偏导数。偏差是用局部差分计算的。暗示高阶导数如 dFdx (dFdx (n))的表达式具有未定义的结果,混合阶导数如 dFdx (dFdy (n))也是如此。假定表达式 p 是连续的,因此,通过非均匀控制流求得的表达式可能是未定义的。

看不懂? 没事,我也看不太懂,只要记得偏导函数只能在3.00及以上版本的 fragment 中使用即可 。看下图。

我接触这个函数的第一反应就是,不可能吧。 我一直以为,片元着色器是每个片元执行一次,各个片元之间互不相干。 如果真是这样,怎么可能求导呢,求导就需要变化量,只知道单个点是没法求导的。 实际上,的确如此。

在三角形光栅化期间,GPU一次运行多个实例,将它们组织为2X2的像素块, 这样就可以取相邻两个像素差值来计算导数了。dFdx 从右侧的值中减去块左侧的像素dFdy 值,从顶部的值中减去底部像素的值。请参见下图,其中网格表示渲染的屏幕像素,并且dFdx为由dFdy片段着色器实例在 (x, y) 屏幕坐标处评估的通用值 p 提供了表达式,并且属于以红色突出显示的 2×2 块。

image.png

使用偏导

使用很简单,如下。 有没有觉得奇怪,dx dy分别只传入了x 和y分量, 因为这是偏导 ,其实应该写作 float dx = dFdx(uv.x + uv.y)
此时的w 应该是个整数,但类型仍为浮点。

  float dx = dFdx(uv.x) * w;
  float dy = dFdy(uv.y) * w;
  return step(abs(uv), vec2( dx,dy)* w   );  

可以看到线宽为1的时候,有些线是看不见的,因为线宽为1,就可能刚好卡在夹缝里。2两个线宽就必然能看见。

w =1 ,repeat =10

image.png

w =2 ,repeat =30
image.png

结语

好了,偏导数在glsl里的用法就这么简单。保险起见线宽可以再宽点。

上述代码中是直接用加法的结果作为混合系数的,所以交叉点的颜色有所不同,可以处理一下使得其不大于1,或者在函数实现里用 if else之类的。

注意版本须得是3.00及以上。

© 版权声明
THE END
喜欢就支持一下吧
点赞8 分享
评论 抢沙发
头像
欢迎您留下宝贵的见解!
提交
头像

昵称

取消
昵称表情代码图片

    暂无评论内容