```lua
local function vector3D(x, y, z)
    return {
        x = x,
        y = y,
        z = z
    }
end

local function subtract(a, b)
    return vector3D(a.x - b.x, a.y - b.y, a.z - b.z)
end

local function normalize(v)
    local length = math.sqrt(v.x ^ 2 + v.y ^ 2 + v.z ^ 2)
    if length == 0 then return vector3D(0, 0, 0) end
    return vector3D(v.x / length, v.y / length, v.z / length)
end

local function cross(a, b)
    return vector3D(a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x)
end

local function dot(a, b)
    return a.x * b.x + a.y * b.y + a.z * b.z
end

local function createViewMatrix(cameraPos, cameraTargetPos, cameraUp)
    local forward = normalize(subtract(cameraTargetPos, cameraPos))
    local right = normalize(cross(forward, cameraUp))
    local trueUp = cross(right, forward)
    return {right.x, trueUp.x, -forward.x, 0, right.y, trueUp.y, -forward.y, 0, right.z, trueUp.z, -forward.z, 0, -dot(cameraPos, right), -dot(cameraPos, trueUp), dot(cameraPos, forward), 1}
end

local function perspectiveMatrix(fov, aspect, near, far)
    local f = 1.0 / math.tan(fov / 2)
    return {f / aspect, 0, 0, 0, 0, f, 0, 0, 0, 0, (far + near) / (near - far), -1, 0, 0, (2 * far * near) / (near - far), 0}
end

local function multiplyMatrices(a, b)
    local result = {}
    for row = 1, 4 do
        for col = 1, 4 do
            result[(row - 1) * 4 + col] = 0
            for i = 1, 4 do
                result[(row - 1) * 4 + col] = result[(row - 1) * 4 + col] + a[(row - 1) * 4 + i] * b[(i - 1) * 4 + col]
            end
        end
    end
    return result
end

local function multiplyMatrixVector(m, v)
    local x = v.x * m[1] + v.y * m[5] + v.z * m[9] + m[13]
    local y = v.x * m[2] + v.y * m[6] + v.z * m[10] + m[14]
    local z = v.x * m[3] + v.y * m[7] + v.z * m[11] + m[15]
    local w = v.x * m[4] + v.y * m[8] + v.z * m[12] + m[16]
    return {
        x = x,
        y = y,
        z = z,
        w = w
    }
end

local function normalizeVertex(v)
    if v.w == 0 then
        return {
            x = v.x,
            y = v.y,
            z = v.z
        }
    end
    return {
        x = v.x / v.w,
        y = v.y / v.w,
        z = v.z / v.w
    }
end

local cubeVertices = {vector3D(-1, -1, -1), vector3D(1, -1, -1), vector3D(1, 1, -1), vector3D(-1, 1, -1), vector3D(-1, -1, 1), vector3D(1, -1, 1), vector3D(1, 1, 1), vector3D(-1, 1, 1)}
local function createModelMatrix(position, rotation, scale)
    local sx, sy, sz = scale.x, scale.y, scale.z
    local px, py, pz = position.x, position.y, position.z
    return {sx, 0, 0, 0, 0, sy, 0, 0, 0, 0, sz, 0, px, py, pz, 1}
end

local cameraPos = vector3D(0, 0, 5)
local cameraTargetPos = vector3D(0, 0, 0)
local cameraUp = vector3D(0, 1, 0)
local viewMatrix = createViewMatrix(cameraPos, cameraTargetPos, cameraUp)
local fov = math.pi / 4
local screenWidth = 800
local screenHeight = 600
local aspectRatio = screenWidth / screenHeight
local nearPlane = 0.1
local farPlane = 1000
local projectionMatrix = perspectiveMatrix(fov, aspectRatio, nearPlane, farPlane)
local viewProjectionMatrix = multiplyMatrices(projectionMatrix, viewMatrix)
local modelPosition = vector3D(0, 0, 0)
local modelRotation = vector3D(0, 0, 0)
local modelScale = vector3D(1, 1, 1)
local modelMatrix = createModelMatrix(modelPosition, modelRotation, modelScale)
local modelViewProjectionMatrix = multiplyMatrices(viewProjectionMatrix, modelMatrix)
local function transformVertex(vertex, mvp)
    local transformed = multiplyMatrixVector(mvp, vertex)
    return normalizeVertex(transformed)
end

local transformedVertices = {}
for i, vertex in ipairs(cubeVertices) do
    transformedVertices[i] = transformVertex(vertex, modelViewProjectionMatrix)
end

local function printTransformedVertices(vertices)
    for i, v in ipairs(vertices) do
        print(string.format("Vertex %d: x = %.2f, y = %.2f, z = %.2f", i, v.x, v.y, v.z))
    end
end

printTransformedVertices(transformedVertices)
```
Оптимизируй код