From 41e43e616f72220ce5e091e5471b22e2f05a26b7 Mon Sep 17 00:00:00 2001
From: Lukas Werner <lks.werner@fau.de>
Date: Mon, 5 Feb 2018 11:04:27 +0100
Subject: [PATCH] Added user-definable body to shading params function

---
 src/pe/raytracing/Raytracer.cpp       |  14 ++-
 src/pe/raytracing/Raytracer.h         |  45 +++------
 src/pe/raytracing/ShadingFunctions.h  | 136 ++++++++++++++++++++++++++
 src/pe/raytracing/ShadingParameters.h |  81 +++++++++++++++
 tests/pe/Raytracing.cpp               | 117 +++++++++++++++++++++-
 5 files changed, 352 insertions(+), 41 deletions(-)
 create mode 100644 src/pe/raytracing/ShadingFunctions.h
 create mode 100644 src/pe/raytracing/ShadingParameters.h

diff --git a/src/pe/raytracing/Raytracer.cpp b/src/pe/raytracing/Raytracer.cpp
index aaea93901..59edeb08d 100644
--- a/src/pe/raytracing/Raytracer.cpp
+++ b/src/pe/raytracing/Raytracer.cpp
@@ -52,7 +52,8 @@ Raytracer::Raytracer(const shared_ptr<BlockStorage> forest, BlockDataID storageI
                      const Vec3& cameraPosition, const Vec3& lookAtPoint, const Vec3& upVector,
                      const Lighting& lighting,
                      const Color& backgroundColor,
-                     real_t blockAABBIntersectionPadding)
+                     real_t blockAABBIntersectionPadding,
+                     std::function<ShadingParameters (const BodyID)> bodyToShadingParamsFunction)
    : forest_(forest), storageID_(storageID), globalBodyStorage_(globalBodyStorage),
    pixelsHorizontal_(pixelsHorizontal), pixelsVertical_(pixelsVertical),
    fov_vertical_(fov_vertical),
