Recent Posts

人体速写笔记


色彩光影理论



Stay Connected

A smart wordpress theme for bloggers
houdini程序化建筑(一)地墙模块规划

houdini程序化建筑(一)地墙模块规划

本篇blog主要记录一下使用houdini开发程序化建筑的思路与流程。

首先,houdini制作程序化建筑的基本原理是:预先制作一些建筑的模块和零件,然后自定义一个组合的规律或形式,最后让houdini自动地拼接出一个完整的建筑出来。

所以前期的核心工作就是——定义、规划、创建建筑模块。

一、地面墙面模块规划

首先我们考虑最基础的建筑模块——地面配墙面。这种模块一般由1块地面+N块墙面组合而成(N∈[0,4])。

现在我们来枚举所有的情况:①只有地面;②地面+一个墙面;③地面+两个相对的墙面;④地面+两个相邻的墙面;⑤地面+三个墙面。

这里我们可以定义一个csv表格,来收纳所有的组合情况:

wall指的是模块名称,xb指的是有x块墙面,axbxc指的是尺寸是a*b*c(其中b是y轴 即竖直向上的轴),opp和adj分别描述相对和相邻,最后的01指的是变体编号。

当然,我们现在的模块叫做“地面配墙面”,其实还可以进一步细分成单独的墙面模块 和单独的地面模块。接下来我们在houdini中定义这些模块,首先创建一个Geo节点,在里面分别装载墙面和地板的subnet:

对于每个subnet,主要定义它的模型构成和名称:

首先创建一个grid,代表模型网格,将细分数改为2;然后连接到Null节点上,这里主要是编一个名字,格式为“模块名称_尺寸_变体编号”;

最后我们使用Attribute Wrangle节点,为我们的模型增加一个属性(什么是属性,例如顶点、法线、uv,这些都叫属性)。定义新属性需要写一个VEX代码:s@name = chs(“nodename”),这样就拥有了一个叫nodename的新属性。如何给它赋值呢?我们可以在属性栏里写一个opinput,即 用该节点的0号输入端口来赋值这个nodename,这样我们Null节点定义的名字就赋值到nodename上了。

二、地面尺寸扩展

之前我们定义的都是400×400的地面,真实建筑的地面不可能都这么方正,所以我们需要扩展一些别的尺寸:400×300、400×200、400×100。这样我们可以扩展一下表格:

将各个情况都扩展出不同的尺寸(除了opp)。此时需要注意的是,对于adj的情况,分为left和right两种情况:

所以需要在后面补一个left和right的标识。

接下来我们需要更新一下houdini节点,在地板的subnet中加入不同尺寸的地面,并将它们合并输出:

到这步就可以理解了,对于wall/floor的subnet,它的意义在于得到所有尺寸的墙体/地板模型。现在我们拥有了wall和floor的subnet,我们就可以对这两者进行组合,拼接成最基础的地墙模块。

三、fbx批量输出

将wall和floor的所有面合并起来后,我们要将它们分别输出成独立的fbx模型,这样后续houdini可以读取模型进行拼接。

首先我们需要创建一个HDA(作为控制器):

一共需要两个输入参数,一个是导出fbx的路径directory,另一个是按钮button,其中后者是用来操纵python代码的,后续再说这段python代码是干嘛的。

HDA函数如下:

我们来梳理一下输出函数的逻辑。首先我们通过一段foreach迭代,来遍历每一个面,并取出当前迭代的次数,存入到面的“index”属性中,这样每个面都有了自己的标识编号:

int iter = detail(1,"iteration",0);

i@index = iter;

由于我们希望每个导出的fbx都只包含一个面,因此我们可以这样设计:对于1号帧我们输出1号面,对于2号帧输出2号面……换言之,对于N号帧,要将所有非N号的面都删除:

if(i@index != $F - 1){
    removeprim(0, @primnum, 1);
}

将面清除干净后,我们可以为要导出的面准备一个属性:输出路径,路径就是前面的directory参数+每个面的name属性:

