Workflow Tips | Random Material ID

[:de]Eines der Hauptprobleme der Wiedergabe ist ihre Perfektion. Baumobjekten, die aus vielen Blattelementen bestehen, auf die das gleiche Material aufgetragen wurde, fehlen oft die natürlichen Variationen, die der Betrachter erwarten würde. In unserem Produktionsablauf sind wir täglich mit diesem Problem konfrontiert. Da wir immer versuchen, die Qualität unserer Architektur-Renderings zu verbessern, haben wir uns entschieden, ein einfach zu bedienendes Skript zu erstellen, mit dem wir zufällige Material-IDs setzen können, um die Wahrnehmung natürlicher Variationen zu ermöglichen.

Unsere wichtigste Software zur Erstellung von Inhalten ist 3ds Max, daher haben wir ein Makro-Skript (in MAXScript) für dieses Problem erstellt.

DE_intro

Rekursiver oder iterativer Ansatz

Es gibt grundsätzlich zwei mögliche Ansätze zur Lösung dieses Problems. Die iterative erste iteriert durch alle Flächen, um eine zufällige Material-ID festzulegen, während die rekursive erste Version die große Menge an Flächen in kleinere Teilgruppen aufteilt und dann allen in dieser Gruppe enthaltenen Elementen eine Material-ID zuweist; verbunden mit der Flächenauswahl.

Zuerst haben wir den iterativen Ansatz implementiert, um einen Blick auf seine Möglichkeiten und Einschränkungen zu werfen. Danach dachten wir über mögliche Optimierungen und andere Ideen nach, wie dieses Problem effizienter gelöst werden könnte, mit einem optimalen Gleichgewicht zwischen Geschwindigkeit und einem visuell hochwertigen Ergebnis. Deshalb haben wir die rekursive Version weiter untersucht, um das Problem von einem anderen perspektivischen Standpunkt aus zu lösen.

Algorithmen

Iterative Version

function changeIDsIterative obj minID maxID stepSize =
(
	getElement = polyop.getElementsUsingFace
	setFaceID = polyop.setFaceMatID
			
	range = 1
			
	for i = 1 to obj.numfaces by stepSize do (
				
		randomID = random minID maxID
				
		range = i + stepSize
				
		if(range > obj.numfaces) do (
			range = obj.numfaces
		)
		
		element = getElement obj #{i..range}
		setFaceID obj element randomID
	)
)

Im Grunde genommen iteriert dieses Skript durch alle Flächen mit einem bestimmten Schritt. Dabei nimmt es die Elemente, die mit den Flächen innerhalb des Stufenbereichs verbunden sind und gibt dieser Auswahl von Flächen eine zufällige Material-ID im Bereich der minimalen ID und der maximalen ID (minID <= ID <= maxID).

Größere Werte der Schrittweite würden zu einer höheren Geschwindigkeit führen, indem viele Flächen übersprungen werden, aber zu einer sichtbaren Wiederholung der gleichen verwendeten Material-ID.

Die Anzahl der Funktionsaufrufe zum Setzen einer zufälligen Material-ID hängt also von der Schrittweite s ab:

Formel0

Vielleicht ist Ihnen aufgefallen, dass in den ersten beiden Zeilen zwei Funktionsaufrufe Variablen zugeordnet sind.

getElement = polyop.getElementsUsingFace
setFaceID = polyop.setFaceMatID

Dies geschieht aus Leistungsgründen. Durch die Iinitialisierung des Funktionsaufrufs als Variable sind diese vielen Funktionsaufrufe etwas schneller auszuführen.

Rekursive Version

