PPG Based Particle Animation Work Flow With ICE

August 7th, 2009 by Hans Payer - Viewed 4579 times - Popularity: 37% [?]

About a year ago already, XSI 7.0 was released with much expectations and enthusiasm. Most of us, by now, have played with ICE and if you’re lucky, you even had the chance to squeeze an operator in a real production. Everyone is still marveled when new videos are posted online showing the latest tricks or achievements made with ICE. ICE is an amazing design tool and definitely opens many doors for all Softimage users. But for many, they hit a wall; in order to create even the simplest simulation, they have to learn the meaning of vectors, scalar, arrays and how to use them. If you do have the technical chops, great, new things to learn, but for the more artistically skilled users, the hill is steep.

Everyone complained (including me) that the old particle system was too weak, too limited…. But it was fast to get some things done. To randomize a value, for example, you simply had to open a property page and set a variance parameter. Partly because ICE is so open, to vary a parameter, you have to search for the desired compound or node modifier, drag & drop, connect ports and then set values in the new compounds. Read me right, ICE is very powerful but if you have to modify dozens of parameters hundreds of times in one typical work day, this work flow becomes redundant an inefficient. There must be a way to be quicker.

Softimage proposed a work flow that can be described as follows: a technical director connects basic nodes, designs compounds and exposes ports. An artist, who is not necessary knowledgeable of ICE, then sets the different values of the exposed parameters. Some problems may arise with this approach. How can a TD predict every alteration needed by the artist? In the context of a particle simulation of falling rain, for example, a TD may have to design his compounds to support, for a simple shot, wind and gravity. Then, another shot may require water splashes from the droplet hitting the ground. Another still, requires the same rain but with the added effect of coagulating droplets on a smooth surface. And so on. You can easily imagine multiple variations of the same effect. So how are TDs supposed to tailor their compounds to fit all of the artists needs? One obvious solution is to build a system of compounds rather than a top level one. Yet the artists would need to be taught and learn how this system works and therefore, learn how to use ICE. Yes, maybe… but unlikely. I know many technical XSI users that were clueless on how create a simple simulation; artists, even less probable. How can technical directors empower artists without limiting them with a simple set of parameters? There should be a window to deliver the power of ICE to artists without being too painful. Artists should be able to easily create simple particle animation. You do not need to be a mechanic to drive a car.

With these observations in mind came the search for a way to simplify the ICE work flow which any Softimage user would understand and produce simulations in no time. The intent is not to replace the current work flow but to complement it and accelerate the multiple iterations needed in order to design particle simulations. Subsequent refinements, complex connections or relationships can and should be achieved the traditional way by connecting nodes and compounds in the ICE tree. But once a new solution is found, it should be easy to re-integrate it in a simpler work flow system. It should also be seen as an added value to cut the time to help generate about 75% of particle animation scenarios.

You do not need to look very far to find solutions. By simply looking at the different ways shaders can be modified, it is easy to wonder why ICE property pages were not designed the same way. Isn’t it faster in many situations to create connections by using the plug icon in a shader’s property page rather than opening it in the render tree, dragging and dropping a node, then making the connection? It should be the same in ICE property pages. No?

dk9zhxb_72gnm2jkfk_b

Also, all parameters affecting the simulation should reside in the same property page. With a typical ICE tree containing easily 20 compounds or more, finding a parameter often requires a search through many compounds. Too many doubles clicks are required to access the compounds’ parameters; why not put them in one location. When a scalar is randomized for example, the parameters associated with randomization should replace the original scalar in the top property page. You say: isn’t the top compound intended for that? Yes, but it doesn’t expose ports automatically. Remember, the goal here is to create particle simulations in a production environment; not to design ICE trees in a R&D context.

dk9zhxb_73fgm8zwtz_b

In the following video, I demonstrate a working prototype. It shows the above ideas in action with the exception of having iterations made through parameter contextual menus instead of the plug connection icon menu. The Softimage development kit does not give access to this type of UI widgets for ICE compounds property pages. Also, for clarity’s sake, it would have been beneficial to have used tabs in the property page to seperate emission, particle type, forces, triggers and events. This was not implemented. This prototype was written about a year and a half ago with a beta version of XSI 7.0 and unfortunately has not been updated to work with the current version of Softimage but would be relatively simple to rewrite. I must also credit my colleagues at the time, Jeff Wilson, who helped with the original concept and Javier von der Pahlen who co-wrote the prototype. It is important also to say that I was an employee of Softimage at the time when that prototype was written. As a long time user of Softimage’s products, I was trying to push solutions that made the work of technical directors easier. Unfortunately, this concept was not accepted. But it remains a great concept on how technical directors can go beyond compounds and integrate ICE in production pipelines more efficiently. It clearly illustrate the ability to build a simple particle animation rapidly without having to access the ICE tree. All iterations are made through the property page.

