|
|
||
|
||
|
|
||
| Страница 1 из 31 |
ВведениеВ уроке из частиц создаются снеговик и другие фигуры. Для получения результата использованы система частиц PF Source (поток частиц) и язык программирования MAXSсript. Рис. 1. Бозон – частица Бога? (источник Яндекс. Картинки: бозон) Весьма эффективно могут быть представлены частицами и работы из бисера (рис. 2). Рис. 2. Мы в ответе за тех, кого приручаем (источник Яндекс. Картинки: бисероплетение) MAXSсript не только предоставляет дополнительные возможности при работе с системами частиц, но и укоряет процесс получения результата. Кроме того, код компактно описывает моделируемую сцену, что весьма часто позволяет оценить генерируемые им образы, не имея под рукой 3ds Max.
Получение результата обеспечивает следующий код: -- Block 1delete $* animationRange = interval 0f 100f ng = 10.0 t = text text:"Some T" size:100 rotation:((eulerAngles -ng ng 0) as quat) addModifier t (extrude amount:0.5) cl = cylinder radius:1 height:40 hide #(t, cl) -- Block 2 pF = PF_Source enable_Particles:true quantity_Viewport:100 \ rotation:((eulerAngles -ng (180 - ng) 0) as quat) isSelected:on particleFlow.BeginEdit() opBth = birth emit_Start:0 emit_Stop:0 type:0 amount:600 opPO = position_Object emitter_Objects:#(t) delete:on location:2 \ apart_Placement:on apart_Distance:8 opSI = shape_Instance shape_Object:cl opSP = scaleParticles type:2 constrain_Scale:off opRt = rotation direction:2 euler_X:ng euler_Y:-ng opRP = renderParticles type:2 opDP = displayParticles color:red type:6 evn = event() particleFlow.EndEdit() evn.AppendAction opBth evn.AppendAction opPO evn.AppendAction opSI evn.AppendAction opSP evn.AppendAction opRt evn.AppendAction opDP pF.AppendAction opRP pF.AppendInitialActionList evn -- Block 3 with redraw off ( max tool animmode; set animate on sliderTime = 0f; opSP.Z_Scale_Factor = 0 sliderTime = 50f; opSP.Z_Scale_Factor = 100 sliderTime = 100f; opSP.Z_Scale_Factor = 50 max tool animmode; set animate off sliderTime = 0f ) max modify mode max tool zoomExtents playAnimation() Последний кадр анимации приведен на рис. 3. Рис. 3. Частицы на ребрах текста В основной программе три блока. Рис. 4. Источник частиц в задаче Текст из частиц Чтобы открыть диалог Particle View (обозреватель систем частиц) и просмотреть состав источника, достаточно выбрать источник частиц PF Source, перейти на вкладку Modify командного окна и нажать на кнопку Particle View группы Setup сопутствующего диалога. Выбор источника и переход на вкладку Modify предусмотрены в приведенном выше коде (свойство источника IsSelected = on и выполнена команда max modify mode). Запуск программыЗапуск программы выполняется в 3ds Max в следующем порядке:
СнежинкаРеализуется следующая сцена: четыре потока частиц в виде разноцветных снежинок пересекаются в одной точке и порождают сферический источник снежинок, медленно удаляющихся от его центра (рис. 5). Рис. 5. Большая снежинка Задача решается по следующей схеме:
Рис. 6. Образец материала для частиц Эту схему реализует следующий код: fn prps = (delete $* sliderTime = 0f animationRange = interval 0f 500f viewport.SetLayout #layout_4 viewport.ActiveViewport = 4 max vpt persp user viewport.SetGridVisibility 4 false backgroundColor = black ) fn mkPF pRt ps pRtCn snwFlk mlt = ( local pF, opDP rt = (eulerAngles pRt[1] pRt[2] 0) as quat rtCn = (eulerAngles pRtCn[1] pRtCn[2] 0) as quat h = 7 cone radius1:1 radius2:2 height:-h rotation:rtCn pos:ps wireColor:white pF = PF_Source enable_Particles:true emitter_Type:0 rotation:rt \ pos:ps quantity_Viewport:100 show_Logo:off show_Emitter:off particleFlow.BeginEdit() opBth = birth emit_Start:0 emit_Stop:(350 * 160) type:1 rate:5 opPI = position_Icon location:0 opSI = shape_Instance shape_Object:snwFlk opRt = rotation direction:0 opSpn = spin rate:360 variation:0 direction:0 opSpd = speed speed:30 direction:0 opSO = script_Operator proceed_Script:" on channelsUsed pCont do pCont.UsePosition = true on proceed pCont do ( nP = pCont.NumParticles() for k = 1 to nP do ( pCont.ParticleIndex = k p = pCont.ParticlePosition if p[3] < 0 do pCont.DeleteParticle k ) )" opMFrq = material_Frequency assigned_Material:mlt \ mtl_ID_1:10 mtl_ID_2:10 mtl_ID_3:10 mtl_ID_4:10 \ mtl_ID_5:10 mtl_ID_6:10 mtl_ID_7:10 mtl_ID_8:10 \ mtl_ID_9:10 mtl_ID_10:10 show_In_Viewport:on opDP = displayParticles type:6 opRP = renderParticles type:2 evn = event() particleFlow.EndEdit() evn.AppendAction opBth evn.AppendAction opPI evn.AppendAction opSI evn.AppendAction opRt evn.AppendAction opSpn evn.AppendAction opSpd evn.AppendAction opSO evn.AppendAction opMFrq evn.AppendAction opDP pF.AppendAction opRP pF.AppendInitialActionList evn ) -- Block 1 prps() arrStd = for k = 1 to 10 collect \ standard diffuse:[random 100 200, random 100 200, random 100 200] showInViewport:on mlt = multimaterial numsubs:10 materialList:arrStd meditMaterials[1] = mlt hdr = hedra radius:2 family:4 -- Star 2 hdr2 = hedra radius:3 family:3 -- Star 1 lght = sphere radius:10 segs:8 wireColor:[200, 200, 200] hide #(hdr, hdr2, lght) zP = 100 arrPs = #([-100, 0, zP], [0, 100, zP], [100, 0, zP], [0, -100, zP]) ng = 45 arrRt = #([0, ng], [ng, 0], [0, -ng], [-ng, 0]) ngCn = -135 arrRtCn = #([0, ngCn], [ngCn, 0], [0, -ngCn], [-ngCn, 0]) -- Block 2 for k = 1 to 4 do mkPF arrRt[k] arrPs[k] arrRtCn[k] hdr mlt -- Block 3 pF = PF_Source enable_Particles:true quantity_Viewport:100 show_Logo:off show_Emitter:off particleFlow.BeginEdit() opBth = birth emit_Start:(150 * 160) emit_Stop:(500 * 160) type:1 rate:25 opPO = position_Object emitter_Objects:#(lght) location:1 opSI = shape_Instance shape_Object:hdr2 opRt = rotation direction:0 opSpn = spin rate:360 variation:0 direction:0 opSpd = speed speed:1.0 direction:1 -- Icon center out opMFrq = material_Frequency assigned_Material:mlt \ mtl_ID_1:10 mtl_ID_2:10 mtl_ID_3:10 mtl_ID_4:10 \ mtl_ID_5:10 mtl_ID_6:10 mtl_ID_7:10 mtl_ID_8:10 \ mtl_ID_9:10 mtl_ID_10:10 show_In_Viewport:on opDP = displayParticles type:6 opRP = renderParticles type:2 evn = event() particleFlow.EndEdit() evn.AppendAction opBth evn.AppendAction opPO evn.AppendAction opSI evn.AppendAction opRt evn.AppendAction opSpn evn.AppendAction opSpd evn.AppendAction opMFrq evn.AppendAction opDP pF.AppendAction opRP pF.AppendInitialActionList evn max tool zoomExtents playAnimation() В основной программе три блока. СнеговикВ примере из частичек (снежинок) лепится снеговик (рис. 7). Рис. 7. Сделан из снежинок Как и в предшествующем примере, создаются лучи из частиц, которые направляются к центрам сферических частей снеговика. Однако на этот раз частицы, достигшие центра, не покидают сцену, а тормозятся и помещаются на поверхности снеговика.
Приведенную последовательность действий реализует следующий код: fn prps = (delete $* sliderTime = 0f animationRange = interval 0f 1000f timeConfiguration.PlaybackLoop = false viewport.SetLayout #layout_4 viewport.ActiveViewport = 4 max vpt persp user viewport.SetGridVisibility 4 false backgroundColor = white ) fn mkPF ps snwFlk mlt scrpt = ( local pF sph = sphere radius:2 pos:ps wireColor:white pF = PF_Source enable_Particles:true emitter_Type:0 \ pos:ps quantity_Viewport:100 show_Logo:off show_Emitter:off particleFlow.BeginEdit() opBth = birth emit_Start:0 emit_Stop:(900 * 160) type:1 rate:6 opPO = position_Object emitter_Objects:#(sph) location:0 opSI = shape_Instance shape_Object:snwFlk opRt = rotation direction:0 opSpn = spin rate:360 variation:0 direction:0 opSO = script_Operator proceed_Script:scrpt opMFrq = material_Frequency assigned_Material:mlt \ mtl_ID_1:10 mtl_ID_2:10 mtl_ID_3:10 mtl_ID_4:10 \ mtl_ID_5:10 mtl_ID_6:10 mtl_ID_7:10 mtl_ID_8:10 \ mtl_ID_9:10 mtl_ID_10:10 show_In_Viewport:on opDP = displayParticles type:6 opRP = renderParticles type:2 evn = event() particleFlow.EndEdit() evn.AppendAction opBth evn.AppendAction opPO evn.AppendAction opSI evn.AppendAction opRt evn.AppendAction opSpn evn.AppendAction opSO evn.AppendAction opMFrq evn.AppendAction opDP pF.AppendAction opRP pF.AppendInitialActionList evn ) -- Block 1 prps() arrStd = for k = 1 to 10 collect \ standard diffuse:[random 100 200, random 100 200, random 100 200] showInViewport:on mlt = multimaterial numsubs:10 materialList:arrStd hR = 2 hdr = hedra radius:hR family:3 hide hdr global dt = 300 * 160 global tm2 = dt, tm3 = tm2 + dt global r = 10, r2 = 6, r3 = 3 global d2 = r + r2 + 2 * hR, d3 = d2 + r2 + r3 + 2 * hR scrpt0 = " on channelsUsed pCont do ( pCont.UsePosition = true pCont.UseSpeed = true pCont.UseAge = true ) " scrpt = scrpt0 + " on proceed pCont do ( nP = pCont.NumParticles() seed nP if nP > 0 then ( pCont.ParticleIndex = 1 sTm = pCont.ParticleAge as integer ) else sTm = 0 pSpd = [0.01, 0, -0.01] pSpd2 = [0.01, 0, -0.01 + d2 * 0.0001] pSpd3 = [0.01, 0, -0.01 + d3 * 0.0001] for k = 1 to nP do ( pCont.ParticleIndex = k p = pCont.ParticlePosition case of ( (sTm > tm3 and p[3] > 99): pCont.ParticleSpeed = pSpd3 (sTm > tm2 and p[3] > 99): pCont.ParticleSpeed = pSpd2 (p[3] > 99): pCont.ParticleSpeed = pSpd ) if p[1] > 0 do ( pSpd = pCont.ParticleSpeed case pSpd of ( pSpd2: (rc = r2; z = d2) pSpd3: (rc = r3; z = d3) default: (rc = r; z = 0) ) pCont.ParticleSpeed = [0, 0, 0] pNg = random [-90, 0] [90, 360] sn = rc * sin pNg[2] pCont.ParticlePosition = [sn * cos pNg[1], sn * sin pNg[1], z + rc * cos pNg[2]] ) ) )" scrpt2 = scrpt0 + " on proceed pCont do ( nP = pCont.NumParticles() seed nP if nP > 0 then ( pCont.ParticleIndex = 1 sTm = pCont.ParticleAge as integer ) else sTm = 0 pSpd = [0, 0.01, -0.01] pSpd2 = [0, 0.01, -0.01 + d2 * 0.0001] pSpd3 = [0, 0.01, -0.01 + d3 * 0.0001] for k = 1 to nP do ( pCont.ParticleIndex = k p = pCont.ParticlePosition case of ( (sTm > tm3 and p[3] > 99): pCont.ParticleSpeed = pSpd3 (sTm > tm2 and p[3] > 99): pCont.ParticleSpeed = pSpd2 (p[3] > 99): pCont.ParticleSpeed = pSpd ) if p[2] > 0 do ( pSpd = pCont.ParticleSpeed case pSpd of ( pSpd2: (rc = r2; z = d2) pSpd3: (rc = r3; z = d3) default: (rc = r; z = 0) ) pCont.ParticleSpeed = [0, 0, 0] pNg = random [-90, 0] [90, 360] sn = rc * sin pNg[2] pCont.ParticlePosition = [sn * cos pNg[1], sn * sin pNg[1], z + rc * cos pNg[2]] ) ) )" scrpt3 = scrpt0 + " on proceed pCont do ( nP = pCont.NumParticles() seed nP if nP > 0 then ( pCont.ParticleIndex = 1 sTm = pCont.ParticleAge as integer ) else sTm = 0 pSpd = [-0.01, 0, -0.01] pSpd2 = [-0.01, 0, -0.01 + d2 * 0.0001] pSpd3 = [-0.01, 0, -0.01 + d3 * 0.0001] for k = 1 to nP do ( pCont.ParticleIndex = k p = pCont.ParticlePosition case of ( (sTm > tm3 and p[3] > 99): pCont.ParticleSpeed = pSpd3 (sTm > tm2 and p[3] > 99): pCont.ParticleSpeed = pSpd2 (p[3] > 99): pCont.ParticleSpeed = pSpd ) if p[1] < 0 do ( pSpd = pCont.ParticleSpeed case pSpd of ( pSpd2: (rc = r2; z = d2) pSpd3: (rc = r3; z = d3) default: (rc = r; z = 0) ) pCont.ParticleSpeed = [0, 0, 0] pNg = random [-90, 0] [90, 360] sn = rc * sin pNg[2] pCont.ParticlePosition = [sn * cos pNg[1], sn * sin pNg[1], z + rc * cos pNg[2]] ) ) )" scrpt4 = scrpt0 + " on proceed pCont do ( nP = pCont.NumParticles() seed nP if nP > 0 then ( pCont.ParticleIndex = 1 sTm = pCont.ParticleAge as integer ) else sTm = 0 pSpd = [0, -0.01, -0.01] pSpd2 = [0, -0.01, -0.01 + d2 * 0.0001] pSpd3 = [0, -0.01, -0.01 + d3 * 0.0001] for k = 1 to nP do ( pCont.ParticleIndex = k p = pCont.ParticlePosition case of ( (sTm > tm3 and p[3] > 99): pCont.ParticleSpeed = pSpd3 (sTm > tm2 and p[3] > 99): pCont.ParticleSpeed = pSpd2 (p[3] > 99): pCont.ParticleSpeed = pSpd ) if p[2] < 0 do ( pSpd = pCont.ParticleSpeed case pSpd of ( pSpd2: (rc = r2; z = d2) pSpd3: (rc = r3; z = d3) default: (rc = r; z = 0) ) pCont.ParticleSpeed = [0, 0, 0] pNg = random [-90, 0] [90, 360] sn = rc * sin pNg[2] pCont.ParticlePosition = [sn * cos pNg[1], sn * sin pNg[1], z + rc * cos pNg[2]] ) ) )" -- Block 2 mkPF [-100, 0, 100] hdr mlt scrpt mkPF [0, -100, 100] hdr mlt scrpt2 -- mkPF [100, 0, 100] hdr mlt scrpt3 -- mkPF [0, 100, 100] hdr mlt scrpt4 -- Block 3 std = standard diffuse:red showInViewport:on std2 = standard diffuse:black showInViewport:on r32 = r3 + hR cn = cone radius1:0.75 radius2:0 height:r32 material:std rotate cn -90 [0, 1, 0] cn2 = cone radius1:(0.75 * r32) radius2:(0.5 * r32) height:(r32 + hR) material:std sp = sphere radius:0.5 material:std2 sp2 = copy sp animate on ( at time 0 ( cn.Pos = cn2.Pos = sp.Pos = sp2.Pos = [0, 0, 0] std.Opacity = std2.Opacity = 0 ) at time 900 ( cn.Pos = cn2.Pos = sp.Pos = sp2.Pos = [0, 0, 0] std.Opacity = std2.Opacity = 0 ) at time 1000 ( cn.Pos = [-r32, 0, d3] cn2.Pos = [0, 0, d3 + (0.75 * r32)] pNg = [-25, -75] sn = r32 * sin pNg[2] sp.Pos = [sn * cos pNg[1], sn * sin pNg[1], d3 + r32 * cos pNg[2]] sp2.Pos = [sn * cos pNg[1], -sn * sin pNg[1], d3 + r32 * cos pNg[2]] std.Opacity = std2.Opacity = 100 ) ) max tool zoomExtents playAnimation() В основной программе три блока. on channelsUsed pCont do ( pCont.UsePosition = true pCont.UseSpeed = true pCont.UseAge = true ) " К этому фрагменту присоединяется код proceed-обработчика. В каждом proceed-обработчике определяется число частиц nP источника, меняется затравка датчика случайных чисел (seed nP), вычисляется текущее время sTm в тиках (sTm берется равным возрасту первой частицы) и задаются значения векторов скорости частиц pSpd, pSpd2 и pSp3 для каждой части анимационного интервала. Значения углов тригонометрических функций этого уравнения определяются при помощи датчика случайных чисел. Рис. 8. 335-й кадр анимации Рис. 9. 635-й кадр анимации Число строк, потраченных на запись скриптов scrpt, scrpt2, scrpt3 и scrpt4 операторов Script_Operator, можно сократить, выделив совпадающие части в отдельные куски scrpt0, scrpt01, scrpt02 и scrpt03: scrpt0 = "on channelsUsed pCont do ( pCont.UsePosition = true pCont.UseSpeed = true pCont.UseAge = true ) " scrpt01= " on proceed pCont do ( nP = pCont.NumParticles() seed nP if nP > 0 then ( pCont.ParticleIndex = 1 sTm = pCont.ParticleAge as integer ) else sTm = 0 " scrpt02= " for k = 1 to nP do ( pCont.ParticleIndex = k p = pCont.ParticlePosition case of ( (sTm > tm3 and p[3] > 99): pCont.ParticleSpeed = pSpd3 (sTm > tm2 and p[3] > 99): pCont.ParticleSpeed = pSpd2 (p[3] > 99): pCont.ParticleSpeed = pSpd ) " scrpt03= " pSpd = pCont.ParticleSpeed case pSpd of ( pSpd2: (rc = r2; z = d2) pSpd3: (rc = r3; z = d3) default: (rc = r; z = 0) ) pCont.ParticleSpeed = [0, 0, 0] pNg = random [-90, 0] [90, 360] sn = rc * sin pNg[2] pCont.ParticlePosition = [sn * cos pNg[1], sn * sin pNg[1], z + rc * cos pNg[2]] ) ) )" scrpt = scrpt0 + scrpt01 + " pSpd = [0.01, 0, -0.01] pSpd2 = [0.01, 0, -0.01 + d2 * 0.0001] pSpd3 = [0.01, 0, -0.01 + d3 * 0.0001]" + scrpt02 + "if p[1] > 0 do (" + scrpt03 scrpt2 = scrpt0 + scrpt01 + " pSpd = [0, 0.01, -0.01] pSpd2 = [0, 0.01, -0.01 + d2 * 0.0001] pSpd3 = [0, 0.01, -0.01 + d3 * 0.0001]" + scrpt02 + "if p[2] > 0 do (" + scrpt03 scrpt3 = scrpt0 + scrpt01 + " pSpd = [-0.01, 0, -0.01] pSpd2 = [-0.01, 0, -0.01 + d2 * 0.0001] pSpd3 = [-0.01, 0, -0.01 + d3 * 0.0001]" + scrpt02 + "if p[1] < 0 do (" + scrpt03 scrpt4 = scrpt0 + scrpt01 + " pSpd = [0, -0.01, -0.01] pSpd2 = [0, -0.01, -0.01 + d2 * 0.0001] pSpd3 = [0, -0.01, -0.01 + d3 * 0.0001]" + scrpt02 + "if p[2] < 0 do (" + scrpt03 ЗаключениеБольшое число частиц и связанные с ними вычисления нередко требуют существенных вычислительных ресурсов. В таких случаях отладку решений следует выполнять на малом числе частиц и небольших анимационных интервалах. Кроме того, желательно наметить несколько путей решения задачи и выбрать среди них наименее ресурсоемкий. Источники
|