function changeIDs depth obj minFace maxFace minID maxID =
(
	if(depth > 0) then ( --Clustering, if depth != 0
		partSize = ((maxFace - minFace) / 2 + 1) as Integer --calculate the clustersize
		
		changeIDs (depth-1) obj minFace (partSize + minFace) minID maxID -- first part
		changeIDs (depth-1) obj (partSize + minFace + 1) maxFace minID maxID -- second part 
		
	) else ( --set material IDs
		getElement = polyop.getElementsUsingFace
		setFaceID = polyop.setFaceMatID
		
		randomID = random minID maxID 
                --get a random ID between the given values
		element = getElement obj #{minFace..maxFace} 
                --select all elements with faces in the given cluster of faces
		setFaceID obj element randomID 
                --set the random ID to all faces in the selection
	)
)

Die rekursive Version teilt die riesige Menge von in kleinere Gruppen von Flächen auf, bevor eine Material-ID festgelegt wird.

Der Algorithmus teilt grundsätzlich die Anzahl der Flächen in Hälften, bis der depth-wert Null ist. Dann wählt das Skript für jeden Unterabschnitt alle Elemente aus, die mit den Flächen des Unterabschnitts verbunden sind, und weist ihnen eine zufällige ID zu. Der zweite Teil ist also gleich der iterativen Version.

Der einzige Unterschied besteht in der Art und Weise der Auswahl der Flächen.

Beispiel für depth = 3

DepthValue

Also die Anzahl der Teile, bei denen eine zufällige Material-ID gesetzt werden soll:

Formel2

Die Anzahl der faces rekursiv in Unterteile zu zerlegen, geht sehr schnell. Der Hauptengpass dieses Skripts ist der Zugriff der 3dx Max-Schnittstellen getElement und setFaceID.

Die äquivalenten Parameter für iterative und rekursive Funktionsaufrufe können durch berechnet werden:

Formel1

Welche ist jetzt besser?

Im Grunde leisten beide Versionen die gleiche Arbeit und sollten ein vergleichbares Ergebnis liefern können. Die Hauptunterschiede sind die Parameter und das interne Verfahren.

Wenn Sie ein Objekt mit vielen Polygonen haben und (fast) jedes Element eine zufällige Material-ID haben sollte, dann ist die iterative Version einfacher zu benutzen, weil Sie einfach 1 oder vergleichbar niedrige Werte für die Schrittweite s. oder andere kleine Werte eingeben. Dann würde das Skript jeder Fläche eine zufällige ID zuweisen und eine zufällige ID festlegen. Aber andererseits, wenn die Elemente viele faces haben, würde die Arbeit oft mehr als einmal durchgeführt werden. Dies wird auch wegen der vielen Schnittstellenzugriffe von 3ds Max sehr langsam sein.

Dann wäre es einfacher, den rekursiven Ansatz zu verwenden und mit kleinen Werten für die depth zu beginnen, bis man ein zufriedenstellendes Ergebnis mit genügend Mischung erhält, indem man den depth-wert erhöht.

Beispiele

Unterschiedliche Material-IDs: 3

DE_mat

Laufzeit: iterativ

Iterative

Laufzeit: rekursive

Rekursiv

Es war uns eine Freude, Ihnen diese kleinen Gedanken über unseren Arbeitsablauf zu zeigen. Wir hoffen, Sie hatten Spaß beim Lesen des Textes und beim Betrachten der Bilder. Um mehr von unseren Projekten zu sehen, besuchen Sie unseren Showroom.[:en]One of the main problems of rendering is its perfection. Tree objects, that consist of many leaf elements with the same material applied to them, are often lack the natural variations the viewer would expect. In our production workflow we faced this problem on a daily basis. As we always try to improve the quality of our architectural renderings we decided to create an easy to use script to set random material IDs to give the perception of natural variations.

Our main content creation software is 3ds Max, so we created a Macro Script (in MAXScript) for this problem.

DE_intro

Recursive or iterative approach

There are basically two possible approaches to handle this problem. The iterative first oneone iterates through all faces for setting a random material ID while the recursive one version divides the big vast amount of faces into smaller parts groups(groups?) and then assigns a material ID to all the elements contained in this group. ?.connected to the face selection.

