Senior Thesis - "Embers" - Week 15

Updated: Apr 21

Hey guys its a big week! It's the last midterm of my college career and that means FX needs to be wrapping up this week so I can focus on look development and rendering.

End of last week I mentioned I made some big changes to the fire. Here is the new set up for creating the attribute flare.

I have six flares so I scattered six points on the same geometry as the source points and each one receives a flare_id, a dropframe and an endframe.

int range = chi("end_emit") - chi("start_frame") - chi("end_padding");
i@distrib = range / npoints(0);

i@dropframe = ((@flare_id + 1) * @distrib) + chi("start_frame");
i@endframe = @dropframe + @distrib;

From those things I can calculate and even distribution of flares to start with. I can match the frame range to original range I had and I added padding at the end so no flare received a dropframe too close to the end of the sim.

I delete every point except for the active one.

if(@Frame < @dropframe || @Frame >= @endframe) removepoint(0, i@ptnum);

That point then receives a pscale and a color.

From there is where it gets slightly complex. In a wrangle I have the source geometry as the first input and the active point as the second.

// calculate size of r based on where in the distribution the current frame is
// and how many frames (clamped by distrib) the flare should take
@r = 1 - ((point(1, "endframe", 0) - @Frame) / clamp(chi("speed_by_frame"), 1, point(1, "distrib", 0)));

// get distance from active point on source mesh
vector a = point(0, "P", i@ptnum);
vector b = point(1, "P", 0);
f@dist = fit(clamp(distance(a, b), 0, 1), 0, 1, 1, 0);

// distance threshold should be set between 0 and 1
// refine area affected by flare
if(@dist < ch("distance_threshold")){
    @dist = 0.0;

//store og dist in case
@og_dist = @dist;
@dist = fit(pow(@dist, ch("power_scale")), 0, 1, 0, 2);

int pts [ ] = pcfind_radius (1, "P", "pscale", rscale, @P, @r, chi("maxpts") );
int pts2 [ ] = pcfind_radius (1, "P", "pscale", rscale, @P, @r + width, chi("maxpts") );

if ( len( pts ) == 0 && len ( pts2 ) > 0)
    //vector color = point (1, "Cd", pts2 [0]);
    vector color = @dist;
    @Cd = color;
    @flare_scale = @dist;

@flare_scale_viz = @dist;

Here I am procedurally animating the spread of the attribute flare using pcfind and distance functions with that active point. I have refined that scaling value by settings a distance threshold to further clamp the gradient created from distance from the active point.

I also use a power function to concentrate the dist value and a fit to redistribute the scaling value within the affected area. The dist value is used to visualize and feeds the flare_scale attribute to be used later.


With the density of points, the scaling and resolution of simulation I did much earlier in the project, this method was very successful. I will shift my camera angles to better see the flares.

Matching speeds of all effects:

Something that has been challenging with project is matching the speeds of all the effects. Sometimes the levels of microsolvers changed the "speed" of the simulation because of scaling of the effect the solver had on the pyro result. As well as, I had to scale my simulations to achieve the voxel resolution I wanted without going to a crazy small number like 0.002. I had every simulation running at 0.1. It was reasonable for cache size, solve time and produced a result that rendered well.

Here is a comparison I did with the fire, smoke, and both the lid and spout steam. I felt the steam was off so I did a quick retime of 1.5x and 2x the original speed before I re-simulated.

I decided to move with something closer to 2x the original speed. The steam is more viscous than the smoke but I don't that difference to be so significant that it's questionable whether or not the speed is cohesive.

Particulate Recache with Adjustments:

I was decently happy with the last run of the particulate, but still didn't have that natural quality that I've been going for with this project. Initially after recaching the smoke I ran the particulate with the new velocities. I wasn't happy with the result.

First, I wanted to make changes with the emission based on temperature rather than random scattering of points. I created a simple emission density attribute with VEX. I also ended up changing the source velocities from the smoke to the flames.

vector center = point(1, "P", 0);
vector P = v@P;
f@dist = length(P - center);

@temp_density *= 1 - @dist;
@temp_density *= ch("scale");

I also added a third particulate simulation. This one is solely for the emission of the particulate that is caused by the flare from the flames. This has allowed for more control. Instead of feeding the smoke velocities like I had originally with the regular emission particulate, I fed in the flame velocities and adjusted those values to give me the result I wanted.

I ended up making the second force the advection is lerped to based on the age {0,0,0} because I applied a pop wind in the post-solve where each of the floats in the wind vector are decided by CHOPS added with an initial value to get it going in the right direction.

The result I'm happier with but still needs tweaks. I'll be making that top priority in my mentor meeting this week.

Burn Maps Setup:

This is a pretty simple setup to do. I wanted to accumulate from the the flame attribute of the fire volume. A burn attribute is generated at the source to define where "detonation" of the flames will occur. This attribute is very controllable for spreading fire (similar to legacy fuel, but is more predictable). This is merged with flame and a divergence fields.

I plugged in the clean geometry and the flame field into a sop solver where inside I have a wrangle that is accumulating and clamping a volume sample of the flame field.

@Cd = set(0,0,0);

float flame = volumesample(1, "flame", v@P);

f@burn += flame;
@Cd = set(clamp(@burn,0,1), 0, 0);

After the SOP solver, I have an attribute blur to soften the mask. I then write the @Cd.r to a float attribute called @burn_mask. I then copied that mask to the render geometry.

Midterm Checkpoint Flipbook:

I recut pretty the entire back half of the sequence. I shortened the portion with the slow motion effects to have the sudden time warp to be more sudden and not be over used. I trimmed about 2-3 seconds off the end as well. I also decided to remove the huge sweeping camera moves which will also benefit me in reducing the amount of environment to make.

In terms of FX, I am finished except for minor tweaks to the particulate and the ashes. This is fine because that doesn't affect my ability to move onto environment and look development because the cache time each time is under 5 minutes.

Now with all the big cache out of the way, I am able to run renders at night instead of cache. I can get started on rendering the volumes locally. I was going to use a third party service, but I am running about 2TB in FX cache which is not something I can facilitate at a reasonable cost.

75% of the way there in the timeline! Time to wrap up soon and be solely rendering.