Rotating camera in 3D?

I’m trying to rotate the cameras view, so I was wondering if there is a way to rotate the camera and the cameras view on a Y axis, without having to manipulate the cameras look X and look Z? It just seems like it would be a lot simpler if there was.

I made a class to help with this a long time ago.

Cam3D = class()

function Cam3D:init()
    self.pos = vec3(0, 5, 0)
    self.rot = vec3(90, 0, 0)
    self.fov = 45
    self.clip = 8192
    self.dist = 0
end

function Cam3D:pose()
    local dy = math.cos(math.rad(self.rot.x))
    local xzMult = 1 - math.abs(math.cos(math.rad(self.rot.x)))
    local dx, dz = math.sin(math.rad(self.rot.y)) * xzMult, math.cos(math.rad(self.rot.y)) * xzMult
    local upx = math.sin(math.rad(self.rot.z + self.rot.y))
    local upy = math.cos(math.rad(self.rot.z))
    local upz = math.cos(math.rad(self.rot.z + self.rot.y))
    camera(self.pos.x - dx * self.dist, self.pos.y - dy * self.dist, self.pos.z - dz * self.dist, self.pos.x + dx, self.pos.y + dy, self.pos.z + dz, upx, upy, upz)
    perspective(self.fov, WIDTH / HEIGHT, 0.05, self.clip)
end

function Cam3D:getRot()
    local dy = math.cos(math.rad(self.rot.x))
    local xzMult = 1 - math.abs(math.cos(math.rad(self.rot.x)))
    local dx, dz = math.sin(math.rad(self.rot.y)) * xzMult, math.cos(math.rad(self.rot.y)) * xzMult
    local upx = math.sin(math.rad(self.rot.z + self.rot.y))
    local upy = math.cos(math.rad(self.rot.z))
    local upz = math.cos(math.rad(self.rot.z + self.rot.y))
    return 0 - dx * self.dist, 0 - dy * self.dist, 0 - dz * self.dist, dx, dy, dz, upx, upy, upz
end

Just create a new Cam3D object, and you can set its pos and rot vec3s to translate and rotate it. Call its pose() function to position the camera and set the perspective (before you draw your 3D stuff)

@SkyTheCoder thanks, i’ll try that out and see if it helps me to make sense of this.

@jrohanian - you may find this a bit simpler to work with. Slide the angle parameter to look left and right. All you really need from the code below is the AdjustCamLook function. Just run it each time you change the viewing angle, to adjust the camera look values.

NB if the function stuff at the end of the parameter line is confusing, don’t worry about it, it simply runs AdjustCamLook for you whenever you change Angle, and the parameter is only there just to show you it works.

function setup()
    parameter.number("Angle",-90,90,0,function() AdjustCamLook(Angle) end)
end

function AdjustCamLook(r)
    r=math.rad(r)
    camLook=vec3(math.sin(r),0,math.cos(r))*100
end

function draw()
    background(50)
    perspective()   
    camera(0,0,300,camLook.x,camLook.y,camLook.z)
    sprite("Planet Cute:Character Princess Girl",0,0)
end

@Ignatz But with that, can you look up and down, and tilt the camera? :wink:

no, but he just wanted to rotate on y!

@ignatz Looks great, what is the importance of using math.rad?

@SkyTheCoder your code looks like something I might have to work up to a bit, though.

Codea’s trig functions use radians not degrees, so you need to convert them

@SkyTheCoder - wrt your code, why do you recalculate all the settings at each draw? Why not calculate them just once when the rotation changes (how does your class allow changes, btw), and store the camera vector for use in draw?

@Ignatz It didn’t occur to me to calculate them only once - easy enough to fix

Cam3D = class()

function Cam3D:init()
    self.pos = vec3(0, 5, 0)
    self.rot = vec3(90, 0, 0)
    self.fov = 45
    self.clip = 8192
    self.dist = 0
    self.lastPos = vec3(self.pos.x, self.pos.y - 1, self.pos.z)
    self.lastRot = vec3(self.rot.x, self.rot.y - 1, self.rot.z)
    self.dy = 0
    self.xzMult = 1
    self.dx, self.dz = 0, 1
    self.upx = 0
    self.upy = 1
    self.upz = 0
end

