注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

豆芽兵的生存探索

因,记录。留下历史,看到未来...

 
 
 

日志

 
 

RealFlow翻译教程——露珠项链(七)  

2011-12-29 21:50:11|  分类: RealFlow探索 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
RealFlow翻译教程——露珠项链(七) - 豆芽兵 - 豆芽兵的天地

      小蜘蛛长的很难看,八条腿,还有六只眼睛,皮肤灰灰的,上面还有细长的毛毛。在这个草丛里,大家都看不起它。就连它自己也觉得自己太丑了。它不敢跟有着红色黑点外套漂亮的瓢虫小姐,还有会唱歌的纺织娘小姐一起玩。就连和毛毛虫一起,也不敢,至少人家以后会变成好看的蝴蝶呢,自己会变成什么呢?,还是丑陋的八脚小怪物。小蜘蛛,只能偷偷的的躲在角落里织网,一圈又一圈。
   因为小蜘蛛这么长时间练习和它那种坚韧的精神,它的网到是织的很漂亮。整整齐齐,又细又有弹性,还能很容易就粘到很多像蚊子,苍蝇这样的坏家伙。虽然小蜘蛛的网织的已经这么好了,可从来没人夸奖过它,它得到的只有嘲笑。
    一天早上小蜘蛛,又向往常一样,看自己织好的网。哇,上面挂了好多露珠,晶晶亮,好漂亮。小蜘蛛心想,赶紧把收起来吧,不然太阳出来,露珠就飞走了。嘿,到了晚上,在阴凉处的露珠真的还在。小蜘蛛小心的碰了碰,叮的,弹一下。没有掉下去。原来经过一天的时间,没有晒到太阳的露珠,已经变成真的珍珠一样的东西,永远不会飞走了。小蜘蛛可高兴了,它想这要是戴在身上多好看啊!它小心的拿了一串挂在自己身上,真的很好看。可小蜘蛛想,这么好看的东西戴在我身上真是可惜了。
  对,送给瓢虫小姐和纺织娘小姐她们吧。两们小姐,收到这个礼物后,可高兴了。还抱着小蜘蛛亲了一下,小蜘蛛也很开心。可是,啊呀,因为一共就做了三串,两串给了,瓢虫小姐和纺织娘小姐,还有一串不小心被自己弄丢了。毛毛虫就没有了。小蜘蛛答应了毛毛虫,一定会在它变成好看的蝴蝶时,把那一串做好的。
   恩,学习了下面内容,你就可以帮助小蜘蛛喽!

 路径转换(Path Converter)器

  这个免费的工具,可以把一个模型的动画路径转换成序列粒子。你可以把粒子调·节成稠密的或稀疏的在这路径上。粒子路径就是物体运动轨迹。另一个应用是模拟生长效果,例如根的生长。

当然,路径转换器,需要动画路径,但不管这路径是在RealFlow或其它三维软件创建都是可以的。这个脚本支持多选,你可以用来同时模拟多个结点。你可以选择整个坐标(XYZ)或只是用一个轴(x,yz)。

路径转换器脚本的用户界面快捷直观,并能返回消息还可以自动重命名。教程里还附有帮助手册。

RealFlow翻译教程——露珠项链(七) - 豆芽兵 - 豆芽兵的天地
RealFlow翻译教程——露珠项链(七) - 豆芽兵 - 豆芽兵的天地