string directory = chs("../directory");

string name = prim(0, "name", 0);

s@filepath = directory + name;

有了filepath,我们就可以在rop_fbx节点中 根据filepath来输出模型:

`details(0,"filepath")`.fbx

这样导出流程就完成了。不过存在两个问题:①现在我们只能手动调节逐个帧;②虽然面上拥有了路径属性,但后面的导出fbx的节点不能获取它,导出fbx的节点获取的是它上一个null节点的名称。(也就是说,假如null节点的名称叫Null,那么fbx输出出来的模型名称就叫Null.fbx,很蠢)

所以现在我们希望:我们能控制当前的帧数,并能控制null节点的名字,这一步就必须依靠python脚本来实现了。

我们在fbx_batch_output子模块中新建一个null节点,将其属性改为python代码,写入:

import hou

node = hou.node('.')

path = node.path() + '/'

dataNode = hou.node(path + "foreach_begin1_metadata1")
dataNodeGeo = dataNode.geometry()
iterations = dataNodeGeo.attribValue("numiterations")

nullNode = hou.node(path + "output")
renderNode = hou.node(path + "rop_fbx1")

for i in range(1,iterations + 1):
    hou.setFrame(i)
    outputGeo = nullNode.geometry()
    outputPrims = outputGeo.prims()
    outputName = outputPrims[0].attribValue("name")
    
    nullNode.setName(outputName)
    
    renderNode.render()
    
nullNode.setName("output")
hou.setFrame(1)

原理很简单,首先我们通过foreach元数据节点获取 面的总个数,然后通过一个循环来获取各个帧下的面数据,并重置null节点的名称为对应面的名字,这样导出fbx时就能导出正确的面的名字了。

最后将null节点的名称还原,并将帧数还原成1。这样我们点击HDA的按钮,就导出成功了:

梳理一下我们前期的思路:

①设计建筑的地墙模块构成

②创建地墙的基础面片,并赋予代号名称

③将所有面片合并,通过foreach循环对每个面片赋予一个编号

④设置N号帧只保留N号面(即N号帧删除所有非N号面),以面片名称作为fbx文件名称输出

⑤创建一个python脚本,控制帧数,从而实现批量导出

四、组合地墙模块

上一节我们将独立的墙和地面导出成fbx了,这一节我们只需读入fbx并将它们自由组合,便可以拼接成不同的模块了。下面放一个组合示例:

首先读入fbx,对模型信息进行pack打包,attribpromote不知道啥意思先跳过,然后对顶点的法线进行一个重置,做一下transform,最后合并形成一个完整的地墙模块。这样的模块一共需要做4+8+4=16个。

五、屋顶模块完善

有了前四节的知识铺垫后,这一节可以继续扩展模块了。首先进一步扩展地板的尺寸,扩展出300×300、300×200、300×100、200×200、200×100、100×100;

然后开始制作屋顶。屋顶一共分为两种:拱形倾斜屋顶 和 平面屋顶:

屋顶的程序化生成流程比较复杂,这里只做一个简单分析。首先是拱形倾斜屋顶:

拱形倾斜屋顶的基形由三个物体组成:屋顶体、屋顶面、边栏,将它们三者合并后进行bend(弯折),然后进行旋转,即可体现出拱形倾斜的形状:

接下来就要分倾斜屋顶的类型了,包括非边缘处和边缘处,非边缘处,如下图所示:

最左侧的就是非边缘处的屋顶,比较简单;而右边这几个就是边缘处的屋顶(边缘处的屋顶需要拦一面墙),对于不同尺寸需要生成不同的模型。

至于平顶的屋顶也是类似的思路,节点实在太多就不一一分析了。在完成了Primitives节点的定义后,点击Export即可将屋顶的模型也全部导出,然后在combined节点中将所有屋顶模型重新读入拼接即可:

Leave a reply

Your email address will not be published. Required fields are marked *

Stay Connected

Instagram Feed

Recent Posts

人体速写笔记


色彩光影理论



×