Houdini

「Houdini」湖边小屋part2 屋顶的崛起

by Ayse, 2022-02-15


Houdini

Snipaste_2022-02-14_00-45-20.png

前面的话就不说太多了,其实这些文章都是之前写好等网站过审了才能传上来的一些思路总结(一些偷懒人x)以及再次提醒一句本系列可能不涉及到具体每一步的操作,只记录有意思的算法和大体的建模思路。如需要特定的每一步的教程需要移步原教程以及知乎冬青大佬的解析


本部分主要对屋顶进行处理,主要分为以下步骤。 (1)屋顶分面 (2)屋顶找屋脊 (3)确定屋顶高度和基准平面 (4)拉起屋顶 (5)屋顶修正


(1)屋顶分面 经过了roof提取后得到的面片是2*2和回缩了一半的面片组成,现在就是需要让其变成真正的屋顶,有共边的面形成一定的组合,合并成为最终的屋顶分块。 思路和之前的类似,也是通过点检测的方法找到相邻面并blast出来(pointprims函数只返回刚好在顶点的情况的面,所以避免掉了一大接一小被融合的情况,同时相邻面只选第一个,保证每次融合只融一个面)然后通过divide函数删除中间的边形成合面,总体的seed通过一开始的sort prim来控制,上图

1.png

在代码中,作者还给prim加了stop的参数来判断面是否有相邻面来减少运算


(2)屋顶找边 本步的目的是求出屋脊的方向。首先要说的是in面的定义,作者把in面定义为了in面是刚好插入到out面的这种面,然后它的dir是要垂直于原有的面的。首先求出来的就是两个in面的dir。

2.png

作者在这里提出了一种新的点云检测点的做法(其实之前撒点也用了不过那一步有更加明智的撒点策略pointsfromvolume)就是先提取出一个面a和其他面,resample其他面,然后采样aprim周围顶点pcopen(),如果两个点检测到了同属于一个prim的点,就说明这个面的这条边和被检测面完全相交,也就说明这个面是一个纯纯的in面,节点上图

3.png

回到这一步的节点总览(上一步是右上角的第一个小包包),第二步就是通过sort后选点45选出刚刚做的这条线。下面的循环的主要目的是求out面的线,这个循环有几步是同步进行的 1、作者首先把每一个面遍历重新采样(这个时候的roof id 和选择中线的roofid是一致的 ),然后删除掉脚点(防止相邻碰触的情况) 2、分离当前循环的中线,其余线来对刚刚重新采样的点云找点,找不到(说明纯纯是1)就把当前面自己的中线作为dir加入数据,要是这个面没中线,就把roofdir和y轴叉乘作为dir 3、如果找到了就说明还有面是插入它的,把这个值标记为当前面的incoming。然后删掉自己和in面,再用同样的方法把其他的面重新采样加入id,然后用当前面的中线来采样, 如果采样到面就附加一个out面到当前面上(说明1别人的部分)然后要是又不in又不out就随便给个dir完事儿。

4.png

然后一切结束后加一个marker看看自己的dir对不对就快乐下一步。


(3)确定屋顶高度和基准平面 这一步内容如其标题,又拿回刚刚的那个图。作者希望的是在out的这部分面的平台是要高一些的,同时它抬起来的高度也得高一些。

5.png

总体的思路分为三部分

  • 第一部分 高度初分 首先确定高度的层级关系,通过一个循环:如果自己的in面大于等于自己,则自己height加一;如果自己的out面小于等于自己,则给out面减1;如果没有in和out则随机给一个height。循环结束后加一个判定处理特殊情况:inout循环互咬的那种就把其中一个点in和out都删掉并标记为convert,通过promote节点找到max_height,存在detail中方便后续调用。排序,下面的代码就是这个算法。

