Tutorial zum Erstellen eines FX mit lua

Support zu Fansubs
Post Reply
shinpei

Tutorial zum Erstellen eines FX mit lua

Post by shinpei »

Tutorial FX mit lua

Manchen könnte vllt der Begriff „lua“ ein Begriff sein. Es ist eine Programmiersprache, wie es viele auf diesem Planeten gibt. Die Besonderheit in diesem Fall: man kann sie zum erstellen von Karaoke-Effekten nutzen. Und dafür dient das Tut.


Was braucht man alles um arbeiten zu können?
Um überhaupt arbeiten zu können, wird als Subtitleprog „Aegisub“ benötigt. Und für das Lua-script eignet sich jeder Editor (er muss nicht einmal irgendwelche Programmiersprachen unterstützen. Der Editor von Windows reicht völlig).
Wichtig: Man muss sich mit den Typsettingbefehlen gut auskennen, sonst sollte man eh die Finger von FX lassen^^ und natürlich sollte auch schon eine Idee des Effektes vorliegen.

Welche Ausgangsmaterialien werden benötigt?
1. Eine *.ass (mit Aegisub erstellt) die die getimeten Silben im Format „[silbe]{\k[zeit]}“.
2. Das Style sollte Anfang an fest sehen. Vor allem muss darauf geachtet werden, dass die Auflösung der .ass der des Videos entspricht (sonst werden die Silben nicht richtig positioniert). Außerdem darf es keine Zeilenumbrüche geben.
3. Das Lua-script. (Dateiendung *.lua)



Wie erstelle ich ein solches Lua-script?
Das ist der eigentliche Teil... darauf werde ich genau eingehen...

1. erstelle eine Textdatei und ändere die Dateiendung in *.lua
2. füge das Grundgerüst ein
3. erstelle die eigentlichen Effecte


Grundgerüst eines Effectes:

Code: Select all

l = table.copy(line)
	l.text = string.format("{[Effekte]}%s", syl.text_stripped)
	l.start_time = [Start_Zeit]
	l.end_time = [End_Zeit]
subs.append(l)

Zeitensetzung:
Es müssen zwei Zeitpunkte definiert werden: Start und Endzeit
Dafür notwenige (wichtigsten) Befehle:

Code: Select all

line.start_time		--Anfangszeit der komletten Zeile
syl.start_time		--Anfangszeit der jeweiligen Silbe
syl.end_time		--Endzeit der jeweiligen Silbe
line.end_time		--Endzeit der komletten Zeile
Diese können/müssen nach belieben kombiniert werden. Und werden jeweils für [Start_Zeit] [End_Zeit] eingesetzt.
Wichtig: es dürfen keine ungewollten Lücken Zwischen „line.start_time“ und „line.end_time“ auftreten.

Eine mögliche Reihenfolge der Zeiten:
Intro: l.start_time = line.start_time - 500 | l.end_time = line.start_time
vor Effekt: l.start_time = line.start_time | l.end_time = line.start_time + syl.start_time
Effekt: l.start_time = line.start_time + syl.start_time | l.end_time = line.start_time + syl.end_time
nach Effect: l.start_time = line.start_time + syl.start_time | l.end_time = line.end_time
Outro: l.start_time = line.end_time | l.end_time = line.end_time + 500



Effekte:
Es können alle Typsettingbefehle verwendet werden, allerdings statt wie in Aegisub mir „\“ müssen „\\“ vor jeden Tag gesetzt werden und Postionen in Variablen gespeichert werden, bzw deren Platzhalten eingesetzt werden.

Hier mal ein Bsp:

Code: Select all

l.text = string.format("{\\an5\\pos(%d,%d)}%s", x, y, syl.text_stripped)
-> \\an5 muss generell gesetzt werden
-> pos oder move muss gesetzt werden
-> %d ist der Platzhalter für den jeweiligen Wert einer Variable, dieser wird anschließend in der Zeile genannt, in diesem Fall „x“ und „y“ für die Koordinaten. Für jeden Platzhalter (%d) muss eine entsprechende Varible/Wert folgen.
-> %s ist der Platzhalter für die Silbe. Kann auch durch Buchstaben ersetzt werden, diese werden dann fest gesetzt und ändern sich nicht mehr.

