Transformations

Learn OpenGL - Transformations

We don't have a set of GLM bindings in Haskell, as far as I know, instead we'll use a library called linear, which does similar sorts of vector math.

Linear Practice

Let's see if we can translate a vector of (1,0,0) by (1,1,0). We should get (2,1,0) as our result. I've never used the linear package before, but it seems to have a Linear.Matrix module, which is probably what we want. Ctrl+F shows no results for "translate", but "translat" matches several of the "translation" operations. mkTransformationMat seems like what we want.

mkTransformationMat :: Num a => M33 a -> V3 a -> M44 a
"Build a transformation matrix from a rotation matrix and a translation vector."

Okay, so clicking on M33 and such it seems those are all specific-dimension matrix and vector inputs. Seems good. Looking around a bit for "id", you can find identity, which can stand in for our rotation matrix for now (since we don't want rotation). Then we just give the V3 value and we get our translation matrix. To mix that with the vector we want to move, there's a lot of funny looking operators at the top of the module for all sorts of matrix and vector mixing. The one we want is (!*), "Matrix * column vector". So let's try all that in ghci:

>stack ghci

-- startup messages skipped

learnopengl> import Linear
learnopengl> let vec = V4 1.0 0.0 0.0 1.0
learnopengl> let trans = mkTransformationMat identity (V3 1 1 0)
learnopengl> trans
V4 (V4 1 0 0 1) (V4 0 1 0 1) (V4 0 0 1 0) (V4 0 0 0 1)
learnopengl> trans !* vec
V4 2.0 1.0 0.0 1.0

Cool, so we can kinda use the library already. One thing to keep in mind is that the linear package uses row-major representation for all of its matrix data.

Next we're going to make a matrix that rotates our pictures by 90 degrees counter-clockwise (widdershins) and then scales it down to 0.5 of normal. For this we'll need to use the Linear.Quaternion module as well. The axisAngle function seems like exactly what we want. It takes an axis as a V3 and an angle in radians and gives us the Quaternion to make such a rotation back. Our axis is (0,0,1), and our angle is 90 degrees, which is pi/2 radians. Remember that there's 2*pi radians in a full circle, so the basic angles are usually just a fractional portion of that. There's all sorts of tables on any number of websites if you want it in chart form. We can use the (*!!) operator to scale a matrix.

Putting this all together, and applying a little pretty printing to the output ourselves, we get something like this:

learnopengl> let rotQ = axisAngle (V3 0 0 1) (pi/2)
learnopengl> let rotM33 = fromQuaternion rotQ
learnopengl> let rotM33' = rotM33 !!* 0.5
learnopengl> mkTransformationMat rotM33' (V3 0 0 0)
V4 (V4 1.1102230246251565e-16 (-0.5)                 0.0 0.0)
   (V4 0.5                    1.1102230246251565e-16 0.0 0.0)
   (V4 0.0                    0.0                    0.5 0.0)
   (V4 0.0                    0.0                    0.0 1.0)

That looks... pretty close? Remember that the e-16 bits are like "*10^(-16)", so those are very small numbers.

Rotating A Face (Static)

Let's try this in our program.

-- setup our transformation
let rotQ = axisAngle (V3 (0::GLfloat) 0 1) (pi/2)
let rotM33 = fromQuaternion rotQ
let rotM33' = rotM33 !!* 0.5
let transformMatrix = mkTransformationMat rotM33' (V3 0 0 0)
transP <- malloc
-- remember that we need to switch from row-major to column-major when we give it to OGL.
poke transP (transpose transformMatrix)

And then in the main loop we also set our uniform value

-- the transform uniform
transformLoc <- glGetUniformLocation shaderProgram transform
glUniformMatrix4fv transformLoc 1 GL_FALSE (castPtr transP)

And now we've got a tiny rotated picture! Code

Rotating A Face (Dynamic)

Making the stuff rotate over time is as easy as getting the time value like we did for the pulsing green triangle example. We'll also apply a translation into the bottom right corner like the C++ version does.

-- Before Main Loop
-- setup our transformation pointer. It will be written to during
-- the Main Loop
transP <- malloc

-- During Main Loop
timeValue <- maybe 0 realToFrac <$> GLFW.getTime
let rotQ = axisAngle (V3 (0::GLfloat) 0 1) timeValue
let rotM33 = fromQuaternion rotQ
let rotM33' = rotM33 !!* 0.5
let transformMatrix = mkTransformationMat rotM33' (V3 0.5 (-0.5) 0)
poke transP (transpose transformMatrix)
transformLoc <- glGetUniformLocation shaderProgram transform
glUniformMatrix4fv transformLoc 1 GL_FALSE (castPtr transP)

And now it rotates in place.

Final Code

results matching ""

    No results matching ""