伏雨朝寒悉不胜,那能还傍杏花行。去年高摘斗轻盈。漫惹炉烟双袖紫,空将酒晕一衫青。人间何处问多情。 ———— 纳兰容若
Houdini
这一步主要解决门窗、屋顶和模型摆放的问题,在原视频中是setdressing、window/door、和roof_module的部分。在做完这一切后感觉模型立刻就生动了起来(顿时觉得一些建筑的体块操作变得很无聊。。)现在基本可以开始慢慢脱离教程去做了。根据作者实现的效果先自己写一遍,然后去知乎对一下答案再改改。完全一点点看教程实在是太慢了。
本文主要包含以下内容 (1)屋顶挤出 (2)窗和门的加入 (3)一些装饰品的加入 (4)天窗、烟囱和屋脊的放置
(1)屋顶挤出 首先先补充一个关于屋顶的操作。插入房子的那部分我们先前已经标记了raypoint,那么这部分相关的是肯定不需要沿着屋脊方向挤出的,往旁边是否挤出主要看屋顶是不是平行且平齐的,这样如果它们同时向外向侧挤出就会穿模。那么我们就可以先拿单块屋顶和删除了raypoint的其他屋顶做点云检测,假如碰到了就给这些点以及它们同等y的neibour(也就是屋檐的另一端)做一个标一个touching_roof,
那么这些点就是不往侧边挤出的。说起来有些复杂,贴个代码吧
int neighs[] = neighbours(0, @ptnum);
int found_rayp = 0;
foreach(int n; neighs)
{
if(point(0, "raypoint", n) == 1)
{
found_rayp++;
}
}//这一步检测到raypoint点或者raypoint对角点或者没有raypoint的点
if(found_rayp == 0)
{
int pcloud = pcopen(1, "P", @P, 0.1, 1);
//如果和其他房顶相接标记touching_roof为1,作者用的点检测,
//相当于只避免掉并行的情况,但反正长度差值最小也是1,插进去但话无所谓?
if( pcnumfound(pcloud) > 0)
{
@touching_roof = 1;
vector pos;
//相邻的其他底边也要标记(主要是为了标记raypoint对角点的旁边点)
foreach(int n; neighs)
{
pos = point(0, "P", n);
if(pos.y == @P.y)
{
setpointattrib(0, "touching_roof", n, 1, "set");
}
}
}
}
然后就是找不往旁边挤出的,往旁边挤出的唯一一种穿模可能性就是和楼梯打架,所以把楼梯那个平台拿出来,把boundingbox算出来然后做一个点云检测【作者在这里用的是检测楼梯点,但是我的楼梯是纯程序化的,大小啥的随便变,不好精确,所以平台的boundingbox是比较稳妥的】(当然要把屋顶和楼梯拉平了),假如有点就表为stair_roof,然后有这个标记的就不进行斜向挤出。得到这些点后变一下法线根据法线就可往两个方向移动一些。然后就结束了 贴个节点图
之前在湖边小屋3.0已经做过了功能筛选了,剩下就是把这些功能给用上去,首先是门窗,这一步比较简单,我基本没有跟着视频或者大佬的方向做自己摸索了出来,首先要拿到模型,窗户是用twist一个box做出拱来然后extrude程序化做出来的,门则是直接建模导入的(还是想尝试一下作者的工作流)。Place module的技巧请跳转用法专门页Houdini常更帖
在一番操作后拿到模型后只需要和墙体布尔一下,然后拆出两个面,把面积小的和大的blast开,然后大的就是外墙,小的可以extrude之后做窗户或者门的shut。
这一步比木桶和吊灯要复杂一些,展开记录一下,作者首先是把每个面循环提取出来了底部的线,然后如果它是window/door (非wall)就对它做两个clip(也就是让paddle只放在窗户和门的两侧)。 提取出一系列的线,在线上随机撒点,作者下一步用的是点云来删点,但是不知道为什么用自己的输入进行点云删点哪怕是在detail中运行也没有办法使得删了的点影响pcopen中的0号输入,所以这里最好的办法是用fuse来实现点的融合,避免过密。然后直接place module就可。
至于木桶和吊灯,只是撒点的面不太一样,总体上是大同小异的。就不再叙述了。吊灯还需要排除掉面积比较小的wall。在这些步骤中最好都把globalseed里面暴露一个出去,方便微调。
看起来不多吧,节点就那么一点:)但这一部分其实蛮头秃的。我觉得看教程有时候很难受的原因是因为你完全对他要做什么是没有概念的,只有当你看完代码自己理解了才行(当然我是跳着看的,作者其实也多数有讲她的思路,如果跟着一点点基本就是嗯抄代码然后抄完看一会才能明白),所以很多时候一头雾水就会花很多时间。这一部分就很典型的是这样的例子,一开始根本不知道为什么要这么做。
-
-
这个是通过一个类似mod的方法做的,计算出distance,然后除以module的长度(这个是提前给定模型的)四舍五入后就是module的数量,然后算出一个scale出来并用长度值去类似resample它。把模型的点都标注出来。当然这里还放了一个屋顶的小尖尖,那个就是raypoint(插到另一个房顶的)那部分就不放,其他就+0.2然后执行上面的操作就完了。法线的话提前facet一下就可以。这里一个循环放了两种东西,核心就是因为只需要给点加不同的参数就完事儿了。
- 这里还需要提到一种特殊情况就是之前也提到的因为连续穿插导致的叠起来的屋顶需要把后面这个屋顶的这一个点变到raypoint的位置,总体思路就是遍历每一个面的顶部两点,提前先判断这个面有没有同dir的incoming,如果有就把这个的bottom点拉到Ray point的位置(因为那个屋顶是连续的)
if(@ptnum < 2 && @raypoint == 0) { vector p_top = @P; int p_top_neighs[] = neighbours(0, @ptnum); int bot_neighs[]; int top_neigh;
//find bottom neighbour foreach(int neigh; p_top_neighs) { vector pos = point(0, "P", neigh); if(p_top.y > pos.y) { append(bot_neighs, neigh); } else { top_neigh = neigh; } } //如果有同dir的incoming,就把底部的点移过去(有点丑但是是为了删重复点) if(npoints(1) > 0) { vector raypoint = point(1, "P", 0); vector neigh_pos1 = point(0, "P", bot_neighs[0]); vector neigh_pos2 = point(0, "P", bot_neighs[1]); float dist1 = distance(raypoint, neigh_pos1); float dist2 = distance(raypoint, neigh_pos2); if(dist1 < dist2) { setpointattrib(0, "P", bot_neighs[0], raypoint, "set"); setpointattrib(0, "raypoint", bot_neighs[0], -1, "set"); } else { setpointattrib(0, "P", bot_neighs[1], raypoint, "set"); setpointattrib(0, "raypoint", bot_neighs[1], -1, "set"); } }
}
-
-
- 这个思路是找到屋顶的下边缘线位置的点,resample之后去除首尾(排除掉天窗在旁边的情况),再重新resample合适的宽度,抬高一点之后射线检测左右和正前方一定距离,要是有东西(body roof 还有后面做的塔楼都要检测)就删掉。一开始记得把法线算出来然后存到天窗点上,最后就根据法线方向移动一点距离就可以了。
- 烟囱
撒点,然后检测周围有没有点,有就删掉,然后fuse一个很大的距离(记得选least number而不是average)就可以得到一个比较好的效果,
然后over,现在的模型就已经有小屋的样子了!
本文参考:知乎冬青大佬的解析文章 https://www.zhihu.com/people/dong-qing-41-73-30 b站转载的原教程视频https://www.bilibili.com/video/av53868198/ (建议可以用chrome的英文字幕插件来进行观看)