(Habe einige Beispiele vorbereitet)



Weitre wichtige Elemente:

Code: Select all

l.layer = [Wert]	--setzt den Layerwert
l.style = „[Name]“	--setzt den Style für die Silbe
l.class = „[name]“	--entweder Dialog oder Kommentar
math.random([Wert1],[Wert2])		--Zufallszahl zwischen [Wert1] und [Wert2]


for-Schleifen:

Code: Select all

	for g = 0, 9 do
	[Anweisung]
	end


if-Bedingung:

Code: Select all

	if [Ausdruck] [Operator] [Ausdruck] then
	[Anweisung]
	end


Intro/Outro (aufeinander folgende Start/Endzeiten):

Code: Select all

	--Aweisungsteil
	if i == 1 then
	[Bezeichner] = 0
	end

	[Effekte]

	[Bezeichner] = [Bezeichner] + (500/line.kara.n)
		--Ende Anweisungsteil

Hinweis: alle Werte der Variablen können nach belieben durch mathematische Ausdrücke verändert werden. "[Wort]" muss durch entsprechendes ersetzt werden.



Wie wird jetzt aus dem Script eine .ass?
1. Script einbingen: in Aegi den „Automation manager“ öffnen und das Lua-script reinladen, wenn es nicht rot hinterlegt wird, ist es richtig erstellt und kann verwendet werden.
2. Unter „File“ „Export Subtitles...“ die Rubrik des Lua-scriptes mit einem Häckchen versehen und exportieren. Eventuell noch den „Text encode“ ändern.



UND FERTIG

unter Automation 4 gibt es ein Beispielscript von shadow auf dessen Basis ich dieses Tut erstellt habe.
und ich übernehme keine Garantie für Richtigkeit^^ für Fragen und Anregungen stehe ich gerne zur Verfügung. Viel Spaß beim FXen!!!

Beispiele für wichtige Effekte und das Grundgerüst:

Code: Select all

include("karaskel.lua")

	--Allgemeine Beschreibung des Scriptes
		--ist nicht zwingend notwenig, aber sinnvoll
script_name = "Tut"
script_description = "Tutorial fürs Erstellen eines FX mit lua"
script_author = "shinpei"
script_version = "1.0"


	--Funktion "tut_fx"
		--Sinn und Zweck: ohne diese Funktion funzt das ganze Script nicht und sollte nur grändert werden, wenn man weiß, was man macht
function tut_fx(subs)
	aegisub.progress.task("Getting header data...")
	local meta, styles = karaskel.collect_head(subs)
	
	aegisub.progress.task("Applying effect...")
	local i, ai, maxi, maxai = 1, 1, #subs, #subs
	while i <= maxi do
		aegisub.progress.task(string.format("Applying effect (%d/%d)...", ai, maxai))
		aegisub.progress.set((ai-1)/maxai*100)
		local l = subs[i]
		if l.class == "dialogue" and
				not l.comment then
			karaskel.preproc_line(subs, meta, styles, l)
			do_fx(subs, meta, l)
			maxi = maxi - 1
			subs.delete(i)
		else
			i = i + 1
		end
		ai = ai + 1
	end
	aegisub.progress.task("Finished!")
	aegisub.progress.set(100)
end

	--Funktion "do_fx" 
		--Sie ist u.a. für die globale Position der Silben verantwortlich
function do_fx(subs, meta, line)
for i = 1, line.kara.n do
local syl = line.kara[i]
local x=syl.center + line.left		--Pos wird in Variable x gespeichert: x-Koordinate  (kann nach belieben verändert werden. Auswirkung auf das gesamte Script)
local y=line.margin_v		--Pos wird in Variable y gespeichert: y-Koordinate  (kann nach belieben verändert werden. Auswirkung auf das gesamte Script)