At first we implemented the iterative approach to take a look at its possibilities and restrictions. After that we thought about possible optimizations and other ideas how to solve this problem more efficiently with an optimum balance of between speed and a visual qualitygood result. That’s why we created further looked into the a recursive version to solve the problem from another perspective point of view.

Algorithms

Iterative version

function changeIDsIterative obj minID maxID stepSize =
(
	getElement = polyop.getElementsUsingFace
	setFaceID = polyop.setFaceMatID
			
	range = 1
			
	for i = 1 to obj.numfaces by stepSize do (
				
		randomID = random minID maxID
				
		range = i + stepSize
				
		if(range > obj.numfaces) do (
			range = obj.numfaces
		)
		
		element = getElement obj #{i..range}
		setFaceID obj element randomID
	)
)

Basically this script iterates through all faces with a given step. There it takes the takes the elements which are connected with the faces within the step range and gives this selection of faces a random material ID in the range of the minimal ID and the maximal ID (minID <= ID <= maxID).

Larger values of stepsizestep size would give result in more speed by skipping a lot of faces but would result in a visible repetition of the same used material ID.

So the number of function calls when to setting a random material ID depends on the step size s:

Formel0

You may have noticed that in the first two lines two function calls are assigned to variables.

getElement = polyop.getElementsUsingFace
setFaceID = polyop.setFaceMatID

That’s because ofThis is done for performance reasons. By Iinitializing the function call as a variable these many function callswill allow it to run are slightly faster.

Recursive version

function changeIDs depth obj minFace maxFace minID maxID =
(
	if(depth > 0) then ( --Clustering, if depth != 0
		partSize = ((maxFace - minFace) / 2 + 1) as Integer --calculate the clustersize
		
		changeIDs (depth-1) obj minFace (partSize + minFace) minID maxID -- first part
		changeIDs (depth-1) obj (partSize + minFace + 1) maxFace minID maxID -- second part 
		
	) else ( --set material IDs
		getElement = polyop.getElementsUsingFace
		setFaceID = polyop.setFaceMatID
		
		randomID = random minID maxID 
                --get a random ID between the given values
		element = getElement obj #{minFace..maxFace} 
                --select all elements with faces in the given cluster of faces
		setFaceID obj element randomID 
                --set the random ID to all faces in the selection
	)
)

The recursive version divides the huge amount of into smaller clusters of faces before setting a material ID.

The algorithm basically divides the amount of faces into halves until the depth value is zero. Then for every subpart the script selects all elements which are connected with the faces of the subpart and assigns a random ID. So the second part is equal to the iterative version.

The only difference is the manner of selecting the faces.

Example for depth = 3

DepthValue

So the number of parts where to set a random material ID is:

Formel2

Dividing the amount of faces recursively into subparts is very fast. The main bottleneck of this script is the access of the 3dx Max-Interfaces getElement and setFaceID.

The equivalent parameters for iterative and recursive function calls can be calculated by:

Formel1

Which one is better now?

Basically both version do the same work and should be able to give a comparable result. The main differences are the parameters and the internal procedure.

If you have an object with a lot of polygons and (nearly) every element should have a random material ID then the iterative version will be easier to use because you simply enter 1 or comparable low values for the step size s. or other small values. Then the script would go assignto a random ID to every face and set a random ID. But On the other hand if the elements have a lot of faces the work would often be done more than once. This will be also very slow because of many interface accesses of 3ds Max.

Then it would be easier to use the recursive approach and begin with small values for the depth until you get a satisfying result with enough mixture by increasing the depth value.

Examples

Different material IDs: 3

DE_mat

Duration – iterative

Iterative

Duration – recursive

Rekursiv

It was a pleasure for us to show you these little thoughts about our workflow. We hope you had fun while reading the text and looking to the pictures. To see more of our projects, visit our Showroom.[:]

Nach oben scrollen