Рассматривается вариант создания слайд-шоу средствами MaxScript– встроенного языка программирования 3dsMax.В приложении генерируется avi-файл, в который затем добавляется мелодия (используется доступный видеоредактор, например Movavi.Video.Suite.12.0.Portable).
Используются два вида изображений:
Число фотографий (jpg-файлов) произвольно, число заставок (тоже jpg-файлов) равно пяти.
Все фотографии и заставки помещаются в одну папку. Шаблон имен фотографий – krstnii.jpg; шаблон имен заставок – fldj.jpg, где i – это целое число от 1 до количества фотографий, а j; – это целое число от 1 до 5.
Изображения используются для создания текстуры, которая, в свою очередь, употребляется в качестве диффузионной карты стандартного материала. Материал назначается плоскости.
Размеры заставок и фотографий произвольны, но близкие к квадрату, поскольку плоскость с заставкой – это квадрат (все плоскости с заставками имеют одинаковые размеры – hmm×hmm).
Размеры (h и w) плоскости с фотографией определяются в функции dfnC посредством следующего кода:
function dfnC fNm hm z = (
-- Открываем файл с фотографией и определяем ее размеры (высоту h и ширину w)
bm = openBitMap fNm
h = bm.height
w = bm.width
-- Определяем коэффициент С, употребляемый далее для определения
-- размеров h и w плоскости для текущей фотографии
c = 1.0
h2 = h
if h2> hm then
while h2 > hm do (
c = c - 0.01
h2 = h * c
)
else
while h2 < hm do (
c = c + 0.01
h2 = h * c
)
c2 = 0.94
-- Вычисляем размеры h и w плоскости для текущей фотографии
h = c2 * h * c
w = c2 * w * c
-- Создаем плоскость с найденными выше размерами
pln = plane length:h width:w pos:[0, 0, z]
-- Создаем стандартный материал с диффузионной картой dMp
dMp = bitmapTexture fileName:fNm
mtrl = standard diffuseMap:dMp showInViewport:true diffuseMapEnable:true opacity:100
#(mtrl, pln)
)
Если высота и ширина фотографии существенно различаются, то для определения размеров плоскости потребуется иной код, проверяющий не только высоту фотографии, но и ее ширину:
…
-- Выполняется после проверки высоты фотографии
cс = 1.0
w2 = w
if w2> hm then
while w2 > hm do (
cc = cc - 0.01
w2 = w * cc
)
else
while w2< hm do (
cc = cc + 0.01
w2 = w * cc
)
c= amin c cc
-- Вычисляем размеры h и w плоскости для текущей фотографии
h = c2 * h * c
w = c2 * w * c
-- Создаем плоскость с найденными выше размерами
pln = plane length:h width:w pos:[0, 0, z]
…
Плоскости с изображениями располагаются одна под другой в виде многослойного пирога (меняется z-координата базовой точки); перед каждой фотографией находится заставка (рис. 1).
Рис. 1. Пирог из фотографий и заставок
При создании этого пирога фотографии выбираются случайным образом из общего массива фотографий. Исключение составляют первая, центральная и последняя фотографии слайд-шоу: на первом месте всегда находится образ, хранимый файлом krstn1.jpg, на последнем – образ из предпоследней фотографии, а в центре – образ из последней фотографии. Например, если показывается всего 45 фотографий, то в центре слайд-шоу будет фотография krstn45.jpg, а в его конце – krstn44.jpg.
Фотографии, кроме последней, поворачиваются относительно своего центра на угол, случайно выбираемый из диапазона [-5°, 5°] (рис. 2).
Рис. 2. Пирог из фотографий и заставок (вид сверху)
Порядок воспроизведения пирога следующий (используется вид сверху):
Использованы следующие способы исчезновения заставки:
После исчезновения заставки открывается очередная фотография.
На переднем плане слайд-шоу наблюдаются разноцветные частицы сферической формы (рис. 3).
Рис. 3. Частицы слайд-шоу
В качестве эмиттера выступает плоскость, в которой удалено несколько граней (рис. 4).
Рис. 4. Эмиттер частиц - плоскость без центральных граней
Источник частиц создается функцией mkPF (см. приводимый ниже код).
-- Масштабирует фотографию и возвращает массив с материалом и плоскостью,
-- на которую будет наложена текстура с фотографией
function dfnC fNm hm z = (
bm = openBitMap fNm
h = bm.height
w = bm.width
c = 1.0
h2 = h
if h2 > hm then
while h2 > hm do (
c = c - 0.01
h2 = h * c
)
else
while h2 < hm do (
c = c + 0.01
h2 = h * c
)
c2 = 0.94
h = c2 * h * c
w = c2 * w * c
-- Текстура на основе растрового файла с фотографией
dMp = bitmapTexture fileName:fNm
mtrl = standard diffuseMap:dMp showInViewport:true diffuseMapEnable:true opacity:100
pln = plane length:h width:w pos:[0, 0, z]
#(mtrl, pln)
)
-- Вспомогательная функция, употребляемая при создании источника частиц
function apActFn pF arrOps hasRP = (
local evn
evn = event()
arrCnt = if hasRP then arrOps.Count - 1 else arrOps.Count
for k = 1 to arrCnt do evn.AppendAction arrOps[k]
if hasRP do pF.AppendAction arrOps[arrCnt + 1]
particleFlow.EndEdit()
pF.AppendInitialActionList evn
return evn
)
-- Создает источник частиц
function mkPF ts te spd tp clr h2 w2 ps drctn rt wndFrc = (
qt = eulerToQuat (eulerAngles 0 -90 0)
wnd = wind strength:wndFrc decay:0 turbulence:0.1 frequency:0.08 scale:1 rotation:qt
mlMt = multimaterial()
mlMt.MaterialList[1].Diffuse = (color 255 255 255) --gray --green
mlMt.MaterialList[2].Diffuse = yellow
mlMt.MaterialList[3].Diffuse = red
mlMt.MaterialList[4].Diffuse = white
mlMt.MaterialList[5].Diffuse = blue
bjct = sphere radius:4
hide bjct
-- Используем плоскость в качестве эмиттера
pln = plane length:h2 width:w2 pos:ps lengthsegs:1 widthsegs:12 isSelected:on
convertToPoly pln
subobjectLevel = 4
pln.EditablePoly.SetSelection #Face #{4..9}
-- Удаляем из плоскости (эмиттера) центральные грани
pln.EditablePoly.delete #Face
subobjectLevel = 0
pF = PF_Source enable_Particles:true quantity_Viewport:100 Emitter_Type:0 Emitter_Length:h2 Emitter_Width:w2 pos:ps
particleFlow.BeginEdit()
opBth = birth emit_Start:ts emit_Stop:te amount:12 type:1 rate:1
opPI = Position_Object Emitter_Objects:#(pln)
opSpd = speed speed:spd direction:drctn Random_Seed:(random 1000 30000) reverse:on
opRt = rotation direction:2 Euler_X:15 Euler_Y:15
opSpn = Spin direction:2 SpinRate:180
--'3D_Type': 16 - star
opSh = Shape_Instance Shape_Object:bjct variation:50
opMFrq = material_Frequency assigned_Material:mlMt mtl_ID_1:20 mtl_ID_2:20 mtl_ID_3:20 mtl_ID_4:20 mtl_ID_5:20
opFc = force force_Space_Warps:#(wnd)
opDP = displayParticles color:clr type:6
-- color 255 255 0
opRP = renderParticles type:2
td = 0.99 * te
tstAT = age_Test test_Type:1 condition_Type:1 test_Value:td variation:0 test_Type:0
apActFn pF #(opBth, opPI, opSpd, opRt, opSpn, opSh, opMFrq, opDP, opFc, tstAT, opRP) true
particleFlow.BeginEdit()
opDlt2 = DeleteParticles type:0
opDP2 = displayParticles color:clr type:6
evn2 = apActFn pF #(opDlt2, opDP2) false
tstAT.SetNextActionList evn2 tstAT
rotate pF (angleaxis rt [1, 0, 0])
)
-- Главный код создания слайд-шоу
delete $*
pth = "C:\\100byte_\\krstn\\"
xt = ".jpg"
arrMtrls2 = #()
-- Массив с материалами для заставок
for k = 1 to 6 do (
dMp = bitmapTexture fileName:(pth + "fld" + (k as string) + xt)
mtrl = standard diffuseMap:dMp showInViewport:true diffuseMapEnable:true
append arrMtrls2 mtrl
)
-- Массив с именами файлов с фотографиями
arrFls = #()
tst = false
--tst = true
cnt = 38
hm = 240 -- 180
hmm = 1.15 * hm
if tst do cnt = 14
for k = 1 to cnt do append arrFls ("krstn" + k as string)
-- Массив с номерами еще неиспользованных фотографий
arrSd = for k = 2 to cnt - 2 collect k
-- Массивы с материалами (текстурами) и плоскостями для фотографий
arrMtrls = #()
arrPln = #()
-- Массив с плоскостями для заставок
arrPln2 = #()
d = 90.0
dt2 = int (d / 2.62)
dt = int (d - dt2)
cnt2 = int (cnt / 2)
z = 2 * (cnt + 2)
k3 = 0
-- Формируем вышеназванные массивы
for k = 1 to cnt do (
ndx = 0
-- Номер используемой заставки
k3 += 1
if k3 == 6 do k3 = 1
-- Находим индекс элемента массива arrSd
-- Первой показываем фотографию с номером 1,
-- последней - с номером cnt - 1, где cnt - это число фотографий,
-- в середине просмотра наблюдаем фотографию с номером cnt
k2 = if k == 1 then
1
else if k == cnt then
cnt - 1
else if k == cnt2 then
cnt
else (
-- Определяем индекс случайным образом
ndx = random 1 arrSd.count
-- Возвращаем номер текущей фотографии
arrSd[ndx]
)
-- Удаляем номер текущей (использованной) фотографии
if ndx > 0 do deleteItem arrSd ndx
arrWH = dfnC (pth + arrFls[k2] + xt) hm (z - 1)
append arrMtrls arrWH[1]
append arrPln arrWH[2]
append arrPln2 (plane length:hmm width:hmm pos:[0, 0, z])
arrPln2[k].material = arrMtrls2[k3]
arrPln[k].material = arrMtrls[k]
--meditMaterials[k] = arrMtrls[k]
ngl = if k2 == cnt then 0 else random -5.0 5.0
-- Слегка поворачиваем плоскость с фотографией
rotate arrPln[k] (angleaxis ngl [0, 0, 1])
z -= 2
)
k3 += 1
if k3 == 6 do k3 = 1
-- Добавляем последнюю заставку-фотографию (последний элемент слайд-шоу)
append arrPln2 (plane length:hmm width:hmm pos:[0, 0, z])
arrPln2[cnt + 1].material = arrMtrls2[k3]
z -= 2
append arrPln2 (plane length:hmm width:hmm pos:[0, 0, z])
arrPln2[cnt + 2].material = arrMtrls2[6]
hmm2 = 0.5 * hmm
ngl = 90
t = 0
-- Реализуем один и рассмотренных выше способов устранения заставки
for k = 1 to cnt + 1 do (
z2 = arrPln2[k].pos[3]
if k == 5 or k == 24 do arrPln2[k].pivot = [-hmm2, -hmm2, z2]
if k == 6 or k == 25 do arrPln2[k].pivot = [hmm2, -hmm2, z2]
if k == 7 or k == 26 do arrPln2[k].pivot = [-hmm2, hmm2, z2]
if k == 8 or k == 27 do arrPln2[k].pivot = [hmm2, hmm2, z2]
animate on (
if k == 1 or k == 20 do (
at time t (arrPln2[k].pos = [0, 0, z2]; arrPln2[k].length = hmm; arrPln2[k].width = hmm)
t += dt2
at time t arrPln2[k].pos = [-hmm, 0, z2]
)
if k == 2 or k == 21 do (
at time t (arrPln2[k].pos = [0, 0, z2]; arrPln2[k].length = hmm; arrPln2[k].width = hmm)
t += dt2
at time t arrPln2[k].pos = [hmm, 0, z2]
)
if k == 3 or k == 22 do (
at time t (arrPln2[k].pos = [0, 0, z2]; arrPln2[k].length = hmm; arrPln2[k].width = hmm)
t += dt2
at time t arrPln2[k].pos = [0, hmm, z2]
)
if k == 4 or k == 23 do (
at time t (arrPln2[k].pos = [0, 0, z2]; arrPln2[k].length = hmm; arrPln2[k].width = hmm)
t += dt2
at time t arrPln2[k].pos = [0, -hmm, z2]
)
if k == 5 or k == 24 do (
at time t (rotate arrPln2[k] (angleaxis 0.001 [0, 0, 1]); arrPln2[k].length = hmm; arrPln2[k].width = hmm)
t += dt2
at time t (rotate arrPln2[k] (angleaxis ngl [0, 0, 1]))
)
if k == 6 or k == 25 do (
at time t (rotate arrPln2[k] (angleaxis -0.001 [0, 0, 1]); arrPln2[k].length = hmm; arrPln2[k].width = hmm)
t += dt2
at time t (rotate arrPln2[k] (angleaxis -ngl [0, 0, 1]))
)
if k == 7 or k == 26 do (
at time t (rotate arrPln2[k] (angleaxis 0.001 [0, 0, 1]); arrPln2[k].length = hmm; arrPln2[k].width = hmm)
t += dt2
at time t (rotate arrPln2[k] (angleaxis ngl [0, 0, 1]))
)
if k == 8 or k == 27 do (
at time t (rotate arrPln2[k] (angleaxis -0.001 [0, 0, 1]); arrPln2[k].length = hmm; arrPln2[k].width = hmm)
t += dt2
at time t (rotate arrPln2[k] (angleaxis -ngl [0, 0, 1]))
)
if k == 9 or k == 28 do (
at time t (arrPln2[k].pos = [0, 0, z2]; arrPln2[k].length = hmm; arrPln2[k].width = hmm)
t += dt2
at time t arrPln2[k].pos = [-hmm, -hmm, z2]
)
if k == 10 or k == 29 do (
at time t (arrPln2[k].pos = [0, 0, z2]; arrPln2[k].length = hmm; arrPln2[k].width = hmm)
t += dt2
at time t arrPln2[k].pos = [hmm, hmm, z2]
)
if k == 11 or k == 30 do (
at time t (arrPln2[k].pos = [0, 0, z2]; arrPln2[k].length = hmm; arrPln2[k].width = hmm)
t += dt2
at time t arrPln2[k].pos = [hmm, -hmm, z2]
)
if k == 12 or k == 31 do (
at time t (arrPln2[k].pos = [0, 0, z2]; arrPln2[k].length = hmm; arrPln2[k].width = hmm)
t += dt2
at time t arrPln2[k].pos = [-hmm, hmm, z2]
)
if k == 13 or k == 32 do (
at time t (arrPln2[k].width = hmm; arrPln2[k].pos = [0, 0, z2])
t += dt2
at time t (arrPln2[k].width = 0; arrPln2[k].pos = [-hmm2, 0, z2])
)
if k == 14 or k == 33 do (
at time t (arrPln2[k].width = hmm; arrPln2[k].pos = [0, 0, z2])
t += dt2
at time t (arrPln2[k].width = 0; arrPln2[k].pos = [hmm2, 0, z2])
)
if k == 15 or k == 34 do (
at time t (arrPln2[k].length = hmm; arrPln2[k].pos = [0, 0, z2])
t += dt2
at time t (arrPln2[k].length = 0; arrPln2[k].pos = [0, hmm2, z2])
)
if k == 16 or k == 35 do (
at time t (arrPln2[k].length = hmm; arrPln2[k].pos = [0, 0, z2])
t += dt2
at time t (arrPln2[k].length = 0; arrPln2[k].pos = [0, -hmm2, z2])
)
if k == 17 or k == 36 do (
at time t (arrPln2[k].width = hmm)
t += dt2
at time t (arrPln2[k].width = 0)
)
if k == 18 or k == 37 do (
at time t (arrPln2[k].length = hmm)
t += dt2
at time t (arrPln2[k].length = 0)
)
if k == 19 or k == 38 do (
at time t (arrPln2[k].width = hmm; arrPln2[k].length = hmm)
t += dt2
at time t (arrPln2[k].width = 0; arrPln2[k].length = 0)
)
if k == cnt + 1 do (
at time t (arrPln2[k].width = hmm; arrPln2[k].length = hmm)
t += dt2
at time t (arrPln2[k].width = 0; arrPln2[k].length = 0)
)
t += dt
ps2 = arrPln2[k].pos
at time t (
if k <= cnt do arrPln[k].pos = [0, 0, arrPln[k].pos[3]]
if k < 13 or k > 19 and k < 32 then
(arrPln2[k].length = 0; arrPln2[k].width = 0)
else
arrPln2[k].pos = [ps2[1], ps2[2], z2]
)
t += 1
ps2 = arrPln2[k].pos
at time t (
if k <= cnt do arrPln[k].pos = [0, 0, -2 * z]
if k < 13 or k > 19 and k < 32 then
h = 0
else
arrPln2[k].pos = [ps2[1], ps2[2], -2 * z2]
)
)
)
--drctn = 0 - Along Icon Arrow
--drctn = 2 - Icon Arrow Out
--drctn = 3 - Random 3D
--drctn = 4 - Random Horizontal
mkPF 0 (t * 160) 15 7 (color 255 255 255) 2 hmm [0, -155, 3 * cnt] 0 -90 0.00
animationRange = interval 0 t
-- Просматриваем анимацию
playAnimation()
backgroundColor = black
useEnvironmentMap = off
Рассмотрен один из возможных вариантов организации слайд-шоу. В этом варианте создается пирог из заставок и фотографий и используются различные способы удаление заставок. При этом время присутствия заставки и фотографии на экране находятся в пропорции золотого сечения. В процентном округленном значении золотое сечение - это деление какой-либо величины в отношении 38% и 62%.
Результат с 38-ю фотографиями показан ниже.