--Anweisungsteil:
	--hier folgen die eigentlichen Befehle fürs FX
	--habe hier einige wichtige Beispiele genannt


	
	

--Position	
l = table.copy(line)
	l.text = string.format("{\\an5\\pos(%d,%d)}%s", x, y, syl.text_stripped)
	l.start_time = line.start_time
	l.end_time = line.start_time + 200 
	l.layer = 1
subs.append(l)




--Move
l = table.copy(line)
	l.text = string.format("{\\an5\\move(%d,%d,%d,%d)}%s", x, y, x + 10, y + 20, syl.text_stripped)
	l.start_time = line.start_time
	l.end_time = line.start_time + 200 
	l.layer = 1
subs.append(l)



--Transformation
l = table.copy(line)
	l.text = string.format("{\\an5\\pos(%d,%d)\\t(\\fs50)}%s", x, y, syl.text_stripped)
	l.start_time = line.start_time
	l.end_time = line.start_time + 200
	l.layer = 1
subs.append(l)


--org
l = table.copy(line)
	l.text = string.format("{\\an5\\pos(%d,%d)\\org(%d,%d)}%s", x, y, x, y, syl.text_stripped)
	l.start_time = line.start_time
	l.end_time = line.start_time + 200
	l.layer = 1
subs.append(l)


--Drawing
l = table.copy(line)
	l.text = string.format("{\\an5\\pos(%d,%d)}{\\p1}m 0 0 l 640 0 l 640 60 l 0 60 l 0 0{\p0}", x, y, syl.text_stripped)
	l.start_time = line.start_time
	l.end_time = line.start_time + 200
	l.layer = 1
subs.append(l)


--fester String
l = table.copy(line)
	l.text = string.format("{\\an5\\pos(%d,%d)}[Bla]", x, y, syl.text_stripped)
	l.start_time = line.start_time
	l.end_time = line.start_time + 200
	l.layer = 1
subs.append(l)


--for-Schleife
l = table.copy(line)
for g=0, 7 do
	l.text = string.format("{\\an5\\pos(%d,%d)}%s",x , y, syl.text_stripped)
	l.start_time = line.start_time + syl.start_time
	l.end_time = line.start_time + syl.end_time
	l.layer = 1
subs.append(l)
end




			
--Ende des Anweisungsteils		

	end

end

aegisub.register_filter("shinpei", "", 2000, tut_fx)
shinpei

Re: Tutorial zum Erstellen eines FX mit lua

Post by shinpei »

falls jemand eine möglichkeit kennt, pixel in form der jeweiligen silben zu positionieren, möge er doch so freundlich sein und das mir sagen^^
Youka

Re: Tutorial zum Erstellen eines FX mit lua

Post by Youka »

Soweit ich weiß, wurden FX-Programme wie asspawaa, asscalc oder aae entwickelt, um mit anderen Programmiersprachen die pos der jeweiligen m 0 0 l 1 0 1 1 0 1 Objekte zu bestimmen, da dies und intelligente Bewegungen/Transforms zu den Nachteilen von Auto4 gehören. unicode kann lediglich nur allgemeinere Details zu den Chars lesen (Auto4 müsste zusätzlich zu den positionierten Pixeln auch noch die richtigen repeat/loop-Werte bestimmen). Unregelmäßige Bewegungen/Transforms müssen halt errechnet werden.

Für den Anfang sollte man die Dollar-Variablen und Modifier lernen, folglich retime, inline-fx, usw. und sich erst dann an interne codes und lua-scripte annähern.

Hilfen:
http://www.lua.org/manual/5.1/
http://lua.gts-stolberg.de/
http://aegisub.cellosoft.com/docs/Lua_Reference(Im blauen Kasten am unteren Ende der Seite findet sich der Lua u. Auto-Index)
Statt sich gleich an das Script Shadow zu setzen, sollte man erstmal sich die einfacheren factory-scripts im "Aegisub->automation->autoload/auto3"-Ordner ansehen.
Last edited by Youka on 11.03.2009 12:58, edited 1 time in total.
Youka

Re: Tutorial zum Erstellen eines FX mit lua

