MY BLOG DIRECTORY:YivanLee:专题概述及目录zhuanlan.zhihu.com
INTRODUCTION:
Metaball常常被用在游戏里制作各种魔幻的效果,下面是我在虚幻材质编辑器里实现的MetaBall。https://www.zhihu.com/video/981288051347185664
这一些列我使用了改造过的材质编辑器,能直接在编辑器里写HLSL代码并且压入Shader,实现方法可以看我之前的材质编辑器篇。
目前ray marching大概有几种机制,第一种是SphereHit的方式,这种方式能快速找到表面,这种raymarching算法适合用来绘制不透明的物体,因为我们绘制这类物体只需要知道它的表面即可。代码如下:
这种方式循环次数可能很少,可能几次循环之后就满足break条件然后弹出了。
第二种是绘制那种透明的物体,如烟雾,透明胶体。这种需要对这条光纤上的浓度进行积分。所以需要用segment hit的方式。这种方式可能就需要在一条光线上执行积分操作了,代码例子如下:
然后第三种就是光追的方式了,这种方式我这里就不多说了,暂时先跳过去。
MAIN CONTENT:
【1】2D MetaBall
Metaball的关键在于距离场组合函数上,iq给出了一种叫Polynomial smooth minimum的方法,代码如下:
// Polynomial smooth minimum by iq
float smin(float a, float b, float k)
{
float h = clamp(0.5 + 0.5 * (a - b) / k, 0.0f, 1.0f);
return lerp(a, b, h) - k * h * (1.0 - h);
}
可是这个理论的原理是什么呢,下面进行推导:
首先要对Metaball这个现象进行数学描述:对两个距离场进行平滑插值,使其在一个范围内的部分融合在一起,且仅对这个范围内的距离场进行平滑处理,对于这个范围以外的距离场还是保持它的原值。完成了数学表述以后就可以列函数了,很显然这时一个分段函数
定义:
距离场
,距离场
。
都是基于距离
的函数
距离场的平滑函数
平滑插值的范围
故可得分段函数
.....................................(1)
可以是很多种方法,不过这里使用线性插值
.......................(2)
其中
是线性插值的权重值,范围是0到1
因为
是一个混合的范围,把
缩放到0到1区间
...............................(3)
对
求关于
的导数
..................(4)
当
时
分段函数
当
时由
故
.................................(5)
为了使分段函数连续,当
时,
故
...............................(6)
当
时,
,分段函数
故
.............................(7)
同理当
时
,分段函数
故
.........................(8)
函数图形如下:
为了使分段函数连续,则要使分段函数分段部分的左右导数相等即(5)=(8)(6)=(7)
所以首先对(5)进行变化,加
故(4)变为
此式中
.......(9)
(5)变为
此式子中
...........................(10)
(6)变为
此式中
......................(11)
对(6)进行变换,加
故(11)式变为
由于此时
故
此时(10)为
由于
故此时(9)为
故连续的
函数图象为:
翻译成HLSL代码即
// Polynomial smooth minimum by iq
float smin(float a, float b, float k)
{
float h = clamp(0.5 + 0.5 * (a - b) / k, 0.0f, 1.0f);
return lerp(a, b, h) - k * h * (1.0 - h);
}
这两行代码却包含了巨量的数学原理,再次拜倒在iq大佬脚下。
【2】3D MetaBall
下面我们开始正式制作meatball吧。raymarching篇的第二篇文章我有做过一个很简单的示例:
其实这个再稍微变一下就是我们的meatball了。meatball和这个示例的区别在于距离场的构造:
我们只要让两个球衔接的地方顺滑平滑即可。
第一个sphere函数是距离场元素公式,是我们最基本的距离场模型。第二个就blob5函数就是处理球与球之间距离场的顺滑过度的。第三个函数就是构建我们距离场的函数啦。
下面是我的完整代码:
float sphere(float3 pos)
{
return length(pos) - 50.0;
}
float blob5(float d1, float d2, float d3, float d4, float d5)
{
float k = 0.1;
return -log(exp(-k*d1)+exp(-k*d2)+exp(-k*d3)+exp(-k*d4)+exp(-k*d5))/k;
}
float scene(float3 pos, float iTime)
{
float t = iTime;
float ec = 1.5;
float s1 = sphere(pos - ec * float3(cos(t*1.1),cos(t*1.3),cos(t*1.7)));
float s2 = sphere(pos + ec * float3(cos(t*0.7),cos(t*1.9),cos(t*2.3)));
float s3 = sphere(pos + ec * float3(cos(t*0.3),cos(t*2.9),sin(t*1.1)));
float s4 = sphere(pos + ec * float3(sin(t*1.3),sin(t*1.7),sin(t*0.7)));
float s5 = sphere(pos + ec * float3(sin(t*2.3),sin(t*1.9),sin(t*2.9)));
return blob5(s1, s2, s3, s4, s5);
}
float4 raymarching( in float3 ro, in float3 rd ,float iTime)
{
float4 ret = float4(0,0,0,1);
for( int i=0; i < 90; i++ )
{
float d = scene(ro, iTime);
if( d < 0.001)
{
ret.xyz = ro;
ret.w = 1;
return ret;
}
ro = ro + d * rd;
}
ret = float4(1, 1, 1, 0);
return ret;
}
float3 calcNormal( in float3 pos , float iTime)
{
const float eps = 0.002;
const float3 v1 = float3( 1.0,-1.0,-1.0);
const float3 v2 = float3(-1.0,-1.0, 1.0);
const float3 v3 = float3(-1.0, 1.0,-1.0);
const float3 v4 = float3( 1.0, 1.0, 1.0);
return normalize( v1*scene( pos + v1*eps ,iTime) +
v2*scene( pos + v2*eps ,iTime) +
v3*scene( pos + v3*eps ,iTime) +
v4*scene( pos + v4*eps ,iTime) );
}
float4 render(float3 ro, float3 rd, float time)
{
float4 outcolor = float4(1, 1, 1, 1);
float4 ret = raymarching(ro, rd, time);
float3 N = calcNormal(ret.xyz, time);
outcolor.rgb = N;
outcolor.a = ret.w;
return outcolor;
}
SUMMARY AND OUTLOOK:
再来梳理一下Raymarching的框架。
//距离场构建的基础元素,就像我们建模用的box sphere是一样的道理
float sphere(float3 pos)
{
}
//对距离场构建的基础元素进行操作,类似3Dmax建模里的布尔操作,把模型塌陷到一起
float blob5(float d1, float d2, float d3, float d4, float d5)
{
}
//构建我们的场景
float scene(float3 pos, float iTime)
{
}
//渲染距离场
float4 raymarching( in float3 ro, in float3 rd ,float iTime)
{
}
//渲染法线
float3 calcNormal( in float3 pos , float iTime)
{
}
//渲染我们最终的图像
float4 render(float3 ro, float3 rd, float time)
{
}
Enjoy it!
NEXT:第五卷:3D体纹理云zhuanlan.zhihu.com
Reference: