Shadows
之前通过向光源方向raymarch的方式得到了很精确的硬边缘阴影(which之前在rhino里梦寐以求的效果😥)。硬边缘的产生是因为我们假定光源都是没有体积的点,但真实物理世界显然不是这样的。 如果某个点到光源的任意一点中间都有障碍物遮挡,那么这个点位于实影(numbra)内;如果某个点到光源的一些点中间有遮挡而另一些点没有,那么这个点位于半影(penumbra)内。由于半影的存在,阴影边缘会产生0~1的过渡,一般越靠近物体的根部,阴影会越硬。 实时渲染中一般使用shadow map技术,先从光照的视角渲染成并生成深度图,然后再从观察视角渲染并用深度图计算阴影。再利用PCF(Percentage Close Filtering)技术计算软阴影:在计算可见性时,PCF不再仅仅只查单独的一个深度值,而是会将着色器点p的深度值与深度贴图上的周围最近深度进行比较,最终对比较的结果进行一个加权平均,得到输出的结果。
基于距离场的RayMarching中,获得软阴影的思路是相似的。我们还是要从计算的点向光源发出一条射线,但是我们返回的信息要记录路径上最靠近的一次的距离: float softShadow(in vec3 ro, in vec3 rd,float k){ float t = TMIN; float res = 1.; for(int i = 0; i < RAYMARCH_TIME ; i++) { vec3 p = ro + t rd; float d = f(p); res = min(res, kd/t);//k是缩放系数,k值越小半影面积越大 t += d; if(d < PRECISION || t > TMAX) break; } return res; } 这样我们就得到了基础版的软阴影,但是可以观察到在锐边附近有很多带状的瑕疵,这是因为我们朝光线方向march的时候很可能错过路径上真正的最近点。 2018年的GDC上Sebastian Aaltonen发布了改进算法,返回值改为两次sdf圆的交线半长——可能错过最近点的位置。 float softShadow(in vec3 ro, in vec3 rd,float k){ float t = TMIN; float res = 1.; float ld = 1e20; for(int i = 0; i < RAYMARCH_TIME ; i++) { vec3 p = ro + t rd; float d = f(p); //res = min(res, kd/t); float y = (i==0) ? 0.0 : dd/(2.0ld); float l = sqrt(dd-yy); res = min(res, k*l/max(0.0,t-y)); ld = d; t += d; if(d < PRECISION || t > TMAX) break; } return res; } 这样就可以一定程度地缓解肥胖纹x:
AO
但是! 当我们把k值调大时,肥胖纹x虽然没那么明显了,但物体交接边缘出现了明显地漏光: 把precision调小可以得到一定的缓解,但这还是跟iq说好的不一样呀——
As you can see, the improvement is massive: not only you get soft shadows, but they even behave realistically as when shadows are sharp next to occluder and occluder contact (see where the bridge touches the floor) and much softer penumbras when the occluder is far from the occluded point.
百思不得其解了很久,然后发现——
淦,iq这个示范图里本来就含着AO的呀。。。。
float calcAO( in vec3 pos, in vec3 nor )
{
float occ = 0.0;
float sca = 1.0;
for( int i=0; i<5; i++ )
{
float h = 0.001 + 0.15float(i)/4.0;
float d = map( pos + hnor );
occ += (h-d)sca;
sca = 0.95;
}
return clamp( 1.0 - 1.5*occ, 0.0, 1.0 );
}
iq这里用的是一个极简版本的AO——甚至没有做半球形的采样,仅判断了法向上是否有遮挡物,不过可以先凑活用————
但为啥还是漏光啊😭😭😭
I'm really tiiiiiired :(
布尔运算
下午写了一遍了,,然后服务器没存上 It's fine 因为反正smooth Intersect和smooth Substract也不工作..... 难过摆烂的一天:( 解决问题了再补上.... 唯一有好好工作的smooth union: float smUni( float d1, float d2, float k ) { float h = clamp( 0.5 + 0.5(d2-d1)/k, 0.0, 1.0 ); return mix( d2, d1, h ) - kh*(1.0-h); }
Metaball
之前做比赛的时候很想做的效果,在SDF中原来实现得这么简单... 只要用上面的这个函数代替基础的min()操作即可。