Post by Youka »

shinpei wrote:falls jemand eine möglichkeit kennt, pixel in form der jeweiligen silben zu positionieren, möge er doch so freundlich sein und das mir sagen^^
Andere Möglichkeit:
Buchstaben x-mal erstellen, wobei x = Silben-Höhe * Silben-Breite ist und dann jeweils jedes einzelne Pixel des Silben-Blocks/-Bereichs ausclippen.
Der Nachteil ist, dass das Script sehr viele bites frisst, da
-Buchstaben größer sind, als "m 0 0 l 1 0 1 1 0 1"-Objekte,
-clips erstellt werden, die sich zusätzlich im \move transformieren müssen,
-auch Leerstellen (z.B. Innenraum von "O") geclippt werden (aber nichts bewirken).

Code: Select all

include("karaskel.lua")

script_name = "Particel"
script_description = "Sylable become Pixels."
script_author = "Youka"
script_version = "1.1"

function particel_fields(subs, meta, line, styles)
	for i = 1, line.kara.n do
		local syl, a = line.kara[i], table.copy(line)
		local posx, posy = a.left + syl.center - syl.width/2 - a.styleref.outline, a.top  - a.styleref.outline
		local max = (syl.width + 2 * a.styleref.outline) * (a.height + 2 * a.styleref.outline)
		for m = 0, max, 1 do
			local maxp = a.left + syl.center + syl.width/2 + a.styleref.outline
			if posx > maxp then
				posx = a.left + syl.center - syl.width/2 - 1 - a.styleref.outline
				posy = posy + 1
			end
			local begin, ende = 0, syl.duration + 200
			local posxq, posyq = posx + 1, posy + 1
			local rndx, rndy
			if posx < a.left + syl.center then
				rndx = math.random(-15,5)
			else
				rndx = math.random(-5,15)
			end
			if posy < a.top + a.height/2 then
				rndy = math.random(-15,5)
			else
				rndy = math.random(-5,15)
			end
			a.text = string.format("{\\alpha&H00&\\move(%f,%f,%f,%f,%f,%f)\\clip(%f,%f,%f,%f)\\t(%d,%d,\\1c%s\\3c%s\\alpha&HFF&\\clip(%f,%f,%f,%f))}%s",a.left + syl.center,a.top,a.left + syl.center+rndx,a.top+rndy,begin,ende,posx,posy,posxq,posyq,begin,ende,a.styleref.color2,a.styleref.color2,posx+rndx,posy+rndy,posxq+rndx,posyq+rndy,syl.text_stripped)
			posx = posx + 1
			a.start_time = line.start_time + syl.start_time
			a.end_time = line.start_time + syl.end_time + 200
			subs.append(a)
		end	
	end
	for i = 1, line.kara.n do	
		local syla, b = line.kara[i], table.copy(line)
		b.text = string.format("{\\pos(%f,%f)}%s",b.left + syla.center,b.top,syla.text_stripped)
		b.end_time = b.start_time + syla.start_time
		subs.append(b)
	end		
end

function load_progress(subs, line, styles)
	aegisub.progress.title("Particel")

	aegisub.progress.task("Sammelt Style- und Meta-Daten")
	local meta, styles = karaskel.collect_head(subs)

	aegisub.progress.task("Effekte ausführen...")
	aegisub.progress.set(0)
	for i = 1, subs.n do
		aegisub.progress.set(i/subs.n*100)
		local s = subs[i]
		if s.comment then
			local txt = "Comments werden nicht bearbeitet!                 "
			aegisub.log(2, string.format("%s", txt))
		end
		if not s.comment and s.class == "dialogue" then
			karaskel.preproc_line(subs, meta, styles, s)
			particel_fields(subs, meta, s, styles)
			if aegisub.progress.is_cancelled() then
				break
			end
		end
		s.comment = true
		subs[i] = s
	end
	aegisub.progress.task("Effekte erfolgreich ausgeführt!")
	aegisub.progress.set(100)
	aegisub.set_undo_point("Particel")
end

