top of page

Senior Thesis - "Embers" - Archived Part 2

Updated: Jan 2, 2022

I've been a little awol on the blog posts for the past several weeks, so let's get up to speed. We are now at week 11 of my thesis project. I've been working on finishing up getting the guts of the FX completed and rerunning simulations.

To better use my time during the week I will be working on look development Monday-Wednesday and then FX simulation, refinement Thursday - Sunday until that portion is completed. After that, it will be look development everyday.

Schedule until midterm (and a little beyond):

Polishing of FX, completed cache and post process

Camera and layout of environment is completed

Texturing is 60% completed

Environment lighting is 50% completed

Compositing and camera effects are set up by week 7

Final rendering will begin the beginning to the middle of week 8 to leave approximately two weeks to save money on outsourcing by local rendering what I can (mainly FX rendering so I don't also need to be paying for simulation because the cache is too large to upload).

Tuesday Progress:

This week I have finished loading up quixel textures for renderman and applying corrections to those, and beginning initial environment layout.

Environment Reference:

I'm mostly leaning towards the two set up on the right with the forest fire and the fire under the hanging kettle. The main aspects that I want to build in the environment are the forest ground and piles of natural debris; the tall, mature trees in the background; the atmosphere from the smoke and morning fog; and light coming through the trees.

Render of textures:

Front and back views of the firepit set up with basic environment lighting.

I want to move a few of the logs around a bit and increase the height of the kettle base. I'll fix those things before recaching the FX because I have to update the sourcing.

Brief overview of FX progress since last midterm:

Fire and Smoke:

Procedurally animating the "flare-up" during the slow motion part of the sequence; being efficient with the cache; refining smoke with microsolvers

The biggest challenge I'm running into now as I get closer to finalizing the fire and smoke simulations is cache size. With about 600 frames cached of just one fire simulation, I have almost 800 GB in cache. That's not absolutely terrible for that length of frames, but it can be improved with some post-processing. That is something I will be focusing on after I complete the simulation.

I've been focusing on animating the "flare-up" in SOPS using VEX. I achieve that simply by defining a frame range and fitting that to a ramp where I can use curves to make the "flare-up" instead of keyframes. I began with keyframes on the volume source in DOPs, but that wasn't very intuitive for making changes.

Using channels where I can define a specific start and end frame worked really well for copying those parameters to filecaches and other nodes where that information is needed. Example: running a filecache for the non slo-motion frames is set to only write a file for every integer frame; the filecache for the slo-motion frames is set to write a file for every substep.

Affecting the temperature cause the flare to be too extreme because it increased the length of the flames and made them too long. I'm currently simulating by affecting only the velocity and burn attributes.

float mff = ch("mff");
float mlf = ch("mlf");
float preroll = ch("preroll");

f@adjust = chramp("Scale_Attribute", fit(@Frame, mff, mlf, 0, 1));
@adjust += 1; //minimum to 1
//@adjust *= 1.5;

vector v_noise = fit(noise(v@P*1.75+@Time*0.5), {0, 0, 0}, {1, 1, 1}, {-1, -1, -1}, {1, 1, 1});
float f_noise = fit(noise(v@P*chf("freq")+7193+float(i@ptnum)*chf("id_mult")+@Time*chf("time_mult")),0, 1, 0, 1);

//v@Cd = v_noise;

if (@Frame >= (mff+1) && @Frame <= (mlf-1)){
    //@temperature += @adjust;
    //@temperature = max(@temperature, point(1, "temperature", i@ptnum));
    @burn *= (@adjust * 1.5);
    //@burn = max(@burn, point(1, "burn", i@ptnum));
    v@v *= (@adjust + v_noise);
    //v@v = max(v@v, point(1, "v", i@ptnum));

For the smoke I was mainly working on the microsolvers being applied to it. I was experimenting with applying multiple layers of turbulence for more detail. It did that, but it also caused the smoke to move way too fast even though the scale was very small. The accumulation of force was too much for what I wanted.

My mentor suggested that if I am able to achieve a better result with less microsolvers that is a fine solution. More solvers and more complicated the set-up doesn't always necessarily mean a better result.

Task list for the weekend with pyro:

- rerun pyro simulation with corrections to attribute "flare"

- rerun smoke simulation with corrections to mircosolvers


Updates on the advection logic; using both POPs and RBDs; geometry development of results.

The main challenge I've had with the embers is the logic of the advection. My mentor, Krzysztof, has been a great resource for me in this area of the project. As per my reference:

I want the ember density and energy to be concentrated towards the hottest parts of the fire when they are born and as they get older they move out away from the fire and float down, dying before they hit the ground.

How this has been done is by creating a direction vector per point that points radially outward from the center of the firepit.

vector center = chv("mycenter"); //or can fed in an input point
vector P = v@P;
P.y = 0;
v@dir = normalize(P - center);

Inside the DOP net, there are wrangles that apply flutter dynamics and spin after the pieces have been advected by applying operations to the angular velocities. Those wrangle were provided to me by my mentor, as well as a solver that injects new RBDs when called (essentially updates the packed object so the RBD solver doesn't get upset).

In my advection wrangle, I am using the volume velocities from the smoke simulation to apply force with the duration based on a age attribute that I have created. Once the piece is old enough, then the @dir vector is applied to move the pieces away from the fire and down to the ground.

vector fieldV =  volumesamplev(1,"vel",v@P);

f@lifespan = fit(rand(@id*1234.432),0,1,ch("Burn_Min"),ch("Burn_Max"));
f@created_age = (@Frame - f@start_frame)/24;

float  local  =  fit(f@created_age, chf("age_min"), chf("age_max"), 1 , 0);

v@force = lerp((v@dir * 0.1), (fieldV * 0.1), local);
v@force *= local;

This logic is applied to the hero POP and RBD simulations for the embers.

In terms of rendered geometry, the very dense POP result is trailed for 3 frames, added a poly lines through those points based on a name attribute, and used a polywire node to make it render-able with Renderman. For the RBD pieces, I procedurally generated 112 (count based on name attribute) different ember "flakes." Those are then copied to points when emitted.

Ember Variants


Writing a logic to check if the whole piece is active to avoid stretching; refining location of ash emission and geometry

Each piece of geometry is fractured using a voronoi fracture and a layers of boolean fractures to make sure there are no large and/or regular pieces left.

The points of those pieces are fed into a POP network where a geometry wrangle determines which pieces are active and forces are applied, and are constrained with a popgrains node. This allows for a tearing/peeling effect.

The resulting cache "P" is copied over to the geometry that will be rendered.

Task list for this weekend on the embers/ashes:

- Set up new logic in POPS network to use for polywire geometry

- Refine logic in RBD network and lower the count of RBD pieces

- Set up based workflow for these simulation (same as simple advection based on camera)

- Write logic check for ashes, refine geometry

Burning Log Shader Progress:

Renderman shader built in Houdini 18.5 in MAT network

Above I have some ambient occlusion maps that I pulled from Quixel to use as bases to six variations of a burning log texture that will be mixed with the individual, original log textures. I then imported the maps for specular face, specular roughness, bump normals and displacement, also all from Quixel.

These maps work really well for pulling float masks to use for mixing color and gain values because they are very clear and have smooth gradients. I also inverted the map in photoshop to use in the reverse. I applied clamps on those maps to isolate values and multiplied those against the colors I wanted. Those were than added together. I stuck to using PxrSeExpr for mixing and layering instead of the actual mix nodes.

The shader is definitely doing the job it needs to but will need to be enhanced with light linking. I will also have to create variations for the primitives whose shader is the non-bark part of the wood, and the cap primitives of the log.


Left: burned/not on fire Middle: color-graded on fire/burned Right: on fire/ high temp


bottom of page