#### Howdy, Stranger!

It looks like you're new here. If you want to get involved, click one of these buttons!

# Rotating camera in 3D?

edited January 2015 Posts: 136

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.

Tagged:

• edited January 2015 Posts: 1,976

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 xzMult = 1 - math.abs(math.cos(math.rad(self.rot.x)))
local upx = math.sin(math.rad(self.rot.z + self.rot.y))
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 xzMult = 1 - math.abs(math.cos(math.rad(self.rot.x)))
local upx = math.sin(math.rad(self.rot.z + self.rot.y))
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)

• Posts: 136

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

• Mod
edited January 2015 Posts: 5,396

@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()
end

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

``````
• Posts: 1,976

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

• Mod
Posts: 5,396

no, but he just wanted to rotate on y!

• Posts: 136

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

• Posts: 136

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

• Mod
Posts: 5,396

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

• Mod
Posts: 5,396

@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?

• edited January 2015 Posts: 1,976

@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
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
``````

• edited January 2015 Posts: 136

@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?

• Mod
Posts: 5,396

@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/

• Posts: 455

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.

• Mod
Posts: 3,297

@Spacemonkey for sure!

• Mod
Posts: 5,396

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

• Posts: 455

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
horizontal = camDir^q
--rotate camPos around vertical
camDir = camDir^q
--rotate camPos and camUp around horizontal
camDir = camDir^q
camUp = camUp^q
``````

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

• Posts: 136

@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.

• Mod
Posts: 5,396

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.

• edited January 2015 Posts: 136

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

• edited January 2015 Posts: 300

This is a pretty good explanation of quaternions

• edited January 2015 Posts: 136

@ignatz @Crumble but according to this video quaternions are never needed for single axis rotations so why does this bother me when I am only rotating on the Y-axis? Could it be because I am misusing my eulers?

• Mod
Posts: 5,396

@jrohanian - I would have thought you would be ok rotating on just y. Maybe you should post some code that shows this behaviour.

• Posts: 136

its just me experimenting with some of the stuff in ignatz ebook

``````-- hello 3d

-- Use this function to perform your initial setup
function setup()
camX,camY,camZ=5,100,-100
lookX,lookY,lookZ=1,15,1
X,Y,Z=50,0,-1000
moveZ=5
angle = 0
arg = 1
vy = 0
camLook = vec3(0,0,0)
argc = 1
RX,RY,RZ=0,0,0
RX2,RY2,RZ2=0,0,0
targetAngle = 0
rX,rY,rZ=0.1,0.5,1.0
mapLevel = 0
SetupCube(25)

end
function SetupCube(s)
--sprite("Platformer Art:Block Brick")
b = Tile(("Platformer Art:Block Brick"), 1)
a = Tile(("Platformer Art:Block Brick"), 3)
h = Tile(("Platformer Art:Block Brick"),1)

end
-- This function gets called once every frame
function draw()
print("look x "..lookX.." and look z "..lookZ.." angle "..angle)
-- This sets a dark background color
background(220)
perspective() -- tells codea we is in the third dimension
camera(camX,camY,camZ,camLook.x,camLook.y,camLook.z)

a.mesh:draw()
b.mesh:draw()
h.mesh:draw()

if CurrentTouch.tapCount == 3 then vy = vy + 1 end

end

camLook=vec3(math.sin(r),1,math.cos(r))*100
end

wallFloorLibrary = class()

--This class tiles an image across a rectangle of any size and provides a mesh
--You can add as many rectangles as you like to the same mesh
--Each rectangle must be vertical or horizontal, not at an angle, ie floor, roof or walls
Tile=class()

--img = image name or the image itself
--s = scale of image (0.5 reduces by half, 2 doubles its size,
function Tile:init(img,s)
self.img=img
self.iw,self.ih=img.width,img.height
self.mesh=mesh()
self.mesh.texture=self.img
self.scale=s
self.v,self.t={},{}
end

--The parameters are as follows
--p1 = vec3(x,y,z) = a corner position
--p2 = vec3(x,y,z) = the diagonally opposite corner position
--s = scale of image (0.5 reduces by half, 2 doubles its size, default is what was provided in the init function)
s=s or self.scale
local w,h=self.img.width*s,self.img.height*s
local d=p2-p1
local v,t
if d.x==0 then
v={vec3(p1.x,p1.y,p1.z),vec3(p1.x,p1.y,p2.z),vec3(p1.x,p2.y,p2.z),vec3(p1.x,p2.y,p1.z)}
t={vec2(0,0),vec2(d.z/w,0),vec2(d.z/w,d.y/h),vec2(0,d.y/h)}
elseif d.y==0 then
v={vec3(p1.x,p1.y,p1.z),vec3(p2.x,p1.y,p1.z),vec3(p2.x,p1.y,p2.z),vec3(p1.x,p1.y,p2.z)}
t={vec2(0,0),vec2(d.x/w,0),vec2(d.x/w,d.z/h),vec2(0,d.z/h)}
elseif d.z==0 then
v={vec3(p1.x,p1.y,p1.z),vec3(p2.x,p1.y,p1.z),vec3(p2.x,p2.y,p1.z),vec3(p1.x,p2.y,p1.z)}
t={vec2(0,0),vec2(d.x/w,0),vec2(d.x/w,d.y/h),vec2(0,d.y/h)}
else return nil
end
local seq={1,2,3,3,4,1}
for i=1,6 do
table.insert(self.v,v[seq[i]])
table.insert(self.t,t[seq[i]])
end
self.mesh.vertices=self.v
self.mesh.texCoords=self.t
end

uniform mat4 modelViewProjection;
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;

void main()
{
vColor = color;
vTexCoord = texCoord;
gl_Position = modelViewProjection * position;
}

]],