aegisub.register_macro("Particel","Sylable become Pixels.",load_progress)
Dieses Script muss mit dem Anhang ".lua" in den Ordner "Aegisub\automation\autoload" kopiert werden. Im Programm ist es dann als Makro verfügbar. Bei Ausführung wandelt es alle Silben (einer timed-Karavorlage; ausgeschlossen Comments) in Pixel um und lässt diese sich zerstreuen.

Edit: Nun etwas optimiert. Fordert zwar trotzdem noch viele bites, aber kann durchaus für einige inline-fx angewendet werden. Pixel wandeln sich bis zur Ausblendung in Color2 um.
Last edited by Youka on 03.05.2009 00:07, edited 4 times in total.
shinpei

Re: Tutorial zum Erstellen eines FX mit lua

Post by shinpei »

das hat irgendwas von "mit kanonen auf spatzen schießen"... aber danke, dass du dich da hingesetzt und das script geschrieben hast^^
shinpei

Re: Tutorial zum Erstellen eines FX mit lua

Post by shinpei »

so und weiter gehts... dieses mal: effecte je nach länge der silben öfters ausführen.

Code: Select all

for i = 0, (syl.duration) do
  start = line.start_time + syl.start_time + i*200           --die "200" ist beliebig und kann je nach wünschen verändert werden. sie bestimmt u.a. die länge und die anzahl (einfach etwas rumprobieren, dann wird es ersichtlich)
  ende = start + 200                               --gleicher wert wie bei "start" sonst gibt es lücken
  if (ende < (line.start_time + syl.end_time)) then
   l = table.copy(line)
   l.layer=1
   l.text = string.format("{[Effekte]}%s", syl.text_stripped)
   l.start_time =  start
   l.end_time = ende
   subs.append(l)
   end
die "if"-abfrage dient zur bestimmung der länge der silbe. damit der effect nicht länger wirkt, wie die silbe lang ist. wahlweise kann auch die for-schleife dementsprechend verändert werden.
Youka

Re: Tutorial zum Erstellen eines FX mit lua

Post by Youka »

Code: Select all

for a=1, line.kara.n do   --Wiederholung in Anzahl der Silben--
  local syl, l = line.kara[a], copy_line(line)   --syl=Silben-Inhalt der Silbe mit dem Index a, l=Kopie der Line(um Orig.-Line als Vorlage für jede Schleife zu sichern)--
  local interval = 200   --Interval/Dauer pro Einblendung!!!--
  for z=1, math.ceil(syl.duration/interval), 1 do   --Wiederholungen in Einblendungen mit der Dauer interval innerhalb der Silbengesamtdauer (aufgerundet)--
    l.text = string.format("{[Effekt]}%s",[Effekt einfügen],syl.text_stripped)   --Effekte + Text--
    l.layer = 1   --Effekt liegt auf Schicht 1 (default:0), daher im Vordergrund--
    l.start_time = line.start_time + syl.start_time + (z-1) * interval   --Startzeit der jeweiligen Einblendung--
    l.end_time = line.start_time + syl.start_time + z * interval   --Endzeit der jeweiligen Einblendung--
    if l.end_time > line.start_time + syl.end_time do    --Da die letzte Einblendung über die Endzeit der Silbe erscheint (^aufgerundet!)...--
      l.end_time = line.start_time + syl.end_time   --...wird diese passend gekürzt.--
    end
    subs.append(l)   --Sub-Line l wird ausgegeben--
  end
end
Auch für Line oder Char möglich. Lediglich Gerüst aufbauen und x.duration anpassen.
Last edited by Youka on 11.03.2009 17:14, edited 1 time in total.
Youka

Re: Tutorial zum Erstellen eines FX mit lua

Post by Youka »

Gerüste:

Grundaufbau/Lesen der Subs und erstellen der Lines!!!

Code: Select all