@@ -63,8 +64,9 @@ Raytracer::Raytracer(const shared_ptr<BlockStorage> forest, BlockDataID storageI
    tBufferOutputEnabled_(false),
    imageOutputEnabled_(false),
    localImageOutputEnabled_(false),
-   filenameTimestepWidth_(5)
-{
+   filenameTimestepWidth_(5),
+   bodyToShadingParamsFunction_(bodyToShadingParamsFunction) {
+   
    setupView_();
    setupFilenameRankWidth_();
 }
@@ -85,8 +87,10 @@ Raytracer::Raytracer(const shared_ptr<BlockStorage> forest, BlockDataID storageI
  */
 Raytracer::Raytracer(const shared_ptr<BlockStorage> forest, BlockDataID storageID,
                      const shared_ptr<BodyStorage> globalBodyStorage,
-                     const Config::BlockHandle& config)
-   : forest_(forest), storageID_(storageID), globalBodyStorage_(globalBodyStorage) {
+                     const Config::BlockHandle& config,
+                     std::function<ShadingParameters (const BodyID)> bodyToShadingParamsFunction)
+   : forest_(forest), storageID_(storageID), globalBodyStorage_(globalBodyStorage),
+   bodyToShadingParamsFunction_(bodyToShadingParamsFunction) {
    WALBERLA_CHECK(config.isValid(), "No valid config passed to raytracer");
    
    pixelsHorizontal_ = config.getParameter<uint16_t>("image_x");
diff --git a/src/pe/raytracing/Raytracer.h b/src/pe/raytracing/Raytracer.h
index 52a0a7987..7e79b619a 100644
--- a/src/pe/raytracing/Raytracer.h
+++ b/src/pe/raytracing/Raytracer.h
@@ -27,9 +27,11 @@
 #include <core/config/Config.h>
 #include <boost/filesystem.hpp>
 #include <core/timing/TimingTree.h>
+#include <functional>
 #include "Ray.h"
 #include "Intersects.h"
 #include "Lighting.h"
+#include "ShadingFunctions.h"
 
 using namespace walberla;
 using namespace walberla::pe;
@@ -60,10 +62,12 @@ public:
                       const Vec3& cameraPosition, const Vec3& lookAtPoint, const Vec3& upVector,
                       const Lighting& lighting,
                       const Color& backgroundColor = Color(0.1, 0.1, 0.1),
-                      real_t blockAABBIntersectionPadding = real_t(0.0));
+                      real_t blockAABBIntersectionPadding = real_t(0.0),
+                      std::function<ShadingParameters (const BodyID)> bodyToShadingParamsFunction = defaultBodyTypeDependentShadingParams);
    explicit Raytracer(const shared_ptr<BlockStorage> forest, BlockDataID storageID,
                       const shared_ptr<BodyStorage> globalBodyStorage,
-                      const Config::BlockHandle& config);
+                      const Config::BlockHandle& config,
+                      std::function<ShadingParameters (const BodyID)> bodyToShadingParamsFunction = defaultBodyTypeDependentShadingParams);
    //@}
 
 private:
@@ -96,6 +100,9 @@ private:
                                   * Use e.g. 5 for ranges from 1 to 99 999: Will result in
                                   * filenames like image_00001.png up to image_99999.png. */
    int8_t filenameRankWidth_;  //!< Width of the mpi rank part in a filename.
+   std::function<ShadingParameters (const BodyID)> bodyToShadingParamsFunction_; /*!< Function which returns a 
+                                                                                  * ShadingParameters struct
+                                                                                  * given the specified body. */
    //@}
    
    /*!\name Member variables for raytracing geometry */
@@ -536,31 +543,7 @@ void Raytracer::rayTrace(const size_t timestep) {
  * \return Computed color.
  */
 inline Color Raytracer::getColor(const BodyID body, const Ray& ray, real_t t, const Vec3& n) const {
-   //----
-   Color diffuseColor(0.6, 0, 0.9);
-   Color specularColor(0.8, 0.8, 0.8);
-   Color ambientColor(0.5, 0, 0.8);
-   real_t shininess = 100;
-
-   if (body->getTypeID() == Plane::getStaticTypeID()) {
-      diffuseColor = Color(real_t(0.7), real_t(0.7), real_t(0.7));
-      ambientColor.set(real_t(0.5), real_t(0.5), real_t(0.5));
-      specularColor.set(real_t(0), real_t(0), real_t(0));
-      shininess = real_t(0);
-   }
-   if (body->getTypeID() == Sphere::getStaticTypeID()) {
-      diffuseColor = Color(real_t(0.98), real_t(0.1), real_t(0.1));
-      ambientColor.set(real_t(0.6), real_t(0.05), real_t(0.05));
-      specularColor.set(real_t(1), real_t(1), real_t(1));
-      shininess = real_t(30);
-   }
-   if (body->getTypeID() == Capsule::getStaticTypeID()) {
-      diffuseColor = Color(real_t(0.15), real_t(0.44), real_t(0.91));
-      ambientColor.set(real_t(0), real_t(0), real_t(0.3));
-      specularColor.set(real_t(1), real_t(1), real_t(1));
-      shininess = real_t(20);
-   }
-   //----
+   const ShadingParameters shadingParams = bodyToShadingParamsFunction_(body);
    
    const Vec3 intersectionPoint = ray.getOrigin() + ray.getDirection() * t;
    Vec3 lightDirection = lighting_.pointLightOrigin - intersectionPoint;
@@ -575,12 +558,12 @@ inline Color Raytracer::getColor(const BodyID body, const Ray& ray, real_t t, co
       Vec3 viewDirection = -ray.getDirection();
       Vec3 halfDirection = (lightDirection + viewDirection).getNormalized();
       real_t specularAngle = std::max(halfDirection * n, real_t(0));
-      specular = real_c(pow(specularAngle, shininess));
+      specular = real_c(pow(specularAngle, shadingParams.shininess));
    }
    
-   Color color = lighting_.ambientColor.mulComponentWise(ambientColor)
-      + lighting_.diffuseColor.mulComponentWise(diffuseColor)*lambertian
-      + lighting_.specularColor.mulComponentWise(specularColor)*specular;
+   Color color = lighting_.ambientColor.mulComponentWise(shadingParams.ambientColor)
+      + lighting_.diffuseColor.mulComponentWise(shadingParams.diffuseColor)*lambertian
+      + lighting_.specularColor.mulComponentWise(shadingParams.specularColor)*specular;
    
    // Capping of color channels to 1.
    // Capping instead of scaling will make specular highlights stronger.
diff --git a/src/pe/raytracing/ShadingFunctions.h b/src/pe/raytracing/ShadingFunctions.h
new file mode 100644
index 000000000..b7af42fe5
--- /dev/null
+++ b/src/pe/raytracing/ShadingFunctions.h
@@ -0,0 +1,136 @@
+//======================================================================================================================
+//
+//  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 Shading.h
+//! \author Lukas Werner
+//
+//======================================================================================================================
+
+#pragma once
+
+#include <pe/basic.h>
+#include <pe/Types.h>
+#include <pe/raytracing/Color.h>
+#include <pe/raytracing/ShadingParameters.h>
+
+
+namespace walberla {
+namespace pe {
+namespace raytracing {
+inline ShadingParameters defaultBodyTypeDependentShadingParams (const BodyID body);
+inline ShadingParameters defaultShadingParams (const BodyID body);
+inline ShadingParameters blackShadingParams (const BodyID body);
+inline ShadingParameters whiteShadingParams (const BodyID body);
+inline ShadingParameters lightGreyShadingParams (const BodyID body);
+inline ShadingParameters greyShadingParams (const BodyID body);
+inline ShadingParameters darkGreyShadingParams (const BodyID body);
+inline ShadingParameters redShadingParams (const BodyID body);
+inline ShadingParameters blueShadingParams (const BodyID body);
+inline ShadingParameters violetShadingParams (const BodyID body);
+
+inline ShadingParameters defaultBodyTypeDependentShadingParams (const BodyID body) {
+   auto bodyTypeID = body->getTypeID();
+   
+   if (bodyTypeID == Plane::getStaticTypeID()) {
+      return lightGreyShadingParams(body).makeMatte();
+   } else if (bodyTypeID == Sphere::getStaticTypeID()) {
+      return redShadingParams(body).makeGlossy();
+   } else if (bodyTypeID == Capsule::getStaticTypeID()) {
+      return blueShadingParams(body).makeGlossy();
+   } else if (bodyTypeID == Box::getStaticTypeID()) {
+      return violetShadingParams(body);
+   } else {
+      return defaultShadingParams(body);
+   }
+}
+
+inline ShadingParameters defaultShadingParams (const BodyID body) {
+   return greyShadingParams(body);
+}
+   
+inline ShadingParameters whiteShadingParams (const BodyID body) {
+   ShadingParameters s(Color(1, 1, 1),
+                       Color(0.9, 0.9, 0.9),
+                       Color(0, 0, 0),
+                       0);
+   return s;
+}
+   
+inline ShadingParameters blackShadingParams (const BodyID body) {
+   ShadingParameters s(Color(0, 0, 0),
+                       Color(0, 0, 0),
+                       Color(0.1, 0.1, 0.1),
+                       0);
+   return s;
+}
+
+inline ShadingParameters lightGreyShadingParams (const BodyID body) {
+   ShadingParameters s(Color(0.82, 0.82, 0.82),
+                       Color(0.5, 0.5, 0.5),
+                       Color(0, 0, 0),
+                       0);
+   return s;
+}
+
+inline ShadingParameters greyShadingParams (const BodyID body) {
+   ShadingParameters s(Color(0.5, 0.5, 0.5),
+                       Color(0.4, 0.4, 0.4),
+                       Color(0.1, 0.1, 0.1),
+                       0);
+   return s;
+}
+
+inline ShadingParameters darkGreyShadingParams (const BodyID body) {
+   ShadingParameters s(Color(0.2, 0.2, 0.2),
+                       Color(0.06, 0.06, 0.06),
+                       Color(0.1, 0.1, 0.1),
+                       0);
+   return s;
+}
+   
+inline ShadingParameters redShadingParams (const BodyID body) {
+   ShadingParameters s(Color(1, 0, 0),
+             Color(0.5, 0, 0),
+             Color(0.1, 0.1, 0.1),
+             0);
+   return s;
+}
+
+inline ShadingParameters greenShadingParams (const BodyID body) {
+   ShadingParameters s(Color(0, 0.72, 0),
+                       Color(0, 0.41, 0),
+                       Color(0.1, 0.1, 0.1),
+                       0);
+   return s;
+}
+
+inline ShadingParameters blueShadingParams (const BodyID body) {
+   ShadingParameters s(Color(0.15, 0.44, 0.91),
+             Color(0, 0, 0.4),
+             Color(0.1, 0.1, 0.1),
+             0);
+   return s;
+}
+
+inline ShadingParameters violetShadingParams (const BodyID body) {
+   ShadingParameters s(Color(0.6, 0, 0.9),
+                       Color(0.5, 0, 0.8),
+                       Color(0, 0, 0),
+                       0);
+   return s;
+}
+}
+}
+}
diff --git a/src/pe/raytracing/ShadingParameters.h b/src/pe/raytracing/ShadingParameters.h
new file mode 100644
index 000000000..d2269d97b
--- /dev/null
+++ b/src/pe/raytracing/ShadingParameters.h
@@ -0,0 +1,81 @@
+//======================================================================================================================
+//
+//  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 Shading.h
+//! \author Lukas Werner
+//
+//======================================================================================================================
+
+#pragma once
+
+#include <pe/basic.h>
+#include <pe/Types.h>
+#include <pe/raytracing/Color.h>
+
+namespace walberla {
+namespace pe {
+namespace raytracing {
+struct ShadingParameters {
+   Color diffuseColor;  //!< Primary color of the material.
+   Color ambientColor;  //!< Color the material has even when its not directly lit.
+   Color specularColor; //!< Color the specular highlight has.
+   real_t shininess;    //!< How shiny a material is (approximate range is between 1 and 100).
+   
+   /*!\brief Instantiation constructor for the Shading struct.
+    */
+   ShadingParameters () {
+      
+   }
+   
+   /*!\brief Instantiation constructor for the Shading struct.
+    * \param diffuseColor Primary color of the material.
+    * \param ambientColor Color the material has even when its not directly lit.
+    * \param specularColor Color this material contributes to on its specular highlights.
+    * \param shininess Shininess of the material.
+    */
+   ShadingParameters (const Color& _diffuseColor, const Color& _ambientColor, const Color& _specularColor, real_t _shininess)
+   : diffuseColor(_diffuseColor), ambientColor(_ambientColor), specularColor(_specularColor), shininess(_shininess) {
+      
+   }
+   
+   /*!\brief Instantiation constructor for the Shading struct.
+    * \param config Config handle.
+    *
+    * Config has to contain diffuseColor (Color), ambientColor (Color), specularColor (Color), shininess (real_t).
+    * Colors are Vec3's with values from 0 to 1.
+    */
+   ShadingParameters (const Config::BlockHandle& config) {
+      diffuseColor = config.getParameter<Color>("diffuseColor");
+      ambientColor = config.getParameter<Color>("ambientColor");
+      specularColor = config.getParameter<Color>("specularColor");
+      shininess = config.getParameter<real_t>("shininess");
+   }
+   
+   ShadingParameters makeGlossy(real_t _shininess = 30) {
+      shininess = _shininess;
+      specularColor.set(1, 1, 1);
+      return *this;
+   }
+   
+   ShadingParameters makeMatte() {
+      shininess = 0;
+      specularColor.set(0.1, 0.1, 0.1);
+      return *this;
+   }
+};
+}
+}
+}
+
diff --git a/tests/pe/Raytracing.cpp b/tests/pe/Raytracing.cpp
index f45b13af0..1bf0b12bc 100644
--- a/tests/pe/Raytracing.cpp
+++ b/tests/pe/Raytracing.cpp
@@ -20,6 +20,7 @@
 #include <pe/raytracing/Intersects.h>
 #include <pe/raytracing/Raytracer.h>
 #include <pe/raytracing/Color.h>
+#include <pe/raytracing/ShadingFunctions.h>
 
 using namespace walberla;
 using namespace walberla::pe;
@@ -209,6 +210,20 @@ void CapsuleIntersectsTest() {
    WALBERLA_CHECK_FLOAT_EQUAL(n[2], real_t(0));
 }
 
+ShadingParameters customBodyToShadingParams(const BodyID body) {
+   if (body->getID() == 10) {
+      return greenShadingParams(body).makeGlossy(30);
+   } else if (body->getID() == 7) {
+      return greenShadingParams(body).makeGlossy(10);
+   } else if (body->getID() == 9) {
+      return darkGreyShadingParams(body).makeGlossy(50);
+   } else if (body->getID() == 3) {
+      return redShadingParams(body).makeGlossy(200);
+   } else {
+      return defaultBodyTypeDependentShadingParams(body);
+   }
+}
+
 void RaytracerTest() {
    WALBERLA_LOG_INFO("Raytracer");
    shared_ptr<BodyStorage> globalBodyStorage = make_shared<BodyStorage>();
@@ -223,7 +238,9 @@ void RaytracerTest() {
                        49.13,
                        Vec3(-5,5,5), Vec3(-1,5,5), Vec3(0,0,1), //-5,5,5; -1,5,5
                        lighting,
-                       Color(0.2,0.2,0.2));
+                       Color(0.2,0.2,0.2),
+                       real_t(2),
+                       customBodyToShadingParams);
 
    MaterialID iron = Material::find("iron");
    
@@ -251,9 +268,9 @@ void RaytracerTest() {
    // Test scene v1 end
    
    // Test scene v2 additions start
-   createBox(*globalBodyStorage, *forest, storageID, 7, Vec3(9,9,5), Vec3(1,1,10));
-   createCapsule(*globalBodyStorage, *forest, storageID, 9, Vec3(3, 9, 1), real_t(0.5), real_t(7), iron);
-   CapsuleID capsule = createCapsule(*globalBodyStorage, *forest, storageID, 9, Vec3(7, 3.5, 7.5), real_t(1), real_t(2), iron);
+   createBox(*globalBodyStorage, *forest, storageID, 9, Vec3(9,9,5), Vec3(1,1,10));
+   createCapsule(*globalBodyStorage, *forest, storageID, 10, Vec3(3, 9, 1), real_t(0.5), real_t(7), iron);
+   CapsuleID capsule = createCapsule(*globalBodyStorage, *forest, storageID, 11, Vec3(7, 3.5, 7.5), real_t(1), real_t(2), iron);
    if (capsule != NULL) capsule->rotate(0,math::M_PI/3,math::M_PI/4-math::M_PI/8);
    // Test scene v2 end
    
@@ -266,6 +283,95 @@ void RaytracerTest() {
    raytracer.rayTrace<BodyTuple>(0);
 }
 
+ShadingParameters customSpheresBodyToShadingParams(const BodyID body) {
+   if (body->getTypeID() == Plane::getStaticTypeID()) {
+      return greyShadingParams(body);
+   }
+   
+   switch (body->getID()) {
+      case 0:
+         return blueShadingParams(body).makeGlossy(1);
+      case 1:
+         return blueShadingParams(body).makeGlossy(10);
+      case 2:
+         return blueShadingParams(body).makeGlossy(30);
+      case 3:
+         return blueShadingParams(body).makeGlossy(80);
+      case 4:
+         return whiteShadingParams(body);
+      case 5:
+         return lightGreyShadingParams(body);
+      case 6:
+         return greyShadingParams(body);
+      case 7:
+         return darkGreyShadingParams(body);
+      case 8:
+         return blackShadingParams(body).makeGlossy(100);
+      case 9:
+         return redShadingParams(body);
+      case 10:
+         return blueShadingParams(body);
+      case 11:
+         return violetShadingParams(body);
+      case 12:
+         return greenShadingParams(body);
+      case 13:
+         return greenShadingParams(body).makeGlossy(30);
+      case 14:
+         return blueShadingParams(body).makeGlossy(1000);
+      default:
+         return lightGreyShadingParams(body);
+   }
+}
+
+void RaytracerSpheresTest() {
+   WALBERLA_LOG_INFO("Raytracer");
+   shared_ptr<BodyStorage> globalBodyStorage = make_shared<BodyStorage>();
+   shared_ptr<BlockForest> forest = createBlockForest(AABB(0,0,0,10,10,10), Vec3(1,1,1), Vec3(false, false, false));
+   auto storageID = forest->addBlockData(createStorageDataHandling<BodyTuple>(), "Storage");
+   Lighting lighting(Vec3(0, 5, 8), // 8, 5, 9.5 gut für ebenen, 0,5,8
+                     Color(1, 1, 1), //diffuse
+                     Color(1, 1, 1), //specular
+                     Color(0.4, 0.4, 0.4)); //ambient
+   Raytracer raytracer(forest, storageID, globalBodyStorage,
+                       size_t(640), size_t(480),
+                       49.13,
+                       Vec3(-5,5,5), Vec3(-1,5,5), Vec3(0,0,1), //-5,5,5; -1,5,5
+                       lighting,
+                       Color(0.2,0.2,0.2),
+                       real_t(2),
+                       customSpheresBodyToShadingParams);
+   
+   MaterialID iron = Material::find("iron");
+   
+   //PlaneID xNegPlane = createPlane(*globalBodyStorage, 0, Vec3(-1,0,0), Vec3(5,0,0), iron);
+   // xNegPlane obstructs only the top left sphere and intersects some objects
+   
+   //PlaneID xNegPlaneClose = createPlane(*globalBodyStorage, 0, Vec3(-1,0,0), Vec3(1,0,0), iron);
+   
+   // Test Scene v1 - Spheres, (rotated) boxes, confining walls, tilted plane in right bottom back corner
+   createPlane(*globalBodyStorage, 0, Vec3(0,-1,0), Vec3(0,10,0), iron); // left wall
+   createPlane(*globalBodyStorage, 0, Vec3(0,1,0), Vec3(0,0,0), iron); // right wall
+   createPlane(*globalBodyStorage, 0, Vec3(0,0,1), Vec3(0,0,0), iron); // floor
+   createPlane(*globalBodyStorage, 0, Vec3(0,0,-1), Vec3(0,0,10), iron); // ceiling
+   createPlane(*globalBodyStorage, 0, Vec3(-1,0,0), Vec3(10,0,0), iron); // back wall
+   createPlane(*globalBodyStorage, 0, Vec3(1,0,0), Vec3(0,0,0), iron); // front wall, should not get rendered
+   
+   walberla::id_t id=0;
+   for (int j=0; j<4; j++) {
+      for (int i=0; i<4; i++) {
+         createSphere(*globalBodyStorage, *forest, storageID, id, Vec3(6,real_c(i+1)*real_t(2),real_c(j+1)*real_t(2)), real_t(0.9));
+         id++;
+      }
+   }
+   
+   
+   raytracer.setImageOutputDirectory("image");
+   raytracer.setImageOutputEnabled(true);
+   
+   raytracer.rayTrace<BodyTuple>(0);
+}
+
 int main( int argc, char** argv )
 {
    walberla::debug::enterTestMode();
@@ -279,6 +385,7 @@ int main( int argc, char** argv )
    //AABBIntersectsTest();
    //CapsuleIntersectsTest();
    RaytracerTest();
-   
+   //RaytracerSpheresTest();
+
    return EXIT_SUCCESS;
 }
-- 
GitLab