Skip to content

Instantly share code, notes, and snippets.

@InterestingBrainPoops
Last active August 4, 2020 19:37
Show Gist options
  • Save InterestingBrainPoops/86ae850e5985775daa880890e9bb3c2c to your computer and use it in GitHub Desktop.
Save InterestingBrainPoops/86ae850e5985775daa880890e9bb3c2c to your computer and use it in GitHub Desktop.
Making a Camera(In software of course.)

Goals

So in this project specifically, I have a couple of goals in mind:

  • Being able to render a scene with a camera
    • This is a long term goal.(So expect it to get happened in the future.)
    • out of the scope of this gist
  • Making it gpu based, and not cpu based
    • oooooooh this is one of those things that is really useful

Steps

So the table of contents for this whole thing is basically as follows:

  1. Get the math worked through
  2. Get a proof of concept worked out (try to keep it streamlined so that i can easily move it to gpu)
  3. Use the above proof of concept to make a low fidelity prototype
  4. Try to make it gpu based

The Math.

Ok, Here we Goooo
The way how the camera is going to work is its going to have a resolution, and a focus length. The focus length determines the field of view.
When you have those two, you can make a 3d matrix, with the shape of (res.height,res.length,3), with res being the resolution, and the 3 representing the 3d vectors that we will be using.
If we work in the 3d plane, then we can come up with this illustration as to what the camera would "look" like.

Method 1

And once we have that, we can work off of that to get a vector from h to each of the coordinates, and the focus length would be the length of the line segment HM.
What the generic coordinate would look like:

Vector hCoords = new Vector(3);// the coordinates of h  
Vector sCoords = new Vector(3);// the coordinates of the "pixel" on the camera "screen"  
Vector camVector = Vector.sub(hCoords,sCoords);// using vector subtraction to get the vector between the two points.

So far this has been a relatively boring one, but we have to do it so that we can understand where we need to go wit the proof of concept and beyond.
So we have that vector, and now we just need one more vector to render one pixel.
So assuming we have an object called Scene that contains all of the surfaces, and which in the previous gist, we saw that they consisted of 4 points, and eich of those points is stored as a vector originating from the origin.
This allows us to then subtract that from the point h, and then we get a vector which we can normalize.(this will be refered to as pVector)
When we then normalize camvector in place, we can compare the two and see if that pixel should have a value.

Cons:

We really have an issue with this don't we.

  • Horribly ineffiecient
    • It has to go through every point in a scene for every pixel on the camera, and the more pixels that there are on a camera screen, and the more vertecies in a scene, we run into HUGE numbers.
  • Involves guessing and checking, which we can definitly improve.

Pros:

Ooh thats a first

  • Allowed me to work through the math once, and gave me an idea for method 2!

Method 2:

So here is how it will work:
You start off with the data structure of all of the points from the Scene like last time, and the coordinates for h as hCoords.

Finding the parametric equation for the line from the focal point to the vertex in the scene

You take the first one, and take the difference of the hCoords and the Scene vertex to get another matrix.
You then take that matrix and mutliply it by a variable t. This should be treated like i the imaginary number. you then add it to the hCoords and that should give you what you need. This will be called SceneLine.
There is also a data type called Complex, That will just allow me to make numbers like 5i+2, etc.

int output1[] = Vector.sub(hCoords,Scene[0]); // elementwise subtraction    
Complex output2[] = Complex[3];// making the second list
for (int i = 0; i < 3; i ++){// converting output1 to complex and moving to output2  
  output2[i] = new Complex(output1[i],Scene[0][i]); // making the output list.
}  

finding the Parametric equation for the camera screen

In this we are basically going to take 3 corners to get the parametric equation. The corners will be a data structure called corners.
The parametric equation will be stored in a data structure of the coeffients in this fashion: [a,b,c,d].
I don't want to explain it, so I will just make psuedocode-esque math.

int between1[] = Vector.sub(corners[0],corners[1]); // elementwise subtraction  
int between2[] = Vector.sub(corners[2],corners[1]); // elementwise subtraction  
int crossproduct[] = Vector.cross(between1,between2); // Crossproduct  
int d = crossproduct[0]*corners[1][0]+crossproduct[1]*corners[1][1]+crossproduct[2]*corners[1][2]; // getting the y offset(i think)  
int plane[] = [crossproduct[0],crossproduct[1],crossproduct[2],d];  

