Houdini : 稲妻 Lightning Bolts
稲妻を制作しようと思います。
電撃びりびりっすね。
サンプルを探してみたのですが、このサイトにありました。
Approaches to Lightning in Houdini
Tool
・Houdini 13
大まかな内容
・Nullを二つ用意
・L-System の最初、最後の頂点の位置をNullに合わせる
vopsopノード
上流が、Nullの位置
下流が、LSystemのポイントの位置です
- それぞれの距離を算出
- スケール
- それぞれのベクトルのノーマルを算出
- Alignでそろえる
と言った所がポイントのようです
Python Script
スクリプト化してみました
#Create Lightning Bolts
#Ver0.1
#2014 11 20
import hou
#Global
gPosA = (3,0,0)
gPosB = (0,0,0)
isGrow = True
isRender = True
gCicle = 30
gLineWidth = 0.02
#L-SystemParam
gLSParamContext = "F+-"
gLSParamPremise = "A"
gLSParamRule = ['A=-F+A:0.45',"A=+F-A:0.45",'A=~(30)[--"!A]A:0.05','A=[++"!A]A:0.05']
def setVopSopParameters(n,null1,null2):
g = n.node("global1")
o = n.node("output1")
#Add Nodes
add1 = n.createNode("add")
ali1 = n.createNode("align")
div1 = n.createNode("divide")
imp1 = n.createNode("importattrib")
imp2 = n.createNode("importattrib")
len1 = n.createNode("length")
len2 = n.createNode("length")
mul1 = n.createNode("multiply")
mul2 = n.createNode("multiply")
nor1 = n.createNode("normalize")
nor2 = n.createNode("normalize")
pos1 = n.createNode("parameter","pos1")
pos2 = n.createNode("parameter","pos2")
ptnum = n.createNode("constant","ptnum")
subc1 = n.createNode("subconst")
sub1 = n.createNode("subtract")
sub3 = n.createNode("subtract")
sub4 = n.createNode("subtract")
#Connect Nodes
o.setInput(0,add1,0)
add1.setInput(0,pos2,0)
add1.setInput(1,mul1,0)
mul1.setInput(0,mul2,0)
mul1.setInput(1,ali1,0)
mul2.setInput(0,sub3,0)
mul2.setInput(1,div1,0)
div1.setInput(0,len1,0)
div1.setInput(1,len2,0)
len1.setInput(0,sub4,0)
len2.setInput(0,sub1,0)
sub4.setInput(0,pos1,0)
sub4.setInput(1,pos2,0)
ali1.setInput(0,nor1,0)
ali1.setInput(1,nor2,0)
nor2.setInput(0,sub4,0)
nor1.setInput(0,sub1,0)
sub1.setInput(0,imp2,1)
sub1.setInput(1,imp1,1)
imp2.setInput(2,subc1,0)
imp1.setInput(2,ptnum,0)
subc1.setInput(0,g,11)
sub3.setInput(0,g,0)
sub3.setInput(1,imp1,1)
#SetParameter
ptnum.parm("consttype").set(1) #integer
ptnum.parm("constname").set("ptnum")
imp1.parm("attrib").set("P")
imp2.parm("attrib").set("P")
pos1.parm("parmname").set("Pos1")
pos1.parm("parmlabel").set("Pos1")
pos1.parm("parmtype").set(6) #Vector
pos2.parm("parmname").set("Pos2")
pos2.parm("parmlabel").set("Pos2")
pos2.parm("parmtype").set(6) #Vector
#setVopSopParam
n.parm("Pos11").setExpression('origin("","' + null1.path() + '","TX")')
n.parm("Pos12").setExpression('origin("","' + null1.path() + '","TY")')
n.parm("Pos13").setExpression('origin("","' + null1.path() + '","TZ")')
n.parm("Pos21").setExpression('origin("","' + null2.path() + '","TX")')
n.parm("Pos22").setExpression('origin("","' + null2.path() + '","TY")')
n.parm("Pos23").setExpression('origin("","' + null2.path() + '","TZ")')
return True
def setLSystemParameters(lSys):
lSys.parm("generations").set(30)
lSys.parm("randseed").setExpression('int(($F - 1)/ ' + str(gCicle) + ")" )
#int( ($F - 1) / 5 )
if(isGrow):
lSys.parm("pointwidth").set(1)
lSys.parm("stepinit").set(0.03)
lSys.parm("stepscale").set(0.9)
lSys.parm("angleinit").set(13)
lSys.parm("anglescale").set(0.9)
lSys.parm("context").set(gLSParamContext)
lSys.parm("premise").set(gLSParamPremise)
lSys.parm("rule1").set(gLSParamRule[0])
lSys.parm("rule2").set(gLSParamRule[1])
lSys.parm("rule3").set(gLSParamRule[2])
lSys.parm("rule4").set(gLSParamRule[3])
return True;
def createBrastOp(n):
folderName = "folder"
gNode = n.createOutputNode("blast","grow")
p = hou.FloatParmTemplate("grow_dist","Grow Dist",1)
folder = hou.FolderParmTemplate("myfolder","myfolder")
folder.addParmTemplate( p )
group = gNode.parmTemplateGroup()
group.append(folder)
gNode.setParmTemplateGroup(group)
#"@arc>`ch('./grow_dist')`"
gNode.parm("group").set("@arc>`ch('./grow_dist')`")
gNode.parm("grouptype").set(3)
gNode.parm("grow_dist").setExpression( "($F -1) %" + str(gCicle) +" * " + str( 1.0 / (gCicle) ) + "+" + str( + 1.0 / (gCicle) ) )
gNode.setDisplayFlag(True)
return True
def setRenderNode(vopsopNode):
node = vopsopNode
if(isGrow):
node = node.outputs()[0]
node = node.createOutputNode("ends")
node = node.createOutputNode("attribcreate::2.0")
node.parm("name1").set("width")
node.parm("value1v1").set(gLineWidth)
return True
def main():
obj = hou.node("/obj")
#nullを作成する
null1 = obj.createNode("null","null_to")
null2 = obj.createNode("null","null_from")
null1.setPosition([1,2])
null2.setPosition([3,2])
null1.parmTuple("t").set(gPosA)
null2.parmTuple("t").set(gPosB)
#Lsystemを作成する
geo = obj.createNode("geo","lightningBolts")
geo.node("file1").destroy()
lSys = geo.createNode("lsystem")
setLSystemParameters(lSys)
geo.setPosition([2,0])
#Vop Sopを作成する
vopsop = lSys.createOutputNode("vopsop")
vopsop.setDisplayFlag(True)
setVopSopParameters(vopsop,null1,null2)
vopsop.setPosition([0,-1])
#GrowChack
if(isGrow):
createBrastOp(vopsop)
#Render
if(isRender):
setRenderNode(vopsop)
return True;
main()
感想
とりあえず作っただけで、制御らしいことは出来てないですね。
L-Systemも良くわかってないし
Houdini Procedural Animation Techniques
という有償チュートリアルのは、
ポリメッシュに生成するので都合が良さそうです。