precision highp float;
uniform lowp sampler2D texture;
varying lowp vec4 vColor;
varying highp vec2 vTexCoord;

void main()
{
lowp vec4 col = texture2D( texture, vec2(mod(vTexCoord.x,1.0), mod(vTexCoord.y,1.0)));
gl_FragColor = col;
}

]]}
``````
• Mod
Posts: 5,396

So where is the problem?

• Posts: 136

@Ignatz same thing, it skips from 180 degrees of rotation to about 270.

• Mod
edited January 2015 Posts: 5,396

Try this for camLook instead of what you have

``````camLook=vec3(math.sin(r)*1000,1,math.cos(r)*1000)
``````
• Posts: 136

@Crumble do you have an example of applying quaternions in code? I think I sort of understand them.

• edited January 2015 Posts: 300

@jrohanian No sorry, I had to learn about them in one of the math classes (linear algebra I believe) for my major. Have never applied them in code, and hoped I would never see them again.

• Posts: 509

I wrote a quaternion library for Codea. Search the forum for quaternions to find it.

• edited January 2015 Posts: 505

I liked that video, took some code from the forum to visualize the quaternion rotation in Codea:

``````function setup()
parameter.number("W",-2,2,1)
parameter.number("X",-2,2,0)
parameter.number("Y",-2,2,0)
parameter.number("Z",-2,2,0)
m = mesh()
m.texture = "Space Art:Red Ship"

time = {value=0}
tween(.5, time, {value=1}, {loop=tween.loop.pingpong})
end

function draw()
background(57, 57, 57, 255)
perspective()
camera(0,0,5,0,0,0,0,1,0)

local t = time.value
pushMatrix()
translate(-.8,1.5)
scale(.3)
m:draw()
translate(1.5,0)
m:draw()
translate(1.5,0)
m:draw()
translate(1.5,0)
m:draw()
popMatrix()

m:draw()
end

//
//

//This is the current model * view * projection matrix
// Codea sets it automatically
uniform mat4 modelViewProjection;

// Parameters set by user
uniform vec3 centre;
uniform vec4 q;

//This is the current mesh vertex position, color and tex coord
// Set automatically
attribute vec4 position;
attribute vec4 color;
attribute vec2 texCoord;

varying lowp vec4 vColor;
varying highp vec2 vTexCoord;

// Basic quaternion functions
mediump vec4 qmult (mediump vec4 p, mediump vec4 q)
{
mediump float a = p.x * q.x - p.y * q.y - p.z * q.z - p.w * q.w;
mediump float b = p.x * q.y + p.y * q.x + p.z * q.w - p.w * q.z;
mediump float c = p.x * q.z - p.y * q.w + p.z * q.x + p.w * q.y;
mediump float d = p.x * q.w + p.y * q.z - p.z * q.y + p.w * q.x;
return vec4(a,b,c,d);
}

mediump vec4 qconj (mediump vec4 q)
{
return vec4(q.x,-q.y,-q.z,-q.w);
}

mediump vec3 qvmult(mediump vec4 q, mediump vec3 v)
{
mediump vec4 p = vec4(0,v);
mediump vec4 pq = qmult(q,qmult(p,qconj(q)));
return vec3(pq.yzw);
}

void main()
{
//Pass the mesh color to the fragment shader
vColor = color;
vTexCoord = vec2(texCoord.x, 1.0 - texCoord.y);
mediump vec3 pos = position.xyz/position.w - centre;
pos = qvmult(q,pos);
pos += centre;
//Multiply the vertex position by our combined transform
gl_Position = modelViewProjection * vec4(pos,1);
}

]], [[
//
//

//Default precision qualifier
precision highp float;

//This represents the current texture on the mesh
uniform lowp sampler2D texture;

//The interpolated vertex color for this fragment
varying lowp vec4 vColor;

//The interpolated texture coordinate for this fragment
varying highp vec2 vTexCoord;

void main()
{
//Sample the texture at the interpolated coordinate
lowp vec4 col = texture2D( texture, vTexCoord );
// colour by given colour
col*=vColor;
//Set the output color to the texture color
gl_FragColor = col;
}

]])
end
``````
• Posts: 136

thanks