大家都知道如果使用点云或者使用类似attribTransform这样的节点来采样或者传值的时候,都是以点为圆心一定半径均匀的采样,近几天为了做一些比较特殊的牵引效果所以好好琢磨了一下非均匀的线性采样方法,这个词有点太绕,我先上两张对比图看一看:
权重改变前 权重改变后
要求是在上面第一张图限定的半径范围内,改变权值为1的中心点位置该点到圆的边缘还是从1到0的线性渐变过度(如果用指数运算就不是线性了,但还是连续过渡的渐变)。
这里的方法其实也不算特别复杂,下面是我对方法求导的思维过程:
这里M是原来的中心点;M'是移动后想要变换过去的中心点;P目标面上的任意一个点,当然该点是以M为中心R为半径的球内部的一个点;P'是从M'P延长线与刚才提到的M为中心的球的焦点。因为这四个点必然在同一个平面上,所以我们直接可以转化为平面三角算数题。
重新变换之后,权重中心点在M'上,所以P点的实际权重值应该是PM' / P'M',点M,M',P都是空间内已知的三个点了,所以难点就是求出M'P'的长度了。
三角数学中学知识啊中学知识啊,全还给老师了,忘记公式也是在所难免的,好在我们还有wiki:http://en.wikipedia.org/wiki/Triangle
这个页面中有提到一个非常重要的公式是我用到了的,当然也许你根本不用查这个页面就知道了:
在我画的草图里面θ角是用向量可以算出来的,这样的话φ角也用是能用M'M算出来的,所以M'P'对应的α角也能求出来,同理M'P'对应的也出来了。
在Houdini实现方法:
pw_maxRadius:
int handle = pcopen(1, "P", P, 99, 1);
float maxDis = ch("maxDis");
float pow = ch("pow");
while(pciterate(handle)){
float distance;
pcimport(handle, "point.distance", distance);
distance = fit(distance, 0, maxDis, 1, 0);
distance = pow(distance, pow);
//f@distance = distance;
@Cd = set(distance, 0, 0);
}
pw_deform_weight:
float pi = 3.14159265354;
float maxRadius = ch("maxRadius");
float gradient = ch("gradient");
int handle = pcopen(1, "P", P, 99, 1);
int pointNum;
while(pciterate(handle)){
pcimport(handle, "point.number", pointNum);
}
vector origPos = point(1, "P", pointNum);
vector deformedPos = point(2, "P", pointNum);
vector m = origPos - deformedPos;
vector s = @P - deformedPos;
float sinTheta = length(cross(normalize(m), normalize(s)));
float factor = maxRadius / sinTheta;
float sinBeta = length(m) / factor;
float alpha;
//check the theta angle is less or more than 90 degrees
if(dot(m, s) > 0){
alpha = pi - asin(sinTheta) - asin(sinBeta);
}
if(dot(m, s) < 0){
alpha = asin(sinTheta) - asin(sinBeta);
}
if(dot(m, s) == 0){
alpha = acos(length(m) / maxRadius);
}
f@alpha = alpha;
float edge = factor * sin(alpha);
float distance = length(s) / edge;
if(distance<=1){
distance = pow((1- distance), gradient);
v@Cd = set(distance, 0, 0);
f@distance = distance;
}
else{
v@Cd = set(0, 0, 0);
f@distance = 0;
}
pw_deform:
float dragScale = ch("dragScale");
float pushScale = ch("pushScale");
int handle = pcopen(geoself(), "P", P, 99, 20);
int count = 0;
vector smoothColor = set(0, 0, 0);
vector origPos = point(1,"P",0);
vector deformedPos = point(2,"P",0);
vector deform = deformedPos - origPos;
while(pciterate(handle)){
int pointNum;
pcimport(handle, "point.number", pointNum);
if(pointNum != @ptnum){
vector tmpColor;
pcimport(handle, "Cd", tmpColor);
smoothColor += tmpColor;
count ++;
}
}
smoothColor /= float(count);
@Cd = smoothColor;
f@distance = smoothColor.r;
@P += (normalize(@N) * pushScale + deform * dragScale) * smoothColor.r;
这个方法的要求是M'M的距离不能超过半径R的值(超出了会出错),而且球外所有点的权重值都会大于1。