### Instruction:
Develop a C++ program that implements a simple ray-tracing algorithm to render 3D scenes with basic shapes (spheres, planes) and lighting. The program should output the rendered image as a PNG file.
PLAINFORMAT

### Response:

```cpp
#include <iostream>
#include <vector>
#include <limits>
#include <memory>
#include <Eigen/Dense>
#include "stb_image_write.h"

using namespace std;
using namespace Eigen;

class Vector3 {
public:
    Vector3(float x = 0, float y = 0, float z = 0) : v(x, y, z) {}

    float x() const { return v.x(); }
    float y() const { return v.y(); }
    float z() const { return v.z(); }

    Vector3 operator+(const Vector3 &other) const {
        return Vector3(v + other.v);
    }

private:
    Vector3(const Eigen::Vector3f &v) : v(v) {}

    Eigen::Vector3f v;
};

class Ray {
public:
    Ray(const Vector3 &origin, const Vector3 &direction)
        : origin(origin), direction(direction.normalized()) {}

    Vector3 origin;
    Vector3 direction;
};

class Shape {
public:
    virtual bool intersect(const Ray &ray, float &t) const = 0;
};

class Sphere : public Shape {
public:
    Sphere(const Vector3 &center, float radius) : center(center), radius(radius) {}

    bool intersect(const Ray &ray, float &t) const override {
        // ... (intersection algorithm)
    }

    Vector3 center;
    float radius;
};

class Plane : public Shape {
public:
    Plane(const Vector3 &point, const Vector3 &normal)
        : point(point), normal(normal.normalized()) {}

    bool intersect(const Ray &ray, float &t) const override {
        // ... (intersection algorithm)
    }

    Vector3 point;
    Vector3 normal;
};

class Light {
public:
    Light(const Vector3 &position, float intensity)
        : position(position), intensity(intensity) {}

    Vector3 position;
    float intensity;
};

class Scene {
public:
    void addShape(unique_ptr<Shape> shape) {
        shapes.push_back(move(shape));
    }

    void addLight(const Light &light) {
        lights.push_back(light);
    }

    const Shape *intersect(const Ray &ray, float &t) const {
        // ... (find the closest intersection)
    }

    vector<unique_ptr<Shape>> shapes;
    vector<Light> lights;
};

Vector3 shade(const Ray &ray, const Scene &scene) {
    float t;
    const Shape *shape = scene.intersect(ray, t);

    if (!shape) {
        return Vector3(0);  // background color
    }

    // Compute the shading based on the intersection and lighting
    // ...

    return color;
}

void render(const Scene &scene, const string &outputFile) {
    int width = 800;
    int height = 600;

    vector<uint8_t> pixels(width * height * 3);

    for (int y = 0; y < height; ++y) {
        for (int x = 0; x < width; ++x) {
            // Cast a ray through the pixel
            Ray ray = ...;

            Vector3 color = shade(ray, scene);

            int index = (y * width + x) * 3;
            pixels[index] = color.x() * 255;
            pixels[index + 1] = color.y() * 255;
            pixels[index + 2] = color.z() * 255;
        }
    }

    stbi_write_png(outputFile.c_str(), width, height, 3, pixels.data(), width * 3);
}

int main() {
    // Create your 3D scene with shapes and lights
    // ...

    // Render the scene and save it as a PNG file
    render(scene, "output.png");

    return 0;
}
```