function Cam3D:pose()
    if self.pos.x ~= self.lastPos.x or self.pos.y ~= self.lastPos.y or self.pos.z ~= self.lastRot.z or self.rot.x ~= self.lastRot.x or self.rot.y ~= self.lastRot.y or self.rot.z ~= self.lastRot.z then
        self.dy = math.cos(math.rad(self.rot.x))
        self.xzMult = 1 - math.abs(math.cos(math.rad(self.rot.x)))
        self.dx, self.dz = math.sin(math.rad(self.rot.y)) * self.xzMult, math.cos(math.rad(self.rot.y)) * self.xzMult
        self.upx = math.sin(math.rad(self.rot.z + self.rot.y))
        self.upy = math.cos(math.rad(self.rot.z))
        self.upz = math.cos(math.rad(self.rot.z + self.rot.y))
    end
    camera(self.pos.x - self.dx * self.dist, self.pos.y - self.dy * self.dist, self.pos.z - self.dz * self.dist, self.pos.x + self.dx, self.pos.y + self.dy, self.pos.z + self.dz, self.upx, self.upy, self.upz)
    perspective(self.fov, WIDTH / HEIGHT, 0.05, self.clip)
    self.lastPos = vec3(self.pos.x, self.pos.y, self.pos.z)
    self.lastRot = vec3(self.rot.x, self.rot.y, self.rot.z)
end

function Cam3D:getRot()
    return 0 - self.dx * self.dist, 0 - self.dy * self.dist, 0 - self.dz * self.dist, self.dx, self.dy, self.dz, self.upx, self.upy, self.upz
end

(untested, not on iPad)

@ignatz , I used your code, then modified it a bit to see if it work for what I am trying to do, and it does, but I notice once I rotate to approximately 180 degrees, The cameras rotation will skip forward. It’s really kind of weird. The way I am using it is calling the function every frame with a variable, angle, then adding to and subtracting from the variable to rotate. Is this because of sine and cosine?

@jrohanian - it’s hard to say without seeing exactly how you rotated, but once you start rotating in 3 dimensions, you can’t just add new rotations to previous ones, and there can be discontinuities and problems at 90 and 180 degrees depending on circumstances.

It’s taken me about a year to partly understand it, and I am writing an ebook about it, which I hope to finish.

This post of mine describes some of the problems. It’s not totally accurate, but will give you some idea.

https://coolcodea.wordpress.com/2013/12/28/142-3d-rotations-flying-a-plane/

You are doing rotations in a classic camera on a tripod form, which is what you want for most things. I did some code yonks ago for totally free rotation in space where the camera can roll upside down/sideways etc. If anyones interested I can dig that out.

@Spacemonkey for sure!

I have a quaternion rotation class which is the right way to deal with incremental rotations in any direction, but it takes a little explanation

Yeah, mines Quartenion based. Can’t explain it, I think I understood it when I did it, but not any more…

Grab Andrew Stacey’s quartenion library here and stick it in a tab http://www.math.ntnu.no/~stacey/documents/Codea/Library/Quaternion.lua

Then I have a camDir and camUp I define in setup

camDir = vec3(0,0,1)
    camUp = vec3(0,1,0)

Draw with

camera(position.x, position.y,position.z, position.x+camDir.x, position.y+camDir.y, position.z+ camDir.z, camUp.x, camUp.y, camUp.z)

Where position is where in 3d space the camera is.

Finally in touch

--get the horizon for vertical rotation
            q = Quaternion.Rotation(math.rad(90),camUp)
            horizontal = camDir^q
            --rotate camPos around vertical
            q = Quaternion.Rotation(math.rad(touch.deltaX/3),camUp)
            camDir = camDir^q
            --rotate camPos and camUp around horizontal
            q = Quaternion.Rotation(math.rad(touch.deltaY/3), horizontal)
            camDir = camDir^q
            camUp = camUp^q

The /3 is just a factor for how fast I wanted it to rotate…

@ignatz yeah I read the section of your eBook on quaternions, but most of it went right over my head. I think i’ll go through it again.

It still goes over my head… @-) There are several ways to derive them - 4D geometry, matrix math, and imaginary numbers - and one day I hope to understand just one of them.

@ignatz as long as I know how to use it i’ll try not to worry too much about how it works.