I simply wanted to share these ideas with the Softimage community and start a discussion. As, hopefully, more and more people will be using ICE, I’m sure that work flows will evolve to more efficient ways to manage ICE trees. Maybe it’ll lead to similar solutions, whether it comes from Autodesk or the community. I think it’s really cool concept and it would save many users time and headaches. What do you think? Is this type work flow worth exploring?

PPG Based Particle Animation Work Flow With ICE from Hans Payer on Vimeo.

Popularity: 37% [?]

Fast Radiosity using Diffuse convolved environment map

March 21st, 2009 by Harry Bardak - Viewed 12792 times - Popularity: 75% [?]

What you need to reproduce theses examples :

HDRShop
HDRShop plugin Diffuse_SH.

 

I used to work for a 2 years at Framestore-CFC in London. That mean I use Maya and PRMan as my main application. During this year I learn a lot of thing but also hear a lot of false assumtion about rendering in general. Typically the common comment was that PRman was more suitable to render complex character and that MR was very slow for this kind of job. In fact people arguing about this doesn’t not really know what are the status of modern rendering such as MR, V-ray or any others assuming everything was frozen since 5 years or just repeating what the veteran keep repeating.

But I am not engaging a new flame wars between renderer. Actually I don’t care which is the better today in 2008 you can produce beautiful picture with any renderer available in the market ( and yes that include maya’s software render well known to be a piece of crap ;) ) The most important is the guys behind the scene. But I need to put everything in their respective context to elaborate and may be justify what and why I will describe later.

Actually mental ray is very fast, if you do things correctly. If you don’t do things correctly ( such as massive spatial and/or temporal oversampling ) it as well as any other renderer will be very slow. But I always get this "PRMan displaces faster" stuff. Of course it does…. until you actually trace a ray.
You have to know that PRMan lives in a mindset where raytracing is so slow that you avoid it at all cost. The thing is that in PRMan, if you try to shoot a ray, it too has to do all those things that a raytracer do by nature. So the minute you actually shoot a single ray, PRMan has to do what mental ray always does…. and the comparasion suddenly isn’t so much in favour of PRMan any more.

So people using PRman developed a completely different approach to same problem : Lighting and Rendering a scene while trying to avoid at all cost raytracing. That what is interesting. What will happen if I use these approach with Mentalray within XSI ? Maybe I can have a hybrid solution keep the best of the two world to render my character(s) ?

 

Diffuse Convolution on Environment map

What’s that ? To keep it simple you can assume that a diffuse convolved map is envmap where you apply a smart blur that will give you the result of sampling this envmap with an infinite number of rays with the Final Gathering on a perfect lambertian surface. For more information you can refer to Debevec’s research ( the father of HDR images ).

Having your envmap pre-blurred is a great advantage because you don’t need to sample it several times to get the correct illumination. Only one sample suffice to get the illumination on your surface. Actually you must cast only one ray. Otherwise you will average twice the map and the illumination will not be correct any more.

The ray direction that you will cast must also be in the same direction as your surface normal. The Xsi’s Ambient Occlusion setup correctly will do perfectly the job. You need one sample , the spread will be very small to not deviate from the normal ( 0.01 ) and the mode should be set to environement sampling. Obviously you can code a shader that do the correct environement lookup. I am using the XSI’s AO shader because it’s available out of the box.

 

Render Balls simple test.

Env sampling brute vs FG vs Diffuse Conv

Env sampling brute vs FG vs Diffuse Conv

 

The way I set up the scene is very simple. I tried to get the contribution of the envmap once convolved and only this. So there is no illumination model just different approach.
The HDR used for this test the one called beach.hdr that you can find at debevec’s website. The map that you download is an angular map so you have to convert it to spherical coordinate system. The easiest way is to HDRshop and convert to angular map to longitute/latitude map. Once the map ready, I put it in my scene as an environement map.
The first sphere is the result of an AO shader set to environement sampling mode with a spread of 0.8. I had to crank up the sampling to 1024 to get a smooth solution. AO env sampling is brute force approach. There is no importance sampling so that mean you have cast a lot of ray to get a smooth result specially with high dynamic range image. And obviously that be very slow. It’s the slowest render.
With the second sphere I used to FG to sample env. Same settings as before except that I use a shader that return irradiance and activate FG. FG is a lot faster that the previous method because it doesn’t sample every point with 1024 rays. Instead FG will sample a certain amount of point and interpolate the result between the point calculated. But for this comparaison I pushed the number of rays to 4096 to make sure I got enough accuracy to have fair comparaison with the quality of the next approach. Even by pushing the number of rays to 4096, it was still faster to render with FG.

