Skip to content

Integration

Sinan Karakaya edited this page Sep 16, 2023 · 1 revision

Integration guide

If you want to implement your own primitive, material, or texture, you can use the following guide.

Primitives

Primitives are the basic building blocks of the engine. They are used to render everything from the skybox to the player. The engine comes with a few primitives, but you can add your own.

Creating a primitive

To create a primitive, you need to create a class that inherits from APrimitive.

#ifndef APRIMITIVE_HPP_
    #define APRIMITIVE_HPP_

    #include "IPrimitive.hpp"

    #include <filesystem>
    #include "Tools.hpp"

namespace RayTracer::Geometry {
    class APrimitive : public IPrimitive {
        public:

            virtual ~APrimitive() override = default;

            /**
            * @brief Checks if a ray intersects with the object
            *
            * @param ray   : The ray to check
            * @param t_min : The minimum distance from the ray origin to the point of intersection
            * @param t_max : The maximum distance from the ray origin to the point of intersection
            * @param rec   : The HitRecord to fill if the ray intersects with the object
            * @return true
            * @return false
            */
            virtual bool hit(const Ray &ray, double t_min, double t_max, HitRecord &rec) const = 0;

            /**
            * @brief Calculates the bounding box of the object
            *
            * @param time0 : The start time of the object
            * @param time1 : The end time of the object
            * @param box  : The bounding box to fill
            * @return true
            * @return false
            */
            virtual bool bounding_box(double time0, double time1, Math::AABB &box) const = 0;

            virtual double pdfValue(const Math::Vector3D &origin, const Math::Vector3D &direction) const override
            {
                (void)origin;
                (void)direction;

                return 0;
            }

            virtual Math::Vector3D random(const Math::Vector3D &origin) const override
            {
                (void)origin;

                return Math::Vector3D(1, 0, 0);
            }

            virtual void builder(const libconfig::Setting &setting) = 0;

            virtual IMaterial *loadMaterial(const libconfig::Setting &setting) override
            {
                std::filesystem::path currentDir = std::filesystem::current_path();
                const std::string pwd = std::string(currentDir.c_str());
                std::string libname = setting["material"]["name"];
                if (libname.empty() || libname == "none")
                    return new AMaterial();

                std::transform(libname.begin(), libname.end(), libname.begin(), ::tolower);
                libname = "raytracer_" + libname + ".so";
                void *handle = LibLoader<IPrimitive>::openModule(pwd + "/lib/" + libname);
                if (!handle)
                    return new AMaterial();
                IMaterial *mat = LibLoader<IMaterial>::loadModule(handle, "EntryPoint");
                mat->init(setting);
                LibLoader<IMaterial>::closeModule(handle);
                return mat;
            }

            // virtual
    };
};

#endif /* !APRIMITIVE_HPP_ */

You need to specify an extern "C" function that will be used to create the primitive.

This is an example of a constructor

extern "C" {
    RayTracer::Geometry::Cube *EntryCubePoint()
    {
        return new RayTracer::Geometry::Cube();
    }
}

Materials

Materials are used to give a color to a primitive. They can be used to create a mirror, a glass, or a simple color.

Creating a material

To create a material, you need to create a class that inherits from AMaterial.

#ifndef AMATERIAL_HPP_
    #define AMATERIAL_HPP_

    #include "IMaterial.hpp"

namespace RayTracer {
    class AMaterial : public IMaterial {
        public:
            virtual ~AMaterial() override = default;
            virtual bool scatter(const Ray &ray, const HitRecord &rec, ScatterRecord &srec) const
            {
                (void)ray;
                (void)rec;
                (void)srec;

                return false;
            }

            /**
                * @brief This function is used to emit light from a material
                * A AMaterial can't emit light, so it returns a black vector
                * @param u (unused)
                * @param v (unused)
                * @param p (unused)
                * @return const Math::Vector3D(0, 0, 0) // Black vector
                */
            virtual Math::Vector3D emitted(const Ray &ray, const HitRecord &rec, double u, double v, const Math::Vector3D &point) const
            {
                (void)ray;
                (void)rec;
                (void)u;
                (void)v;
                (void)point;

                return Math::Vector3D(0, 0, 0);
            }

            /**
            * @brief This function is used to calculate the probability density function
            * A AMaterial can't calculate the probability density function, so it returns 0
            * @param ray
            * @param rec
            * @param scattered
            * @return double
            */
            virtual double scattering_pdf(const Ray &ray, const HitRecord &rec, const Ray &scattered) const
            {
                (void)ray;
                (void)rec;
                (void)scattered;

                return 0;
            }

            virtual void init(const libconfig::Setting &setting)
            {
                (void)setting;
            }
    };
};

#endif /* !AMATERIAL_HPP_ */

You need to specify an extern "C" function that will be used to create the material.

This is an example of a constructor

extern "C" {
    RayTracer::AMaterial *EntryPoint()
    {
        return new RayTracer::Plugin::Metal();
    }
}

Textures

Textures are used to give a color to a material. They can be used to create a checkerboard, a simple color, or a noise texture.

Creating a texture

To create a texture, you need to create a class that inherits from ATexture.

#ifndef ATEXTURE_HPP_
    #define ATEXTURE_HPP_

    #include "ITexture.hpp"

namespace RayTracer {
    class ATexture : public ITexture {
        public:
            /////////////////
            // Ctor & Dtor //
            /////////////////

            /**
             * @brief Construct a new ATexture object
             * with a default texture matrix initialized to the identity matrix
             */
            ATexture() : _textureMatrix(Math::Matrix(1, 1))
            {
                _textureMatrix = _textureMatrix.identityMatrix(4);
            }

            virtual ~ATexture() override {};

            //////////////////////
            // Getter & Setters //
            //////////////////////

            /**
             * @brief Get the Texture Matrix object
             * The Matrix represent the transformation to apply to the texture
             * @return Math::Matrix The texture matrix
             */
            Math::Matrix getTextureMatrix() const override
            {
                return _textureMatrix;
            }

            /**
             * @brief Set the Texture Matrix object
             * The Matrix represent the transformation to apply to the texture
             * @param Matrix The texture matrix
             */
            void setTextureMatrix(const Math::Matrix Matrix) override
            {
                _textureMatrix = Matrix;
            }

            /////////////
            // Methods //
            /////////////

            /**
             * @brief Get the color at the given point
             *
             * @param point The point to get the color from
             * @return const Math::Vector3D The color at the given point
             */
            const Math::Vector3D colorAt(const Math::Vector3D &point) const override
            {
                Math::Vector3D texturePoint = _textureMatrix.inverse() * point;

                return textureColorAt(texturePoint);
            }

            virtual const Math::Vector3D textureColorAt(const Math::Vector3D &point) const = 0;

            /**
             * @brief Create a new texture from the given setting
             *
             * @param setting The setting to create the texture from
             * @return ITexture* The created texture
             */
            virtual void builder(const libconfig::Setting &setting) = 0;

        private:
            // The texture matrix
            Math::Matrix _textureMatrix;
    };
};

#endif /* !ATEXTURE_HPP_ */

You need to specify an extern "C" function that will be used to create the texture.

This is an example of a constructor

extern "C" {
    RayTracer::ChessTexture *EntryPoint()
    {
        return new RayTracer::ChessTexture();
    }
}

Clone this wiki locally