Thread: opengl camera for beginners

  1. #1
    Registered User
    Join Date
    Aug 2012
    Posts
    9

    Question opengl camera for beginners

    Hi guys, I'm not totally new to opengl but my knowledge is a bit patchy.

    a few mentions before getting started.
    1. im working with gles20 on android
    2. i have a basic understanding of camera but not too great
    3. i'm fairly new to programming in general

    OK with that being said I can draw 3D shapes, I even wrote a .obj model loader and can load .obj files and render them with gles20 and shaders on android.

    I wanted to go and use that knowledge then I realize hey, I can't move so now I have to learn how cameras work.

    opengl has the lookat function which is OK but doesn't provide a lot of freedom of movement so from my research it seems a camera needs its own local coordinates;
    up vector, right vector, direction vector
    also a view, projection and combined matrices.
    a matrix is 16 element float array. I use android which has a what seems to be a decent matrix class for my needs so far. I also created a vec3 class since android doesn't have that, with basic vector3 functions.

    I create a camera class but I am unsure what to do next, I am looking for feedback and probably some guidance. I've looked at many online tutorials but following them I always seem to get stuck in the weeds.
    Code:
    import android.opengl.Matrix;
    
    
    public class Camera {
        vec3 pos;
        vec3 up;
        vec3 right;
        vec3 dir;
        int width, height;
        float near;
        float far;
        float[] viewMatrix;
        float[] projMatrix;
        float[] combMatrix;
        
        public Camera(int width, int height){
            float ratio = (float)width/height;
            float left = -ratio;
            float right = ratio;
            float bottom = -1.0f;
            float top = 1.0f;
            near = 1.0f;
            far = 100.0f;
            Matrix.frustumM(projMatrix, 0, left, right, bottom, top, bottom, top);
            
            float eyeX = 0.0f;
            float eyeY = 0.0f;
            float eyeZ = 0.0f;
            float centerX = 0.0f;
            float centerY = 0.0f;
            float centerZ = -1.0f;
            float upX = 0.0f;
            float upY = 1.0f;
            float upZ = 0.0f;
            Matrix.setLookAtM(viewMatrix, 0, eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
        }
        
        public void rotate(float angle, vec3 axis){
            Matrix.rotateM(viewMatrix, 0, angle, axis.x, axis.y, axis.z);
        }
        
        public void rotate(float angle, float x, float y, float z){
            Matrix.rotateM(viewMatrix, 0, angle, x, y, z);
        }
        
        public void translate(float dist, vec3 axis){
            Matrix.translateM(viewMatrix, 0, axis.x, axis.y, axis.z);
        }
        
        public void translate(float dist, float x, float y, float z){
            Matrix.translateM(viewMatrix, 0, x, y, z);
        }
        
    }
    any advice on my class so far, how would i use this class as a part of my rendering loop?

    Here is a sample of my simple rendering system. I load a texture and bind it to a cube, basically 2 triangles. Now I want to be able to use the camera class to be able to move my views around.

    Code:
    public class MyRenderer implements GLSurfaceView.Renderer {
    
    
        Context context;
        TextureLoader tLoader;
        int buf;
        int number;
        
        public MyRenderer(Context c){
            this.context = c;
            
            tLoader = new TextureLoader(this.context);
            
            
            vertsBuffer = ByteBuffer.allocateDirect(verts.length*4).order(ByteOrder.nativeOrder()).asFloatBuffer();
            vertsBuffer.put(verts).position(0);
            
            textBuffer = ByteBuffer.allocateDirect(text.length*4).order(ByteOrder.nativeOrder()).asFloatBuffer();
            textBuffer.put(text).position(0);
            
            indexBuffer = ByteBuffer.allocateDirect(index.length*2).order(ByteOrder.nativeOrder()).asShortBuffer();
            indexBuffer.put(index).position(0);
        }
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            vShader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
            GLES20.glShaderSource(vShader, vCode);
            GLES20.glCompileShader(vShader);
            
            fShader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
            GLES20.glShaderSource(fShader, fCode);
            GLES20.glCompileShader(fShader);
            
            sProgram = GLES20.glCreateProgram();
            GLES20.glAttachShader(sProgram, vShader);
            GLES20.glAttachShader(sProgram, fShader);
            GLES20.glLinkProgram(sProgram);
            
            GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
            
            buf = tLoader.loadTexture(R.raw.bkg);
            
            float eyeX = 0.0f;
            float eyeY = 0.0f;
            float eyeZ = 0.0f;
            float centerX = 0.0f;
            float centerY = 0.0f;
            float centerZ = -1.0f;
            float upX = 0.0f;
            float upY = 1.0f;
            float upZ = 0.0f;
            Matrix.setLookAtM(viewMatrix, 0, eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
        }
    
    
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            mWidth = width;
            mHeight = height;
            GLES20.glViewport(0, 0, mWidth, mHeight);
            float ratio = (float)mWidth/mHeight;
            float left = -ratio;
            float right = ratio;
            float bottom = -1.0f;
            float top = 1.0f;
            float near = 1.0f;
            float far = 100.0f;
            Matrix.orthoM(projMatrix, 0, left, right, bottom, top, near, far);
            
        }
    
    
        public void onDrawFrame(GL10 gl) {
            GLES20.glViewport(0, 0, mWidth, mHeight);
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
            GLES20.glUseProgram(sProgram);
            
            GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, buf);
            GLES20.glUniform1i(GLES20.glGetUniformLocation(sProgram, "texture"), 0);
            
            
            
            Matrix.setIdentityM(modelMatrix, 0);
            Matrix.translateM(modelMatrix, 0, 0.0f, 0.0f, -7.0f);
            
            GLES20.glVertexAttribPointer(GLES20.glGetAttribLocation(sProgram, "aPos"), 3, GLES20.GL_FLOAT, false, 0, vertsBuffer);
            GLES20.glEnableVertexAttribArray(GLES20.glGetAttribLocation(sProgram, "aPos"));
    
    
            GLES20.glVertexAttribPointer(GLES20.glGetAttribLocation(sProgram, "aTexPos"), 2, GLES20.GL_FLOAT, false, 0, textBuffer);
            GLES20.glEnableVertexAttribArray(GLES20.glGetAttribLocation(sProgram, "aTexPos"));
            
            Matrix.multiplyMM(modViewProjMatrix, 0, viewMatrix, 0, modelMatrix, 0);
            Matrix.multiplyMM(modViewProjMatrix, 0, projMatrix, 0, modViewProjMatrix, 0);
            GLES20.glUniformMatrix4fv(GLES20.glGetUniformLocation(sProgram, "MVPMat"), 1, false, modViewProjMatrix, 0);
            
            GLES20.glDrawElements(GLES20.GL_TRIANGLES, 6, GLES20.GL_UNSIGNED_SHORT, indexBuffer);
        } 
        private float[] text = {0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f };
        
        private float[] verts = {-1.0f, -1.0f, 0.0f, 1.0f, -1.0f, 0.0f, 1.0f, 1.0f, 0.0f, -1.0f, 1.0f, 0.0f};
    
    
        private final short[] index = {0, 1, 2, 2, 3, 0};
        private int vShader, fShader, sProgram, mWidth, mHeight;
        private FloatBuffer vertsBuffer, textBuffer;;
        private ShortBuffer indexBuffer;
        private float[] modelMatrix = new float[16];
        private float[] viewMatrix = new float[16];
        private float[] projMatrix = new float[16];
        private float[] modViewProjMatrix = new float[16];
        private final String vCode = 
                 "uniform mat4 MVPMat;                  \n"
                +"attribute vec2 aTexPos;               \n"
                +"attribute vec3 aPos;                  \n"
                +"varying   vec2 vTexPos;               \n"
                +"void main(){                          \n"
                +" vTexPos = aTexPos;                    \n"
                +" gl_Position = MVPMat * vec4(aPos, 1.0);       \n"
                +"}                                     \n";
        
        private final String fCode =
                "precision mediump float;      \n"
                +"uniform sampler2D texture;   \n"
                +"varying vec2 vTexPos;        \n"
                +"void main(){                 \n"     \n"
                +"gl_FragColor = texture2D(texture, vTexPos);       \n"
                +"}                          \n";
        
    }

  2. #2
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    Sorry I've never done OpenGL programming from Java, I'm going to use C++ terms here but hopefully you can figure it out...

    The basic idea of a camera is simple. You have (e.g.) a matrix that represents where your camera is and the direction that it's looking, and every frame when you're doing a repaint, you use the matrix somehow. With a fixed-function pipeline (earlier versions of OpenGL), you might transform the world by simply multiplying the modelview matrix with your camera matrix. In your case, you seem to be using some shader stuff so you could do the camera transforms in shaders, but this is probably simpler: take your modelMatrix and just multiply the camera matrix into it.
    Code:
            Matrix.setIdentityM(modelMatrix, 0);
            Matrix.translateM(modelMatrix, 0, 0.0f, 0.0f, -7.0f);
            Matrix.multiplyM(camera.viewMatrix, ...);  // I'm guessing something like this function exists. You'll need a getter func for viewMatrix
    Taking a step back, you could do an even simpler camera system with gluLookAt (or whatever it's called in your API). Just keep track of a position where the camera is, hard-code a position or an object that you're always looking at, hard-code an orientation. Once that works, start using a variable to keep track of the orientation as well and let the mouse move this around or something. Just pass these variables to a lookat call every time you're painting a new frame.

    Alternatively you can do what I always did at the beginning, which was ignore lookat entirely and keep track of a cumulative matrix that you keep translating and rotating (if you keep track of its inverse as well you can do unprojection). This is harder than it needs to be. And if you want a really nice camera you should look into quaternions, but that's even more complicated. gluLookAt gets bashed a lot but it's a good starting place for a simple camera. Cheers.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  3. #3
    Registered User
    Join Date
    Aug 2012
    Posts
    9
    dwks

    Thank you a lot for those tips. I was able to implement a camera that works but still a little confused about a few things. Here is a video of the camera in action : simple camera (w.i.p) - YouTube

    here is my updated camera class, i basically had to fix a few null pointers in the constructor
    Code:
    import android.opengl.Matrix;
    
    
    public class Camera {
        vec3 pos;
        vec3 up;
        vec3 right;
        vec3 dir;
        int width, height;
        float near;
        float far;
        float[] viewMatrix;
        float[] projMatrix;
        float[] combMatrix;
        
        public Camera(int width, int height){
            projMatrix = new float[16];
            viewMatrix = new float[16];
            combMatrix = new float[16];
            
            float ratio = (float)width/height;
            float left = -ratio;
            float right = ratio;
            float bottom = -1.0f;
            float top = 1.0f;
            near = 1.0f;
            far = 100.0f; 
            Matrix.frustumM(projMatrix, 0, left, right, bottom, top, near, far);
            
            float eyeX = 0.0f;
            float eyeY = 0.0f;
            float eyeZ = 0.0f;
            float centerX = 0.0f;
            float centerY = 0.0f;
            float centerZ = -1.0f;
            float upX = 0.0f;
            float upY = 1.0f;
            float upZ = 0.0f;
            Matrix.setLookAtM(viewMatrix, 0, eyeX, eyeY, eyeZ, centerX, centerY, centerZ, upX, upY, upZ);
        }
        
        public void rotate(float angle, vec3 axis){
            Matrix.rotateM(viewMatrix, 0, angle, axis.x, axis.y, axis.z);
        }
        
        public void rotate(float angle, float x, float y, float z){
            Matrix.rotateM(viewMatrix, 0, angle, x, y, z);
        }
        
        public void translate(float dist, vec3 axis){
            Matrix.translateM(viewMatrix, 0, axis.x, axis.y, axis.z);
        }
        
        public void translate(float dist, float x, float y, float z){
            Matrix.translateM(viewMatrix, 0, x, y, z);
        }
    and my updated drawing class using the new camera object
    Code:
    public class MyRenderer implements GLSurfaceView.Renderer {
    
    
        Context context;
        TextureLoader tLoader;
        ObjModelLoader loader;
        int buf;
        int number;
        Camera cam;
        
        public MyRenderer(Context c){
            this.context = c;
            
            loader = new ObjModelLoader(this.context);
            tLoader = new TextureLoader(this.context);
            
            loader.load(R.raw.cu);
            verts = loader.vtx;
            number = loader.numberOfVertices;
            
            vertsBuffer = ByteBuffer.allocateDirect(verts.length*4).order(ByteOrder.nativeOrder()).asFloatBuffer();
            vertsBuffer.put(verts).position(0);
            
            textBuffer = ByteBuffer.allocateDirect(text.length*4).order(ByteOrder.nativeOrder()).asFloatBuffer();
            textBuffer.put(text).position(0);
            
            indexBuffer = ByteBuffer.allocateDirect(index.length*2).order(ByteOrder.nativeOrder()).asShortBuffer();
            indexBuffer.put(index).position(0);
        }
        public void onSurfaceCreated(GL10 gl, EGLConfig config) {
            vShader = GLES20.glCreateShader(GLES20.GL_VERTEX_SHADER);
            GLES20.glShaderSource(vShader, vCode);
            GLES20.glCompileShader(vShader);
            
            fShader = GLES20.glCreateShader(GLES20.GL_FRAGMENT_SHADER);
            GLES20.glShaderSource(fShader, fCode);
            GLES20.glCompileShader(fShader);
            
            sProgram = GLES20.glCreateProgram();
            GLES20.glAttachShader(sProgram, vShader);
            GLES20.glAttachShader(sProgram, fShader);
            GLES20.glLinkProgram(sProgram);
            
            GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
            
    
    
            buf = tLoader.loadTexture(R.raw.bkg);
        }
    
    
        public void onSurfaceChanged(GL10 gl, int width, int height) {
            cam = new Camera(mWidth, mHeight);
        }
    
    
        public void onDrawFrame(GL10 gl) {
            GLES20.glViewport(0, 0, mWidth, mHeight);
            GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);
            GLES20.glUseProgram(sProgram);
            
            GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
            GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, buf);
            GLES20.glUniform1i(GLES20.glGetUniformLocation(sProgram, "texture"), 0);
            
              
            
            Matrix.setIdentityM(modelMatrix, 0);
            Matrix.translateM(modelMatrix, 0, 0.0f, 0.0f, -7.0f);
            
            GLES20.glVertexAttribPointer(GLES20.glGetAttribLocation(sProgram, "aPos"), 3, GLES20.GL_FLOAT, false, 0, vertsBuffer);
            GLES20.glEnableVertexAttribArray(GLES20.glGetAttribLocation(sProgram, "aPos"));
    
    
            GLES20.glVertexAttribPointer(GLES20.glGetAttribLocation(sProgram, "aTexPos"), 2, GLES20.GL_FLOAT, false, 0, textBuffer);
            GLES20.glEnableVertexAttribArray(GLES20.glGetAttribLocation(sProgram, "aTexPos"));
            
            Matrix.multiplyMM(cam.combMatrix, 0, cam.viewMatrix, 0, modelMatrix, 0);
            Matrix.multiplyMM(cam.combMatrix, 0, cam.projMatrix, 0, cam.combMatrix, 0);
            GLES20.glUniformMatrix4fv(GLES20.glGetUniformLocation(sProgram, "MVPMat"), 1, false, cam.combMatrix, 0);
            
           GLES20.glDrawArrays(GLES20.GL_LINE_LOOP, 0, number);
        } 
        
        private final short[] index = {0, 1, 2, 2, 3, 0};
        private int vShader, fShader, sProgram, mWidth, mHeight;
        private FloatBuffer vertsBuffer, textBuffer;;
        private ShortBuffer indexBuffer;
        private float[] modelMatrix = new float[16];
        private float[] viewMatrix = new float[16];
        private float[] projMatrix = new float[16];
        private float[] modViewProjMatrix = new float[16];
        private final String vCode = 
                 "uniform mat4 MVPMat;                  \n"
                +"attribute vec2 aTexPos;               \n"
                +"attribute vec3 aPos;                  \n"
                +"varying   vec2 vTexPos;               \n"
                +"void main(){                          \n"
                +" vTexPos = aTexPos;                    \n"
                +" gl_Position = MVPMat * vec4(aPos, 1.0);       \n"
                +"}                                     \n";
        
        private final String fCode =
                "precision mediump float;      \n"
                +"uniform sampler2D texture;   \n"
                +"varying vec2 vTexPos;        \n"
                +"void main(){                 \n"
                +"gl_FragColor = texture2D(texture, vTexPos);       \n"
                +"}                          \n";
        
    }
    the code to move my camera around
    ag = angle;
    d = 1.0f;
    Code:
    queueEvent(new Runnable(){
                    public void run(){
                        switch(m){ 
                        case 0:
                            render.cam.translate(0.001f, d, 0.0f, 0.0f);
                            break;
                        case 1:
                            render.cam.translate(0.001f, 0.0f, d, 0.0f);
                            break;
                        case 2:
                            render.cam.rotate(ag, 0.0f, 0.0f, 1.0f);
                            break;
                        case 3:
                            render.cam.rotate(ag, 0.0f, 1.0f, 0.0f);
                            break;
                        }
                    }
    Now as you can see in the very early in the video when i translate the camera along the x axis it seems the cube goes in a circle around the camera. This is cool but I would like the cube to stay in the center and have the camera rotate around the cube.

    I've read that it is just as easy to move the world objects or move the camera in the opposite direction but I am not quite understanding that part right now, if anyone have any tips as to how I can get the desired effect of moving the camera. I think this setup is kinda cool and may have uses although i'm not quite sure what they are at this moment. Anyone have any suggestions?

    Thanks,
    bluBee

    quick edit!
    So after the last post I looked at the code a realized that my camera basically does opengl look at right now, and even though I have my right, up and direction vectors they aren't used for anything.

    I think to get my camera to free roam I need to set these vectors and then do something with their cross products but it gets really fuzzy right about that part. So I think I need to set the right, up and direction vectors, then I can translate, rotate the camera object/ viewing volume around in the world to get the effect i want?
    Last edited by blubee; 08-28-2012 at 09:07 PM. Reason: quick realization after looking at the code.

  4. #4
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    (See the italic bit at the end, it might save you some trouble.)

    Good work on getting that much working! I like the video, never seen that before on CBoard... a good way to demonstrate what's going on in a graphical program.
    Quote Originally Posted by blubee View Post
    Now as you can see in the very early in the video when i translate the camera along the x axis it seems the cube goes in a circle around the camera. This is cool but I would like the cube to stay in the center and have the camera rotate around the cube.
    That's a very simple fix. You have basically a "first-person" camera right now where the focus point and the view point are the same (these aren't technical terms, just trying to get the idea across). You want a "third-person" camera, where the focus point is in front of you. So just translate the camera away, in the opposite direction that it's looking. Start with e.g. translation along the negative Z axis.

    I've read that it is just as easy to move the world objects or move the camera in the opposite direction but I am not quite understanding that part right now, if anyone have any tips as to how I can get the desired effect of moving the camera.
    Well, consider this. You change the modelview matrix by multiplying in the camera matrix, and then you draw the rest of the scene. So one way of looking at it is that you've "moved" the origin and hence the camera to a new location. Another way of looking at it is that you've suddenly changed every other subsequent thing you're going to draw, i.e. you've translated the whole world while your camera has remained in place. I think it's just a matter of perspective.

    For me, the best way to think about it is to imagine the camera at some position and orientation. At the beginning of a repaint, I translate by the negative of this position and the opposite of the orientation. Thus I've taken care of the camera and I can do everything else without worrying about it. I guess if I had to think about it mentally, I'd imagine that I'd just moved the entire world. Now, time to paint.

    BTW you can also put the camera transformation into the projection matrix... I'm not sure what this accomplishes other than requiring you to switch between GL_PROJECTION and GL_MODELVIEW all the time. And it's essentially the same thing since projection and modelview get multiplied together in the end.

    So after the last post I looked at the code a realized that my camera basically does opengl look at right now, and even though I have my right, up and direction vectors they aren't used for anything.

    I think to get my camera to free roam I need to set these vectors and then do something with their cross products but it gets really fuzzy right about that part. So I think I need to set the right, up and direction vectors, then I can translate, rotate the camera object/ viewing volume around in the world to get the effect i want?
    Okay, so having a free-roam camera is basically the same as just adding a translation to whatever you already have for a camera. Trust me, once you have a third-person camera, if you can move it around it will feel like a free-roam camera. So theoretically that's all you need.

    The trouble is that you need to be able to change the orientation of the camera. Basically you can define a camera in terms of three vectors: a "forwards" or normal vector that points from your eye to the targer; an "up" vector that points upwards (according to your camera); and a "right" vector that points to your right. Fortunately, you can take the cross product of the normal and the up to get the right vector. Actually you can take the cross of any two and get the third vector, but normal and up are easiest to find so get those first.

    The normal vector you're probably keeping track of already, it's essentially the orientation of the camera. You can use a neat trick for the up vector: take the cross product of (0,1,0) and your normal vector, temp = (0,1,0) x normal. Now temp is perpendicular to (0,1,0) and normal; in fact it's a kind of right vector (but not quite). Then take the cross temp x normal, and you get something that's perpendicular to both of those; this is actually your up vector right away! That's it. Do one more cross to find your real right vector and you're good.

    (Note: this breaks down if you're looking straight up or down, then (0,1,0) x normal will be a zero vector. temp is actually a right vector, it's just the wrong length. You can normalize it and use that. You should normalize all three vectors when you're done. Ah, normalize means divide by the length so that you have a vector of length 1.)

    ---
    So now you have three vectors, normal up and right. What next? Actually, you can form a matrix out of these three vectors and that will be your orientation matrix. Imagine you have a matrix M, and you're trying to figure out what should happen to your Z axis. Well, we know that (negative) Z is the normal of the camera in the origin... so
    Code:
    M = [
        [*,*,-normalx],
        [*,*,-normaly],
        [*,*,-normalz]]
    The X axis is really the right vector, the Y axis is the up vector.
    Code:
    M = [
        [rightx,upx,-normalx],
        [righty,upy,-normaly],
        [rightz,upz,-normalz]]
    Now, if you take the vector (1,0,0) and multiply it by this matrix, you'll get (rightx,righty,rightz). And so on for the other basis vectors. So the matrix M takes the identity and "moves" it to our camera's orientation! Add some translation, and you have a proper affine matrix, the kind OpenGL likes...
    Code:
    M = [
        [rightx,upx,-normalx, transx],
        [righty,upy,-normaly, transy],
        [rightz,upz,-normalz, transz],
        [0, 0, 0, 1]]
    I guess I'm assuming here you know some basic matrix math. Let me know if that's not true.

    Only problem is, if you take the identity, multiply by M, then draw something at (x,y,z) in world coordinates, it won't look right. You'll have the camera's position applied, then the x,y,z will move the object even further... if you think about this a bit you'll realize you need the inverse of the matrix M, denoted M^(-1).

    You can tweak this a little, use the negative of the translation and play around with rotations until it works -- it's helpful to have angles for your camera 'cause you can rotate in the opposite direction. Or you can use a matrix library to find the inverse. Here's a neat trick: you can also maintain the inverse yourself. For example.
    Code:
        public void rotate(float angle, vec3 axis){
            Matrix.rotateM(viewMatrix, 0, angle, axis.x, axis.y, axis.z);
            Matrix.rotateM(viewMatrixInverse, 0, -angle, axis.x, axis.y, axis.z);
        }
    Just make each of your functions that changes the viewMatrix change the inverse in the opposite way....

    Then basically just load the identity, multiply by M^(-1), draw your scene, and you're ready to go. BTW having M and M^(-1) around is very convenient; you can take a world position (vector), multiply it by M^(-1) to find out if the camera is touching it or something. You can take a mouse-position, unproject it to find a 3D point, then multiply by M to find the position in world-space and match it to an object.

    DISCLAIMER. I don't know a whole lot about cameras but this is the way I have created camera systems before. Also, I probably messed up some of the details (inverses, maybe a transposed matrix or two), but I hope you get the general idea.

    Italic bit. The above is what you can do if you want a proper frenet frame for your camera... but I have to say that this is much simpler: record a y-axis "rotation" variable and a x-axis "tilt" variable separately. Adjust these variables as required. Multiply by two rotate calls to create an orientation matrix for this. Multiply by a translate matrix to move the camera around. That's it. You don't need anything more complicated unless the camera is going to be rotating into crazy orientations, like a flight simulator or whatever. Trust me on this. Several of my projects just use a simple camera like this and it works just fine.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

  5. #5
    Registered User
    Join Date
    Aug 2012
    Posts
    9
    Hi again dkws

    This post is really informative and im getting a lot out of it. So with your help and this link Matrix Basics. How to step away from storing an orientation as ’3 angles’ « Steve's perspectives

    I reworked my camera so instead of doing glu lookat and using the eye, up and right vectors basically I understand what lookat does now.

    So I can achieve the same goal by setting my camera's initial view matrix to an identity matrix. then translating it in -1 unit in the z direction to do what my previous lookat was doing.

    I think keeping a a few extra matrices will be helpful, 1 for the inverse of the current matrix and a temp matrix to do some calculations with. For example later the inverse matrix can be used for ray picking like you said plus its also good to get the inverse transpose to use for lighting calculations in the fragment shader.

    Its funny the italic bit confused me more than the part about matrix math and that type of stuff. I honestly dont get that part but I think I will just go all in and learn the freenet camera for now, i'm so close might as well finish.

    I think something that I am still having issues with. In my camera class I have 3 matrices.
    view matrix, projection matrix and combined matrix.
    Now I split modelview matrix(I never learned fixed function opengl) and I know they combined them into one because basically any operation you do on the model, doing the inverse on the view matrix achieves the same goal.

    I keep them separate because that's just the way I learned it in the beginning. So I have a model matrix that deals with the model.

    Inside my camera i have a view and projection and combined matrices.

    I setup my projection matrix using the width/ height aspect ratios for now but I know I have some questions that needed to be sorted out with that but save that for another day.

    So the main things with the camera are the view, for a free roaming camera. I can have the normal vector pointing in any direction and recalculate the up and right vectors depending on where I want to move the camera, is that correct? for example the identity matrix represents my camera

    Code:
             [1, 0, 0,  0],
    Code:
             [0, 1, 0,  0],
              [0, 0, -1, 0],
              [0, 0, 0,  1]
    


    the camera right is pointing to the positive x, up is positive y, view direction is looking down the neg z and its at position 0,0,0.

    A few questions is this the local camera position or world camera position? This is basically the view matrix for the camera so if rotate the camera to this new position
    Code:
              [1,   0,     0,     0],   row 1
    Code:
              [0,   -1,    0,     0],   row 2
              [0,   0,     -1,    0],   row 3
              [0,   0,     0,     1]    row 4
              col1  col2 col3  col4
    

    flipping the up vector to -1, the camera will be upside down, and still looking down the negative z direction so basically my model would have been flipped upside down?

    Since I am no longer using gl_lookat function, say I want to have my camera orbit an object i figure I would use the 4th row of my 4x4 matrix?

    One final question just to be sure in the code snippet above;
    col 1 element 0-2 is the camera's right vector
    col 2 '' is the camera's up vector
    col 3 '' is the cameras normal or view vector
    and row 4 is its position in
    homogeneous coordinates space?

    p.s.
    sorry for the bad code formatting but the forum kept putting a break after my brackets.

  6. #6
    Frequently Quite Prolix dwks's Avatar
    Join Date
    Apr 2005
    Location
    Canada
    Posts
    8,057
    I reworked my camera so instead of doing glu lookat and using the eye, up and right vectors basically I understand what lookat does now.
    That's good... glad I could help.

    I think keeping a a few extra matrices will be helpful, 1 for the inverse of the current matrix and a temp matrix to do some calculations with. For example later the inverse matrix can be used for ray picking like you said plus its also good to get the inverse transpose to use for lighting calculations in the fragment shader.
    Agreed. It's surprising how often the inverse matrix comes in handy.

    Its funny the italic bit confused me more than the part about matrix math and that type of stuff. I honestly dont get that part but I think I will just go all in and learn the freenet camera for now, i'm so close might as well finish.
    The frenet camera is definitely more powerful, so for sure go ahead with that. Yeah I didn't elaborate on the italic bit very much... let me know if you're curious, but if you have a frenet camera working you should be good.

    I think something that I am still having issues with. In my camera class I have 3 matrices.
    view matrix, projection matrix and combined matrix.
    Now I split modelview matrix(I never learned fixed function opengl) and I know they combined them into one because basically any operation you do on the model, doing the inverse on the view matrix achieves the same goal.

    I keep them separate because that's just the way I learned it in the beginning. So I have a model matrix that deals with the model.
    Probably better that way... you don't have to unlearn the fixed-function stuff.

    So the main things with the camera are the view, for a free roaming camera. I can have the normal vector pointing in any direction and recalculate the up and right vectors depending on where I want to move the camera, is that correct? for example the identity matrix represents my camera

    Code:
             [1, 0, 0,  0],
    [/COLOR]
    Code:
             [0, 1, 0,  0],
              [0, 0, -1, 0],
              [0, 0, 0,  1]
    


    the camera right is pointing to the positive x, up is positive y, view direction is looking down the neg z and its at position 0,0,0.

    A few questions is this the local camera position or world camera position?

    To me it looks like the world position of the camera.. In the camera's local coordinate frame, (0,0,1) is always the normal vector, pointing straight ahead.

    See, you're basically doing a change of basis vectors. Originally, OpenGL's X means (1,0,0) and Y means (0,1,0) and Z means (0,0,1). But you're changing to a new coordinate system (via that matrix) where "X" means the right vector, "Y" means the up vector, and "Z" means the normal vector. So (0,0,1) in the local camera frame means 0*X, 0*Y, 1*Z, and Z is the normal vector, so (0,0,1) means the normal vector; which might be e.g. (0,0,-1) or something crazier in world coordinates.

    By the way, a change-of-basis matrix from standard world coordinates to some other coordinate frame can always be formed by filling out the matrix with the new basis vectors. This comes in handy when you're doing animation and you're essentially generating frenet frames (up, normal, right) for each animated model... but that's another story....

    This is basically the view matrix for the camera so if rotate the camera to this new position
    Code:
              [1,   0,     0,     0],   row 1
    Code:
              [0,   -1,    0,     0],   row 2
              [0,   0,     -1,    0],   row 3
              [0,   0,     0,     1]    row 4
              col1  col2 col3  col4
    

    flipping the up vector to -1, the camera will be upside down, and still looking down the negative z direction so basically my model would have been flipped upside down?

    Yep. Something like (1,1,1) in local coordinates would change in global/world coordinates. As you can see it's not very useful to represent things in camera-local coordinates relative to the camera; however, imagine you have a roller-coaster being animated along a path, with a frenet frame. Then you could draw a box representing the roller coaster by using constant quads, like (0,0) to (0,1) to (1,1) to (1,0), in the roller-coaster-local coordinate frame. You wouldn't have to keep track of the crazy twists of the roller coaster; drawing it would be as simple as multiplying by the change-of-basis matrix and then drawing the standard roller coaster model.

    Since I am no longer using gl_lookat function, say I want to have my camera orbit an object i figure I would use the 4th row of my 4x4 matrix?
    The 4th row is almost always (0,0,0,1) unless you're doing projections. You use the 4th column to represent translations. That's what an affine transformation is.

    Here's the long story. 3x3 matrices are equivalent to 3D linear transformations and that lets you do a lot of things, like rotate and scale and shear objects. But translation is not possible with 3x3 matrices: suppose you have the origin (0,0,0) and you're trying to move it to (1,2,3). It doesn't take much thinking to see that there's no possible 3x3 matrix that does this:
    Code:
    [1,2,3] = [[a,b,c], [d,e,f], [g,h,i]] * [0,0,0]
    That system of equations has no solution. So graphics people started using four-dimensional points, where the fourth coordinate is always 1 (except when doing projections). Now look what happens.
    [code][1,2,3,1] = [[1,0,0,1], [0,1,0,2], [0,0,1,3], [0,0,0,1]] * [0,0,0,1][/[code]
    It's now possible to write translations by using the fourth column of the matrix. As an added bonus, you can represent vectors with the fourth index always being 0 (not 1), and multiplying a vector by a matrix won't apply the translations.. which is exactly what we mean by a vector in computer graphics. Vectors don't have a position so translation doesn't affect them; points are coordinate-frame-dependent and can be moved around.

    For more details: http://en.wikipedia.org/wiki/Translation_%28geometry%29#Matrix_representation

    One final question just to be sure in the code snippet above;
    col 1 element 0-2 is the camera's right vector
    col 2 '' is the camera's up vector
    col 3 '' is the cameras normal or view vector
    and row 4 is its position in
    homogeneous coordinates space?
    Yes exactly (regarding the columns; like I said before, usually col 4 is used for homogeneous coordinate representation). That's the easiest way to think about it. But like I said before you're basically forming a change-of-basis matrix (no pun intended). If you made column 1 the camera's up vector, then for some point (x,y,z) in the camera's local coordinate frame, the "x" would mean how much of the camera's up vector to use. Not particularly useful in this case, but it could let you swap axes around if you're feeling inventive. Cheers.
    dwk

    Seek and ye shall find. quaere et invenies.

    "Simplicity does not precede complexity, but follows it." -- Alan Perlis
    "Testing can only prove the presence of bugs, not their absence." -- Edsger Dijkstra
    "The only real mistake is the one from which we learn nothing." -- John Powell


    Other boards: DaniWeb, TPS
    Unofficial Wiki FAQ: cpwiki.sf.net

    My website: http://dwks.theprogrammingsite.com/
    Projects: codeform, xuni, atlantis, nort, etc.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Please help openGL program -camera
    By evildotaing in forum C++ Programming
    Replies: 0
    Last Post: 05-30-2012, 11:55 PM
  2. OpenGL Camera Question
    By Astra in forum Game Programming
    Replies: 2
    Last Post: 03-25-2007, 01:53 PM
  3. OpenGL : Swinging camera
    By Necrofear in forum Game Programming
    Replies: 13
    Last Post: 04-16-2005, 03:30 AM
  4. Really Need Help With My Opengl Camera!!!!
    By psychopath in forum Game Programming
    Replies: 13
    Last Post: 05-28-2004, 03:05 PM
  5. OpenGL Camera
    By frenchfry164 in forum Game Programming
    Replies: 15
    Last Post: 12-10-2003, 02:50 PM

Tags for this Thread