这个脚本基本思想是:动画曲线中的分量值(如,Y值)转换成RealFlow 粒子位移(Position.这种转换可用于可视化运动轨迹,创建抽像图案或模拟生长效果,例如根的生长。

请注意这个脚本需要一定编程语言知识,如果你想理解每步思路,你需要熟悉Python基本概念。这个教程不是Python入门,但您可以在RealFlow 5手册中第 20 和 21 章 找到用许多实例的指导。(也可以等待后续译者翻译的关于RealFlow Python的书更新)

动画曲线

在计算机图形学中,有三种基本方法做动画

1.key动画

2.动力学(解算)动画

3.表达式

第一种方法在确定时间点记录关键帧,软件会进行插值。你可以根据需要加很多关键帧,然后进行Baking(烘焙)。Baking就是,在每一帧给一个或更多参数加上关键帧。因此最后的关键帧数目取决于调整的帧速率。

动力学动画,也称为解算动画,不需要key关键帧,因为全部动画都是自动计算。这个方法局限性是如果你要播放,就要全部再解算一遍。想绕开这一问题,程序就要记录物体位移输出到外部文件,使它可以重复播放。另外可以bake动力学模拟出的动画,重新创建关键帧动画。

RealFlow翻译教程——露珠项链(七) - 豆芽兵 - 豆芽兵的天地

1:该脚本将很复杂的动画转换成粒子

RealFlow也支持第三种方法:表达式利用公式和计算出的值给对像创建动画。从表达式创建出的动画也能bake成动画曲线。

无论使用哪种方法,你都需要动画帧,否则不能提取位移值。key和曲线发挥重要作用,RealFlow中曲线编辑器很容易编辑修改它们。你也能用类似或更先进的方法在三维软件中操作。路径转换器脚本,我们用曲线和key来创建基本粒子,表示对像的动画路径。输出单个帧,用Python是很容易的,但如果曲线是基于很少的帧生成,就会得到很粗糙的分辨率。

要增加分辨率,我们需要更多数量点,最好的方法是设置一个可定义参数。

分析动画曲线

第一步就是分析动画曲线,然后找出合适的Pyhon函数访问所有这些信息。如上所述,曲线基于关键帧,程序自动插值。对这个任务,动画程序有不同插值方法,像 TCB,Bezier,linearstep.这些方法决定了曲线的光滑度,还用来创建加速或减速效果。如果你想知道更多关于Realflow的插值方法,请看用户手册,17章 “The Curve Editor Tool Bar(g.Node Type).而对我们的目的来说,插值方法是不重要的。Python获取动画曲线函数是:

getParameterCurve(property name)

property name”一定要替换成合适的参数,例如 rotationposition,但也能用别的参数,例如viscosity speed.在我们例子中它是position。相比于speed属性,position值是所谓的矢量,总是用x,yz三个分量组成。

三个分量合在一起就是对像在三维空间位置。所以为了完整描述对像的位置,我需要三条曲线。为了完整描述,脚本还得知道是哪个对像使用的,例如:

animatedObject = scene.getObject(object name)

object name”是在你的场景中所有有动画的对像节点名。有动画的参数是显示成橘黄色。如果对像是“Sphere01”,你就要这样写:

animatedObject = scene.getObject("Sphere01")

完整代码是:

positionCurveX = animatedObject.getParameterCurve("Position.X")

positionCurveY = animatedObject.getParameterCurve("Position.Y")

positionCurveZ = animatedObject.getParameterCurve("Position.Z")

RealFlow翻译教程——露珠项链(七) - 豆芽兵 - 豆芽兵的天地

2.RealFlow曲线编辑器里的x,y轴动画关键帧

getKeys()

上面这个函数不是取决于x,yz,仅是追加在“getParameterCurve()”语句后。我们得到一个所有曲线关键帧( key)的列表(list)

keyListX = positionCurveX.getKeys()

keyListY = positionCurveY.getKeys()

keyListZ = positionCurveZ.getKeys()

到目前为止,我们只有特定时间的点,想要连续的曲线,我们需要更多的采样点。幸运的是,RealFlowPython接口提供了一个函数,可输出一条曲线每一个点的值。此函数已被用到position曲线:

currentPosX = positionCurveX.evaluate(time)

currentPosY = positionCurveY.evaluate(time)

currentPosZ = positionCurveZ.evaluate(time)

time”,实际上是当前处理的曲线的位置,当我们有足够的时间步骤,我们可以创建一个可定制插值率的粒子表示形式。问题是我们为什么仍然要key,尽管我们有方法可以找出曲线上任何一点的值?答案是,key决定了动画开始结束时间。这意味着转换将只能执行每个position动画曲线起始和结束点之间。这个脚本总是需要三个position曲线,如果条件不满足就会终止。你可以创建一个动画曲线,只简单的加一个点。

所有与position相关的操作都要有x,y,z,下面的例子说明只是针对x轴的操作模式。

现在我们有动画曲线,可以继续key。因为有三条曲线x,y,z我们得找出单个关键帧给每条曲线,这个功能的函数是: 

查找开始和结束值

为了得到开始和结束值,我们要找出key的时间,列出下表:

timeListX = []

timeList Y = ...

for keyX in keyListX:

  timeX = keyX.getTime()

  timeListX.append(timeX)

for keyY in ...

然后我们列出:

timeListX.sort()

最低的值是入口,出口是最高的值:

startTimeX = timeListX[0]

stopTimeX = timeListX[len(timeListX) - 1]

从开始和结束值我们再一次找出输入的最小和最大值,这决定了了动画最大范围。

要做到这一点,我们已经把所有的最大值和最小值追加到一个新的列表中,并加以比较:

minStartTime = 0

maxStartTime = 0

for startTime in startList:

  if (startTime <= minStartTime):

   minStartTime = startTime

for stopTime in stopList:

  if (stopTime >= maxStartTime):

   maxStartTime = stopTime

所有的动画仅是这两个值的不同:

maxAnimationLength = maxStartTime - minStartTime

创建一个可定制的分辨率

现在,我们已经计算整个长度。可以很容易地划分为一个采样点曲线:

numberOfSamplePoints = int(maxAnimationLength * (1 / interval) + 1)

这个变量“numberOfSamplePoints”将用作终止值循环。当循环运行时,我们用“evaluate()”语句,在给出的时间找出position值:

posListX = []

posListY = ...

for i in range(0, numberOfSamplePoints):

  currentPosX = positionCurveX.evaluate(minStartTime + i * interval)

  posListX.append(currentPosX)

  posListY.append ...

组合position向量

最后我们要组合position矢量,从我们计算的值中,然后画出粒子。因此我们需要另一个循环:

nullVeloctiy = Vector.new(0, 0, 0)

for j in range(0, numberOfSamplePoints):

  posX = posListX[j]

  posY = ...

  posVector = Vector.new(posX, posY, posZ)

  emitter.addParticle(posVector, nullVelocity)

这些操作得计算x,yz。矢量“nullVelocity”是必须的,因为粒子总是需要两个参数:positionvelocity.因为我们不想粒子移动,速度必须是0.

下面你会看到一个mesh好的例子。

RealFlow翻译教程——露珠项链(七) - 豆芽兵 - 豆芽兵的天地

最终脚本

上面收集到的这些信息,可以创建一个工作用的脚本了。创建一个批处理(batch)脚本或事件脚本,直接执行计算。目前batch脚本是不错的。你可以用在别的三维软件做的动画,导出SD文件,导入到RealFlow,运行“Path Converter”脚本。

如果你想同时转换多个对像,你只要加上两个部分:

1.获取所有结点。可以用getObject()语句,或文件选取。

2.遍历对像。

此教程提供的脚本(rfs文件),已经提供了一个文件选取器,一个GUI,创建必须的发射器和保持所有以前制作的转换。这个脚本也能检测你场景中是否有对像。还可以反馈出脚本当前状态和可能的错误。

 

仔细看看,找出脚本如何支持这些元素进行制作和排列。

RealFlow翻译教程——露珠项链(七) - 豆芽兵 - 豆芽兵的天地

3:脚本GUI

如何处理动力学动画?

至今我们仅是用基于关键帧动画曲线,但动力学动画没有关键帧。所有相关position数据存储在外部缓存文件中。要使用动力学动画,我们需要脚本能转换当前每帧position成关键帧。这个过程称作“烘焙”(baking.)

为了能烘焙,我们需要的第二个脚本必须接受直接模拟过程,因此我们得在“Frame Post”创建事件脚本。先看下,RF 5手册脚本副本,在21.05章“Recording Animation Keys”。这脚本思路符合我们的需要,但你要写出自己的东西,因为这有助于提高你的Python技能。这里还介绍几个技巧:

1.Recording Animation Keys”脚本是分为三个部分。第一部分初始化记录过程,和定义对像。这里仅是一个单个对像,但如果你想用多个结点,这部分要改变下

2.如果你想用单个对像,你实际上什么也不用做。但用多个结点,SimulationPost部分可以在FramePost执行,否则你会在这列表,遇到有个别值跳出循环的麻烦。你也需要一个循环,考虑每个对像正在做的过程。

3.记录过程完成后,你可以运行在“Path Converter”脚本,做为批处理或直接在“Simulation Post”加入。

动画路径转换

RF_toolfactory首页的视频展示了用动力学动画进行的路径转换效果。

为达到这个效果,必须应用另一个脚本。这个已经清空发射器,保证每一帧开始粒子("FramePre".用脚本更新发射器是非常容易的:

emitter = scene.getEmitter(emitter name)

particleList = emitter.getParticles()

for particle in particleList:

  emitter.removeParticle(particle.getId())

此外路径转换器(Path Converter)脚本不能做为一个批处理脚本运行了。它必须是一个事件脚本“FramePost”你得要根据新需要定义变量。GUI部分也要移动到“SimulationPre”。因此,动画路径转换器需要的部分是:

1.初始脚本 定义对像,必须是全局变量

2.脚本刷新发射器

3.关键帧记录脚本给动力学动画烘焙

4.路径转换脚本

RealFlow翻译教程——露珠项链(七) - 豆芽兵 - 豆芽兵的天地

4:三个事件脚本,是程序必须的

根据使用的分辨率(interval”间隔)对像数量,和动画长度,所有过程要很长时间,但结果真的很好。最终,你要mesh这个结构,然后到就可以去渲染了。



一句话总结:哈哈,本教程并不是教你做露珠而是讲解了路径转换器的使用。不过吗,你会了上面,加上之前的教程,露珠项链,你一定是可以做出来的。



--------------------------------下面是路径转器代码,你也可以到源网站下载更方便-----------------------------------------------------------------


# -------------------------------------------------------------
# PathConverter
# Python script for RealFlow 4+
# Written by RF_toolfactory/Thomas Schlick
# Distrbuted under GNU Public License Agreement
#
# Use this script at your own risk!
#
# Do not remove this copyright header.
# -------------------------------------------------------------


# -------------------------------------------------------------
# Function "initGUI" - Initialize the GUI
# -------------------------------------------------------------

def initGUI():

    objectNodes   = scene.getObjects()
    axisSelection = ["XYZ","X","Y","Z"]
    objectNames   = []

    # Check for existing object nodes and open a node picker.
    # If there's at least one object, the GUI will be displayed.
    if (len(objectNodes) == 0):
        scene.message("\n\n--- Please add at least one moveable object.\n")
    else:
        objNodePicker = GUINodesPickerDialog.new()
        objNodeSelection = objNodePicker.show(TYPE_OBJECT)
        if (len(objNodeSelection) == 0):
            scene.message("\n\n--- Please select at least one object.\n")

        else:
            for objectNode in objNodeSelection:
                objectName = objectNode.getName()
                objectNames.append(objectName)

            # Create the GUI and assign the global variables.
            form = GUIFormDialog.new()
            form.addFloatField("Interval", 0.001)
            form.addListField("Select Axis Setup", axisSelection,0)

            if (form.show() == GUI_DIALOG_ACCEPTED):
                interval  = form.getFieldValue("Interval")
                axisIndex = form.getFieldValue("Select Axis Setup")

                selectedAxis = axisSelection[axisIndex]

                scene.setGlobalVariableValue("objNodeSelection",objNodeSelection)
                scene.setGlobalVariableValue("interval",interval)
                scene.setGlobalVariableValue("selectedAxis",selectedAxis)
                scene.message("\n\n--- Your selection contains "+str(len(objectNames))+" object(s).\n")

                emitterRenamer()

            else:
                scene.message("\n\n--- Action aborted by user.\n")


# -------------------------------------------------------------
# Function "emitterRenamer" - Create and rename the emitter
# -------------------------------------------------------------

def emitterRenamer():

    nodeCounter = 0

    # Check for existing emitters, create a new container if necessary and rename it.
    # We want to keep all previous conversions, so a new container will always be created.
    emitter = scene.addEmitter("Container")
    availableEmitters = scene.getEmitters()
    searchString = "ConvertedParticles"
    for emitterNode in availableEmitters:
        nodeCounter += 1
        emitterName = emitterNode.getName()
        string = emitterName.find(searchString)
        if (string != 1):
            newEmitterName = "ConvertedParticles"+str(nodeCounter)

    # Rename the emitter and print out its name.
    emitter.setName(newEmitterName)
    usedEmitterName = emitter.getName()
    scene.message("\n\n--- Using \""+usedEmitterName+"\" for this conversion pass.\n")
    scene.setGlobalVariableValue("emitter", emitter)

    pathConverter()


# -------------------------------------------------------------
# Function "pathConverter" - Start the path conversion
# -------------------------------------------------------------

def pathConverter():

    # Get globals and make locals
    emitter      = scene.getGlobalVariableValue("emitter")
    nodesGroup   = scene.getGlobalVariableValue("objNodeSelection")
    interval     = scene.getGlobalVariableValue("interval")
    selectedAxis = scene.getGlobalVariableValue("selectedAxis")

    for animatedObject in nodesGroup:

        # Define all mecessary lists...
        timeListX     = []
        timeListY     = []
        timeListZ     = []
        startList     = []
        stopList      = []
        posListX      = []
        posListY      = []
        posListZ      = []

        # ...and the other parameters
        nullVelocity  = Vector.new(0,0,0)
        minStartTime  = 0.0
        maxStopTime   = 0.0
        counter       = 1

        # Get the animation curves for the positions...
        positionCurveX = animatedObject.getParameterCurve("Position.X")
        positionCurveY = animatedObject.getParameterCurve("Position.Y")
        positionCurveZ = animatedObject.getParameterCurve("Position.Z")

        # ...and their appropriate keys
        keyListX = positionCurveX.getKeys()
        keyListY = positionCurveY.getKeys()
        keyListZ = positionCurveZ.getKeys()

        if (len(keyListX) == 0 or len(keyListY) == 0 or len(keyListZ) == 0):
            scene.message("\n\n--- "+str(animatedObject.getName())+"has no animation curve!\n")
            return()

        # Find start and stop times of the animation curves
        for keyX in keyListX:
            timeX = keyX.getTime()
            timeListX.append(timeX)

        for keyY in keyListY:
            timeY = keyY.getTime()
            timeListY.append(timeY)

        for keyZ in keyListZ:
            timeZ = keyZ.getTime()
            timeListZ.append(timeZ)

        # Sort the lists to find the min/max values
        timeListX.sort()
        timeListY.sort()       
        timeListY.sort()

        # Now, min and max values are the first and the last elements in the lists
        startTimeX = timeListX[0]; startList.append(startTimeX)
        startTimeY = timeListY[0]; startList.append(startTimeY)
        startTimeZ = timeListZ[0]; startList.append(startTimeZ)

        stopTimeX  = timeListX[len(timeListX)-1]; stopList.append(stopTimeX)
        stopTimeY  = timeListY[len(timeListY)-1]; stopList.append(stopTimeY)
        stopTimeZ  = timeListZ[len(timeListZ)-1]; stopList.append(stopTimeZ)

        # Determine minimum start time and maximum start time = total length of the animation path
        for startTime in startList:
            if (startTime <= minStartTime):
                minStartTime = startTime

        for stopTime in stopList:
            if (stopTime >= maxStopTime):
                maxStopTime = stopTime

        # Find the maximum length of the animation path (= domain)
        maxAnimationLength = maxStopTime - minStartTime
        minPosX = positionCurveX.evaluate(startTimeX)
        maxPosX = positionCurveX.evaluate(stopTimeX)

        # The path's "resolution" depends on the user's "interval" value
        numberOfSamplePoints = int(maxAnimationLength * (1/interval) + 1)
        scene.message("\n\n--- Sample Points "+str(animatedObject.getName())+": "+str(numberOfSamplePoints))
        counter += 1

        # Loop through the XYZ curve values within the domain save everything to lists
        for i in range (0, numberOfSamplePoints):
            currentPosX = positionCurveX.evaluate(minStartTime + i * interval)
            posListX.append(currentPosX)

            currentPosY = positionCurveY.evaluate(minStartTime + i * interval)
            posListY.append(currentPosY)

            currentPosZ = positionCurveZ.evaluate(minStartTime + i * interval)
            posListZ.append(currentPosZ)

        # Create the position vectors based on the XYZ values
        for j in range(0, numberOfSamplePoints):
            posX = posListX[j]
            posY = posListY[j]
            posZ = posListZ[j]

            # Extract the individual axis based on the user's selection
            if (selectedAxis == "XYZ"):
                posVector = Vector.new(posX,posY,posZ)
            if (selectedAxis == "X"):
                posVector = Vector.new(posX,0,0)
            if (selectedAxis == "Y"):
                posVector = Vector.new(0,posY,0)
            if (selectedAxis == "Z"):
                posVector = Vector.new(0,0,posZ)

            emitter.addParticle(posVector, nullVelocity)

        emitter.export()

        scene.message("\n\n--- Finished!\n")


if (__name__ == "RealFlow"):
    initGUI()

--------------------------------------把上面内容复制到一个txt,然后后缀改成.rfs格式,就可以直接载入RealFlow使用了。

  评论这张
 
阅读(4606)| 评论(3)
推荐

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017