Finding the point of intersection

So what we need to do here is substitute each of the x,y,z components in the planes' equation(plane) with the defenititons from the equation for the line(output2).
Once we do that we then solve for t. We then substitute t in the x,y,and z components to get the coordinates for the point.

Complex sub[] = new Complex[3]; // this is the equation  
for (int x = 0; x < 3; x++ ){//this is substituting the (x,y,z) with the defenitions from the line equation(x = foo, y = bar, z = baz)  
 sub[x] = Complex.mult(plane[x],output2[x]);  
}  
Complex left = new Complex();//left side of equation  
for (x = 0; x < 3; x++){ basically adding together all of the pieces so that the eqation can be solved properly;  
 left.add(sub[x]);  
}  
float d = plane[3]-left.s;// subtracting the integer from the right  
left.sub(left.s); //Basically subtracting the integer from the left  
d = d/left.c; // isolating t, and we have the value of t  
int lout[] = [sub[0].c*d+sub[0].s,sub[1].c*d+sub[1].s,sub[2].c*d+sub[1].s]; // The coordinates of the intersection.  

Pros

I need to do these more often.

  • Can be ported to kernels much more easily.
    • all it needs is refactoring, and then needs to be ported.
    • This is because we can assign one kernel for each point, and in drastic cases you can assign one kernel to each surface.(4 points)
  • Hella efficient
    • We don't do any guessing and checking, but there is a lot of list math, and a lot of cycles as of now. I need to refactor and vectorize the functions, but that is going to happen in the Low Fidelity Prototype.

Cons

Why is this here?

  • A hot steaming pile of garbage
    • I slapped this together from a bunch of pages, and you can find those in Resources.md.
  • For loops and lists.
    • basically I need to benchmark this to see how much time it takes.

Method 3

Ok, so the way this is going to work is it will be a per pixel system as opposed to a per vertex system.

Steps:

  1. Get a line between the focus point and the pixel point.
  2. Loop through all of the triangles' planes in the scene.
  3. Go and do a line-plane intersection point, and then check if that point lies within the triangle.
    2. Say that the point does lie within the triangle, then we check the length of the vector from the pixel to the point. If it is shorter than the current shortest, then replace it.
  4. Then map the points' location to the texture, and then display that color on the pixel.

Step 1.

//That line should be determined pretty easily.  
Camera.getFocal();// Returns the focal point of the camera in the form of a vector.  
Camera.getPix(x,y);// Returns the pixel x,y in the form of the 3D plane (x,y,z).  
Vector camLine = new Vector(0,0,0);// This is because openCL doesn't support overloaded constructors.
float output1[] = Vector.sub(Camera.getFocal(),Camera.getPix(x,y)); //
Complex output2[3];  
for (int i = 0; i < 3; i ++){// converting output1 to complex and moving to output2  
  output2[i] = new Complex(output1[i],Scene[0][i]); // making the output list.  
}  

So we have the line. Onward!!!

Step 2.

So the way how the Scene object works is it has a list of Triangle Objects, and each of those has the plane function already defined. (They also have a bunch of other things like the normal, and location of the 3 points, but that doesn't really matter as of now.)
Also, I'm going to be lazy and make a method that gets the intersection point between a plane and line.getIntersection(line,plane);
There will also be a method to check if a point is inside of a triangle.inTriangle(point,triangle);

Vector shortest = new Vector(Float.MAX_VALUE,Float.MAX_VALUE,Float.MAX_VALUE); // shortest triangle (basically the one that has the intersection point)  
for (var i = 0; i < Scene.getTriangleNum(); i++){// go through all triangles  
   if (inTriangle(getIntersection(output2,Scene.getTriangle(i).getPlane()),Scene.getTriangle(i))){ // go and check intersection and if it is in the triangle.  
      if(Vector.getMag(Vector.sub(getIntersection(output2,Scene.getTriangle(i).getPlane()),Camera.getFocal()))<Vector.getMag(shortest)){// check if it is shorter then the shortest one.  
         shortest = Vector.sub(getIntersection(output2,Scene.getTriangle(i).getPlane()),Camera.getFocal());// replace the shortest one  
      }  
   }  
};  

STEP 3

OOOOOOOOOOOOF
So, small confession, this is still in progress, so updates will happen. but I am still working on other stuff, so be ready.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment