First pass proof-reading

This commit is contained in:
2024-12-15 15:55:27 -05:00
parent a05acf6748
commit 1ce6137c17
7 changed files with 132 additions and 118 deletions

View File

@ -6,9 +6,9 @@ authors: [bspeice]
tags: []
---
So far, our `plot()` function has been fairly simple; map an input coordinate to a specific pixel,
So far, our `plot()` function has been fairly simple; map a fractal flame coordinate to a specific pixel,
and color in that pixel. This works well for simple function systems (like Sierpinski's Gasket),
but more complex systems (like our reference parameters) produce grainy images.
but more complex systems (like the reference parameters) produce grainy images.
In this post, we'll refine the image quality and add color to really make things shine.
@ -20,20 +20,21 @@ In this post, we'll refine the image quality and add color to really make things
This post covers sections 4 and 5 of the Fractal Flame Algorithm paper
:::
To start, it's worth demonstrating how much work is actually "wasted"
when we treat pixels as a binary "on" (opaque) or "off" (transparent).
One problem with the existing chaos game is that we waste work
by treating pixels as a binary "on" (opaque) or "off" (transparent).
If the chaos game encounters the same location twice, nothing actually changes.
We'll render the reference image again, but this time, count each time
we encounter a pixel during the chaos game. This gives us a kind of "histogram"
of the image:
To demonstrate how much work is wasted, we'll render the reference image again.
However, we'll also count each time the chaos game encounters a pixel.
This gives us a kind of image "histogram":
import chaosGameHistogramSource from "!!raw-loader!./chaosGameHistogram"
<CodeBlock language="typescript">{chaosGameHistogramSource}</CodeBlock>
When the chaos game finishes, find the pixel we encountered most frequently.
Finally, "paint" the image by setting each pixel's alpha value (transparency)
to the ratio of times encountered, divided by the maximum value:
When the chaos game finishes, we find the pixel encountered most frequently.
Finally, we "paint" the image by setting each pixel's alpha value (transparency)
to the ratio of times encountered divided by the maximum:
import CodeBlock from "@theme/CodeBlock";
@ -49,27 +50,27 @@ import {paintLinear} from "./paintLinear";
## Tone mapping
While using a histogram to paint the image improves the quality, it also leads to some parts vanishing entirely.
While using a histogram reduces the "graininess" of the image, it also leads to some parts vanishing entirely.
In the reference parameters, the outer circle is preserved, but the interior appears to be missing!
To fix this, we'll introduce the second major innovation of the fractal flame algorithm: [tone mapping](https://en.wikipedia.org/wiki/Tone_mapping).
This is a technique used in computer graphics to compensate for differences in how
computers represent color, and how people see color.
computers represent brightness, and how people see brightness.
As a concrete example, high dynamic range (HDR) photography uses this technique to capture
nice images of scenes with wide brightness ranges. To take a picture of something dark,
you need a long exposure time. However, long exposures can lead to "hot spots" in images that are pure white.
By taking multiple pictures using different exposure times, we can combine them to create
nice images of scenes with a wide range of brightnesses. To take a picture of something dark,
you need a long exposure time. However, long exposures lead to "hot spots" (sections that are pure white).
By taking multiple pictures with different exposure times, we can combine them to create
a final image where everything is visible.
In fractal flames, this "tone map" is accomplished by scaling brightness according to the _logarithm_
of how many times we encounter a pixel. This way, "dark spots" (pixels the chaos game visits infrequently)
will still be visible, and "bright spots" (pixels the chaos game visits frequently) won't wash out.
of how many times we encounter a pixel. This way, "cold spots" (pixels the chaos game visits infrequently)
will still be visible, and "hot spots" (pixels the chaos game visits frequently) won't wash out.
<details>
<summary>Log-scale vibrancy is also why fractal flames appear to be 3D...</summary>
<summary>Log-scale vibrancy also explains fractal flames appear to be 3D...</summary>
As explained in the Fractal Flame paper:
As mentioned in the paper:
> Where one branch of the fractal crosses another, one may appear to occlude the other
> if their densities are different enough because the lesser density is inconsequential in sum.
@ -89,15 +90,17 @@ import {paintLogarithmic} from './paintLogarithmic'
## Color
Finally, we'll introduce the last innovation of the fractal flame algorithm: color.
By including a color coordinate ($c$) in the chaos game, we can illustrate the transforms
responsible for each part of the image.
By including a third coordinate ($c$) in the chaos game, we can illustrate the transforms
responsible for the image.
### Color coordinate
Color in a fractal flame uses a range of $[0, 1]$. This is important for two reasons:
Color in a fractal flame is continuous on the range $[0, 1]$. This is important for two reasons:
- It helps blend colors together in the final image
- It allows us to swap in new color palettes easily
- It helps blend colors together in the final image. Slight changes in the color value lead to
slight changes in the actual color
- It allows us to swap in new color palettes easily. We're free to choose what actual colors
each color value represents
We'll give each transform a color value ($c_i$) in the $[0, 1]$ range.
Then, at each step in the chaos game, we'll set the current color
@ -139,14 +142,14 @@ import mixColorSource from "!!raw-loader!./mixColor"
Color speed values work just like transform weights. A value of 1
means we take the transform color and ignore the previous color state.
Similarly, a value of 0 means we keep the current color state and ignore the
A value of 0 means we keep the current color state and ignore the
transform color.
### Palette
Now, we need to map the color coordinate to a pixel color. Fractal flames typically use
256 colors (each color has 3 values - red, green, blue) to define a palette.
Then, the color coordinate becomes an index into the palette.
The color coordinate then becomes an index into the palette.
There's one small complication: the color coordinate is continuous, but the palette
uses discrete colors. How do we handle situations where the color coordinate is
@ -163,11 +166,11 @@ import colorFromPaletteSource from "!!raw-loader!./colorFromPalette";
<summary>As an alternative...</summary>
...you could also interpolate between colors in the palette.
For example: [`flam3` code](https://github.com/scottdraves/flam3/blob/7fb50c82e90e051f00efcc3123d0e06de26594b2/rect.c#L483-L486)
For example, `flam3` uses [linear interpolation](https://github.com/scottdraves/flam3/blob/7fb50c82e90e051f00efcc3123d0e06de26594b2/rect.c#L483-L486)
</details>
In the diagram below, each color in our palette is plotted on a small vertical strip.
Putting the strips side by side shows the palette used by our reference image:
Putting the strips side by side shows the full palette used by the reference parameters:
import * as params from "../src/params"
import {PaletteBar} from "./FlameColor"
@ -177,14 +180,15 @@ import {PaletteBar} from "./FlameColor"
### Plotting
We're now ready to plot our $(x_f,y_f,c_f)$ coordinates. After translating from color coordinate ($c_f$)
to RGB value, add that value to the image histogram:
to RGB value, add that to the image histogram:
import chaosGameColorSource from "!!raw-loader!./chaosGameColor"
<CodeBlock language="typescript">{chaosGameColorSource}</CodeBlock>
Finally, painting the image. With tone mapping, logarithms scale the image brightness to match
how it is perceived. When using color, we scale each color channel by the alpha channel:
how it is perceived. With color, we use a similar method, but scale each color channel
by the alpha channel:
import paintColorSource from "!!raw-loader!./paintColor"
@ -206,10 +210,10 @@ Next, introducing a third coordinate to the chaos game makes color images possib
the third major innovation of the fractal flame algorithm. Using a continuous
color scale and color palette adds a splash of color to our transforms.
The Fractal Flame Algorithm paper goes on to describe more techniques
not covered here. Image quality can be improved with density estimation
The Fractal Flame Algorithm paper does go on to describe more techniques
not covered here. For example, Image quality can be improved with density estimation
and filtering. New parameters can be generated by "mutating" existing
fractal flames. Fractal flames can even be animated to produce videos!
fractal flames. And fractal flames can even be animated to produce videos!
That said, I think this is a good place to wrap up. We were able to go from
an introduction to the mathematics of fractal systems all the way to