include("karaskel.lua")   --Laden der verfügbaren Werte und Befehle aus dem Kara-Skelett--
function need_name(subs)   --Erstellen der Funktion, laden/miteinbeziehen des Tables subs--
  local meta, styles = karaskel.collect_head(subs)   --Lesen der Meta- und Style-Daten--
  for i = 1, subs.n do   --Durchgang der Sub-Lines--
    local line=subs[i]   --line=Inhalt der Sub-Line mit dem Index i--
    if line.class == "dialogue" then   --Bearbeitung erfolgt nur für die Klasse "Dialog"-Feld--
      karaskel.preproc_line(subs, meta, styles, line)   --Einlesen weiterer Inhalte der jeweiligen Line (z.B. Größen, Positionen, usw.)--
      --In folgende Zeilen werden die Gerüste der Lines, Silben und Chars eingefügt, also die Effekte selbst--
      .
      .
      --/--
    end
    line.comment = true  --Alternativ: Line wird zum Comment (als Vorlage im Script enthalten, jedoch als Comment nicht im Vid angezeigt)--
    subs[i] = line   --Alternativ: Einfügen der Werte von Line in das Original(in dem Fall nur zu Comment geändert, sämtliche Effekte haben Kopien erzeugt, um Line nicht zu verändern)--
  end
end
aegisub.register_macro("Name des Makros","Beschreibung",need_name)   --Erstellen des Makros im Automation-Menu. An 3ter Stelle und folgenden können die auszuführenden Funktionen angegeben werden. Das Script wird unter "Aegisub\automation\autoload" abgelegt.--
Line

Code: Select all

local l = table.copy(line)   --Kopie der Orig.-Line zur Weiterverarbeitung--
.
.
subs.append(l)   --Ausgabe der bearbeiteten Line--
Silbe

Code: Select all

for s=1, line.kara.n do   --Wiederholung in Anzahl an Silben--
  local syl, l = line.kara[s], table.copy(line)   --syl=Silben-Inhalt der Silbe mit dem Index s, l=Kopie der Line--
  .
  .
  subs.append(l)   --Ausgabe der bearbeiteten Line mit ermöglichten Silben-Werten--
end
Char

Code: Select all

local cs = table.copy(line) --Kopie der Line--
local call = unicode.len(line.text_stripped)   --Gesamtanzahl der Chars--
local posa, width = line.left, 0   --posa = Position des ersten Chars, width = Zahlen-Variable width erstellt--
for w, z in unicode.chars(line.text_stripped) do    --Text der Line wird gelesen und nacheinander durchgegangen, w = der Text (also der Char), z=Char-Index--
  w.width = aegisub.text_extents(line.styleref, w)   --Einlesen der Breite des Chars abhängig vom Style und dem Text--
  posx = posa + w.width/2   --posa="left" des Chars, nun posx="center" des Chars--
  .
  .
  cs.text = string.format("{[Effekt]}%s",[Effekt einfügen],w)   --w (also der Char) wird zum Text--
  posa = posa+w.width   --Für die Position des nächsten Chars wird die des momentanen Chars um seine eigene Breite übersprungen und somit "left" neu erzeugt (Leerzeichen werden auch als Text gelesen)--
  subs.append(cs)
end
Kann auch in das Syl-Gerüst eingebaut werden (line->syl, +syl definieren)!
_____________________________________________________________________________________________________
Weitere Optionen, wie z.B. der Prozess-Balken, können in den Aegisub-Contents nachgelesen werden.
https://forum.animedrache.de/viewtopic. ... 658#p10891
User avatar
Jin|sama
Sensei-sama
Sensei-sama
Posts: 264
Joined: 14.05.2008 16:10

Re: Tutorial zum Erstellen eines FX mit lua

Post by Jin|sama »

Ich würde gerne Lua in Eclipse testen, weiß aber nicht welchen Vorteil das hat, bis auf Auto - Complete per Tastencombo.
Wenn ich das Script mit Constanten in der IDE ausführe, erscheint dann ein Fenster wo mir das dann angezeigt wird oder bekomm ich nur Exeptions wenn ich Mist gebaut habe?
Lasst meine verf****** Threads in ruhe
Post Reply

Who is online

Users browsing this forum: No registered users and 22 guests