6.png

  • 第二部分 宽度初分 求出每个面的宽度(求出两个edge的向量,和dir方向不一样的就是宽)原理很简单,不过多解释。(一个小技巧:作者这里都采用了在== 的比较前面加一个rint,可以减少误差的发生。)然后promote求max_width方便后续使用。

  • 第三部分 最终高度和补正平面的确定 引入了新变量platform(pr)、elevation(pr)、overall_elevation(de)。思路是
    第一步在所有的面中找到最宽(即有max_width)同时height最小的面作为基准面(所有的高度都要高于这个面)然后计算其在40度抬脚的时候的高度。作者在这里还加入了一个if限制最宽面当小于3的时候,抬起的角度还要更大一些为50度。最后把高度(上一步算出来的)、放入这个面的elevation、overall_elevation作为自己以及参考值。

    第二步就是创建梯度列表,先加入标准高度ref,然后遍历所有面:大的height就加一个0.2-0.5的随机值到elevation上,小height同理减,生成「min ref max1 max2」 类似结构的梯度值。

    第三步,在这个基础上再对elevation进行一次矫正,作者在一开始设置倾斜角为30-50度,如果elevation超过了50度的高度,就通过抬高platform来实现补偿(作者还加入了一个关于elevation比max还要大两倍的情况,直接就放弃抬高平台让它变高【类塔】),刚刚那些convert了的就直接给一个超高平台3就完事儿,然后输出一个包含了最终elev,platform的向量,直接赋值给面。

小技巧分享:在wrangle中,detail、prim等等都值都需要用一个获取函数来获取并赋值才可以使用

上图 7.png

  • 第四部分 特殊情况处理

8.png

要是最后因为outout连接导致二者的dir一致。就拉平platform,让他们只有高度差值就ok。然后把顶点往下做射线检测,拉到pw点上使得她们呈现这样的和谐关系


(4)拉起屋顶 第一步当然是直接根据elevation拉高屋顶,过程就是clip然后把两个新顶点往上移动。(图中的颜色是elevation的值) 9.png


(5)屋顶的修正 主要的目的是把屋顶连接起来,并且横向封面,总体的思路分为三步 首先提取出是提取出一个个的面以及他们的out面,修正法线为屋脊方向后,利用三种不同的intersect来讨论分开讨论(在normal_ray的wrangle中实现)并且把射到面的标记为raypoint 1

· 第一种情况,out与自己垂直,需要把房顶的点连接到out的房顶的平面上(向法线检测)然后把屋檐的点也连接上去 · 第二种情况,out与自己平行,需要把房顶的点调整到同等高度(向下检测) · 第三种情况,没有out,则需要填充这个面并且称之为side(啥都没有,或者碰到墙)

备注:这里用到了addprim和addvertex函数的共同工作流,前者可以直接先加面,然后后者加入顶点的位置

int side_pr = addprim(0, "poly");
setprimattrib(0, "side", side_pr, 1, "set");
addvertex(0, side_pr, @ptnum);

10.png

然后,根据platform挤出不同的平台,如果platform不为0就和挤出的平台去merge。这里很简单,不再过多赘述。

最后,修正新生成的side面的normal。因为随机生成的normal可能会出问题。作者用的方法是利用facet可以重新计算法线的原理

【猜测:facet法线的生成是按照顶点的顺序来做的,符合右手定则,而顶点自己的法线理论上平均后为prim的法线,但是这里可能出现顶点的法线是原来变成的法线,面的法线又是按照出现顺序决定的所以反了的问题,facet相当于可以校正点法线,如果面是正确的,facet没有变化】

·获取一个点的法线,判断facet前后的法线是不是一样的,如果不一样就重新创建prim(反着输入vertex)即可

11.png 12.png facet前后的对比图

备注:这里知乎上还提到了一个对比前后不一致的,由于houdini用科学计数法,校正这种带0的误差一般就是四舍五入,相减小于,或者点乘判断方向的方法

到此,屋顶搞定,返回和bodybase合并一下就行


本文参考:知乎冬青大佬的解析文章 https://www.zhihu.com/people/dong-qing-41-73-30 b站转载的原教程视频https://www.bilibili.com/video/av53868198/ (建议可以用chrome的英文字幕插件来进行观看)

作者: Ayse

2024 © typecho & elise