0

I am looking to how to make a 3D projection to a 2D screen, or, in other words, take a 3D point with 3 coordinates (x, y, z), and get the corresponding 2D point to draw on the screen (x, y).

I have found this answer, which I liked, and I implemented it in my code, but I would like to be able to change the camera angle. I also found this answer, which I understood very partially.

I currently can only change the camera position in 3D space, but not the angle. For as to what my code is, here it is, it is written in Python and I am coding on the Casio Graph 90+E calculator, so I only have access to a couple of functions (I also made a small 2D graphics module with simple functions for drawing primitives shapes, but dont care about it, since I'm just looking for simple 3D points) :

  • set_pixel(x, y, RGB color), which sets a specific pixel on the screen to the RGB color specified
  • get_pixel(x, y), which returns the color of the pixel of the screen (I dont think it will be useful here)
  • show_screen(), which updates the screen
  • and clear_screen(), which clears the screen in white
    from casioplot import * #this is the calculator's graphics library
    
    
    points = [
        (-5, -5, 5),
        (5, -5, 5),
        (5, 5, 5),
        (-5, 5, 5),
        (-5, -5, 15),
        (5, -5, 15),
        (5, 5, 15),
        (-5, 5, 15)
    ] #simple square corners coordinates
    
    
    def threeDToTwoD(point, cam):
        f = point[2] - cam[2]
        x = (point[0] - cam[0]) * (f / point[2]) + cam[0]
        y = (point[1] - cam[1]) * (f / point[2]) + cam[1]
        return (round(x), round(y)) #the rounding is because I need integer coordinates for setting the pixel
    
    
    cam = [0, 0, 0]
    while True:
        clear_screen()
        for point in points:
            x, y = threeDToTwoD(point, cam)
            set_pixel(191 + x, 96 - y, (0, 0, 0))
        show_screen()
        cam[2] -= 1 #to move towards the points
Hovercraft Full Of Eels
  • 283,665
  • 25
  • 256
  • 373
Zecter
  • 9
  • 3
  • [tag:java] tag removed -- please choose one and only one most appropriate language tag, and since you're showing Python code, then that tag should be [tag:python]. – Hovercraft Full Of Eels Jul 04 '22 at 12:10

1 Answers1

1

I've made this fiddle in JavaScript, but it's just to demonstrate the math, so it can be adapted. https://jsfiddle.net/ue0am6fs/

It demonstrates how you can use 3D transform matrices to rotate a cube (the angle of your camera would define the matrix you use). The only transforms implemented in the fiddle are rotations around the X and Y axis, like:

function createXRotationMatrix(angle) {
  return [
  [1, 0, 0, 0],
  [0, Math.cos(angle), Math.sin(angle), 0],
  [0, -Math.sin(angle), Math.cos(angle), 0],
  [0, 0, 0, 1]
]

But you can implement other forms of 3D transform and combine them with matrix multiplication:

function multiplyMatrix(a, b) {
  const c = [[],[],[],[]];
  for (let x = 0; x < 4; x++) {
    for (let y = 0; y < 4; y++) {
      let acc = 0;
      for (let n = 0; n < 4; n++) {
        acc += a[n][x] * b[y][n];
      }
      c[y][x] = acc;
    }
  }
  return c;
}

Then points can be transformed by multiplying them by the matrix.

function multiplyPointByMatrix(matrix, point) {
  return [
  (point[0] * matrix[0][0]) + (point[1] * matrix[0][1]) + (point[2] * matrix[0][2]) + (1 * matrix[0][3]),
  (point[0] * matrix[1][0]) + (point[1] * matrix[1][1]) + (point[2] * matrix[1][2]) + (1 * matrix[1][3]),
  (point[0] * matrix[2][0]) + (point[1] * matrix[2][1]) + (point[2] * matrix[2][2]) + (1 * matrix[2][3])
  ]
}

Once the points are transformed, you can draw them with the set_pixel command, by calculating the length and interpolating along the line.

function drawTransformedLine(a, b) {
  var ta = multiplyPointByMatrix(transform, a);
  var tb = multiplyPointByMatrix(transform, b);
  var dx = ta[0] - tb[0];
  var dy = ta[1] - tb[1];
  var d = Math.sqrt(dx * dx + dy * dy);
  for (let i = 0; i <= d; i++) {
    var interpolated = lerp(ta, tb, i / d);
    set_pixel(interpolated[0], interpolated[1], [255, 0, 0]);
  }
}
Doug Blank
  • 2,031
  • 18
  • 36
aptriangle
  • 1,395
  • 1
  • 9
  • 11
  • Thanks for that answer ! In the manwhile, I looked at the Wikipedia page for 3D projection and I started to understand. I see that what you implemented was parallel projection, although I was thinking more about perspective projection, but I didn't precise it so I'm ok. I dont understand why I should use 4x4 matrices instead of 3x3 ? The second answer I linked did that too. – Zecter Jul 04 '22 at 20:40
  • The size of the matrices are usually one larger than the number of dimensions, because they represent equations of the form ax+by+cz+d, The fourth number represents constants in the equations, which are primarily used to represent an offset (a.k.a. translation). – aptriangle Jul 05 '22 at 07:17