With last sphere I made a diffuse convolution in HDRshop ( using the SH_diffuse plug in because it ’s a lot faster than HDRshop’s function), applied the resulting image as an environement map and use the XSI’s AO shader with the sampling set to 1 and the spread to 0.01.
The render time is ultra fast and match FG solution. It ’s actually realtime because the convolution has been already calculated once in HDRshop.

 

Render Balls too simple let’s try with something that got balls.

We got a exact match with the FG for a fraction of time. We can conclude that the diffuse convolution is good enough to simulate FG. But in our test we were using simple sphere. This an ideal case but we needed to check first if the lookup was correct before to move on something more serious.

I like buddha. I like him because it’s a one million polygon model that can be compared to any displaced model push out from Zbrush or Mudbox.
I have simply applied what I did with the spheres.
The first render is with an diffuse convolved envmap. It took approximately 50 sec to render with a large part of preprocessing the scene. If I had a realtime shader could get the same result ( minus anti aliasing ) in realtime. Obviously there is no occlusion calculated but that due to fact I asked only an environment lookup around the normal of the surface.
The second image is what you get if you were using FG and third one is a difference done in shake
to highlights the difference between the two renders.



As you can see only the occlusion and the FG colored bounce are missing. If you apply a simple occlusion on the top you will endup with an image that is fairly close to the FG solution but with only a fraction of the time involved by the process.

 

OK but in production is it worth to use it ?

Well I will say yes and no. It depand the number of shot you have to do and the time you got to complete them. This technique involve a bit of setup at the shading level while the using FG is straight foward. So if you are working on movie that need a 2K ( or more ) render then yes. Memory foot print is very low and it’s damn fast to render it specially with very heavy object like displaced object or Hair object..
At the moment you need to set this at the shader for every object. Ideally the best will be to set this as a global ambience. Unfortunately you can’t plug anything in the global ambience parameter. So the best if to use a light that cast only ambient light. And for that you need to code it so ask your favourites shader writer to do it.

 

Publications :

An Efficient Representation for Irradiance Environment Maps

 

Popularity: 75% [?]

Decorating your Python code

October 10th, 2008 by Patrick Boucher - Viewed 2998 times - Popularity: 65% [?]

I have touched on Python decorators in the past in this article (although that decorator was a bit convoluted). Decorators can be extremely useful in many situations and here are a few that I propose may help streamline script development.

Note: For those of you who would like a bit of background information on decorators you can check out this page.

Keeping things quiet

Let’s look at the code and we’ll go for a few explanations afterward.

1
2
3
4
5
6
7
8
9
10
11
def suspendXSILogging(func):
    def closure(*args, **kwargs):
        logPrefs = getXSILoggingPrefs()
        try:
            ret = func(*args, **kwargs)
        except Exception, e:
            setXSILoggingPrefs(logPrefs)
            raise
        setXSILoggingPrefs(logPrefs)
        return ret
    return closure

The first function suspendXSILogging can be used as a decorator and will disable all logging features of XSI. This has the advantage of making large scripts that may be command dependent a bit quicker.

You could always implement a plugin with a custom command but sometimes a script is simpler. Even in the command context this decorator could be useful if you wanted part of the command to log to the script editor while keeping other parts silent.

Another advantage of this decorator is that it puts your original function in a large trap that, if an error should occur, will make sure that logging is reset to the users original preferences.

Time for decorating

1
2
3
4
5
6
7
8
9
10
11
12
13
def timeExecution(func):
    def closure(*args, **kwargs):
        startTime = time.time()
        try:
            ret = func(*args, **kwargs)
        except Exception, e:
            delta = time.time() - startTime
            log.error('Failed in %f seconds' % delta)
            raise
        delta = time.time() - startTime
        log.info('Finished in %f seconds' % delta)
        return ret
    return closure

This second function, timeExecution, can decorate almost any function and will print to the script editor the amount of time the decorated function took to execute. This can be used as a debugging or performance tracking tool or as a way to provide feedback to your users. Again, a trap is implemented that will log execution time even if an error occurs.

Please not that this decorator uses a logging system described here. If you would rather use simple logging you should replace log.info( instances by log(, xsi.LogMessage( or Application.LogMessage( depending on your scripting habits.

Usage example

1
2
3
4
5
@timeExecution
@suspendXSILogging
def main():
    # Do some fun useful stuff - well... more useful than this!
    xsi.CreatePrim("Cube", "MeshSurface", "", "")

You can chain decorators without hesitation like in this example, one thing to note in this particular case is that you’ll want timeExecution higher in the decorator chain than suspendXSILogging otherwise the time information will never make it to the script editor. Duh!

Support code

Before I sign off on this latest article, here are two functions that, if useful on their own, are essential to the functioning of the suspendXSILogging decorator.

1
2
3
4
5
6
7
8
9
10
11
def getXSILoggingPrefs():
    prefs = xsi.Preferences
    vals = {}
    for n in ['scripting.cmdlog', 'scripting.msglog', 'scripting.msglogverbose']:
        vals[n] = prefs.GetPreferenceValue(n)
        prefs.SetPreferenceValue(n, False)
    return vals
 
def setXSILoggingPrefs(vals):
    for key, val in vals.iteritems():
        xsi.Preferences.SetPreferenceValue(key, val)

Cheers!

Popularity: 65% [?]

Logging and being proactive

October 5th, 2008 by Patrick Boucher - Viewed 3904 times - Popularity: 67% [?]

You write tools for artists under a deadline (yours and theirs). You live in a production oriented world. Unit testing, beta testing and anticipating can only go so far, and that is only if you have time to properly test. You have to accept that eventually, your code will break. How it breaks and how you react to such a break becomes as important as your capacity to create the tool in the first place.

You’ve probably seen the following idiom used many times on this website and in other places that do XSI scripting in Python:

1
2
3
from win32com.client import constants as c
xsi = Application
log = xsi.LogMessage

It is a shortcut that allows you to use log as if it was the native LogMessage call:

1
log('This is an info message.', c.siInfo)

Here is a way to extend this by using the standard logging module in a way that can help you be more proactive. I suggest you get familiar with the logging module from the standard Python docs, it’s bound to eventually be helpful.

Logging in XSI

Here is a construct I’ve started using lately that I am starting to enjoy, its advantages are:

  • Concise and self documenting
  • It has a trap to catch any unexpected error and log it
  • Logs both to the script editor and to file
  • Easily extensible to other logging mechanisms (email, event viewer)
  • You can pass non string messages and it will convert to string for you (Yay!)
  • Easily useable in scripts and other modules
1
2
3
4
5
6
7
8
9
10
11
12
13
from hookolo.xsi import *
 
def main():
	log.comment('comment')
	log.debug('debug')
	log.info('info')
	log.warning('warning')
	log.error('error')
	log.fatal('fatal')
	log.critical('critical')
	raise Exception('Totally unexpected exception!')
 
run('demoScript', main)

The code above would produce the following output in XSI’s script editor.

# comment
# VERBOSE : debug
# INFO : info
# WARNING : warning
# ERROR : error
# ERROR : fatal
# FATAL : critical
# ERROR : Trap reached in demoScript
# Traceback (most recent call last):
#   File "C:\hookolo\libs\hookolo\xsi\__init__.py", line 18, in run
#     func(*args, **kwargs)
#   File "<script Block >", line 11, in main
# Exception: Totally unexpected exception!
# ERROR : Traceback (most recent call last):
#   File "<script Block >", line 13, in <module>
#     run('demoScript', main)
#   File "C:\hookolo\libs\hookolo\xsi\__init__.py", line 21, in run
#     raise StopScriptError('Check the logs!')
# StopScriptError: Check the logs!
#  - [line 13]

The calls to log.comment, log.debug, log.info, log.warning, log.error and log.critical are all self explanatory as they are all equivalent to LogMessage calls with the appropriate severity argument. The call log.fatal is an addition of my own who’s severity is equivalent or just a tiny bit lower than critical. Fatal errors will not pop a dialog box.

In the setup I have here, both fatal and critical, will be logged to a file in the users’ XSI_HOME directory that looks like follows.

2008-10-04 23:46:13,720 - hookolo.xsi - FATAL - fatal
2008-10-04 23:46:13,720 - hookolo.xsi - CRITICAL - critical
2008-10-04 23:46:13,720 - hookolo.xsi - FATAL - Trap reached in demoScript
Traceback (most recent call last):
  File "C:\hookolo\libs\hookolo\xsi\__init__.py", line 18, in run
    func(*args, **kwargs)
  File "<script Block >", line 11, in main
Exception: Totally unexpected exception!

By having a file like this, I don’t have to wade through XSI’s scripting log as I have a file that only includes important script errors. I also don’t have to worry about a user coming to me and saying: “Your script exploded.” Followed by the inevitable: “No, I don’t remember the error and I don’t have it in my scripting window anymore.” Now I can just open up this file from their XSI_HOME directory and look for myself.

The usage of a main() function and a run() function allows to build a trap for any unexpected errors that might occur and allow for logging. By putting the runner in a library we can benefit from it with very little hassle in even the tiniest of scripts.

Pushing the envelope

A system such as this one could even easily be extended to allow for sending of fatal and critical errors via email. You would know that a script failed even before the artist had walked the corridor to your office to tell you about the failure. This extension wouldn’t even be that hard as the logging module includes an SMTPHandler for just this purpose.

Support code

Here is the library that makes this all possible.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
import logging
import os
import sys
import types
 
from win32com.client import constants as c
from win32com.client import Dispatch
 
__all__ = ['xsi', 'log', 'c', 'run', 'XSIError']
 
COMPANY_NAME = 'Hookolo'
COMPANY_PREFIX = 'hookolo'
 
xsi = Dispatch('XSI.Application').Application
 
def run(scriptName, func, *args, **kwargs):
	try:
		func(*args, **kwargs)
	except:
		log.log(45, 'Trap reached in %s' % scriptName, exc_info=1)
		raise StopScriptError('Check the logs!')
 
class XSILogger(logging.Logger):
	def fatal(self, msg, *args, **kwargs):
		self.log(45, msg, *args, **kwargs)
 
	def comment(self, msg, *args, **kwargs):
		self.log(5, msg, *args, **kwargs)
 
class XSIHandler(logging.Handler):
	def emit(self, record):
		if record.levelno == logging.CRITICAL:
			xsiLvl = c.siFatal
		elif record.levelno in [logging.ERROR, 45]:
			xsiLvl = c.siError
		elif record.levelno == logging.WARNING:
			xsiLvl = c.siWarning
		elif record.levelno == logging.INFO:
			xsiLvl = c.siInfo
		elif record.levelno == logging.DEBUG:
			xsiLvl = c.siVerbose
		else:
			xsiLvl = c.siComment
 
		if isinstance(record.msg, types.StringTypes):
			msg = self.format(record)
		else:
			record.msg = str(record.msg)
			msg = self.format(record)
 
		xsi.LogMessage(msg, xsiLvl)
 
def getLogger(name=None):
	if name is None:
		return logging.getLogger(COMPANY_PREFIX + '.xsi')
	else:
		return logging.getLogger(COMPANY_PREFIX + '.xsi.' + name)
 
if not hasattr(sys, 'XSI_LOGGING_CONFIGURED'):
	logging.setLoggerClass(XSILogger)
	logging.addLevelName(45, 'FATAL')
	logging.addLevelName(5, 'COMMENT')
	log = logging.getLogger()
	xsiHandler = XSIHandler()
	xsiHandler.setLevel(0)
	fileHandler = logging.FileHandler(os.path.join(os.environ['XSI_USERHOME'], 'xsiScriptLog.txt'))
	fileHandler.setFormatter(logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s"))
	fileHandler.setLevel(45)
	log.addHandler(xsiHandler)
	log.addHandler(fileHandler)
	log.setLevel(0)
	sys.XSI_LOGGING_CONFIGURED = True
 
log = getLogger()
 
class XSIError(Exception):
	pass
 
class StopScriptError(XSIError):
	pass

Popularity: 67% [?]

Gerstner Waves 102

October 1st, 2008 by Patrick Boucher - Viewed 3339 times - Popularity: 64% [?]

During the Gerstner Wave 101 video I alluded to stacking multiple waves together to create more complex surfaces. The idea is that for each successive wave or octave that is added onto the effect, the number of waves be greater, the amplitude be smaller and the speed be slower.

Note: To see your full effect, make sure when you stack multiple waves onto each other, that the mute and solo checkboxes be clear and that the “last wave” checkbox be only active on the Gerstner Wave compound plugged lowest into the terminal node or your ICE tree.

embedded by Embedded Video

vimeo - Direct link to

If you wish to purchase this set of wave tools, you can go here.

Have fun!

Popularity: 64% [?]