public void Start() { camera = new Camera(); draw = new DrawString(); input = new Thread(ManageInput); input.Start(); // Perspective Projection /*Sources: * https://stackoverflow.com/questions/53245632/general-formula-for-perspective-projection-matrix * https://www.scratchapixel.com/lessons/3d-basic-rendering/perspective-and-orthographic-projection-matrix/building-basic-perspective-projection-matrix */ float fovRad = 1.0f / (float)Math.Tan(camera.Fov * 0.5f * (Math.PI / 180.0f)); perspProjection = new float[4, 4] { { fovRad *ASPECT_RATIO, 0, 0, 0 }, { 0, fovRad, 0, 0 }, { 0, 0, -camera.FarPlane / (camera.FarPlane - camera.NearPlane), -(camera.FarPlane * camera.NearPlane) / (camera.FarPlane - camera.NearPlane) }, { 0, 0, 1, 0 } }; // ###################### }
private Mat4x4 GetViewMatrix() { float cosPitch = (float)Math.Cos(Pitch * (Math.PI / 180.0f)); float sinPitch = (float)Math.Sin(Pitch * (Math.PI / 180.0f)); float cosYaw = (float)Math.Cos(Yaw * (Math.PI / 180.0f)); float sinYaw = (float)Math.Sin(Yaw * (Math.PI / 180.0f)); float cosRoll = (float)Math.Cos(Roll * (Math.PI / 180.0f)); float sinRoll = (float)Math.Sin(Roll * (Math.PI / 180.0f)); Mat4x4 rotX = new float[4, 4] { { 1, 0, 0, 0 }, { 0, cosPitch, -sinPitch, 0 }, { 0, sinPitch, cosPitch, 0 }, { 0, 0, 0, 1 } }; Mat4x4 rotY = new float[4, 4] { { cosYaw, 0, sinYaw, 0 }, { 0, 1, 0, 0 }, { -sinYaw, 0, cosYaw, 0 }, { 0, 0, 0, 1 } }; Mat4x4 rotZ = new float[4, 4] { { cosRoll, -sinRoll, 0, 0 }, { sinRoll, cosRoll, 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } }; Mat4x4 camRot = Mat4x4.MatMul(rotZ, rotY); camRot = Mat4x4.MatMul(camRot, rotX); Vector up = new Vector(0, 1, 0); Vector target = new Vector(0, 0, 1); Vector lookDir = Mat4x4.MatMul(target, camRot); target = Position + lookDir; return(LookAt(Position, target, up)); }
// https://learnopengl.com/Getting-started/Camera public Mat4x4 LookAt(Vector worldPos, Vector targetPos, Vector newUp) { Forward = (targetPos - worldPos).Normalized; Up = (newUp - (Forward * Vector.DotProduct(newUp, Forward))).Normalized; Right = Vector.CrossProduct(Up, Forward); Mat4x4 rotation = new float[4, 4] { { Right.X, Right.Y, Right.Z, 0 }, { Up.X, Up.Y, Up.Z, 0 }, { Forward.X, Forward.Y, Forward.Z, 0 }, { 0, 0, 0, 1 } }; Mat4x4 translation = new float[4, 4] { { 1, 0, 0, -Position.X }, { 0, 1, 0, -Position.Y }, { 0, 0, 1, -Position.Z }, { 0, 0, 0, 1 } }; return(Mat4x4.MatMul(rotation, translation)); }
// Render 3D Objects private void Render3D() { //Mat4x4 camTranform = Mat4x4.MatMul(Mat4x4.VecToMat(camera.Position), Mat4x4.VecToMat()) Light simpleLight = new Light { Direction = new Vector(0, 0, 1) }; simpleLight.Direction.Normalize(); Mat4x4 rotationX = new float[4, 4] { { 1, 0, 0, 0 }, { 0, (float)Math.Cos(xAngle), (float)-Math.Sin(xAngle), 0 }, { 0, (float)Math.Sin(xAngle), (float)Math.Cos(xAngle), 0 }, { 0, 0, 0, 1 } }; Mat4x4 rotationY = new float[4, 4] { { (float)Math.Cos(yAngle), 0, (float)Math.Sin(yAngle), 0 }, { 0, 1, 0, 0 }, { (float)-Math.Sin(yAngle), 0, (float)Math.Cos(yAngle), 0 }, { 0, 0, 0, 1 } }; Mat4x4 rotationZ = new float[4, 4] { { (float)Math.Cos(zAngle), (float)-Math.Sin(zAngle), 0, 0 }, { (float)Math.Sin(zAngle), (float)Math.Cos(zAngle), 0, 0 }, { 0, 0, 1, 0 }, { 0, 0, 0, 1 } }; // XYZ rotation = (((Z * Y) * X) * Vector) or (Z×Y×X)×V Mat4x4 rotMatrix = Mat4x4.MatMul(rotationZ, rotationY); rotMatrix = Mat4x4.MatMul(rotMatrix, rotationX); Mat4x4 mvp = ortho ? Mat4x4.MatMul(orthoProjection, camera.ViewMatrix) : Mat4x4.MatMul(perspProjection, camera.ViewMatrix); Triangle[] updatedTri = new Triangle[mesh.Polygons.Length]; for (int i = 0; i < mesh.Polygons.Length; i++) { updatedTri[i] = new Triangle(new Vector[mesh.Polygons[i].VertexCount]); for (int j = 0; j < mesh.Polygons[i].VertexCount; j++) { // Apply rotation to vertex Vector rotated = Mat4x4.MatMul(mesh.Polygons[i][j], rotMatrix); // Translate vertex (slightly) to not draw on top of camera Vector translated = new Vector(rotated.X, rotated.Y, rotated.Z + 3.0f); updatedTri[i][j] = translated; } float lightDP = Math.Max(0.1f, Vector.DotProduct(updatedTri[i].Normal, simpleLight.Direction)); ShadeTri(ref updatedTri[i], lightDP); } Triangle[] projected = new Triangle[mesh.Polygons.Length]; for (int i = 0; i < projected.Length; i++) { projected[i] = updatedTri[i]; for (int j = 0; j < projected[i].VertexCount; j++) { projected[i][j] = Mat4x4.MatMul(projected[i][j], mvp); // Scale Vectors projected[i][j] *= ortho ? 20.0f : projectionScale; // Magic numbers :( } } if (rotate) { if (rotateX) { xAngle -= 0.03f; } if (rotateY) { yAngle -= 0.03f; } if (rotateZ) { zAngle -= 0.03f; } } if (hasMesh && hasSurface) { // Plot Faces and Mesh // After testing with modified versions of the methods below // it's safe to assume this needs its own method // If implementation becomes unnecessary, remove this } else if (hasSurface) { draw.PlotFaces(projected); } else if (hasMesh) { draw.PlotMesh(projected); } else { draw.PlotVertices(projected); } }