diff --git a/src/pe/raytracing/Intersects.h b/src/pe/raytracing/Intersects.h index 0bba3fbd71e5f83218a9958528050ca22028ae9c..a1fdb7c657d7e09bda33fd9f30448aebd84e1eb1 100644 --- a/src/pe/raytracing/Intersects.h +++ b/src/pe/raytracing/Intersects.h @@ -189,7 +189,7 @@ inline bool intersects(const BoxID box, const Ray& ray, real_t& t, Vec3& n) { if (transformedRay.getDirection() * n > 0) { n = -n; - } + } n = box->vectorFromBFtoWF(n); t = t_; diff --git a/src/pe/raytracing/Lighting.h b/src/pe/raytracing/Lighting.h new file mode 100644 index 0000000000000000000000000000000000000000..f09985b02c274cd5f856f30498bca2a5b728dac7 --- /dev/null +++ b/src/pe/raytracing/Lighting.h @@ -0,0 +1,85 @@ +//====================================================================================================================== +// +// This file is part of waLBerla. waLBerla is free software: you can +// redistribute it and/or modify it under the terms of the GNU General Public +// License as published by the Free Software Foundation, either version 3 of +// the License, or (at your option) any later version. +// +// waLBerla is distributed in the hope that it will be useful, but WITHOUT +// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +// for more details. +// +// You should have received a copy of the GNU General Public License along +// with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>. +// +//! \file PointLight.h +//! \author Lukas Werner +// +//====================================================================================================================== + +#pragma once + +#include <pe/basic.h> +#include <pe/Types.h> +#include <core/math/Vector3.h> + +namespace walberla { +namespace pe { +namespace raytracing { +struct Lighting { + Vec3 pointLightOrigin; + + Vec3 ambientLight; + + Vec3 diffuseColor; + real_t diffusePower; + + Vec3 specularColor; + real_t specularPower; + + /*!\brief Instantiation constructor for the Lighting struct. + */ + Lighting () { + + } + + /*!\brief Instantiation constructor for the Lighting struct. + * \param pointLightOrigin Origin of the point light. + * \param ambientLight Color of the ambient light. + * \param diffuseColor Diffuse color. + * \param diffusePower Diffuse color power. + * \param specularColor Specular color. + * \param specularPower Specular color power. + */ + Lighting (const Vec3& _pointLightOrigin, const Vec3& _ambientLight, + const Vec3& _diffuseColor, real_t _diffusePower, + const Vec3& _specularColor, real_t _specularPower) + : pointLightOrigin(_pointLightOrigin), ambientLight(_ambientLight), + diffuseColor(_diffuseColor), diffusePower(_diffusePower), + specularColor(_specularColor), specularPower(_specularPower) { + + } + + /*!\brief Instantiation constructor for the Lighting struct. + * \param config Config handle. + * + * The config block has to contain a pointLightOrigin parameter (Vec3). + * Optional are ambientLight (Vec3), for diffuse coloring diffuseColor (Vec3) and diffusePower (real) and + * for specular color specularColor (Vec3) and specularPower (real). + * Colors are Vec3's with values from 0 to 1. + */ + Lighting (const Config::BlockHandle& config) { + WALBERLA_CHECK(config.isValid(), "No valid config passed to raytracer lighting."); + + pointLightOrigin = config.getParameter<Vec3>("pointLightOrigin"), + ambientLight = config.getParameter<Vec3>("ambientLight", Vec3(0,0,0)), + diffuseColor = config.getParameter<Vec3>("diffuseColor", Vec3(0,0,0)); + diffusePower = config.getParameter<real_t>("diffusePower", real_t(0)); + specularColor = config.getParameter<Vec3>("specularColor", Vec3(0,0,0)); + specularPower = config.getParameter<real_t>("specularPower", real_t(0)); + } +}; +} +} +} diff --git a/src/pe/raytracing/Ray.h b/src/pe/raytracing/Ray.h index cfd80cab71245f9671f3c010e50fbe929f9d9e2f..c3e5d28d63291f4864667ac3144379ffe83c924c 100644 --- a/src/pe/raytracing/Ray.h +++ b/src/pe/raytracing/Ray.h @@ -38,7 +38,7 @@ public: } /*!\brief Instantiation constructor for the Raytracer class. - * \param origin Origin of the ray. + * \param origin Origin of the ray. () * \param direction Normalized direction of the ray. */ Ray (Vec3 origin, Vec3 direction) { diff --git a/src/pe/raytracing/Raytracer.cpp b/src/pe/raytracing/Raytracer.cpp index f79883314b0efa046541d60d1889f7f8450e3aa2..ad903e598f8a270fc35f596ba51e52604b3d30f7 100644 --- a/src/pe/raytracing/Raytracer.cpp +++ b/src/pe/raytracing/Raytracer.cpp @@ -48,13 +48,17 @@ Raytracer::Raytracer(const shared_ptr<BlockStorage> forest, BlockDataID storageI size_t pixelsHorizontal, size_t pixelsVertical, real_t fov_vertical, const Vec3& cameraPosition, const Vec3& lookAtPoint, const Vec3& upVector, + const Lighting& lighting, real_t blockAABBIntersectionPadding) : forest_(forest), storageID_(storageID), globalBodyStorage_(globalBodyStorage), pixelsHorizontal_(pixelsHorizontal), pixelsVertical_(pixelsVertical), fov_vertical_(fov_vertical), cameraPosition_(cameraPosition), lookAtPoint_(lookAtPoint), upVector_(upVector), + lighting_(lighting), blockAABBIntersectionPadding_(blockAABBIntersectionPadding), - tBufferOutputEnabled_(false) + tBufferOutputEnabled_(false), + imageOutputEnabled_(false), + localImageOutputEnabled_(false) { setupView_(); } @@ -68,6 +72,10 @@ Raytracer::Raytracer(const shared_ptr<BlockStorage> forest, BlockDataID storageI * The config block has to contain image_x (int), image_y (int), fov_vertical (real, in degrees) * and tbuffer_output_directory (string) parameters. Additionally a vector of reals * for each of cameraPosition, lookAt and the upVector. Optional is blockAABBIntersectionPadding (real). + * To output both process local and global tbuffers after raytracing, set tbuffer_output_directory (string). + * For image output after raytracing, set image_output_directory (string); for local image output additionally set + * local_image_output_enabled (bool) to true. + * For the lighting a config block named Lighting has to be defined, information about its contents is in Lighting.h. */ Raytracer::Raytracer(const shared_ptr<BlockStorage> forest, BlockDataID storageID, const shared_ptr<BodyStorage> globalBodyStorage, @@ -85,9 +93,20 @@ Raytracer::Raytracer(const shared_ptr<BlockStorage> forest, BlockDataID storageI WALBERLA_LOG_INFO_ON_ROOT("t buffers will be written to " << getTBufferOutputDirectory() << "."); } + setLocalImageOutputEnabled(config.getParameter<bool>("local_image_output_enabled", false)); + + if (config.isDefined("image_output_directory")) { + setImageOutputEnabled(true); + setImageOutputDirectory(config.getParameter<std::string>("image_output_directory")); + WALBERLA_LOG_INFO_ON_ROOT("Images will be written to " << getImageOutputDirectory() << "."); + } else if (getLocalImageOutputEnabled()) { + WALBERLA_ABORT("Cannot enable local image output without image_output_directory parameter being set."); + } + cameraPosition_ = config.getParameter<Vec3>("cameraPosition"); lookAtPoint_ = config.getParameter<Vec3>("lookAt"); upVector_ = config.getParameter<Vec3>("upVector"); + lighting_ = Lighting(config.getBlock("Lighting")); blockAABBIntersectionPadding_ = config.getParameter<real_t>("blockAABBIntersectionPadding", real_t(0.0)); @@ -180,6 +199,49 @@ void Raytracer::writeTBufferToFile(const std::vector<real_t>& tBuffer, const std ofs.close(); } +/*!\brief Writes a image buffer to a file in the image output directory. + * \param imageBuffer Buffer with color vectors. + * \param timestep Timestep this image is from. + * \param isGlobalImage Whether this image is the fully stitched together one. + */ +void Raytracer::writeImageBufferToFile(const std::vector<Vec3>& imageBuffer, size_t timestep, bool isGlobalImage) const { + WALBERLA_CHECK(timestep < 100000, "Raytracer only supports outputting 99 999 timesteps."); + mpi::MPIRank rank = mpi::MPIManager::instance()->rank(); + uint8_t padding = (timestep < 10 ? 4 : + (timestep < 100 ? 3 : + (timestep < 1000 ? 2 : + (timestep < 10000 ? 1 : + 0)))); + std::string fileName = "image_" + std::string(padding, '0') + std::to_string(timestep) + "+" + (isGlobalImage ? "global" : std::to_string(rank)) + ".ppm"; + writeImageBufferToFile(imageBuffer, fileName); +} + +/*!\brief Writes the image buffer to a file in the image output directory. + * \param imageBuffer Buffer with color vectors. + * \param fileName Name of the output file. + */ +void Raytracer::writeImageBufferToFile(const std::vector<Vec3>& imageBuffer, const std::string& fileName) const { + namespace fs = boost::filesystem; + + fs::path dir (getImageOutputDirectory()); + fs::path file (fileName); + fs::path fullPath = dir / file; + + std::ofstream ofs(fullPath.string<std::string>(), std::ios::out | std::ios::binary); + ofs << "P6\n" << pixelsHorizontal_ << " " << pixelsVertical_ << "\n255\n"; + for (size_t y = pixelsVertical_-1; y > 0; y--) { + for (size_t x = 0; x < pixelsHorizontal_; x++) { + size_t i = coordinateToArrayIndex(x, y); + const Vec3& color = imageBuffer[i]; + char r = (char)(255 * color[0]); + char g = (char)(255 * color[1]); + char b = (char)(255 * color[2]); + ofs << r << g << b; + } + } + + ofs.close(); +} } } } diff --git a/src/pe/raytracing/Raytracer.h b/src/pe/raytracing/Raytracer.h index 3dc53f15fbdca89425d06d147492e32547f5266f..e738d2b518b31afd87f6e577a90b21d163a36dce 100644 --- a/src/pe/raytracing/Raytracer.h +++ b/src/pe/raytracing/Raytracer.h @@ -29,6 +29,7 @@ #include <core/timing/TimingTree.h> #include "Ray.h" #include "Intersects.h" +#include "Lighting.h" using namespace walberla; using namespace walberla::pe; @@ -45,6 +46,7 @@ struct BodyIntersectionInfo { size_t imageY; //!< viewing plane y pixel coordinate the ray belongs to. walberla::id_t bodySystemID; //!< system ID of body which was hit. real_t t; //!< distance from camera to intersection point on body. + Vec3 color; //!< color computed for the pixel. }; class Raytracer { @@ -56,6 +58,7 @@ public: size_t pixelsHorizontal, size_t pixelsVertical, real_t fov_vertical, const Vec3& cameraPosition, const Vec3& lookAtPoint, const Vec3& upVector, + const Lighting& lighting, real_t blockAABBIntersectionPadding = real_t(0.0)); explicit Raytracer(const shared_ptr<BlockStorage> forest, BlockDataID storageID, const shared_ptr<BodyStorage> globalBodyStorage, @@ -77,11 +80,16 @@ private: Vec3 lookAtPoint_; /*!< The point the camera looks at in the global world frame, marks the center of the view plane.*/ Vec3 upVector_; //!< The vector indicating the upwards direction of the camera. + Lighting lighting_; //!< The lighting of the scene. real_t blockAABBIntersectionPadding_; /*!< The padding applied in block AABB intersection pretesting, as some objects within a block might protrude from the block's AABB.*/ - bool tBufferOutputEnabled_; //!< Enable / disable dumping the tbuffer to a file - std::string tBufferOutputDirectory_; //!< Path to the tbuffer output directory + bool tBufferOutputEnabled_; //!< Enable / disable dumping the tbuffer to file. + std::string tBufferOutputDirectory_; //!< Path to the tbuffer output directory. + + bool imageOutputEnabled_; //!< Enable / disable writing images to file. + bool localImageOutputEnabled_; //!< Enable / disable writing images of the local process to file. + std::string imageOutputDirectory_; //!< Path to the image output directory. //@} Vec3 n_; //!< The normal vector of the viewing plane. @@ -107,12 +115,18 @@ public: inline const Vec3& getUpVector() const; inline bool getTBufferOutputEnabled() const; inline const std::string& getTBufferOutputDirectory() const; + inline bool getImageOutputEnabled() const; + inline bool getLocalImageOutputEnabled() const; + inline const std::string& getImageOutputDirectory() const; //@} /*!\name Set functions */ //@{ inline void setTBufferOutputEnabled(const bool enabled); inline void setTBufferOutputDirectory(const std::string& path); + inline void setImageOutputEnabled(const bool enabled); + inline void setLocalImageOutputEnabled(const bool enabled); + inline void setImageOutputDirectory(const std::string& path); //@} /*!\name Functions */ @@ -124,8 +138,13 @@ public: private: void writeTBufferToFile(const std::vector<real_t>& tBuffer, size_t timestep, bool isGlobalImage = false) const; void writeTBufferToFile(const std::vector<real_t>& tBuffer, const std::string& fileName) const; + void writeImageBufferToFile(const std::vector<Vec3>& imageBuffer, size_t timestep, bool isGlobalImage = false) const; + void writeImageBufferToFile(const std::vector<Vec3>& imageBuffer, const std::string& fileName) const; + inline bool isPlaneVisible(const PlaneID plane, const Ray& ray) const; inline size_t coordinateToArrayIndex(size_t x, size_t y) const; + + inline Vec3 getColor(const BodyID body, const Ray& ray, real_t t, const Vec3& n) const; //@} }; @@ -200,7 +219,31 @@ inline const std::string& Raytracer::getTBufferOutputDirectory() const { return tBufferOutputDirectory_; } -/*!\brief Enabled / disable outputting the tBuffer to a file in the specified directory. +/*!\brief Returns true if image output to a file is enabled. + * + * \return True if image output enabled, otherwise false. + */ +inline bool Raytracer::getImageOutputEnabled() const { + return imageOutputEnabled_; +} + +/*!\brief Returns true if local image output to a file is enabled. + * + * \return True if local image output enabled, otherwise false. + */ +inline bool Raytracer::getLocalImageOutputEnabled() const { + return localImageOutputEnabled_; +} + +/*!\brief Returns the directory where the images will be saved in. + * + * \return Path to the image output directory. + */ +inline const std::string& Raytracer::getImageOutputDirectory() const { + return imageOutputDirectory_; +} + +/*!\brief Enable / disable outputting the tBuffer to a file in the specified directory. * \param enabled Set to true / false to enable / disable tbuffer output. */ inline void Raytracer::setTBufferOutputEnabled(const bool enabled) { @@ -219,6 +262,32 @@ inline void Raytracer::setTBufferOutputDirectory(const std::string& path) { tBufferOutputDirectory_ = path; } +/*!\brief Enable / disable outputting images in the specified directory. + * \param enabled Set to true / false to enable / disable image output. + */ +inline void Raytracer::setImageOutputEnabled(const bool enabled) { + imageOutputEnabled_ = enabled; +} + +/*!\brief Enable / disable outputting local images in the specified directory. + * \param enabled Set to true / false to enable / disable image output. + */ +inline void Raytracer::setLocalImageOutputEnabled(const bool enabled) { + localImageOutputEnabled_ = enabled; +} + +/*!\brief Enable / disable outputting images in the specified directory. + * \param enabled Set to true / false to enable / disable image output. + */ +inline void Raytracer::setImageOutputDirectory(const std::string& path) { + namespace fs = boost::filesystem; + + fs::path dir (path); + WALBERLA_CHECK(fs::exists(dir) && fs::is_directory(dir), "Image output directory " << path << " is invalid."); + + imageOutputDirectory_ = path; +} + /*!\brief Checks if a plane should get rendered. * \param plane Plane to check for visibility. * \param ray Ray which is intersected with plane. @@ -251,11 +320,13 @@ void Raytracer::rayTrace(const size_t timestep) { real_t inf = std::numeric_limits<real_t>::max(); std::vector<real_t> tBuffer(pixelsVertical_ * pixelsHorizontal_, inf); + std::vector<Vec3> imageBuffer(pixelsVertical_ * pixelsHorizontal_); std::vector<BodyIntersectionInfo> intersections; // contains for each pixel information about an intersection, if existent real_t t, t_closest; Vec3 n; - RigidBody* body_closest = NULL; + Vec3 n_closest; + BodyID body_closest = NULL; Ray ray(cameraPosition_, Vec3(1,0,0)); IntersectsFunctor func(ray, t, n); tp_["Raytracing"].start(); @@ -265,7 +336,7 @@ void Raytracer::rayTrace(const size_t timestep) { Vec3 direction = (pixelLocation - cameraPosition_).getNormalized(); ray.setDirection(direction); - n[0] = n[1] = n[2] = real_t(0); + n.reset(); t_closest = inf; body_closest = NULL; for (auto blockIt = forest_->begin(); blockIt != forest_->end(); ++blockIt) { @@ -289,6 +360,7 @@ void Raytracer::rayTrace(const size_t timestep) { // body was shot by ray and currently closest to camera t_closest = t; body_closest = *bodyIt; + n_closest = n; } } } @@ -310,16 +382,20 @@ void Raytracer::rayTrace(const size_t timestep) { // body was shot by ray and currently closest to camera t_closest = t; body_closest = *bodyIt; + n_closest = n; } } } if (!realIsIdentical(t_closest, inf) && body_closest != NULL) { + Vec3 color = getColor(body_closest, ray, t_closest, n_closest); + imageBuffer[coordinateToArrayIndex(x, y)] = color; BodyIntersectionInfo intersectionInfo = { x, y, body_closest->getSystemID(), - t_closest + t_closest, + color }; intersections.push_back(intersectionInfo); } @@ -334,10 +410,13 @@ void Raytracer::rayTrace(const size_t timestep) { // intersections synchronisieren mpi::SendBuffer sendBuffer; for (auto& info: intersections) { - sendBuffer << info.imageX << info.imageY << info.bodySystemID << info.t; + sendBuffer << info.imageX << info.imageY + << info.bodySystemID << info.t + << info.color[0] << info.color[1] << info.color[2]; } int gatheredIntersectionCount = 0; - std::vector<real_t> fullImage(pixelsHorizontal_ * pixelsVertical_, inf); + std::vector<real_t> fullTBuffer(pixelsHorizontal_ * pixelsVertical_, inf); + std::vector<Vec3> fullImageBuffer(pixelsHorizontal_ * pixelsVertical_); mpi::RecvBuffer recvBuffer; mpi::gathervBuffer(sendBuffer, recvBuffer, 0); @@ -350,11 +429,15 @@ void Raytracer::rayTrace(const size_t timestep) { recvBuffer >> info.imageY; recvBuffer >> info.bodySystemID; recvBuffer >> info.t; + recvBuffer >> info.color[0]; + recvBuffer >> info.color[1]; + recvBuffer >> info.color[2]; size_t i = coordinateToArrayIndex(info.imageX, info.imageY); - real_t currentFullImageT = fullImage[i]; - if (currentFullImageT > info.t) { - fullImage[i] = info.t; + real_t currentFullTBufferT = fullTBuffer[i]; + if (currentFullTBufferT > info.t) { + fullTBuffer[i] = info.t; + fullImageBuffer[i] = info.color; } gatheredIntersectionCount++; @@ -372,14 +455,44 @@ void Raytracer::rayTrace(const size_t timestep) { tpReduced->print(std::cout); } + if (getImageOutputEnabled()) { + writeImageBufferToFile(imageBuffer, timestep); + WALBERLA_ROOT_SECTION() { + writeImageBufferToFile(fullImageBuffer, timestep, true); + } + } + if (getTBufferOutputEnabled()) { writeTBufferToFile(tBuffer, timestep); WALBERLA_ROOT_SECTION() { - writeTBufferToFile(fullImage, timestep, true); + writeTBufferToFile(fullTBuffer, timestep, true); } } } + +inline Vec3 Raytracer::getColor(const BodyID body, const Ray& ray, real_t t, const Vec3& n) const { + Vec3 objectColor(real_t(0.6), real_t(0), real_t(0.9)); + + if (body->getTypeID() == Plane::getStaticTypeID()) { + objectColor = Vec3(real_t(0.7), real_t(0.7), real_t(0.7)); + } + if (body->getTypeID() == Sphere::getStaticTypeID()) { + objectColor = Vec3(real_t(1), real_t(0.1), real_t(0.1)); + } + const Vec3 intersectionPoint = ray.getOrigin() + ray.getDirection() * t; + Vec3 lightDirection = lighting_.pointLightOrigin - intersectionPoint; + lightDirection = lightDirection.getNormalized(); + real_t lambertian = std::max(real_t(0), lightDirection * n); + + Vec3 color = objectColor * lambertian + lighting_.ambientLight; + + for (size_t c = 0; c < 2; c++) { + color[c] = std::min(color[c], real_t(1)); + } + + return color; +} } } } diff --git a/tests/pe/Raytracing.cpp b/tests/pe/Raytracing.cpp index 3722f69f8f9ef131cc46e7cf0fb91ba19bb45275..297b6a779135b0aee83d03bff84ff7376e2c80b2 100644 --- a/tests/pe/Raytracing.cpp +++ b/tests/pe/Raytracing.cpp @@ -98,6 +98,13 @@ void PlaneIntersectsTest() { // plane with center -10,3,3 and parallel to y-z plane Plane pl4(1, 1, Vec3(-10, 3, 3), Vec3(1, 0, 0), real_t(1.0), iron); WALBERLA_CHECK(!intersects(&pl4, ray1, t, n), "ray hit plane behind origin"); + + Plane pl6(1, 1, Vec3(3, 3, 0), Vec3(-1, 0, 0), real_t(1.0), iron); + Ray ray4(Vec3(0,0,5), Vec3(1, 0, -1).getNormalized()); + WALBERLA_CHECK(intersects(&pl6, ray4, t, n), "ray didnt hit"); + WALBERLA_CHECK_FLOAT_EQUAL(n[0], real_t(-1), "incorrect normal calculated"); + WALBERLA_CHECK_FLOAT_EQUAL(n[1], real_t(0), "incorrect normal calculated"); + WALBERLA_CHECK_FLOAT_EQUAL(n[2], real_t(0), "incorrect normal calculated"); } void BoxIntersectsTest() { @@ -181,11 +188,14 @@ void RaytracerTest() { shared_ptr<BodyStorage> globalBodyStorage = make_shared<BodyStorage>(); shared_ptr<BlockForest> forest = createBlockForest(AABB(0,-5,-5,10,5,5), Vec3(1,1,1), Vec3(false, false, false)); auto storageID = forest->addBlockData(createStorageDataHandling<BodyTuple>(), "Storage"); - + Lighting lighting(Vec3(0, 3, 3), Vec3(0.1, 0.1, 0.1), + Vec3(0.1, 0.1, 0.1), real_t(2), + Vec3(0.4, 0.4, 0.4), real_t(4)); Raytracer raytracer(forest, storageID, globalBodyStorage, size_t(640), size_t(480), 49.13, - Vec3(-5,0,0), Vec3(-1,0,0), Vec3(0,0,1)); + Vec3(-5,0,0), Vec3(-1,0,0), Vec3(0,0,1), + lighting); MaterialID iron = Material::find("iron"); @@ -214,6 +224,8 @@ void RaytracerTest() { raytracer.setTBufferOutputDirectory("/Users/ng/Desktop/walberla"); raytracer.setTBufferOutputEnabled(true); + raytracer.setImageOutputDirectory("/Users/ng/Desktop/walberla"); + raytracer.setImageOutputEnabled(true); raytracer.rayTrace<BodyTuple>(0); }