In this post, we’ll restrict our discussion to the transformation that turns world space coordinates into eye space coordinates. We’ll call the matrix that performs this transformation the view matrix $\mathbf{V}$. What does $\mathbf{V}$ look like? When I first learned computer graphics, the structure of the view matrix was just handed down to me from on high. But you can only do so much with someone else’s magic.
We have the following four constraints to help us figure out what $\mathbf{V}$ looks like.
Let’s make our constraints a bit more precise by considering how the transformation turns world space coordinates into eye space coordinates.
We can put these four vector multiplications together into a single matrix-matrix multiplication.
Notice the structure of our matrix of viewer parameters. It is the product of a translation matrix $\mathbf{T}$ and a rotation matrix $\mathbf{R}$. Additionally, the right-hand side is the identity matrix.
Let’s solve for $\mathbf{V}$. We pull a few identities out of our hat to make this happen. First, multiplying a matrix by its inverse turns it into the identity matrix. Second, multiplying a matrix by the identity yields the original matrix. Third, the inverse of a product is the product of its operands’ inverses, commuted.
In separating our parameter matrix into $\mathbf{R}$ and $\mathbf{T}$, we have made finding the inverses easier. The job of the inverse of a transformation is to undo the transformation. The inverse of a translation, then, is just a translation by the negative offsets. The inverse of a rotation matrix “unrotates,” and the inverse just happens to be the transpose—though I do not find this obvious. Therefore, we have the following construction for $\mathbf{V}$:
One could also arrive at this matrix by thinking of it as a translation of the viewer to the origin and then a rotation the viewer’s frame to align with the standard axes. I prefer the construction above because I don’t trust myself to understand rotation matrices.
]]>I absently connected in my mind the three sides of a triangle to the three components of an RGB color. If I treated the red, green, and blue intensities as the lengths of the three line segments, which colors could make triangles?
This seemed like a job for volume rendering, but my volume renderer is in a state of disrepair. So, I just generated a bunch of slices across the color cube with this Kotlin code:
for (b in 0..255) {
val image = BufferedImage(256, 256, BufferedImage.TYPE_INT_RGB)
for (g in 0..255) {
for (r in 0..255) {
if (r + g > b && r + b > g && b + g > r) {
image.setRGB(r, g, Color(r, g, b).rgb)
}
}
}
val path = String.format("frame%03d.png", b)
ImageIO.write(image, "png", File(path))
}
Red increases along the x-axis, green along the y-axis, and blue along the z-axis. In this animation, I advance along the z-axis, slice by slice:
I think I like the complement better:
But I really think I need to get my volume renderer working again. And maybe write it in Javascript this time.
]]>If it’s not terrible enough, reload the page.
I saw a clock like this many years ago in a Pink Floyd video, but I can’t find that video now. If you know it, please share in a comment!
Perhaps a phone number widget would be assignment 2.
]]>
When I first encountered the problem, I just hacked away until I found some scale factors and logic that did the trick. But now that I’ve solved it N times, I need a little more certainty that I am doing the right thing. So, I sat down to prove to myself that my solution works.
]]>
]]>
A right-handed coordinate system is one that can be modeled on your right hand. Point your index finger along the positive x-axis ([1 0 0]). Point your middle finger along the viewing direction. When you take the cross product of your index finger and the viewing direction, you get the positive y-axis ([0 1 0]). For index-cross-middle to produce thumb, the direction the middle finger points must be along the negative z-axis ([0 0 -1]). This is easily proven with your favorite cross product calculator.
In other words, we can define a right-handed coordinate system as one where taking the cross product of [1 0 0] with the viewing direction produces [0 1 0]. The only viewing direction that allows this and is orthogonal to both these vectors is [0 0 -1].
A left-handed coordinate system can be defined as one where taking the cross product of the viewing direction and [1 0 0] produces [0 1 0]. (Note the order has flipped.) The only viewing direction that allows this and is orthogonal to both these vectors is [0 0 1].
]]>However, I’m a sucker for a transformation problem. I spent some time thinking about it in 1-D dimension. These are my givens:
Somehow I needed to calculate P. After a few wrong turns, I realized that this was a similarity problem. Traveling from P to C was a certain proportion of traveling from P to A. Traveling from P to D was also a certain proportion of traveling from P to B. The correct P would make these proportions equal, so that as we scaled up CD around P, C would reach A and D would reach B at the same time.
After setting up my ratios, I was able to solve for P:
This only solves for the pivot point in one dimension. The pivot coordinate for other dimensions can be found similarly, using the other dimensions’ source and destination bounds.
I tested the calculation in two-dimensions with a little HTML5 canvas. One can randomly place a red source rectangle and zoom it into fit the destination black rectangle.
]]>Occasionally I need to be back out of eye space and get back to object space, so I need the inverse of my modelview matrix. Finding an arbitrary inverse is non-trivial, but finding the inverse of a matrix that simply rotates and translates is simpler.
Unluckily, someone thought a logarithmic scale would be effective here. To match, I think I’ll have to make my data usage exponential as the month progresses.
]]>