Learn more about the project
The scene was heavily inspired by some of the more memorable moments in the 2009 Video Game: Assassin's Creed 2, which mainly takes place in a historical rendition of Renaissance Venice.
Originally, our ambition was to represent a view from the streets of Venice, with the fireworks reflecting in the water. However, we quickly realized that this was way too ambitious, and would require more assets than we could find. We therefore decided to take the one best looking asset we could find, the mask represented in our final scene, and focus our attention on that instead, hoping to have the fireworks visible in the reflections on the mask.
hours Rendering Time
Upon first attempting to render the mask, we quickly realized that it had an absurd amount of triangles (600,000). Therefore, with the use of Blender, we manually reduced the amount of triangles for each object composing the mask. In the end, we ended up with a total of 40,969 triangles, which, when using with our BVH, was relatively quick to render.
After this process, we had to choose the location of the fireworks. We wanted them to seem natural while also not spawning them randomly so that we had more control over the lighting conditions and reflections on the mask. For this purpose, we rendered a low definition image with 1 sample per pixel, and only 1 (big) particle per firework. Once we found the ideal positions, we started working on the materials of the mask. The final positions of the fireworks are represented by the spheres in the pictures on the right. As becomes obvious from the second picture, most fireworks are behind the mask. This was a conscious choice so as to have the lighting coming only from the right side of the mask, to add a more interesting effect.
Each component of the mask is imported seperately and has its own Combined Material. For the inner, reflective part of the mask, we attempted to extend the perlin noise texture to replicate marble, as it was shown on the lecture slides. The materials used are different combinations of phong, lambertian, and mirror materials. Since we unfortunately could only define a single color for the whole material, as we had no textures, a lot of the detail from the original model was lost, but we offset that with the lighting from the fireworks.
Creating believable Fireworks
On the lowest level, Fireworks are composed of tons of individual particles. For this implementation, we introduced a new class of Solids
solids/particle.h. The difference between particles and solids is that particles have a specific trajectory, which change their position based on a time parameter.
When they are created, particles get assigned a direction, angle, and initial velocity, which is then used to calculate their trajectory.
Particles also make use of a ParticleMaterial
materials/particlematerial.h, which can also change its color based on the time, and is purely emmissive.
When calling the traditional
intersect method on a particle, the position is set based on the default time value (1, with 0 being it's origin and 1 being it's final position at the time of rendering the image).
However, the particle also has a new
intersect method, which takes an additional time parameter. This time parameter modifies the position, size, and material properties of the particle.
We can now randomize the time as we see fit, for the final results we use a 50% probability of the particle being at it's final position, with the remaining rays intersecting the particle at random times between its creation and final position.
This creates the trail of each particle, based on the principle of Motion Blur.
The bounding box of each particle makes use of the derivative of the trajectory function to calculate its maximum and minimum points in each direction.
One single particle isn't interesting enough, so the Particle Emitter's
primmod/particleemitter.h job is to initialize a large amount of particles with a certain amount of randomization.
The particle emitter can create a certain amount of particles, each having the same origin, velocity, material and finaly time (so that they are all at the same percentage in their trajectory at the time of rendering the image), but different directions.
For the final submission, each firework is created by its own emitter. The direction is chosen at random, but the emitter can also be configure to emit them all in a general direction within a certain angle.
The Particle System
groups/particlesystem.h is responsible for containing all particles emitted from a certain particle emitter. Since the bounding boxes of each particle within a system overlap, we implemented the particle system as a modified BVH
makes use of the time parameter introduced for the particles. Basically, upon intersecting a particle system, a ray is assigned a certain time, and the Particle System then checks for intersection with the particles it contains at that certain time.
Additionaly, this system is perfect in this scenario as it creates a seperate BVH for all particles that are very close to each other and whose bounding boxes often intersect.
Lastly, particles need to emmit light. Point lights were not an option, as they only emmit but cannot be intersected themselves, and therefore cannot be seen in vacuum.
Therefore, we implemented a variation of the Area Light, called the Particle Light
Rather than having a different light source for each particle, the whole particle system contained inside the ParticleLight is considered as the light source. Upon sampling this light source, the particle system then chooses one particle at random that it contains and returns its sample.
This greatly enhances performance as opposed to having each particle being its own light with its own shadow rays.
Ideally, we would have wished to make use of volumetric fog around the fireworks to emphasize the lighting emmited by them, but unfortunately we could not get it to work efficiently in time, so we scrapped this feature.
Features used for this submission
Implemented by Simon Lebailly in
Implemented by Christian Schmidt in