美文网首页
CityEngine中街道和建筑的构建

CityEngine中街道和建筑的构建

作者: Dragon_boy | 来源:发表于2020-08-14 01:53 被阅读0次

    这一节介绍一下相关模型的构建。

    上一节说过,CityEngine中,基本的几何图元是Node、Segement、Block、Shape,其中Shape是可以赋予规则文件的多边形。



    上面是街道Shape的一个例子。CityEngine中可以为每个Shape赋予一个规则文件,然后按照规则文件构建模型,下方的StartRule是起始的规则,可以理解为其它编程语言中的main函数,是个入口,类推的话,我们所编写的所有规则都可以看作是一个函数,只不过CityEngine中这些规则是按照树结构连接在一起的,CityEngine中有一个功能窗口可以显示这种结构,打开Model Hierarchy后,选中生成的模型,点击Inspect model即可:



    上面是我生成一个街道的规则树,,点击每一个部分都会展示执行当前规则后会有什么效果,要说类似的功能的话大概是Unity中的帧调试器。

    说这么多,下面介绍一下街道的规则文件编写。在任意一个项目文件夹下创建一个CGA文件后,默认会有注释,以及当前cityengine版本的关键字标识:

    /**
     * File:    Street.cga
     * Created: 24 Apr 2020 15:44:19 GMT
     * Author:  Dragonboy
     */   
    
    version "2019.0"   
    

    接着cityengine中的attr关键字可以定义变量,这里我定义一些纹理以及模型的路径变量:

    //road
    attr color_tex = "images/Asphalt003_2K-JPG/Asphalt003_2K_Color.jpg"
    attr normal_tex = "images/Asphalt003_2K-JPG/Asphalt003_2K_Normal.jpg"
    attr roughness_tex = "images/Asphalt003_2K-JPG/Asphalt003_2K_Roughness.jpg"
    attr occlusion_tex = "images/Asphalt003_2K-JPG/Asphalt003_2K_AmbientOcclusion.jpg"
    
    //road line
    attr roadline_color = "images/RoadLines007_2K-JPG/RoadLines007_2K_Color.jpg"
    attr roadline_normal = "images/RoadLines007_2K-JPG/RoadLines007_2K_Normal.jpg"
    attr roadline_roughness = "images/RoadLines007_2K-JPG/RoadLines007_2K_Roughness.jpg"
    attr roadline_opacity = "images/RoadLines007_2K-JPG/RoadLines007_2K_Opacity.jpg"
    
    //cross line
    attr crossline_color = "images/RoadLines003_2K-JPG/RoadLines003_2K_Color.jpg"
    attr crossline_normal = "images/RoadLines003_2K-JPG/RoadLines003_2K_Normal.jpg"
    attr crossline_roughness = "images/RoadLines003_2K-JPG/RoadLines003_2K_Roughness.jpg"
    attr crossline_opacity = "images/RoadLines003_2K-JPG/RoadLines003_2K_Opacity.jpg"
    
    //busstop
    attr busStopOBJ = "assets/BusStop.obj"
    
    //lamp
    attr lampOBJ = "assets/lamp.obj"
    
    //fireHydrant
    attr fireHydrantOBJ = "assets/fire_hydrant/fire_hydrant.obj"
    

    路径是相对项目主文件夹的。

    接着const关键字和C++类似,用于定义不可改变的只读变量,这里我定义一下模型的大小:

    const width  = assetInfo("assets/lamp.obj", sx)
    const height = assetInfo("assets/lamp.obj", sy)
    const depth  = assetInfo("assets/lamp.obj", sz)
    
    const busStopWidth = assetInfo("assets/BusStop.obj", sx)
    const busStopheight = assetInfo("assets/BusStop.obj", sx)
    const busStopdepth = assetInfo("assets/BusStop.obj", sx)
    

    assetInfo可以读取第一个参数中路径对应模型的三维大小,或者是图片的宽高。三个轴向分别对应sx,sy,sz

    这里只是进行展示而已,建议是在规则编写时需要什么变量再去定义,不要一股脑地写在文件最前面。

    然后,规则文件中一个规则的构成是:

    Rule -->
        something
    

    -->标识,回车和制表符并不是必须的,只是个习惯而已。

    本质上,规则文件中规则编写时排列的顺序并不是很重要,因为上面说过,他们按照树状结构按需调用,并从起始规则开始,不过为了直观,这里从起始规则开始编写:

    Street -->
        SetUpTexture
        Split
    

    我先初始化纹理投影,并投射纹理,接着对街道进行分割,两个规则对应:

    // Texture road 
    SetUpTexture -->
        setupProjection(0, scope.zx, scope.sz/50, scope.sx/50)
        setupProjection(5, scope.zx, scope.sz/50, scope.sx/50)
        setupProjection(7, scope.zx, scope.sz/50, scope.sx/50)
        setupProjection(8, scope.zx, scope.sz/50, scope.sx/50)
        set(material.colormap, color_tex)
        projectUV(0)
        set(material.normalmap, normal_tex)
        projectUV(5)
        set(material.occlusionmap, occlusion_tex)
        projectUV(7)
        set(material.roughnessmap, roughness_tex)
        projectUV(8)
    
    // InnerRect    
    Split -->
        comp(f){
            all : InnerRect
        }
    

    setupProjection()可以初始化UV投影,第一个参数是纹理通道,CityEngine中有10个通道:


    可以用来完成大部分已知的效果。第二个参数是UV展开的方向,scope是一个基本图元的Bounding box,大部分时候是贴合图元的,即可以认为沿街道方向是x轴,穿过街道为z轴,指向上方是y轴(不一定),按照右旋规则去判断。很明显,这里沿zx展开UV。第三、四个参数是每个纹理单元的大小,设置为对应数量后,会根据shape整体的大小去平铺纹理单元UV。

    set方法用于设定属性,这里是material属性,还可以设定属性很多,可以查看帮助文档。material本身一共有10个成员,这在上面的图中显示了,对应10个纹理通道。

    projectUV方法可以将纹理映射到对应的纹理通道上。

    Split规则中的comp函数可以分割点、边、面、线面等,将它们分离为单个基本体或合并,主要是用来整体使用某一规则或单独使用某一规则。这个函数特别强大,参数也很多,这里就不一一距离。comp(f)即分割面,之后用{}括起,里面为每个分离的基本体执行某一操作,这里的all表明所有的分离面合并,执行相同的操作,在:后写明。

    InnerRect:

    InnerRect -->
        case geometry.nEdges < 5:
            case geometry.area > 200:
                innerRectangle(edge){
                    shape : Primitive |
                    remainder : NIL
                }
            else : NIL
        else : NIL
    

    InnerRect用来插入四边形。因为如果不太懂城市规划的话,设计出来的道路可能有些不太符合实际(外行人应该看不出来),还有其它因素,造成道路shape在连接node处的分段数可能特别多,且面积小,如:



    我之所以要插入四边形,就是要忽略这些区域的影响。

    因为执行过comp,所以每个分段的多边形就是一个独立的个体了,只不过all会让它们共享邻边,那么每个单独的区域或执行一个InnerRect,这就有点像图形学里每个片段会调用一次片元着色器一样。

    case不用多说,大家在许多语言中见过,只不过规则文件的语法中没有if-else结构,只有case-else结构,所以不要搞混了。内置的属性geometry.nEdges是当前几何体的边数量,小于5来筛选掉一些几何体,然后geometry.area是当前几何体的面积,200是我在自己的场景测试的一个值。不符合条件的使用NIL,这是不执行任何操作,或者剔除掉当前几何体的意思(如果该几何体之前没有做过纹理投射操作)。

    InnerRectangle函数可以在当前几何体中分段出最大的一个四边形,有两种参数,edge表明会根据最长边生成,scope表明会根据当前几何体的scope生成。生成完分化为两部分,shape是生成的最大四边形,remainder是剩余部分,注意,不同的部分间用|隔开。

    生成的最大四边形会执行Primitive规则:

    // Primtive to set road line    
    Primitive -->       
        alignScopeToGeometry(yUp, 0, 0) 
        t(0,0.005,0)
        SetRoadLine
        InsertSplit 
    

    因为生成的最大四边形scope轴向可能会有些混乱,尤其是我的街道本身的scope就有些不对头,所以这相当于是些修正命令。

    alignScopeToGeometry函数可以修改当前scope轴向,第一个参数选择指向上的轴向,这里是yUp,第二个参数是一个面索引,第三个参数是一个边索引,该边会成为新的x轴,之所以填0是我试出来的。

    之后向上平移一点,因为生成的四边形是用来贴上道路线的,不这么做的话可能会造成Z-Fighting的现象,深度测试混乱。

    接着设置道路线和插入路灯等模型。

    SetRoadLine:

    SetRoadLine -->
        case scope.sx > 20:
            case scope.sz >49:
                setupProjection(0, scope.zx, scope.sz/6, scope.sx/7)
                setupProjection(4, scope.zx, scope.sz/6, scope.sx/7)
                setupProjection(5, scope.zx, scope.sz/6, scope.sx/7)
                setupProjection(8, scope.zx, scope.sz/6, scope.sx/7)
                SplitTex    
            else :
                setupProjection(0, scope.zx, scope.sz, scope.sz)
                setupProjection(4, scope.zx, scope.sz, scope.sz)
                setupProjection(5, scope.zx, scope.sz, scope.sz)
                setupProjection(8, scope.zx, scope.sz, scope.sz)
                SplitTex
        else:NIL
    

    这里的一些条件语句主要是为了防止贴图颠倒,具体情况是街道长度和宽度定。

    设置道路线的一系列规则

    SplitTex -->
        split(x){
            3:TexCrossline|
            0.5:TexStopline|
            ~1:TextureRoadLine|
            0.5:TexStopline|
            3:TexCrossline
        }
        
    TexCrossline -->
        setupProjection(0, scope.zx, scope.sx/3, scope.sz)
        setupProjection(4, scope.zx, scope.sx/3, scope.sz)
        setupProjection(5, scope.zx, scope.sx/3, scope.sz)
        setupProjection(8, scope.zx, scope.sx/3, scope.sz)
        set(material.colormap, crossline_color)
        projectUV(0)
        set(material.opacitymap, crossline_opacity)
        projectUV(4)
        set(material.normalmap, crossline_normal)
        projectUV(5)
        set(material.roughnessmap, crossline_roughness)
        projectUV(8)
        
        
    TexStopline -->
        setupProjection(0, scope.xz, scope.sx/2, scope.sz)
        setupProjection(4, scope.xz, scope.sx/2, scope.sz)
        setupProjection(5, scope.xz, scope.sx/2, scope.sz)
        setupProjection(8, scope.xz, scope.sx/2, scope.sz)
        set(material.colormap, crossline_color)
        projectUV(0)
        set(material.opacitymap, crossline_opacity)
        projectUV(4)
        set(material.normalmap, crossline_normal)
        projectUV(5)
        set(material.roughnessmap, crossline_roughness)
        projectUV(8)
    
    TextureRoadLine -->
        set(material.colormap, roadline_color)
        projectUV(0)
        set(material.opacitymap, roadline_opacity)
        projectUV(4)
        set(material.normalmap, roadline_normal)
        projectUV(5)
        set(material.roughnessmap, roadline_roughness)
        projectUV(8)
    

    值得关注的是split函数,它会将当前几何体沿某一个轴向分段,这里是x轴,分配的数字或数值变量有3种操作:

    • 什么符号都不加的话,就是以meter为单位分割一段,不够就不分割或要剩下的,是个绝对的值。
    • 加上'前缀的话,值会与当前几何体的scope大小相乘,然后分割,然后其它和上面的一致。
    • 加上~前缀的话,就是一个相对量,它会根据前后分割的情况自行调整分割大小。

    然后是插入模型:

    InsertSplit -->
        split(z){
            1 : InsertL|
            ~1 : NIL |
            1: InsertR
        }
        
    InsertL -->
        10% :
            InsertLWithBusStop
        else :
            InsertLWithoutBusStop
            
    InsertR -->
        10% :
            InsertRWithBusStop
        else :
            InsertRWithoutBusStop
            
    InsertLWithBusStop -->
        split(x){
            {~5 :NIL|
            1 : InsertLObj|
            ~5 : NIL}* |
            8 : InsertLBusStop |
            {~5 :NIL|
            1 : InsertLObj|
            ~5 : NIL}* 
            
        }
        
    InsertRWithBusStop -->
        split(x){
            {~5 :NIL|
            1 : InsertRObj|
            ~5 : NIL}* |
            8 : InsertRBusStop |
            {~5 :NIL|
            1 : InsertRObj|
            ~5 : NIL}* 
            
        }
        
    InsertLWithoutBusStop -->
        split(x){
            {~5 :NIL|
            1 : InsertLObj|
            ~5 : NIL}*
        }
        
    InsertRWithoutBusStop -->
        split(x){
            {~5 :NIL|
            1 : InsertRObj|
            ~5 : NIL}*
        }
    InsertLBusStop -->
        t(0,0.2,-2.5)
        s(0.1*busStopWidth,0.05*busStopheight,0.04*busStopdepth)
        i(busStopOBJ)
        print(scope.tx)
        print(scope.ty)
        
    InsertRBusStop -->
        s(0.1*busStopWidth,0.05*busStopheight,0.04*busStopdepth)
        r(0,180,0)
        t(-6,0.2,-3.5)
        i(busStopOBJ)
    InsertLObj -->
        10% :
            t(-0.5,0.15,-1)
            s(0.15 * assetInfo(fireHydrantOBJ, sx), 
            0.15 * assetInfo(fireHydrantOBJ , sy), 
            0.15 * assetInfo(fireHydrantOBJ , sz))
            i(fireHydrantOBJ)
        else :
            r(0,-90,0)
            t(-0.5,0.15,0)
            s(0.02*width,0.02*height,0.02*depth)
            i(lampOBJ)
            
        
        
    InsertRObj -->
        10% :
            t(-1.5,0.15,1)
            s(0.15 * assetInfo(fireHydrantOBJ, sx), 
            0.15 * assetInfo(fireHydrantOBJ , sy), 
            0.15 * assetInfo(fireHydrantOBJ , sz))
            i(fireHydrantOBJ)
        else :
            r(0,90,0)
            t(-1.5,0.15,0)
            s(0.02*width,0.02*height,0.02*depth)
            i(lampOBJ)
    

    大部分操作都已经介绍过了。

    10%是一个概率操作,意思是当前几何体会有10%的概率执行之后的操作。必须和else搭配。

    i函数是一个插入模型函数,会将参数中路径对应的模型插入到当前几何体的位置上。

    rts分别是旋转,平移和缩放函数,不用多介绍。

    将规则文件赋予后,点击Generate就可以生成一个街道了:

    然后人行道,这里先给出规则文件:

    /**
     * File:    Sidewalk.cga
     * Created: 24 Apr 2020 15:36:45 GMT
     * Author:  Dragonboy
     */
    
    version "2019.0"
    
    attr sideWalkColorMap = "images/PavingStones038_2K-JPG/PavingStones038_2K_Color.jpg"
    attr sideWalkNormalMap = "images/PavingStones038_2K-JPG/PavingStones038_2K_Normal.jpg"
    attr sideWalkRoughMap = "images/PavingStones038_2K-JPG/PavingStones038_2K_Roughness.jpg"
    attr sideWalkAOMap = "images/PavingStones038_2K-JPG/PavingStones038_2K_AmbientOcclusion.jpg"
    attr nPoints = rand(2,3)
    attr nPoints2 = rand(1,3)
    attr trashCan = 
        33% : "assets/trash_can/trash can.obj"
        33% : "assets/trash_can/trash can2.obj"
        else : "assets/trash_can/trash can3.obj"
    
    
    Sidewalk -->
        setupProjection(0, scope.xz, 1.5, 1.5)
        setupProjection(5, scope.xz, 1.5, 1.5)
        setupProjection(7, scope.xz, 1.5, 1.5)
        setupProjection(8, scope.xz, 1.5, 1.5)
        extrude(0.15)
        SidewalkFacecade
        
    SidewalkFacecade -->
        comp(f){
            top : SidewalkFace |
            street.side : SideSidewalkTex
        }
        
    SidewalkFace -->
        SidewalkTex
        Scatter
        
        
    SidewalkTex -->
        set(material.colormap, sideWalkColorMap)
        set(material.normalmap, sideWalkNormalMap)
        set(material.occlusionmap, sideWalkAOMap)
        set(material.roughnessmap, sideWalkRoughMap)
        projectUV(0)
        projectUV(5)
        projectUV(7)
        projectUV(8)
        
        
    Scatter -->
        comp(f){
            all : InnerRect
        }
        
    InnerRect -->
        case geometry.nEdges < 5 :
            case geometry.area > 200:
                innerRectangle(edge){
                    shape : ScatterSplit |
                    remainder : NIL
                }
            else : NIL
        else : NIL
        
    ScatterSplit -->
        alignScopeToGeometry(yUp, 0, 0)
        split(z){
            0.4 : Rubbish |
            ~1 : Manhole
        }
    
    Rubbish -->
        scatter(surface, nPoints, uniform){TrashCan}
        
        
    TrashCan -->
        s(0.15 * assetInfo(trashCan, sx), 
        0.15 * assetInfo(trashCan, sy), 
        0.15 * assetInfo(trashCan, sz))
        i(trashCan)
        
    Manhole -->
        scatter(surface, nPoints2, uniform){ManholeInsert}
        
    ManholeInsert -->
        primitiveQuad(1,1)
        t(0,0.001,0)
        ManholeTex
    
    attr manholeColor = "images/ManholeCover005_2K-JPG/ManholeCover005_2K_Color.jpg"
    attr manholeOpacity = "images/ManholeCover005_2K-JPG/ManholeCover005_2K_Opacity.jpg"
    attr manholeNormal = "images/ManholeCover005_2K-JPG/ManholeCover005_2K_Normal.jpg"
    attr manholeAO = "images/ManholeCover005_2K-JPG/ManholeCover005_2K_AmbientOcclusion.jpg"
    attr manholeRough = "images/ManholeCover005_2K-JPG/ManholeCover005_2K_Roughness.jpg"
    attr manholeMetal = "images/ManholeCover005_2K-JPG/ManholeCover005_2K_Metalness.jpg"
        
    ManholeTex -->
        setupProjection(0, scope.xz, '1, '1)
        setupProjection(4, scope.xz, '1, '1)
        setupProjection(5, scope.xz, '1, '1)
        setupProjection(7, scope.xz, '1, '1)
        setupProjection(8, scope.xz, '1, '1)
        setupProjection(9, scope.xz, '1, '1)
        set(material.colormap, manholeColor)
        set(material.opacitymap, manholeOpacity)
        set(material.normalmap, manholeNormal)
        set(material.occlusionmap, manholeAO)
        set(material.roughnessmap, manholeRough)
        set(material.metallicmap, manholeMetal)
        projectUV(0)
        projectUV(4)
        projectUV(5)
        projectUV(7)
        projectUV(8)
        projectUV(9)
    
        
    SideSidewalkTex -->
        setupProjection(0, scope.xy, 1.5, 1.5)
        setupProjection(5, scope.xy, 1.5, 1.5)
        setupProjection(7, scope.xy, 1.5, 1.5)
        setupProjection(8, scope.xy, 1.5, 1.5)
        set(material.colormap, sideWalkColorMap)
        set(material.normalmap, sideWalkNormalMap)
        set(material.occlusionmap, sideWalkAOMap)
        set(material.roughnessmap, sideWalkRoughMap)
        projectUV(0)
        projectUV(5)
        projectUV(7)
        projectUV(8)
    

    需要注意的是scatter函数,它可以生成一些粒子,第一个参数是生成粒子的位置,有surface|volume|scope三种选项,这里选surface,毕竟是一个表面。第二个参数是生成点的数量,这里我使用rand函数传入了一个随机量。该函数有两种重载,如果选择的是uniform方式生成粒子的话(即粒子在几何体上是统一排布的,在概率学上来说就是分布函数很均匀),就是三个参数,第三个参数填uniform,如果选择gaussian方式生成粒子的话(粒子会靠近某一中心扩散,分布函数是高斯函数),就是四参数的,第四个参数填gaussian,第三个参数分布中心,有center|front|back|left|right|top|bottom。函数后的{}是每个粒子执行的操作。

    生成人行道:


    然后是生成建筑。为了方便,这里只介绍插入已有模型的建筑构建方式,如果是要手动创建的话,只要铭记分段操作即可。

    /**
     * File:    Bussiness.cga
     * Created: 11 Aug 2020 07:16:43 GMT
     * Author:  Dragonboy
     */
    
    version "2019.0"
    
    attr Building01 = "assets/Buildings/Building01/Building1.obj"
    attr Building02 = "assets/Buildings/Building02/Building2.obj"
    attr Building03 = "assets/Buildings/Building03/Building3.obj"
    attr Building04 = "assets/Buildings/Building04/Building4.obj"
    attr Building05 = "assets/Buildings/Building05/Building5.obj"
    //attr Building06 = "assets/Buildings/Building06/Building6.obj"
    attr Building07 = "assets/Buildings/Building07/Building7.obj"
    attr Building08 = "assets/Buildings/Building08/Building8.obj"
    attr Building09 = "assets/Buildings/Building09/Building9.obj"
    attr Building10 = "assets/Buildings/Building10/Building10.obj"
    attr Building11 = "assets/Buildings/Building11/Building11.obj"
    attr Building12 = "assets/Buildings/Building12/Building12.obj"
    attr Building13 = "assets/Buildings/Building13/Building13.obj"
    attr Building14 = "assets/Buildings/Building14/Building14.obj"
    attr Building15 = "assets/Buildings/Building15/Building15.obj"
    attr Building16 = "assets/Buildings/Building16/Building16.obj"
    attr Building17 = "assets/Buildings/Building17/Building17.obj"
    attr Building18 = "assets/Buildings/Building18/Building18.obj"
    attr Building19 = "assets/Buildings/Building19/Building19.obj"
    
    attr sideWalkColorMap = "images/PavingStones038_2K-JPG/PavingStones038_2K_Color.jpg"
    attr sideWalkNormalMap = "images/PavingStones038_2K-JPG/PavingStones038_2K_Normal.jpg"
    attr sideWalkRoughMap = "images/PavingStones038_2K-JPG/PavingStones038_2K_Roughness.jpg"
    attr sideWalkAOMap = "images/PavingStones038_2K-JPG/PavingStones038_2K_AmbientOcclusion.jpg"
    
    
    Bussiness -->
        FloorTex
        FloorUp
        
    FloorTex -->
        setupProjection(0, scope.xz, 1.5, 1.5)
        setupProjection(5, scope.xz, 1.5, 1.5)
        setupProjection(7, scope.xz, 1.5, 1.5)
        setupProjection(8, scope.xz, 1.5, 1.5)
        set(material.colormap, sideWalkColorMap)
        set(material.normalmap, sideWalkNormalMap)
        set(material.occlusionmap, sideWalkAOMap)
        set(material.roughnessmap, sideWalkRoughMap)
        projectUV(0)
        projectUV(5)
        projectUV(7)
        projectUV(8)
        
    FloorUp -->
        extrude(0.15)
        FaceCade
        
    FaceCade -->
        comp(f){
            top:
            InnerRect
        }
        
    InnerRect -->
        innerRectangle(scope){
            shape:BuildingInsert
        }
    
    BuildingInsert -->
        5.5%:
            alignScopeToGeometry(yUp, any, longest)
            i(Building01)
            cleanupGeometry(all, 0)
        5.5%:
            alignScopeToGeometry(yUp, any, longest)
            i(Building02)
            cleanupGeometry(all, 0)
        5.5%:
            alignScopeToGeometry(yUp, any, longest)
            i(Building03)
            cleanupGeometry(all, 0)
        5.5%:
            alignScopeToGeometry(yUp, any, longest)
            i(Building04)
            cleanupGeometry(all, 0)
        5.5%:
            alignScopeToGeometry(yUp, any, longest)
            i(Building05)
            cleanupGeometry(all, 0)
        5.5%:
            alignScopeToGeometry(yUp, any, longest)
            i(Building07)
            cleanupGeometry(all, 0)
        5.5%:
            alignScopeToGeometry(yUp, any, longest)
            i(Building08)
            cleanupGeometry(all, 0)
        5.5%:
            alignScopeToGeometry(yUp, any, longest)
            i(Building09)
            cleanupGeometry(all, 0)
        5.5%:
            alignScopeToGeometry(yUp, any, longest)
            i(Building10)
            cleanupGeometry(all, 0)
        5.5%:
            alignScopeToGeometry(yUp, any, longest)
            i(Building11)
            cleanupGeometry(all, 0)
        5.5%:
            alignScopeToGeometry(yUp, any, longest)
            i(Building12)
            cleanupGeometry(all, 0)
        5.5%:
            alignScopeToGeometry(yUp, any, longest)
            i(Building13)
            cleanupGeometry(all, 0)
        5.5%:
            alignScopeToGeometry(yUp, any, longest)
            i(Building14)
            cleanupGeometry(all, 0)
        5.5%:
            alignScopeToGeometry(yUp, any, longest)
            i(Building15)
            cleanupGeometry(all, 0)
        5.5%:
            alignScopeToGeometry(yUp, any, longest)
            i(Building16)
            cleanupGeometry(all, 0)
        5.5%:
            alignScopeToGeometry(yUp, any, longest)
            i(Building17)
            cleanupGeometry(all, 0)
        5.5%:
            alignScopeToGeometry(yUp, any, longest)
            i(Building18)
            cleanupGeometry(all, 0)
        else:
            alignScopeToGeometry(yUp, any, longest)
            i(Building19)
            cleanupGeometry(all, 0)
    

    cleanupGeometry可以优化模型,第一个参数是优化的组件,可以是vertices|edges|faces|all,第二个参数是优化的程度,0是最严格的,1最松散,[0,1]介于二者之间。

    没有什么技术含量,我这里提前准备了其它软件里得来的模型和纹理资源,然后生成即可:


    有几点要注意是:

    • 生成街道或城市的时候,建议是生成一部分然后导出一部分,因为在模型构建期间,CityEngine会往C盘写入大量的文件,尤其是分段数多起来的时候,所以生成一部分然后导出,删除,等待缓存文件删除,接着重复操作。当然,如果有一个超大的系统盘当我没说。这也是我先不直接在CityEngine中构建建筑的原因,因为模型一复杂的话,光是生成一个建筑就把我C盘吃满了,目前我暂时没有找到其它解决方法。
    • 建议合理利用内置的Python脚本,毕竟这么大个城市,手动搞真的费手费眼。而且上面的方法也可以用Python批量来弄。

    下一节介绍一下导出选项,以及导入到Unity后一些材质的重设定和shader的编写修正。

    以上。

    相关文章

      网友评论

          本文标题:CityEngine中街道和建筑的构建

          本文链接:https://www.haomeiwen.com/subject/seewdktx.html