Hello, World!
Yeah this is quite a thicc first commit but there's no way I'm untangling this mess just for a nice set of starting commits :^)
This commit is contained in:
13
Dockerfile
Normal file
13
Dockerfile
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
FROM weckyy702/raychel_ci:latest
|
||||||
|
|
||||||
|
#Copy source files and set the working director
|
||||||
|
|
||||||
|
COPY . /usr/src/Raychel
|
||||||
|
WORKDIR /usr/src/Raychel
|
||||||
|
|
||||||
|
ARG COMPILER
|
||||||
|
|
||||||
|
RUN cmake -DCMAKE_CXX_COMPILER=${COMPILER} -DRAYCHEL_BUILD_TESTS=ON .
|
||||||
|
RUN cmake --build . --target all test
|
||||||
|
|
||||||
|
LABEL Name=raychel Version=0.0.1
|
||||||
166
include/Raychel/Core/Deserialize.h
Normal file
166
include/Raychel/Core/Deserialize.h
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
/**
|
||||||
|
* \file Deserializer.h
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Header file for Deserializer class
|
||||||
|
* \date 2022-05-19
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef RAYCHEL_DESERIALIZER_H
|
||||||
|
#define RAYCHEL_DESERIALIZER_H
|
||||||
|
|
||||||
|
#include <istream>
|
||||||
|
#include <optional>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
#include "SDFTransforms.h"
|
||||||
|
#include "Scene.h"
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
concept DeserializableWithoutTarget = requires()
|
||||||
|
{
|
||||||
|
// clang-format off
|
||||||
|
requires !has_target_v<T>;
|
||||||
|
{ do_deserialize(std::declval<std::istream&>(), DeserializationTag<T>{}) } -> std::same_as<std::optional<T>>;
|
||||||
|
// clang-format on
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Container>
|
||||||
|
concept DeserializableWithTarget = requires()
|
||||||
|
{
|
||||||
|
// clang-format off
|
||||||
|
requires has_target_v<T>;
|
||||||
|
{ do_deserialize(std::declval<std::istream&>(), std::declval<Container>(), DeserializationTag<T>{}) } -> std::same_as<std::optional<T>>;
|
||||||
|
// clang-format on
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T, typename Container>
|
||||||
|
concept Deserializable = DeserializableWithoutTarget<T> || DeserializableWithTarget<T, Container>;
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
struct IDeserializer
|
||||||
|
{
|
||||||
|
IDeserializer() = default;
|
||||||
|
|
||||||
|
RAYCHEL_MAKE_NONCOPY_NONMOVE(IDeserializer)
|
||||||
|
|
||||||
|
virtual std::optional<Container>
|
||||||
|
deserialize(std::istream& is, std::optional<Container>&& maybe_target) const noexcept = 0;
|
||||||
|
|
||||||
|
virtual SerializableObjectData<Container> get_serializer() const noexcept = 0;
|
||||||
|
|
||||||
|
virtual std::string_view contained_type_name() const noexcept = 0;
|
||||||
|
|
||||||
|
virtual ~IDeserializer() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Container, Deserializable<Container> Object>
|
||||||
|
class Deserializer final : public IDeserializer<Container>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Deserializer() = default;
|
||||||
|
|
||||||
|
std::optional<Container> deserialize(std::istream& is, std::optional<Container>&& maybe_target) const noexcept final
|
||||||
|
{
|
||||||
|
if constexpr (has_target_v<Object>) {
|
||||||
|
return _deserialize_with_target(is, std::move(maybe_target));
|
||||||
|
} else {
|
||||||
|
if (maybe_target.has_value()) {
|
||||||
|
Logger::warn("Type ", Logger::details::type_name<Object>(), " did not expect to have a target!\n");
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return _deserialize_without_target(is);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SerializableObjectData<Container> get_serializer() const noexcept final
|
||||||
|
{
|
||||||
|
return SerializableObjectData<Container>{SerializableObjectDescriptor<Object>{}};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string_view contained_type_name() const noexcept final
|
||||||
|
{
|
||||||
|
return serializable_type_name_for<Object>();
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~Deserializer() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
static std::optional<Container> _deserialize_without_target(std::istream& is) noexcept
|
||||||
|
{
|
||||||
|
auto maybe_object = do_deserialize(is, DeserializationTag<Object>{});
|
||||||
|
if (!maybe_object.has_value())
|
||||||
|
return std::nullopt;
|
||||||
|
return Container{std::move(maybe_object).value()};
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::optional<Container>
|
||||||
|
_deserialize_with_target(std::istream& is, std::optional<Container> maybe_target) noexcept
|
||||||
|
{
|
||||||
|
if (!maybe_target.has_value()) {
|
||||||
|
Logger::warn("Type ", Logger::details::type_name<Object>(), " expectec to have a target!\n");
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
auto maybe_object = do_deserialize(is, std::move(maybe_target).value(), DeserializationTag<Object>{});
|
||||||
|
if (!maybe_object.has_value())
|
||||||
|
return std::nullopt;
|
||||||
|
return Container{std::move(maybe_object).value()};
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
using DeserializerPtr = std::unique_ptr<IDeserializer<Container>>;
|
||||||
|
|
||||||
|
template <typename Container, typename... Args>
|
||||||
|
auto object_deserializers()
|
||||||
|
{
|
||||||
|
std::vector<DeserializerPtr<Container>> res{};
|
||||||
|
|
||||||
|
((res.emplace_back(std::make_unique<Deserializer<Container, Args>>())), ...);
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
template <typename... Objects>
|
||||||
|
auto object_deserializers()
|
||||||
|
{
|
||||||
|
return details::object_deserializers<SDFContainer, Objects...>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename... Materials>
|
||||||
|
auto material_deserializers()
|
||||||
|
{
|
||||||
|
return details::object_deserializers<MaterialContainer, Materials...>();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] Scene deserialize_scene(
|
||||||
|
std::istream& is, const std::vector<details::DeserializerPtr<SDFContainer>>& object_deserializers,
|
||||||
|
const std::vector<details::DeserializerPtr<MaterialContainer>>& material_deserializers) noexcept;
|
||||||
|
|
||||||
|
} //namespace Raychel
|
||||||
|
|
||||||
|
#endif //!RAYCHEL_DESERIALIZER_H
|
||||||
66
include/Raychel/Core/Raymarch.h
Normal file
66
include/Raychel/Core/Raymarch.h
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* \file Raymarch.h
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Header file for Raymarch class
|
||||||
|
* \date 2022-04-11
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef RAYCHEL_RAYMARCH_H
|
||||||
|
#define RAYCHEL_RAYMARCH_H
|
||||||
|
|
||||||
|
#include "Types.h"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
constexpr static auto no_hit = std::numeric_limits<std::size_t>::max();
|
||||||
|
|
||||||
|
struct RaymarchResult
|
||||||
|
{
|
||||||
|
vec3 point{};
|
||||||
|
double ray_depth{};
|
||||||
|
std::size_t ray_steps{};
|
||||||
|
std::size_t hit_index{no_hit};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RaymarchOptions
|
||||||
|
{
|
||||||
|
std::size_t max_ray_steps{1'000};
|
||||||
|
double max_ray_depth{100};
|
||||||
|
double surface_epsilon{1e-3};
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] std::pair<double, std::size_t>
|
||||||
|
evaluate_distance_field(const std::vector<SDFContainer>& surfaces, const vec3& point) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]] RaymarchResult raymarch(
|
||||||
|
vec3 current_point, const vec3& direction, const std::vector<SDFContainer>& surfaces, RaymarchOptions options) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]] vec3 get_normal(const vec3& point, const SDFContainer& surface, double normal_offset = 1e-6) noexcept;
|
||||||
|
|
||||||
|
} // namespace Raychel
|
||||||
|
|
||||||
|
#endif //!RAYCHEL_RAYMARCH_H
|
||||||
87
include/Raychel/Core/SDFBooleans.h
Normal file
87
include/Raychel/Core/SDFBooleans.h
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
/**
|
||||||
|
* \file SDFBooleans.h
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Header file for boolean operations on SDFs
|
||||||
|
* \date 2022-05-27
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef RAYCHEL_SDF_BOOLEANS_H
|
||||||
|
#define RAYCHEL_SDF_BOOLEANS_H
|
||||||
|
|
||||||
|
#include "Types.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
template <typename Target1, typename Target2>
|
||||||
|
struct Union
|
||||||
|
{
|
||||||
|
Target1 target1;
|
||||||
|
Target2 target2;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
Union(T1, T2) -> Union<T1, T2>;
|
||||||
|
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
double evaluate_sdf(const Union<T1, T2>& object, const vec3& p) noexcept
|
||||||
|
{
|
||||||
|
return std::min(evaluate_sdf(object.target1, p), evaluate_sdf(object.target2, p));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Target1, typename Target2>
|
||||||
|
struct Difference
|
||||||
|
{
|
||||||
|
Target1 target1;
|
||||||
|
Target2 target2;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
Difference(T1, T2) -> Difference<T1, T2>;
|
||||||
|
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
double evaluate_sdf(const Difference<T1, T2>& object, const vec3& p) noexcept
|
||||||
|
{
|
||||||
|
return std::max(-evaluate_sdf(object.target1, p), evaluate_sdf(object.target2, p));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Target1, typename Target2>
|
||||||
|
struct Intersection
|
||||||
|
{
|
||||||
|
Target1 target1;
|
||||||
|
Target2 target2;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
Intersection(T1, T2) -> Intersection<T1, T2>;
|
||||||
|
|
||||||
|
template <typename T1, typename T2>
|
||||||
|
double evaluate_sdf(const Intersection<T1, T2>& object, const vec3& p) noexcept
|
||||||
|
{
|
||||||
|
return std::max(evaluate_sdf(object.target1, p), evaluate_sdf(object.target2, p));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Raychel
|
||||||
|
|
||||||
|
#endif //!RAYCHEL_SDF_BOOLEANS_H
|
||||||
213
include/Raychel/Core/SDFContainer.h
Normal file
213
include/Raychel/Core/SDFContainer.h
Normal file
@@ -0,0 +1,213 @@
|
|||||||
|
/**
|
||||||
|
* \file SDFContainer.h
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Header file for SDFContainer class
|
||||||
|
* \date 2022-04-09
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef RAYCHEL_SDF_CONTAINER_H
|
||||||
|
#define RAYCHEL_SDF_CONTAINER_H
|
||||||
|
|
||||||
|
#include "Raychel/Core/SDFPrimitives.h"
|
||||||
|
#include "Types.h"
|
||||||
|
|
||||||
|
#include "RaychelCore/Badge.h"
|
||||||
|
#include "RaychelCore/ClassMacros.h"
|
||||||
|
|
||||||
|
#include <RaychelCore/Raychel_assert.h>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool has_custom_normal_v = requires(T t)
|
||||||
|
{
|
||||||
|
{
|
||||||
|
evaluate_normal(t, vec3{})
|
||||||
|
} -> std::same_as<vec3>;
|
||||||
|
};
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class TypeId
|
||||||
|
{
|
||||||
|
static char _;
|
||||||
|
|
||||||
|
public:
|
||||||
|
static std::uintptr_t id()
|
||||||
|
{
|
||||||
|
return reinterpret_cast<std::uintptr_t>(&_);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
char TypeId<T>::_{};
|
||||||
|
|
||||||
|
struct ISDFContainerImpl
|
||||||
|
{
|
||||||
|
ISDFContainerImpl() = default;
|
||||||
|
|
||||||
|
RAYCHEL_MAKE_NONCOPY_NONMOVE(ISDFContainerImpl)
|
||||||
|
|
||||||
|
[[nodiscard]] virtual std::uintptr_t type_id() const noexcept = 0;
|
||||||
|
|
||||||
|
virtual void debug_log() const noexcept = 0;
|
||||||
|
|
||||||
|
virtual ~ISDFContainerImpl() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class SDFContainerImpl final : public ISDFContainerImpl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit SDFContainerImpl(T&& object) noexcept(std::is_nothrow_move_constructible_v<T>)
|
||||||
|
: object_{std::forward<T>(object)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
void debug_log() const noexcept override
|
||||||
|
{
|
||||||
|
std::cout << "SDFContainer with object type " << Logger::details::type_name<T>() << " (type id " << type_id()
|
||||||
|
<< ")";
|
||||||
|
if constexpr (has_target_v<T>) {
|
||||||
|
std::cout << " and target ";
|
||||||
|
if constexpr (std::is_same_v<decltype(T::target), SDFContainer>) {
|
||||||
|
object_.target.unsafe_impl()->debug_log();
|
||||||
|
} else {
|
||||||
|
std::cout << Logger::details::type_name<decltype(T::target)>() << '\n';
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
std::cout << '\n';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::uintptr_t type_id() const noexcept override
|
||||||
|
{
|
||||||
|
return TypeId<T>::id();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] T& object() noexcept
|
||||||
|
{
|
||||||
|
return object_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const T& object() const noexcept
|
||||||
|
{
|
||||||
|
return object_;
|
||||||
|
}
|
||||||
|
|
||||||
|
~SDFContainerImpl() override = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
T object_;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct Eval
|
||||||
|
{
|
||||||
|
static double eval(ISDFContainerImpl* ptr, const vec3& p)
|
||||||
|
{
|
||||||
|
auto& obj = get_ref(ptr);
|
||||||
|
return evaluate_sdf(obj, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
static vec3 get_normal(ISDFContainerImpl* ptr, const vec3& p)
|
||||||
|
{
|
||||||
|
if constexpr (has_custom_normal_v<T>) {
|
||||||
|
auto& obj = get_ref(ptr);
|
||||||
|
return evaluate_normal(obj, p);
|
||||||
|
}
|
||||||
|
RAYCHEL_ASSERT_NOT_REACHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
static T& get_ref(ISDFContainerImpl* ptr)
|
||||||
|
{
|
||||||
|
return reinterpret_cast<SDFContainerImpl<T>*>(ptr)->object();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
class SDFContainer
|
||||||
|
{
|
||||||
|
using EvalFunction = double (*)(details::ISDFContainerImpl*, const vec3&);
|
||||||
|
using NormalFunction = vec3 (*)(details::ISDFContainerImpl*, const vec3&);
|
||||||
|
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
using Impl = details::SDFContainerImpl<T>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
requires(!std::is_same_v<std::remove_all_extents_t<T>, SDFContainer>) //otherwise the move constructor would be hidden
|
||||||
|
explicit SDFContainer(T&& object) noexcept(std::is_nothrow_move_constructible_v<T>)
|
||||||
|
: impl_{std::make_unique<Impl<T>>(std::forward<T>(object))},
|
||||||
|
eval_{details::Eval<T>::eval},
|
||||||
|
get_normal_(details::Eval<T>::get_normal),
|
||||||
|
has_custom_normal_{has_custom_normal_v<T>}
|
||||||
|
{}
|
||||||
|
|
||||||
|
RAYCHEL_MAKE_NONCOPY(SDFContainer)
|
||||||
|
|
||||||
|
RAYCHEL_MAKE_DEFAULT_MOVE(SDFContainer)
|
||||||
|
|
||||||
|
[[nodiscard]] double evaluate(const vec3& p) const noexcept
|
||||||
|
{
|
||||||
|
return eval_(impl_.get(), p);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] bool has_custom_normal() const noexcept
|
||||||
|
{
|
||||||
|
return has_custom_normal_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] vec3 get_normal(const vec3& p) const noexcept
|
||||||
|
{
|
||||||
|
return get_normal_(impl_.get(), p);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto type_id() const noexcept
|
||||||
|
{
|
||||||
|
return impl_->type_id();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto* unsafe_impl() const noexcept
|
||||||
|
{
|
||||||
|
return impl_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
~SDFContainer() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<details::ISDFContainerImpl> impl_{};
|
||||||
|
EvalFunction eval_;
|
||||||
|
NormalFunction get_normal_;
|
||||||
|
bool has_custom_normal_ : 1 {};
|
||||||
|
};
|
||||||
|
|
||||||
|
inline double evaluate_sdf(const SDFContainer& obj, const vec3& p)
|
||||||
|
{
|
||||||
|
return obj.evaluate(p);
|
||||||
|
}
|
||||||
|
} // namespace Raychel
|
||||||
|
|
||||||
|
#endif //! RAYCHEL_SDF_CONTAINER_H
|
||||||
84
include/Raychel/Core/SDFModifiers.h
Normal file
84
include/Raychel/Core/SDFModifiers.h
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
/**
|
||||||
|
* \file SDFModifiers.h
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Header file for modifiers on SDFs
|
||||||
|
* \date 2022-05-27
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef RAYCHEL_SDF_MODIFIERS_H
|
||||||
|
#define RAYCHEL_SDF_MODIFIERS_H
|
||||||
|
|
||||||
|
#include "Raychel/Core/Types.h"
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
template <typename Target>
|
||||||
|
struct Hollow
|
||||||
|
{
|
||||||
|
Target target;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Hollow(T) -> Hollow<T>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
double evaluate_sdf(const Hollow<T>& object, const vec3& p) noexcept
|
||||||
|
{
|
||||||
|
return std::abs(evaluate_sdf(object.target, p));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Target>
|
||||||
|
struct Rounded
|
||||||
|
{
|
||||||
|
Target target;
|
||||||
|
double radius;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Rounded(T, double) -> Rounded<T>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
double evaluate_sdf(const Rounded<T>& object, const vec3& p) noexcept
|
||||||
|
{
|
||||||
|
return evaluate_sdf(object.target, p) - object.radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Target>
|
||||||
|
struct Onion
|
||||||
|
{
|
||||||
|
Target target;
|
||||||
|
double thickness;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Onion(T, double) -> Onion<T>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
double evaluate_sdf(const Onion<T>& object, const vec3& p) noexcept
|
||||||
|
{
|
||||||
|
return std::abs(evaluate_sdf(object.target, p)) - object.thickness;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Raychel
|
||||||
|
|
||||||
|
#endif //!RAYCHEL_SDF_MODIFIERS_H
|
||||||
105
include/Raychel/Core/SDFPrimitives.h
Normal file
105
include/Raychel/Core/SDFPrimitives.h
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
/**
|
||||||
|
* \file SDFPrimitives.h
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Header file for SDF primitives
|
||||||
|
* \date 2022-05-27
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef RAYCHEL_SDF_PRIMITIVES_H
|
||||||
|
#define RAYCHEL_SDF_PRIMITIVES_H
|
||||||
|
|
||||||
|
#include "Types.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <iostream>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
struct Sphere
|
||||||
|
{
|
||||||
|
double radius{1.0};
|
||||||
|
};
|
||||||
|
|
||||||
|
inline double evaluate_sdf(const Sphere& object, const vec3& p) noexcept
|
||||||
|
{
|
||||||
|
return mag(p) - object.radius;
|
||||||
|
}
|
||||||
|
|
||||||
|
//optimized normal calculation for spheres
|
||||||
|
inline vec3 evaluate_normal(const Sphere& /*unused*/, const vec3& p) noexcept
|
||||||
|
{
|
||||||
|
return normalize(p);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool do_serialize(std::ostream& os, const Sphere& object) noexcept;
|
||||||
|
|
||||||
|
std::optional<Sphere> do_deserialize(std::istream& is, DeserializationTag<Sphere>) noexcept;
|
||||||
|
|
||||||
|
struct Box
|
||||||
|
{
|
||||||
|
vec3 size{1, 1, 1};
|
||||||
|
};
|
||||||
|
|
||||||
|
inline double evaluate_sdf(const Box& box, const vec3& p) noexcept
|
||||||
|
{
|
||||||
|
using std::abs, std::max, std::min;
|
||||||
|
const auto q = vec3{abs(p.x()), abs(p.y()), abs(p.z())} - box.size;
|
||||||
|
|
||||||
|
return mag(vec3{max(q.x(), 0.0), max(q.y(), 0.0), max(q.z(), 0.0)}) + min(max(q.x(), max(q.y(), q.z())), 0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool do_serialize(std::ostream& os, const Box& object) noexcept;
|
||||||
|
|
||||||
|
std::optional<Box> do_deserialize(std::istream& is, DeserializationTag<Box>) noexcept;
|
||||||
|
|
||||||
|
struct Plane
|
||||||
|
{
|
||||||
|
vec3 normal{0, 1, 0};
|
||||||
|
};
|
||||||
|
|
||||||
|
inline double evaluate_sdf(const Plane& object, const vec3& p) noexcept
|
||||||
|
{
|
||||||
|
return std::abs(dot(object.normal, p));
|
||||||
|
}
|
||||||
|
|
||||||
|
inline vec3 evaluate_normal(const Plane& object, const vec3&) noexcept
|
||||||
|
{
|
||||||
|
return object.normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool do_serialize(std::ostream& os, const Plane& object) noexcept;
|
||||||
|
|
||||||
|
std::optional<Plane> do_deserialize(std::istream& is, DeserializationTag<Plane>) noexcept;
|
||||||
|
|
||||||
|
struct DeserializationErrorPlaceHolder
|
||||||
|
{};
|
||||||
|
|
||||||
|
inline double evaluate_sdf(const DeserializationErrorPlaceHolder /*unused*/, const vec3& /*unused*/) noexcept
|
||||||
|
{
|
||||||
|
return 1e9;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Raychel
|
||||||
|
|
||||||
|
#endif //!RAYCHEL_SDF_PRIMITIVES_H
|
||||||
115
include/Raychel/Core/SDFTransforms.h
Normal file
115
include/Raychel/Core/SDFTransforms.h
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
/**
|
||||||
|
* \file SDFTransforms.h
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Header file for SDFTransforms class
|
||||||
|
* \date 2022-05-27
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef RAYCHEL_SDF_TRANSFORM_H
|
||||||
|
#define RAYCHEL_SDF_TRANSFORM_H
|
||||||
|
|
||||||
|
#include "Types.h"
|
||||||
|
#include "SDFContainer.h"
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
template <typename Target = SDFContainer>
|
||||||
|
struct Translate
|
||||||
|
{
|
||||||
|
Target target;
|
||||||
|
vec3 translation{};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct has_target<Translate<T>> : std::true_type
|
||||||
|
{};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Translate(T, vec3) -> Translate<T>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
double evaluate_sdf(const Translate<T>& object, const vec3& p) noexcept
|
||||||
|
{
|
||||||
|
return evaluate_sdf(object.target, p - object.translation);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool do_serialize(std::ostream& os, const Translate<T>& object) noexcept
|
||||||
|
{
|
||||||
|
os << object.translation << '\n';
|
||||||
|
return os.good();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::optional<Translate<T>> do_deserialize(std::istream& is, SDFContainer target, DeserializationTag<Translate<T>>) noexcept
|
||||||
|
{
|
||||||
|
vec3 translation{};
|
||||||
|
|
||||||
|
if (!(is >> translation))
|
||||||
|
return std::nullopt;
|
||||||
|
return Translate<T>{std::move(target), translation};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Target = SDFContainer>
|
||||||
|
struct Rotate
|
||||||
|
{
|
||||||
|
Target target;
|
||||||
|
Quaternion rotation{};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct has_target<Rotate<T>> : std::true_type
|
||||||
|
{};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
Rotate(T, Quaternion) -> Rotate<T>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
double evaluate_sdf(const Rotate<T>& object, const vec3& p) noexcept
|
||||||
|
{
|
||||||
|
return evaluate_sdf(object.target, p * inverse(object.rotation));
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool do_serialize(std::ostream& os, const Rotate<T>& object) noexcept
|
||||||
|
{
|
||||||
|
os << object.rotation << '\n';
|
||||||
|
return os.good();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
std::optional<Rotate<T>> do_deserialize(std::istream& is, SDFContainer target, DeserializationTag<Rotate<T>>) noexcept
|
||||||
|
{
|
||||||
|
Quaternion rotation{};
|
||||||
|
|
||||||
|
if (!(is >> rotation))
|
||||||
|
return std::nullopt;
|
||||||
|
return Rotate{std::move(target), rotation};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Raychel
|
||||||
|
|
||||||
|
#endif //!RAYCHEL_SDF_TRANSFORM_H
|
||||||
36
include/Raychel/Core/SDFs.h
Normal file
36
include/Raychel/Core/SDFs.h
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
/**
|
||||||
|
* \file SDFs.h
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Header file for predefined signed distance functions
|
||||||
|
* \date 2022-04-12
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef RAYCHEL_PREDEFINED_SIGNED_DISTANCE_FUNCTIONS_H
|
||||||
|
#define RAYCHEL_PREDEFINED_SIGNED_DISTANCE_FUNCTIONS_H
|
||||||
|
|
||||||
|
#include "SDFBooleans.h"
|
||||||
|
#include "SDFModifiers.h"
|
||||||
|
#include "SDFPrimitives.h"
|
||||||
|
#include "SDFTransforms.h"
|
||||||
|
|
||||||
|
#endif //!RAYCHEL_PREDEFINED_SIGNED_DISTANCE_FUNCTIONS_H
|
||||||
141
include/Raychel/Core/Scene.h
Normal file
141
include/Raychel/Core/Scene.h
Normal file
@@ -0,0 +1,141 @@
|
|||||||
|
/**
|
||||||
|
* \file Scene.h
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Header file for Scene class
|
||||||
|
* \date 2022-04-09
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef RAYCHEL_SCENE_H
|
||||||
|
#define RAYCHEL_SCENE_H
|
||||||
|
|
||||||
|
#include "Raychel/Render/MaterialContainer.h"
|
||||||
|
#include "SDFContainer.h"
|
||||||
|
#include "Serialize.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <iterator>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
template <typename Object, typename Material>
|
||||||
|
struct RaymarchableObject
|
||||||
|
{
|
||||||
|
std::size_t index_in_scene;
|
||||||
|
Object& object;
|
||||||
|
Material& material;
|
||||||
|
};
|
||||||
|
|
||||||
|
//Clang needs this deduction guide
|
||||||
|
template <typename Object, typename Material>
|
||||||
|
RaymarchableObject(std::size_t, Object&, Material&) -> RaymarchableObject<Object, Material>;
|
||||||
|
|
||||||
|
class Scene
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
Scene() = default;
|
||||||
|
|
||||||
|
static Scene unsafe_from_data(
|
||||||
|
std::vector<SDFContainer> objects, std::vector<SerializableObjectData<SDFContainer>> object_serializers,
|
||||||
|
std::vector<MaterialContainer> materials,
|
||||||
|
std::vector<SerializableObjectData<MaterialContainer>> material_serializers) noexcept;
|
||||||
|
|
||||||
|
template <typename Object, typename Material>
|
||||||
|
auto add_object(Object&& object, Material&& material) noexcept
|
||||||
|
{
|
||||||
|
const auto where = std::lower_bound(
|
||||||
|
objects_.begin(), objects_.end(), details::TypeId<Object>::id(), [](const auto& cont, const auto& id) {
|
||||||
|
return cont.type_id() < id;
|
||||||
|
});
|
||||||
|
|
||||||
|
const auto obj = objects_.emplace(where, std::forward<Object>(object));
|
||||||
|
const auto index = std::distance(objects_.begin(), obj);
|
||||||
|
|
||||||
|
const auto mat = materials_.emplace(materials_.begin() + index, std::forward<Material>(material));
|
||||||
|
|
||||||
|
object_serializers_.emplace(object_serializers_.begin() + index, details::SerializableObjectDescriptor<Object>{});
|
||||||
|
material_serializers_.emplace(
|
||||||
|
material_serializers_.begin() + index, details::SerializableObjectDescriptor<Material>{});
|
||||||
|
|
||||||
|
return RaymarchableObject{
|
||||||
|
objects_.size() - 1U,
|
||||||
|
details::get_container_content<Object>(*obj),
|
||||||
|
details::get_container_content<Material>(*mat)};
|
||||||
|
}
|
||||||
|
|
||||||
|
void remove_object(std::size_t index) noexcept;
|
||||||
|
|
||||||
|
template <std::invocable<const RenderData&> F>
|
||||||
|
requires(std::is_same_v<std::invoke_result_t<F, const RenderData&>, color>) void set_background_function(F&& f) noexcept
|
||||||
|
{
|
||||||
|
background_function_ = f;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const auto& objects() const noexcept
|
||||||
|
{
|
||||||
|
return objects_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const auto& materials() const noexcept
|
||||||
|
{
|
||||||
|
return materials_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const auto& background_function() const noexcept
|
||||||
|
{
|
||||||
|
return background_function_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const auto& object_serializers() const noexcept
|
||||||
|
{
|
||||||
|
return object_serializers_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const auto& material_serializers() const noexcept
|
||||||
|
{
|
||||||
|
return material_serializers_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
Scene(
|
||||||
|
std::vector<SDFContainer> objects, std::vector<SerializableObjectData<SDFContainer>> object_serializers,
|
||||||
|
std::vector<MaterialContainer> materials, std::vector<SerializableObjectData<MaterialContainer>> material_serializers)
|
||||||
|
: object_serializers_{std::move(object_serializers)},
|
||||||
|
material_serializers_{std::move(material_serializers)},
|
||||||
|
objects_{std::move(objects)},
|
||||||
|
materials_{std::move(materials)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
std::vector<SerializableObjectData<SDFContainer>> object_serializers_{};
|
||||||
|
std::vector<SerializableObjectData<MaterialContainer>> material_serializers_{};
|
||||||
|
|
||||||
|
std::vector<SDFContainer> objects_{};
|
||||||
|
std::vector<MaterialContainer> materials_{};
|
||||||
|
BackgroundFunction background_function_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
bool serialize_scene(const Scene& scene, std::ostream& os) noexcept;
|
||||||
|
|
||||||
|
} // namespace Raychel
|
||||||
|
|
||||||
|
#endif //! RAYCHEL_SCENE_H
|
||||||
163
include/Raychel/Core/Serialize.h
Normal file
163
include/Raychel/Core/Serialize.h
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
/**
|
||||||
|
* \file SerializableObjectData.h
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Header file for SerializableObjectData class
|
||||||
|
* \date 2022-05-18
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef RAYCHEL_SERIALIZABLE_OBJECT_DATA_H
|
||||||
|
#define RAYCHEL_SERIALIZABLE_OBJECT_DATA_H
|
||||||
|
|
||||||
|
#include "Types.h"
|
||||||
|
|
||||||
|
#include "RaychelCore/ClassMacros.h"
|
||||||
|
#include "RaychelCore/Raychel_assert.h"
|
||||||
|
#include "RaychelLogger/Logger.h"
|
||||||
|
|
||||||
|
#include <concepts>
|
||||||
|
#include <functional>
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
template <typename Contained, typename Container>
|
||||||
|
auto& get_container_content(Container& container) noexcept
|
||||||
|
{
|
||||||
|
using Impl = typename Container::template Impl<Contained>;
|
||||||
|
|
||||||
|
auto* impl_interface = container.unsafe_impl();
|
||||||
|
auto* impl = dynamic_cast<Impl*>(impl_interface);
|
||||||
|
RAYCHEL_ASSERT(impl != nullptr);
|
||||||
|
|
||||||
|
return impl->object();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Contained, typename Container>
|
||||||
|
const auto& get_container_content(const Container& container) noexcept
|
||||||
|
{
|
||||||
|
using Impl = typename Container::template Impl<Contained>;
|
||||||
|
|
||||||
|
auto* impl_interface = container.unsafe_impl();
|
||||||
|
auto* impl = dynamic_cast<Impl*>(impl_interface);
|
||||||
|
RAYCHEL_ASSERT(impl != nullptr);
|
||||||
|
return impl->object();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
requires(has_target_v<T>) constexpr std::string_view serializable_type_name_for()
|
||||||
|
{
|
||||||
|
constexpr auto full_type_name = Logger::details::type_name<T>();
|
||||||
|
constexpr auto bracket_index = full_type_name.find('<');
|
||||||
|
static_assert(bracket_index != std::string_view::npos);
|
||||||
|
constexpr std::string_view base_type_name{full_type_name.begin(), full_type_name.begin() + bracket_index};
|
||||||
|
|
||||||
|
return base_type_name;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr std::string_view serializable_type_name_for()
|
||||||
|
{
|
||||||
|
return Logger::details::type_name<T>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
template <typename Object>
|
||||||
|
concept Serializable = requires(const Object& obj)
|
||||||
|
{
|
||||||
|
{ do_serialize(std::declval<std::ostream&>(), obj) } -> std::same_as<bool>;
|
||||||
|
};
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
template <typename Object>
|
||||||
|
struct SerializableObjectDescriptor
|
||||||
|
{};
|
||||||
|
|
||||||
|
template <Serializable T>
|
||||||
|
bool serialize_internal(std::ostream&, const T&, std::size_t = 0) noexcept;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
bool serialize_internal(std::ostream&, const T&, std::size_t = 0) noexcept;
|
||||||
|
|
||||||
|
template <Serializable Object>
|
||||||
|
requires(has_target_v<Object>) bool serialize_with_target(
|
||||||
|
std::ostream& os, const Object& object, std::size_t recursion_depth) noexcept
|
||||||
|
{
|
||||||
|
os << serializable_type_name_for<Object>() << "<> with ";
|
||||||
|
return os.good() && do_serialize(os, object) && serialize_internal(os, object.target, recursion_depth + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <Serializable Object>
|
||||||
|
bool serialize_with_target(std::ostream& os, const Object& object, std::size_t /*unused*/) noexcept
|
||||||
|
{
|
||||||
|
os << serializable_type_name_for<Object>() << " with ";
|
||||||
|
return os.good() && do_serialize(os, object);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <Serializable Object>
|
||||||
|
bool serialize_internal(std::ostream& os, const Object& object, std::size_t recursion_depth) noexcept
|
||||||
|
{
|
||||||
|
for (std::size_t i{}; i != recursion_depth; ++i)
|
||||||
|
os << " ";
|
||||||
|
return serialize_with_target(os, object, recursion_depth);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Object>
|
||||||
|
bool serialize_internal(std::ostream& os, const Object& /*unused*/, std::size_t /*unused*/) noexcept
|
||||||
|
{
|
||||||
|
constexpr auto type_name = Logger::details::type_name<Object>();
|
||||||
|
Logger::warn(
|
||||||
|
"Object of type '",
|
||||||
|
type_name,
|
||||||
|
"' cannot be serialized. Make sure the function bool do_serialize(std::ostream&, const ",
|
||||||
|
type_name,
|
||||||
|
"&) exists and can be located by ADL\n");
|
||||||
|
os << "__NONSERIALIZABLE__\n";
|
||||||
|
return os.good();
|
||||||
|
}
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
class SerializableObjectData
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template <typename Contained>
|
||||||
|
explicit SerializableObjectData(details::SerializableObjectDescriptor<Contained>&& /*unused*/)
|
||||||
|
: serialize_{[](std::ostream& os, const Container& container) -> bool {
|
||||||
|
return details::serialize_internal(os, details::get_container_content<Contained>(container));
|
||||||
|
}}
|
||||||
|
{}
|
||||||
|
|
||||||
|
bool serialize(const Container& container, std::ostream& os) const noexcept
|
||||||
|
{
|
||||||
|
return serialize_(os, container);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::function<bool(std::ostream&, const Container&)> serialize_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Raychel
|
||||||
|
|
||||||
|
#endif //!RAYCHEL_SERIALIZABLE_OBJECT_DATA_H
|
||||||
70
include/Raychel/Core/Types.h
Normal file
70
include/Raychel/Core/Types.h
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
/**
|
||||||
|
* \file Types.h
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Header file for Types class
|
||||||
|
* \date 2022-04-10
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef RAYCHEL_TYPES_H
|
||||||
|
#define RAYCHEL_TYPES_H
|
||||||
|
|
||||||
|
#include "RaychelMath/Transform.h"
|
||||||
|
#include "RaychelMath/color.h"
|
||||||
|
#include "RaychelMath/vec2.h"
|
||||||
|
#include "RaychelMath/vec3.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
using vec3 = basic_vec3<double>;
|
||||||
|
using color = basic_color<double>;
|
||||||
|
using Quaternion = basic_quaternion<double>;
|
||||||
|
using Transform = basic_transform<double>;
|
||||||
|
|
||||||
|
using Size2D = basic_vec2<std::size_t>;
|
||||||
|
|
||||||
|
class SDFContainer;
|
||||||
|
|
||||||
|
class Scene;
|
||||||
|
|
||||||
|
struct RenderData;
|
||||||
|
|
||||||
|
using BackgroundFunction = std::function<color(const RenderData&)>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct DeserializationTag
|
||||||
|
{};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct has_target : std::false_type
|
||||||
|
{};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool has_target_v = has_target<T>::value && requires(T t){
|
||||||
|
t.target;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Raychel
|
||||||
|
|
||||||
|
#endif //!RAYCHEL_TYPES_H
|
||||||
153
include/Raychel/Core/Xoroshiro128+.h
Normal file
153
include/Raychel/Core/Xoroshiro128+.h
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
/**
|
||||||
|
* \file Xoroshiro128+.h
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Header file for Xoroshiro128+ class
|
||||||
|
* \date 2022-04-24
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef RAYCHEL_XOROSHIRO128_H
|
||||||
|
#define RAYCHEL_XOROSHIRO128_H
|
||||||
|
|
||||||
|
#include <bit>
|
||||||
|
#include <concepts>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
//This is an implementation of the xoroshiro128+ algorithm as described at https://prng.di.unimi.it/xoroshiro128plus.c ported to C++20
|
||||||
|
|
||||||
|
// template<int A=24, int B=16, int C=37>
|
||||||
|
|
||||||
|
//TODO: template this once we figure out how jump() and long_jump() can be generalized
|
||||||
|
class Xoroshiro128
|
||||||
|
{
|
||||||
|
struct TwoUint64s
|
||||||
|
{
|
||||||
|
std::uint64_t first, second;
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
static constexpr std::int32_t A = 24, B = 16, C = 37;
|
||||||
|
|
||||||
|
constexpr Xoroshiro128() = default;
|
||||||
|
|
||||||
|
constexpr Xoroshiro128(std::uint64_t seed) : state_{seed, 0}
|
||||||
|
{
|
||||||
|
jump(); //jump through the domain a bit so we don't get weird results on the first call to next()
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr Xoroshiro128(std::uint64_t s0, std::uint64_t s1) : state_{s0, s1}
|
||||||
|
{}
|
||||||
|
|
||||||
|
static constexpr auto min() noexcept
|
||||||
|
{
|
||||||
|
return std::numeric_limits<std::uint64_t>::min();
|
||||||
|
}
|
||||||
|
|
||||||
|
static constexpr auto max() noexcept
|
||||||
|
{
|
||||||
|
return std::numeric_limits<std::uint64_t>::max();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::integral Result = std::uint64_t>
|
||||||
|
[[nodiscard]] constexpr Result operator()() noexcept
|
||||||
|
{
|
||||||
|
return next<Result>();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::integral Result = std::uint64_t>
|
||||||
|
constexpr Result next() noexcept
|
||||||
|
{
|
||||||
|
static_assert(sizeof(Result) <= sizeof(std::uint64_t), "Xoroshiro128+ cannot produce more than 64 random bits!");
|
||||||
|
|
||||||
|
auto [s0, s1] = state_;
|
||||||
|
const auto result = static_cast<Result>(s0 + s1);
|
||||||
|
|
||||||
|
s1 ^= s0;
|
||||||
|
state_.first = std::rotl(s0, A) ^ s1 ^ (s1 << B);
|
||||||
|
state_.second = std::rotl(s1, C);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void jump() noexcept
|
||||||
|
{
|
||||||
|
uint64_t s0{};
|
||||||
|
uint64_t s1{};
|
||||||
|
|
||||||
|
for (std::size_t b{}; b != 64U; ++b) {
|
||||||
|
if (short_jump_.first & std::uint64_t{1} << b) {
|
||||||
|
s0 ^= state_.first;
|
||||||
|
s1 ^= state_.second;
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t b{}; b != 64U; ++b) {
|
||||||
|
if (short_jump_.second & std::uint64_t{1} << b) {
|
||||||
|
s0 ^= state_.first;
|
||||||
|
s1 ^= state_.second;
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
state_.first = s0;
|
||||||
|
state_.second = s1;
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr void long_jump() noexcept
|
||||||
|
{
|
||||||
|
uint64_t s0{};
|
||||||
|
uint64_t s1{};
|
||||||
|
|
||||||
|
for (std::size_t b{}; b != 64; ++b) {
|
||||||
|
if (long_jump_.first & std::uint64_t{1} << b) {
|
||||||
|
s0 ^= state_.first;
|
||||||
|
s1 ^= state_.second;
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t b{}; b != 64; ++b) {
|
||||||
|
if (long_jump_.second & std::uint64_t{1} << b) {
|
||||||
|
s0 ^= state_.first;
|
||||||
|
s1 ^= state_.second;
|
||||||
|
}
|
||||||
|
next();
|
||||||
|
}
|
||||||
|
|
||||||
|
state_.first = s0;
|
||||||
|
state_.second = s1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
TwoUint64s state_{123456789, 987654321};
|
||||||
|
|
||||||
|
static constexpr TwoUint64s short_jump_{0xdf900294d8f554a5, 0x170865df4b3201fc};
|
||||||
|
static constexpr TwoUint64s long_jump_{0xd2a98b26625eee7b, 0xdddf9b1090aa7ac1};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Raychel
|
||||||
|
|
||||||
|
#endif //!RAYCHEL_XOROSHIRO128_H
|
||||||
39
include/Raychel/Core/ZigguratNormal.h
Normal file
39
include/Raychel/Core/ZigguratNormal.h
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
/**
|
||||||
|
* \file ZigguratNormal.h
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Header file for ZigguratNormal class
|
||||||
|
* \date 2022-04-24
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef RAYCHEL_ZIGGURAT_NORMAL_H
|
||||||
|
#define RAYCHEL_ZIGGURAT_NORMAL_H
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
[[nodiscard]] double uniform_random() noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]] double ziggurat_normal() noexcept;
|
||||||
|
|
||||||
|
} // namespace Raychel
|
||||||
|
|
||||||
|
#endif //!RAYCHEL_ZIGGURAT_NORMAL_H
|
||||||
43
include/Raychel/Render/Camera.h
Normal file
43
include/Raychel/Render/Camera.h
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
/**
|
||||||
|
* \file Camera.h
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Header file for Camera class
|
||||||
|
* \date 2022-04-11
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef RAYCHEL_CAMERA_H
|
||||||
|
#define RAYCHEL_CAMERA_H
|
||||||
|
|
||||||
|
#include "Raychel/Core/Types.h"
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
struct Camera
|
||||||
|
{
|
||||||
|
Transform transform;
|
||||||
|
double zoom{1.0};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace Raychel
|
||||||
|
|
||||||
|
#endif //!RAYCHEL_CAMERA_H
|
||||||
50
include/Raychel/Render/Denoise.h
Normal file
50
include/Raychel/Render/Denoise.h
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* \file Denoise.h
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Header file for Denoise class
|
||||||
|
* \date 2022-05-06
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef RAYCHEL_DENOISE_H
|
||||||
|
#define RAYCHEL_DENOISE_H
|
||||||
|
|
||||||
|
#include "FatPixel.h"
|
||||||
|
#include "Framebuffer.h"
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
struct DenoisingOptions
|
||||||
|
{
|
||||||
|
std::size_t half_patch_size{1};
|
||||||
|
std::size_t half_search_window_size{6};
|
||||||
|
double distance_threshold{1.};
|
||||||
|
std::size_t num_scales{3};
|
||||||
|
};
|
||||||
|
|
||||||
|
Framebuffer denoise_single_scale(const FatFramebuffer& input_pixels, DenoisingOptions options = {}) noexcept;
|
||||||
|
|
||||||
|
Framebuffer denoise_multiscale(const FatFramebuffer& input_pixels, DenoisingOptions options = {}) noexcept;
|
||||||
|
|
||||||
|
} // namespace Raychel
|
||||||
|
|
||||||
|
#endif //!RAYCHEL_DENOISE_H
|
||||||
64
include/Raychel/Render/FatPixel.h
Normal file
64
include/Raychel/Render/FatPixel.h
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
/**
|
||||||
|
* \file FatPixel.h
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Header file for FatPixel class
|
||||||
|
* \date 2022-05-01
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef RAYCHEL_FAT_PIXEL_H
|
||||||
|
#define RAYCHEL_FAT_PIXEL_H
|
||||||
|
|
||||||
|
#include "RayHistogram.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
template <std::size_t NumBins>
|
||||||
|
struct FatPixel
|
||||||
|
{
|
||||||
|
using Histogram = RayHistogram<NumBins>;
|
||||||
|
|
||||||
|
FatPixel operator+(const FatPixel& other) const noexcept
|
||||||
|
{
|
||||||
|
return {noisy_color + other.noisy_color, histogram + other.histogram};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::convertible_to<double> T>
|
||||||
|
FatPixel operator/(T s) const noexcept
|
||||||
|
{
|
||||||
|
return {noisy_color / s, histogram / s};
|
||||||
|
}
|
||||||
|
|
||||||
|
color noisy_color{};
|
||||||
|
Histogram histogram{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
using FatPixel = details::FatPixel<30U>;
|
||||||
|
|
||||||
|
} // namespace Raychel
|
||||||
|
|
||||||
|
#endif //!RAYCHEL_FAT_PIXEL_H
|
||||||
77
include/Raychel/Render/Framebuffer.h
Normal file
77
include/Raychel/Render/Framebuffer.h
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
/**
|
||||||
|
* \file Framebuffer.h
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Header file for Framebuffer class
|
||||||
|
* \date 2022-04-11
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef RAYCHEL_FRAMEBUFFER_H
|
||||||
|
#define RAYCHEL_FRAMEBUFFER_H
|
||||||
|
|
||||||
|
#include "FatPixel.h"
|
||||||
|
#include "Raychel/Core/Types.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
template <typename Pixel>
|
||||||
|
struct BasicFramebuffer
|
||||||
|
{
|
||||||
|
|
||||||
|
constexpr Pixel& at(std::size_t x, std::size_t y)
|
||||||
|
{
|
||||||
|
return const_cast<Pixel&>(const_cast<const BasicFramebuffer*>(this)->at(x, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
constexpr const Pixel& at(std::size_t x, std::size_t y) const
|
||||||
|
{
|
||||||
|
RAYCHEL_ASSERT((x < size.x()) && (y < size.y()));
|
||||||
|
|
||||||
|
return pixel_data.at(x + (y * size.x()));
|
||||||
|
}
|
||||||
|
|
||||||
|
const Size2D size{};
|
||||||
|
std::vector<Pixel> pixel_data{};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr auto begin(const BasicFramebuffer<T>& obj)
|
||||||
|
{
|
||||||
|
return obj.pixel_data.begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr auto end(const BasicFramebuffer<T>& obj)
|
||||||
|
{
|
||||||
|
return obj.pixel_data.end();
|
||||||
|
}
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
using Framebuffer = details::BasicFramebuffer<color>;
|
||||||
|
using FatFramebuffer = details::BasicFramebuffer<FatPixel>;
|
||||||
|
|
||||||
|
} // namespace Raychel
|
||||||
|
|
||||||
|
#endif //!RAYCHEL_FRAMEBUFFER_H
|
||||||
130
include/Raychel/Render/MaterialContainer.h
Normal file
130
include/Raychel/Render/MaterialContainer.h
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
/**
|
||||||
|
* \file MaterialContainer.h
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Header file for MaterialContainer class
|
||||||
|
* \date 2022-04-10
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef RAYCHEL_MATERIAL_CONTAINER_H
|
||||||
|
#define RAYCHEL_MATERIAL_CONTAINER_H
|
||||||
|
|
||||||
|
#include "Materials.h"
|
||||||
|
|
||||||
|
#include "Raychel/Core/Types.h"
|
||||||
|
#include "RaychelCore/Badge.h"
|
||||||
|
#include "RaychelCore/ClassMacros.h"
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
struct IMaterialContainerImpl
|
||||||
|
{
|
||||||
|
IMaterialContainerImpl() = default;
|
||||||
|
|
||||||
|
RAYCHEL_MAKE_NONCOPY_NONMOVE(IMaterialContainerImpl)
|
||||||
|
|
||||||
|
[[nodiscard]] virtual color get_surface_color_internal(const ShadingData& data) const noexcept = 0;
|
||||||
|
|
||||||
|
[[nodiscard]] virtual double get_material_ior_internal() const noexcept = 0;
|
||||||
|
|
||||||
|
virtual ~IMaterialContainerImpl() = default;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
class MaterialContainerImpl final : public IMaterialContainerImpl
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit MaterialContainerImpl(T&& object) noexcept(std::is_nothrow_move_constructible_v<T>)
|
||||||
|
: object_{std::forward<T>(object)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
[[nodiscard]] color get_surface_color_internal(const ShadingData& data) const noexcept override
|
||||||
|
{
|
||||||
|
return get_surface_color(object_, data);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] double get_material_ior_internal() const noexcept override
|
||||||
|
{
|
||||||
|
if constexpr (is_transparent_material_v<T>)
|
||||||
|
return get_material_ior(object_);
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] T& object() noexcept
|
||||||
|
{
|
||||||
|
return object_;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] const T& object() const noexcept
|
||||||
|
{
|
||||||
|
return object_;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
T object_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
class MaterialContainer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
template <typename T>
|
||||||
|
using Impl = details::MaterialContainerImpl<T>;
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
requires(!std::is_same_v<T, MaterialContainer>) explicit MaterialContainer(T&& object) noexcept(
|
||||||
|
std::is_nothrow_move_constructible_v<T>)
|
||||||
|
: impl_{std::make_unique<details::MaterialContainerImpl<T>>(std::forward<T>(object))}
|
||||||
|
{}
|
||||||
|
|
||||||
|
RAYCHEL_MAKE_NONCOPY(MaterialContainer)
|
||||||
|
RAYCHEL_MAKE_DEFAULT_MOVE(MaterialContainer)
|
||||||
|
|
||||||
|
[[nodiscard]] color get_surface_color(const ShadingData& data) const noexcept
|
||||||
|
{
|
||||||
|
return impl_->get_surface_color_internal(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] double get_material_ior() const noexcept
|
||||||
|
{
|
||||||
|
return impl_->get_material_ior_internal();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] auto* unsafe_impl() const noexcept
|
||||||
|
{
|
||||||
|
return impl_.get();
|
||||||
|
}
|
||||||
|
|
||||||
|
~MaterialContainer() = default;
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<details::IMaterialContainerImpl> impl_{};
|
||||||
|
};
|
||||||
|
|
||||||
|
} //namespace Raychel
|
||||||
|
|
||||||
|
#endif //!RAYCHEL_MATERIAL_CONTAINER_H
|
||||||
66
include/Raychel/Render/Materials.h
Normal file
66
include/Raychel/Render/Materials.h
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
/**
|
||||||
|
* \file Materials.h
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Header file for Materials class
|
||||||
|
* \date 2022-05-20
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef RAYCHEL_MATERIALS_H
|
||||||
|
#define RAYCHEL_MATERIALS_H
|
||||||
|
|
||||||
|
#include "Raychel/Core/Types.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
struct RenderState;
|
||||||
|
|
||||||
|
struct ShadingData
|
||||||
|
{
|
||||||
|
vec3 position;
|
||||||
|
vec3 normal;
|
||||||
|
vec3 incoming_direction;
|
||||||
|
|
||||||
|
const RenderState& state;
|
||||||
|
std::size_t recursion_depth;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
struct is_transparent_material : std::false_type
|
||||||
|
{};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
constexpr bool is_transparent_material_v = is_transparent_material<T>::value;
|
||||||
|
|
||||||
|
struct DeserializationErrorMaterial
|
||||||
|
{};
|
||||||
|
|
||||||
|
constexpr color get_surface_color(DeserializationErrorMaterial /*unused*/, const ShadingData& /*unused*/) noexcept
|
||||||
|
{
|
||||||
|
return color{1, 0, 1};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Raychel
|
||||||
|
|
||||||
|
#endif //!RAYCHEL_MATERIALS_H
|
||||||
153
include/Raychel/Render/RayHistogram.h
Normal file
153
include/Raychel/Render/RayHistogram.h
Normal file
@@ -0,0 +1,153 @@
|
|||||||
|
/**
|
||||||
|
* \file RayHistogram.h
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Header file for RayHistogram class
|
||||||
|
* \date 2022-04-30
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef RAYCHEL_RAY_HISTOGRAM_H
|
||||||
|
#define RAYCHEL_RAY_HISTOGRAM_H
|
||||||
|
|
||||||
|
#include "Raychel/Core/Types.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
struct BinData
|
||||||
|
{
|
||||||
|
std::size_t low_bin_index{};
|
||||||
|
double low_bin_weight{};
|
||||||
|
|
||||||
|
std::size_t high_bin_index{};
|
||||||
|
double high_bin_weight{};
|
||||||
|
};
|
||||||
|
}; // namespace details
|
||||||
|
|
||||||
|
template <std::size_t NumBins>
|
||||||
|
requires(NumBins > 2U) class RayHistogram
|
||||||
|
{
|
||||||
|
using BinnedChannel = std::array<double, NumBins>;
|
||||||
|
|
||||||
|
public:
|
||||||
|
RayHistogram() = default;
|
||||||
|
|
||||||
|
void add_sample(color c) noexcept
|
||||||
|
{
|
||||||
|
_add_channel(red_, c.r());
|
||||||
|
_add_channel(green_, c.g());
|
||||||
|
_add_channel(blue_, c.b());
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& red_channel() const noexcept
|
||||||
|
{
|
||||||
|
return red_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& green_channel() const noexcept
|
||||||
|
{
|
||||||
|
return green_;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& blue_channel() const noexcept
|
||||||
|
{
|
||||||
|
return blue_;
|
||||||
|
}
|
||||||
|
|
||||||
|
RayHistogram operator+(const RayHistogram& other) const noexcept
|
||||||
|
{
|
||||||
|
BinnedChannel union_red{}, union_green{}, union_blue{};
|
||||||
|
|
||||||
|
for (std::size_t i{}; i != NumBins; ++i) {
|
||||||
|
union_red[i] = red_[i] + other.red_[i];
|
||||||
|
union_green[i] = green_[i] + other.green_[i];
|
||||||
|
union_blue[i] = blue_[i] + other.blue_[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
return {std::move(union_red), std::move(union_green), std::move(union_blue)};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::convertible_to<double> T>
|
||||||
|
RayHistogram operator/(T _s) const noexcept
|
||||||
|
{
|
||||||
|
const auto s = static_cast<double>(_s);
|
||||||
|
|
||||||
|
BinnedChannel new_red{}, new_green{}, new_blue{};
|
||||||
|
for (std::size_t i{}; i != NumBins; ++i) {
|
||||||
|
new_red[i] = red_[i] / s;
|
||||||
|
new_green[i] = green_[i] / s;
|
||||||
|
new_blue[i] = blue_[i] / s;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {std::move(new_red), std::move(new_green), std::move(new_blue)};
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
RayHistogram(BinnedChannel red, BinnedChannel green, BinnedChannel blue)
|
||||||
|
: red_{std::move(red)}, green_{std::move(green)}, blue_{std::move(blue)}
|
||||||
|
{}
|
||||||
|
|
||||||
|
static void _add_channel(BinnedChannel& channel, double value) noexcept
|
||||||
|
{
|
||||||
|
const auto [low_bin, low_weight, high_bin, high_weight] = _get_bin_data(value);
|
||||||
|
channel.at(low_bin) += low_weight;
|
||||||
|
channel.at(high_bin) += high_weight;
|
||||||
|
}
|
||||||
|
|
||||||
|
static details::BinData _get_bin_data(double value) noexcept
|
||||||
|
{
|
||||||
|
constexpr auto max_value = 7.5;
|
||||||
|
constexpr auto saturated_value = 2.5;
|
||||||
|
constexpr auto fbin_factor = static_cast<double>(NumBins - 2U);
|
||||||
|
|
||||||
|
auto v = std::max(value, 0.0);
|
||||||
|
v = std::pow(v, 1.0 / 2.2);
|
||||||
|
v /= max_value;
|
||||||
|
|
||||||
|
v = std::min(saturated_value, v);
|
||||||
|
|
||||||
|
const auto fbin = v * fbin_factor;
|
||||||
|
|
||||||
|
const auto bin_low = static_cast<std::size_t>(fbin);
|
||||||
|
if (bin_low < (NumBins - 2U)) {
|
||||||
|
const auto high_weight = std::fmod(fbin, 1.0);
|
||||||
|
const auto low_weight = 1.0 - high_weight;
|
||||||
|
|
||||||
|
return {bin_low, low_weight, bin_low + 1U, high_weight};
|
||||||
|
}
|
||||||
|
const auto high_weight = (v - 1) / (saturated_value - 1);
|
||||||
|
const auto low_weight = 1.0 - high_weight;
|
||||||
|
|
||||||
|
return {NumBins - 2U, low_weight, NumBins - 1U, high_weight};
|
||||||
|
}
|
||||||
|
|
||||||
|
BinnedChannel red_{};
|
||||||
|
BinnedChannel green_{};
|
||||||
|
BinnedChannel blue_{};
|
||||||
|
};
|
||||||
|
} // namespace Raychel
|
||||||
|
|
||||||
|
#endif //!RAYCHEL_RAY_HISTOGRAM_H
|
||||||
55
include/Raychel/Render/RenderUtils.h
Normal file
55
include/Raychel/Render/RenderUtils.h
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* \file RenderHelper.h
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Header file for RenderHelper class
|
||||||
|
* \date 2022-04-12
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef RAYCHEL_RENDER_UTILS_H
|
||||||
|
#define RAYCHEL_RENDER_UTILS_H
|
||||||
|
|
||||||
|
#include "Renderer.h"
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
struct RefractionData
|
||||||
|
{
|
||||||
|
vec3 surface_point;
|
||||||
|
vec3 incoming_direction;
|
||||||
|
vec3 normal;
|
||||||
|
|
||||||
|
double material_ior;
|
||||||
|
double ior_variation;
|
||||||
|
|
||||||
|
const RenderState& state;
|
||||||
|
std::size_t recursion_depth;
|
||||||
|
};
|
||||||
|
|
||||||
|
[[nodiscard]] color get_shaded_color(const RenderData& data) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]] color get_diffuse_lighting(const ShadingData& data) noexcept;
|
||||||
|
|
||||||
|
[[nodiscard]] color get_refraction(const RefractionData& data) noexcept;
|
||||||
|
} // namespace Raychel
|
||||||
|
|
||||||
|
#endif //!RAYCHEL_RENDER_UTILS_H
|
||||||
95
include/Raychel/Render/Renderer.h
Normal file
95
include/Raychel/Render/Renderer.h
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
/**
|
||||||
|
* \file Renderer.h
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Header file for Renderer class
|
||||||
|
* \date 2022-04-11
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#ifndef RAYCHEL_RENDERER_H
|
||||||
|
#define RAYCHEL_RENDERER_H
|
||||||
|
|
||||||
|
#include "Camera.h"
|
||||||
|
#include "Framebuffer.h"
|
||||||
|
#include "MaterialContainer.h"
|
||||||
|
#include "Raychel/Core/SDFContainer.h"
|
||||||
|
|
||||||
|
#include <functional>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
struct RenderOptions
|
||||||
|
{
|
||||||
|
//Size of the output image
|
||||||
|
Size2D output_size{1280, 720};
|
||||||
|
|
||||||
|
//Maximum number of steps until raymarching terminates
|
||||||
|
std::size_t max_ray_steps{1'024};
|
||||||
|
|
||||||
|
//Maximum depth for recursive algorithms
|
||||||
|
std::size_t max_recursion_depth{6};
|
||||||
|
|
||||||
|
//Maximum number of light bounces for indirect lighting
|
||||||
|
std::size_t max_lighting_bounces{2};
|
||||||
|
|
||||||
|
//Number of samples per pixel for rendering. Dramatically increases render times!
|
||||||
|
std::size_t samples_per_pixel{128};
|
||||||
|
|
||||||
|
//If antialiasing is used. (Low performance impact)
|
||||||
|
bool do_aa{true};
|
||||||
|
|
||||||
|
//How many threads are used for rendering. If 0, the library will choose
|
||||||
|
std::size_t thread_count{0};
|
||||||
|
|
||||||
|
//Maximum distance a ray can travel
|
||||||
|
double max_ray_depth{500};
|
||||||
|
|
||||||
|
//Maximum distance between the ray and a surface
|
||||||
|
double surface_epsilon{1e-6};
|
||||||
|
//Radius used for normal calculation. Should be smaller than surface_epsilon to avoid weirdness
|
||||||
|
double normal_epsilon{1e-12};
|
||||||
|
//Offset along the surface normal to avoid shadow weirdness. Should be larger than surface_epsilon
|
||||||
|
double shading_epsilon{1e-5};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RenderState
|
||||||
|
{
|
||||||
|
const std::vector<SDFContainer>& surfaces;
|
||||||
|
const std::vector<MaterialContainer>& materials;
|
||||||
|
BackgroundFunction get_background{};
|
||||||
|
RenderOptions options{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct RenderData
|
||||||
|
{
|
||||||
|
vec3 origin, direction;
|
||||||
|
|
||||||
|
const RenderState& state;
|
||||||
|
std::size_t recursion_depth;
|
||||||
|
};
|
||||||
|
|
||||||
|
FatFramebuffer render_scene(const Scene& scene, const Camera& camera, const RenderOptions& options = {}) noexcept;
|
||||||
|
|
||||||
|
} // namespace Raychel
|
||||||
|
|
||||||
|
#endif //!RAYCHEL_RENDERER_H
|
||||||
47
misc/generate_ziggurat_tables.py
Normal file
47
misc/generate_ziggurat_tables.py
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
from math import *
|
||||||
|
|
||||||
|
C = 128
|
||||||
|
R = 3.442619855899
|
||||||
|
V = 9.91256303526217e-3
|
||||||
|
|
||||||
|
zig_x = [0 for i in range(C+1)]
|
||||||
|
zig_r = [0 for i in range(C)]
|
||||||
|
|
||||||
|
#C code:
|
||||||
|
#static void zigNorInit(int iC, double dR, double dV)
|
||||||
|
#{
|
||||||
|
# int i; double f;
|
||||||
|
# f = exp(-0.5 * R * R);
|
||||||
|
# zig_x[0] = V / f;
|
||||||
|
# zig_x[1] = R;
|
||||||
|
# zig_x[iC] = 0;
|
||||||
|
# for (i = 2; i < C; ++i)
|
||||||
|
# {
|
||||||
|
# zig_x[i] = sqrt(-2 * log(V / zig_x[i - 1] + f));
|
||||||
|
# f = exp(-0.5 * zig_x[i] * zig_x[i]);
|
||||||
|
# }
|
||||||
|
# for (i = 0; i < C; ++i)
|
||||||
|
# zig_r[i] = zig_x[i + 1] / zig_x[i];
|
||||||
|
#}
|
||||||
|
|
||||||
|
def main():
|
||||||
|
f = exp(-0.5 * R * R)
|
||||||
|
zig_x[0] = V / f
|
||||||
|
zig_x[1] = R
|
||||||
|
zig_x[C] = 0
|
||||||
|
for i in range(2, C):
|
||||||
|
zig_x[i] = sqrt(-2 * log(V / zig_x[i - 1] + f))
|
||||||
|
f = exp(-0.5 * zig_x[i] * zig_x[i])
|
||||||
|
for i in range(C):
|
||||||
|
zig_r[i] = zig_x[i + 1] / zig_x[i]
|
||||||
|
|
||||||
|
print("X:")
|
||||||
|
for x in zig_x:
|
||||||
|
print(f"{x}, ")
|
||||||
|
|
||||||
|
print("R: ")
|
||||||
|
for r in zig_r:
|
||||||
|
print(f"{r}, ")
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
267
src/Core/Deserialize.cpp
Normal file
267
src/Core/Deserialize.cpp
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
/**
|
||||||
|
* \file Deserialize.cpp
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Implementation file for Deserialize class
|
||||||
|
* \date 2022-05-19
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Raychel/Core/Deserialize.h"
|
||||||
|
#include "Raychel/Core/SDFPrimitives.h"
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
struct DeserializerState
|
||||||
|
{
|
||||||
|
std::istream& input_stream;
|
||||||
|
const std::vector<details::DeserializerPtr<SDFContainer>>& object_deserializers;
|
||||||
|
const std::vector<details::DeserializerPtr<MaterialContainer>>& material_deserializers;
|
||||||
|
|
||||||
|
std::vector<SDFContainer> objects{};
|
||||||
|
std::vector<SerializableObjectData<SDFContainer>> object_serializers{};
|
||||||
|
|
||||||
|
std::vector<MaterialContainer> materials{};
|
||||||
|
std::vector<SerializableObjectData<MaterialContainer>> material_serializers{};
|
||||||
|
|
||||||
|
bool is_in_object_section{true};
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
struct ContainerAndSerializer
|
||||||
|
{
|
||||||
|
Container container;
|
||||||
|
SerializableObjectData<Container> serializer;
|
||||||
|
};
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
ContainerAndSerializer(T, SerializableObjectData<T>) -> ContainerAndSerializer<T>;
|
||||||
|
|
||||||
|
enum class ParseObjectFromLineResult {
|
||||||
|
ok = 0,
|
||||||
|
entered_material_section,
|
||||||
|
no_type_name_separator,
|
||||||
|
empty_line,
|
||||||
|
};
|
||||||
|
|
||||||
|
static bool check_object_header(std::istream& is) noexcept
|
||||||
|
{
|
||||||
|
std::string line{};
|
||||||
|
std::getline(is, line);
|
||||||
|
|
||||||
|
if (line == "--BEGIN SURFACES--")
|
||||||
|
return true;
|
||||||
|
|
||||||
|
Logger::warn("Incorrect surface section header '", line, "'\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string get_shortened_line(std::istream& is) noexcept
|
||||||
|
{
|
||||||
|
std::string line{};
|
||||||
|
if (!std::getline(is, line))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
//skip whitespace characters
|
||||||
|
auto current_char = line.begin();
|
||||||
|
while (std::isspace(*current_char) != 0) {
|
||||||
|
current_char++;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {current_char, line.end()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
static details::IDeserializer<Container>* find_deserializer_for(
|
||||||
|
std::string_view type_name, const std::vector<details::DeserializerPtr<Container>>& deserializers) noexcept
|
||||||
|
{
|
||||||
|
for (const auto& deserializer : deserializers) {
|
||||||
|
if (deserializer->contained_type_name() == type_name) {
|
||||||
|
return deserializer.get();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Logger::warn("Could not find deserializer for type name '", type_name, "'\n");
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
static std::optional<ContainerAndSerializer<Container>> parse_object(
|
||||||
|
std::string_view type_name, std::string_view rest_of_line,
|
||||||
|
const std::vector<details::DeserializerPtr<Container>>& deserializers) noexcept
|
||||||
|
{
|
||||||
|
const auto maybe_deserializer = find_deserializer_for(type_name, deserializers);
|
||||||
|
if (maybe_deserializer == nullptr)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
std::stringstream line_stream{std::string{rest_of_line}}; //Hmmmmmmmmmmmmmmmmmmm
|
||||||
|
auto maybe_object = maybe_deserializer->deserialize(line_stream, std::nullopt);
|
||||||
|
if (!maybe_object.has_value()) {
|
||||||
|
Logger::warn("Could not deserialize object of type '", type_name, " with data '", rest_of_line, "'\n");
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return ContainerAndSerializer{std::move(maybe_object).value(), maybe_deserializer->get_serializer()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
static std::optional<ContainerAndSerializer<Container>> parse_targeted(
|
||||||
|
DeserializerState& state, std::string_view type_name, std::string_view rest_of_line,
|
||||||
|
const std::vector<details::DeserializerPtr<Container>>& deserializers) noexcept
|
||||||
|
{
|
||||||
|
const auto maybe_deserializer = find_deserializer_for(type_name, deserializers);
|
||||||
|
if (maybe_deserializer == nullptr)
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
auto [maybe_target_and_serializer, result] = parse_object_from_line(state, deserializers);
|
||||||
|
if (result != ParseObjectFromLineResult::ok)
|
||||||
|
return std::nullopt;
|
||||||
|
if (!maybe_target_and_serializer.has_value())
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
Container target = std::move(maybe_target_and_serializer).value().container;
|
||||||
|
|
||||||
|
std::stringstream line_stream{std::string{rest_of_line}};
|
||||||
|
auto maybe_object = maybe_deserializer->deserialize(line_stream, std::move(target));
|
||||||
|
if (!maybe_object.has_value()) {
|
||||||
|
Logger::warn("Could not deserialize object of type '", type_name, " with data '", rest_of_line, "'\n");
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return ContainerAndSerializer{std::move(maybe_object).value(), maybe_deserializer->get_serializer()};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
static std::optional<ContainerAndSerializer<Container>> parse_type(
|
||||||
|
DeserializerState& state, std::string_view type_name, std::string_view rest_of_line,
|
||||||
|
const std::vector<details::DeserializerPtr<Container>>& deserializers) noexcept
|
||||||
|
{
|
||||||
|
//If the type is has a target, recursively resolve that target
|
||||||
|
if (type_name.ends_with("<>")) {
|
||||||
|
type_name.remove_suffix(2U);
|
||||||
|
return parse_targeted(state, type_name, rest_of_line, deserializers);
|
||||||
|
}
|
||||||
|
return parse_object(type_name, rest_of_line, deserializers);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
static bool add_object(
|
||||||
|
std::optional<ContainerAndSerializer<Container>> maybe_object, std::vector<Container>& containers,
|
||||||
|
std::vector<SerializableObjectData<Container>>& serializers) noexcept
|
||||||
|
{
|
||||||
|
if (!maybe_object.has_value())
|
||||||
|
return false;
|
||||||
|
auto [container, serializer] = std::move(maybe_object).value();
|
||||||
|
containers.emplace_back(std::move(container));
|
||||||
|
serializers.emplace_back(std::move(serializer));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename Container>
|
||||||
|
static std::pair<std::optional<ContainerAndSerializer<Container>>, ParseObjectFromLineResult> parse_object_from_line(
|
||||||
|
DeserializerState& state, const std::vector<details::DeserializerPtr<Container>>& deserializers) noexcept
|
||||||
|
{
|
||||||
|
const auto line = get_shortened_line(state.input_stream);
|
||||||
|
|
||||||
|
if (line.empty())
|
||||||
|
return {std::nullopt, ParseObjectFromLineResult::empty_line};
|
||||||
|
|
||||||
|
if (line == "--BEGIN MATERIALS--") {
|
||||||
|
return {std::nullopt, ParseObjectFromLineResult::entered_material_section};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto end_of_type_name_index = line.find(" with ");
|
||||||
|
if (end_of_type_name_index == std::string::npos) {
|
||||||
|
Logger::warn("Incorrect type name separator!\n");
|
||||||
|
return {std::nullopt, ParseObjectFromLineResult::no_type_name_separator};
|
||||||
|
}
|
||||||
|
const auto end_of_type_name = line.begin() + static_cast<std::ptrdiff_t>(end_of_type_name_index);
|
||||||
|
std::string_view type_name{line.begin(), end_of_type_name};
|
||||||
|
std::string_view rest_of_line{end_of_type_name + 6, line.end()};
|
||||||
|
|
||||||
|
return std::make_pair(parse_type(state, type_name, rest_of_line, deserializers), ParseObjectFromLineResult::ok);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool parse_line(DeserializerState& state) noexcept
|
||||||
|
{
|
||||||
|
if (state.is_in_object_section) {
|
||||||
|
auto [maybe_object, result] = parse_object_from_line(state, state.object_deserializers);
|
||||||
|
if (result == ParseObjectFromLineResult::entered_material_section) {
|
||||||
|
state.is_in_object_section = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (result == ParseObjectFromLineResult::empty_line)
|
||||||
|
return true;
|
||||||
|
if (result != ParseObjectFromLineResult::ok)
|
||||||
|
return false;
|
||||||
|
return add_object(std::move(maybe_object), state.objects, state.object_serializers);
|
||||||
|
}
|
||||||
|
auto [maybe_material, result] = parse_object_from_line(state, state.material_deserializers);
|
||||||
|
if (result == ParseObjectFromLineResult::entered_material_section) {
|
||||||
|
Logger::warn("Entered material section twice!\n");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (result == ParseObjectFromLineResult::empty_line)
|
||||||
|
return true;
|
||||||
|
if (result != ParseObjectFromLineResult::ok) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return add_object(std::move(maybe_material), state.materials, state.material_serializers);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void place_dummy(DeserializerState& state) noexcept
|
||||||
|
{
|
||||||
|
if (state.is_in_object_section) {
|
||||||
|
state.objects.emplace_back(DeserializationErrorPlaceHolder{});
|
||||||
|
state.object_serializers.emplace_back(details::SerializableObjectDescriptor<DeserializationErrorPlaceHolder>{});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
state.materials.emplace_back(DeserializationErrorMaterial{});
|
||||||
|
state.material_serializers.emplace_back((details::SerializableObjectDescriptor<DeserializationErrorMaterial>{}));
|
||||||
|
}
|
||||||
|
|
||||||
|
Scene deserialize_scene(
|
||||||
|
std::istream& is, const std::vector<details::DeserializerPtr<SDFContainer>>& object_deserializers,
|
||||||
|
const std::vector<details::DeserializerPtr<MaterialContainer>>& material_deserializers) noexcept
|
||||||
|
{
|
||||||
|
DeserializerState state{is, object_deserializers, material_deserializers};
|
||||||
|
|
||||||
|
if (!check_object_header(is))
|
||||||
|
return {};
|
||||||
|
|
||||||
|
while (state.input_stream) {
|
||||||
|
if (!parse_line(state)) {
|
||||||
|
Logger::debug("Placing dummy\n");
|
||||||
|
place_dummy(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state.is_in_object_section) {
|
||||||
|
Logger::warn("Parser did not leave object section! Incorrect material section header?\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Scene::unsafe_from_data(
|
||||||
|
std::move(state.objects),
|
||||||
|
std::move(state.object_serializers),
|
||||||
|
std::move(state.materials),
|
||||||
|
std::move(state.material_serializers));
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace Raychel
|
||||||
87
src/Core/Raymarch.cpp
Normal file
87
src/Core/Raymarch.cpp
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
/**
|
||||||
|
* \file Raymarch.cpp
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Implementation file for Raymarch class
|
||||||
|
* \date 2022-06-11
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Raychel/Core/Raymarch.h"
|
||||||
|
#include "Raychel/Core/SDFContainer.h"
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
std::pair<double, std::size_t> evaluate_distance_field(const std::vector<SDFContainer>& surfaces, const vec3& point) noexcept
|
||||||
|
{
|
||||||
|
const auto surfaces_size = surfaces.size();
|
||||||
|
|
||||||
|
double min_distance{1e9};
|
||||||
|
auto hit_index = no_hit;
|
||||||
|
for (std::size_t i{}; i != surfaces_size; ++i) {
|
||||||
|
const auto surface_distance = std::abs(surfaces[i].evaluate(point));
|
||||||
|
|
||||||
|
if (surface_distance < min_distance) {
|
||||||
|
hit_index = i;
|
||||||
|
min_distance = surface_distance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {min_distance, hit_index};
|
||||||
|
}
|
||||||
|
|
||||||
|
RaymarchResult raymarch(
|
||||||
|
vec3 current_point, const vec3& direction, const std::vector<SDFContainer>& surfaces, RaymarchOptions options) noexcept
|
||||||
|
{
|
||||||
|
double depth{};
|
||||||
|
std::size_t step{};
|
||||||
|
while (step != options.max_ray_steps && depth < options.max_ray_depth) {
|
||||||
|
const auto [max_distance, hit_index] = evaluate_distance_field(surfaces, current_point);
|
||||||
|
if (max_distance < options.surface_epsilon) {
|
||||||
|
return {current_point, depth, step, hit_index};
|
||||||
|
}
|
||||||
|
current_point += direction * max_distance;
|
||||||
|
depth += max_distance;
|
||||||
|
++step;
|
||||||
|
}
|
||||||
|
return {current_point, depth, step, no_hit};
|
||||||
|
}
|
||||||
|
|
||||||
|
vec3 get_normal(const vec3& point, const SDFContainer& surface, double normal_offset) noexcept
|
||||||
|
{
|
||||||
|
//This implements the tetrahedon sampling technique found at https://iquilezles.org/articles/normalsSDF/
|
||||||
|
constexpr vec3 xyy{1, -1, -1}, yyx{-1, -1, 1}, yxy{-1, 1, -1}, xxx{1, 1, 1};
|
||||||
|
|
||||||
|
if (surface.has_custom_normal()) {
|
||||||
|
return surface.get_normal(point);
|
||||||
|
}
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
return normalize(vec3{
|
||||||
|
xyy*surface.evaluate(point+xyy*normal_offset) +
|
||||||
|
yyx*surface.evaluate(point+yyx*normal_offset) +
|
||||||
|
yxy*surface.evaluate(point+yxy*normal_offset) +
|
||||||
|
xxx*surface.evaluate(point+xxx*normal_offset)
|
||||||
|
});
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Raychel
|
||||||
78
src/Core/SDFPrimitives.cpp
Normal file
78
src/Core/SDFPrimitives.cpp
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
/**
|
||||||
|
* \file SDFPrimitives.cpp
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Implementation file for SDFPrimitives class
|
||||||
|
* \date 2022-05-27
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Raychel/Core/SDFPrimitives.h"
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
bool do_serialize(std::ostream& os, const Sphere& object) noexcept
|
||||||
|
{
|
||||||
|
os << object.radius << '\n';
|
||||||
|
return os.good();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Sphere> do_deserialize(std::istream& is, DeserializationTag<Sphere>) noexcept
|
||||||
|
{
|
||||||
|
double radius{};
|
||||||
|
if (!(is >> radius))
|
||||||
|
return std::nullopt;
|
||||||
|
return Sphere{radius};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool do_serialize(std::ostream& os, const Box& object) noexcept
|
||||||
|
{
|
||||||
|
os << object.size << '\n';
|
||||||
|
return os.good();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Box> do_deserialize(std::istream& is, DeserializationTag<Box> /*unused*/) noexcept
|
||||||
|
{
|
||||||
|
vec3 size{};
|
||||||
|
if (!(is >> size))
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return Box{size};
|
||||||
|
}
|
||||||
|
|
||||||
|
bool do_serialize(std::ostream& os, const Plane& object) noexcept
|
||||||
|
{
|
||||||
|
os << object.normal << '\n';
|
||||||
|
return os.good();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<Plane> do_deserialize(std::istream& is, DeserializationTag<Plane>) noexcept
|
||||||
|
{
|
||||||
|
vec3 normal{};
|
||||||
|
if (!(is >> normal))
|
||||||
|
return std::nullopt;
|
||||||
|
if (normal == vec3{})
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return Plane{normalize(normal)};
|
||||||
|
}
|
||||||
|
} //namespace Raychel
|
||||||
75
src/Core/Scene.cpp
Normal file
75
src/Core/Scene.cpp
Normal file
@@ -0,0 +1,75 @@
|
|||||||
|
/**
|
||||||
|
* \file Scene.cpp
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Implementation file for Scene class
|
||||||
|
* \date 2022-05-17
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Raychel/Core/Scene.h"
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
void Scene::remove_object(std::size_t _index) noexcept
|
||||||
|
{
|
||||||
|
using Diff = std::iter_difference_t<decltype(objects_.begin())>;
|
||||||
|
if (_index >= objects_.size()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
RAYCHEL_ASSERT(_index < std::numeric_limits<Diff>::max())
|
||||||
|
|
||||||
|
const auto index = static_cast<Diff>(_index);
|
||||||
|
|
||||||
|
objects_.erase(objects_.begin() + index);
|
||||||
|
materials_.erase(materials_.begin() + index);
|
||||||
|
object_serializers_.erase(object_serializers_.begin() + index);
|
||||||
|
material_serializers_.erase(material_serializers_.begin() + index);
|
||||||
|
}
|
||||||
|
|
||||||
|
Scene Scene::unsafe_from_data(
|
||||||
|
std::vector<SDFContainer> objects, std::vector<SerializableObjectData<SDFContainer>> object_serializers,
|
||||||
|
std::vector<MaterialContainer> materials,
|
||||||
|
std::vector<SerializableObjectData<MaterialContainer>> material_serializers) noexcept
|
||||||
|
{
|
||||||
|
//These all need to be the exact same size
|
||||||
|
if (!(objects.size() == object_serializers.size() && objects.size() == materials.size() &&
|
||||||
|
objects.size() == material_serializers.size())) {
|
||||||
|
Logger::warn(
|
||||||
|
"Unable to create Scene from invalid data! Data sizes:"
|
||||||
|
"\n Objects: ",
|
||||||
|
objects.size(),
|
||||||
|
"\n Object serializers: ",
|
||||||
|
object_serializers.size(),
|
||||||
|
"\n Materials: ",
|
||||||
|
materials.size(),
|
||||||
|
"\n Material serializers: ",
|
||||||
|
material_serializers.size(),
|
||||||
|
'\n');
|
||||||
|
return Scene{};
|
||||||
|
}
|
||||||
|
|
||||||
|
return Scene{std::move(objects), std::move(object_serializers), std::move(materials), std::move(material_serializers)};
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace Raychel
|
||||||
55
src/Core/Serialize.cpp
Normal file
55
src/Core/Serialize.cpp
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
/**
|
||||||
|
* \file Serialize.cpp
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Implementation file for Serialize class
|
||||||
|
* \date 2022-05-19
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Raychel/Core/Serialize.h"
|
||||||
|
#include "Raychel/Core/Scene.h"
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
bool serialize_scene(const Scene& scene, std::ostream& os) noexcept
|
||||||
|
{
|
||||||
|
std::size_t i{};
|
||||||
|
os << "--BEGIN SURFACES--\n";
|
||||||
|
for (const auto& serializer : scene.object_serializers()) {
|
||||||
|
if (!serializer.serialize(scene.objects().at(i), os))
|
||||||
|
return false;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
i = 0U;
|
||||||
|
os << "--BEGIN MATERIALS--\n";
|
||||||
|
for (const auto& serializer : scene.material_serializers()) {
|
||||||
|
if (!serializer.serialize(scene.materials().at(i), os))
|
||||||
|
return false;
|
||||||
|
++i;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace Raychel
|
||||||
212
src/Core/ZigguratNormal.cpp
Normal file
212
src/Core/ZigguratNormal.cpp
Normal file
@@ -0,0 +1,212 @@
|
|||||||
|
/**
|
||||||
|
* \file ZigguratNormal.cpp
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Implementation for the Ziggurat algorithm to approximate a normal distribution (taken from https://www.jstatsoft.org/article/download/v005i08/623)
|
||||||
|
* \date 2022-04-24
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Raychel/Core/ZigguratNormal.h"
|
||||||
|
#include "Raychel/Core/Xoroshiro128+.h"
|
||||||
|
|
||||||
|
#include "RaychelCore/Raychel_assert.h"
|
||||||
|
#include "RaychelMath/math.h"
|
||||||
|
|
||||||
|
#include <array>
|
||||||
|
#include <cmath>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <random>
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
namespace details {
|
||||||
|
|
||||||
|
constexpr auto R = 3.6541528853610088;
|
||||||
|
|
||||||
|
constexpr static std::array X_table{
|
||||||
|
3.7130862467425505, 3.442619855899, 3.2230849845811416,
|
||||||
|
3.0832288582168683, 2.9786962526477803, 2.894344007021529,
|
||||||
|
2.8231253505489105, 2.761169372387177, 2.7061135731218195,
|
||||||
|
2.6564064112613597, 2.6109722484318474, 2.569033625924938,
|
||||||
|
2.5300096723888275, 2.493454522095372, 2.4590181774118305,
|
||||||
|
2.42642064553375, 2.3954342780110625, 2.3658713701176386,
|
||||||
|
2.3375752413392368, 2.310413683698763, 2.2842740596774718,
|
||||||
|
2.2590595738691985, 2.2346863955909795, 2.2110814088787034,
|
||||||
|
2.188180432076049, 2.165926793748922, 2.1442701823603953,
|
||||||
|
2.1231657086739766, 2.1025731351892385, 2.082456237992017,
|
||||||
|
2.0627822745083084, 2.0435215366550676, 2.0246469733773855,
|
||||||
|
2.006133869963472, 1.98795957412762, 1.9701032608543265,
|
||||||
|
1.9525457295535567, 1.9352692282966228, 1.9182573008645099,
|
||||||
|
1.901494653105151, 1.884967035707759, 1.8686611409944887,
|
||||||
|
1.8525645117280911, 1.836665460258446, 1.8209529965961255,
|
||||||
|
1.8054167642192285, 1.7900469825998586, 1.7748343955860695,
|
||||||
|
1.7597702248995934, 1.7448461281138004, 1.7300541605637305,
|
||||||
|
1.7153867407136676, 1.7008366185699169, 1.6863968467791681,
|
||||||
|
1.672060754097601, 1.6578219209540241, 1.6436741568628686,
|
||||||
|
1.6296114794706347, 1.615628095043161, 1.6017183802213781,
|
||||||
|
1.5878768648905761, 1.5740982160230008, 1.560377222366169,
|
||||||
|
1.5467087798599104, 1.5330878776740433, 1.5195095847659401,
|
||||||
|
1.5059690368632033, 1.492461423781354, 1.4789819769899242,
|
||||||
|
1.4655259573427108, 1.4520886428892246, 1.4386653166845635,
|
||||||
|
1.42525125451406, 1.4118417124470577, 1.3984319141310053,
|
||||||
|
1.3850170377326518, 1.3715922024273426, 1.3581524543301435,
|
||||||
|
1.344692751753547, 1.3312079496656273, 1.317692783209414,
|
||||||
|
1.3041418501286168, 1.2905495919261964, 1.2769102735601556,
|
||||||
|
1.263217961454621, 1.2494664995730682, 1.2356494832633627,
|
||||||
|
1.2217602305399964, 1.2077917504159497, 1.1937367078331287,
|
||||||
|
1.1795873846639882, 1.1653356361647524, 1.1509728421488674,
|
||||||
|
1.1364898520131608, 1.1218769225825422, 1.107123647534036,
|
||||||
|
1.0922188769072774, 1.0771506248928957, 1.0619059636948243,
|
||||||
|
1.0464709007640454, 1.0308302360681956, 1.0149673952513305,
|
||||||
|
0.9988642334929836, 0.982500803515429, 0.9658550794011499,
|
||||||
|
0.9489026255113064, 0.9316161966151508, 0.9139652510230323,
|
||||||
|
0.8959153525809377, 0.8774274291129234, 0.8584568431938132,
|
||||||
|
0.8389522142975774, 0.8188539067003573, 0.7980920606440569,
|
||||||
|
0.7765839878947599, 0.7542306644540556, 0.7309119106424888,
|
||||||
|
0.7064796113354365, 0.6807479186691546, 0.6534786387399752,
|
||||||
|
0.6243585973360507, 0.5929629424714483, 0.5586921784081852,
|
||||||
|
0.5206560387620606, 0.4774378372966898, 0.4265479863554235,
|
||||||
|
0.36287143109703196, 0.27232086481396467, 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
constexpr static std::array R_table{
|
||||||
|
0.9271586026096681, 0.9362302895738892, 0.9566079929529229, 0.9660963845448882,
|
||||||
|
0.971681487982781, 0.9753938521821022, 0.9780541171685178, 0.980060694640489,
|
||||||
|
0.9816315315239645, 0.9828963811271866, 0.9839375456663325, 0.9848098704733534,
|
||||||
|
0.9855513792328944, 0.9861893030819736, 0.9867436799867864, 0.9872295978111943,
|
||||||
|
0.9876586437103296, 0.9880398701570176, 0.9883804563121089, 0.9886861715693078,
|
||||||
|
0.9889617072428545, 0.9892109183130244, 0.9894370025436909, 0.9896426351781105,
|
||||||
|
0.9898300715969688, 0.9900012265183524, 0.9901577357834697, 0.9903010050508025,
|
||||||
|
0.9904322485336944, 0.9905525200843218, 0.9906627383358567, 0.9907637071892196,
|
||||||
|
0.9908561326209719, 0.9909406365607181, 0.991017768416579, 0.9910880146997187,
|
||||||
|
0.991151807102165, 0.991209529308185, 0.9912615227624552, 0.9913080915739614,
|
||||||
|
0.9913495066999154, 0.9913860095266759, 0.9914178149430195, 0.9914451139838447,
|
||||||
|
0.9914680761085329, 0.9914868511670121, 0.9915015710974835, 0.9915123513923666,
|
||||||
|
0.9915192923629307, 0.9915224802280646, 0.9915219880484646, 0.9915178765240442,
|
||||||
|
0.9915101946694387, 0.9914989803800052, 0.9914842608986051, 0.9914660531916395,
|
||||||
|
0.9914443642412228, 0.9914191912590011, 0.9913905218258715, 0.9913583339607497,
|
||||||
|
0.9913225961204966, 0.9912832671321499, 0.9912402960576856, 0.991193621990624,
|
||||||
|
0.991143173782899, 0.991088869699481, 0.9910306169972894, 0.9909683114239041,
|
||||||
|
0.9909018366304913, 0.9908310634921467, 0.9907558493275227, 0.9906760370080955,
|
||||||
|
0.9905914539457294, 0.9905019109452362, 0.9904072009063883, 0.990307097357238,
|
||||||
|
0.990201352797563, 0.9900896968277136, 0.9899718340339569, 0.9898474415964779,
|
||||||
|
0.9897161665803526, 0.9895776228628198, 0.9894313876418468, 0.9892769974609422,
|
||||||
|
0.9891139436730952, 0.9889416672520418, 0.9887595528412437, 0.9885669219091597,
|
||||||
|
0.9883630248526034, 0.9881470318569457, 0.9879180222809051, 0.987674972282531,
|
||||||
|
0.9874167403388364, 0.9871420502305995, 0.9868494709610887, 0.9865373929461655,
|
||||||
|
0.986203999644239, 0.9858472335755389, 0.98546475539409, 0.9850538942989907,
|
||||||
|
0.9846115875710347, 0.9841343063494573, 0.9836179638544746, 0.9830578010168337,
|
||||||
|
0.9824482427525728, 0.9817827157061126, 0.9810534148544756, 0.9802510014227667,
|
||||||
|
0.9793642073274506, 0.9783793105963312, 0.9772794298852922, 0.9760435609386315,
|
||||||
|
0.9746452378300764, 0.9730506368752245, 0.9712158326862985, 0.9690827290502092,
|
||||||
|
0.9665728537853818, 0.9635775863118795, 0.959942176565901, 0.9554384188286962,
|
||||||
|
0.9497153478809163, 0.9422042060159378, 0.9319193267489506, 0.9169927970716931,
|
||||||
|
0.8934105197245976, 0.8507165493794344, 0.7504610213889943, 0.0,
|
||||||
|
};
|
||||||
|
|
||||||
|
//NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
static thread_local std::uint32_t xorshift32_seed{2463534242U};
|
||||||
|
[[nodiscard]] std::uint32_t random32() noexcept
|
||||||
|
{
|
||||||
|
xorshift32_seed ^= (xorshift32_seed << 13U);
|
||||||
|
xorshift32_seed ^= (xorshift32_seed >> 17U);
|
||||||
|
xorshift32_seed ^= (xorshift32_seed << 5U);
|
||||||
|
|
||||||
|
return xorshift32_seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
//NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
static thread_local std::uint64_t xorshift64_seed{88172645463325252U};
|
||||||
|
[[nodiscard]] std::uint64_t random64() noexcept
|
||||||
|
{
|
||||||
|
xorshift64_seed ^= (xorshift64_seed << 13U);
|
||||||
|
xorshift64_seed ^= (xorshift64_seed >> 7U);
|
||||||
|
xorshift64_seed ^= (xorshift64_seed << 17U);
|
||||||
|
|
||||||
|
return xorshift64_seed;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Fast way to generate "uniform" reals in the range [0; 1]. Credit to Inigo Quilez (https://iquilezles.org/articles/sfrand/)
|
||||||
|
[[nodiscard]] double uniform01() noexcept
|
||||||
|
{
|
||||||
|
//The idea is to generate 52 random bits for the mantissa and fix the exponent to 1023 to generate a random number between 1 and 2.
|
||||||
|
//We can then easily map that number into the [0; 1] range by subtracting 1
|
||||||
|
static_assert(sizeof(std::uint64_t) == sizeof(double));
|
||||||
|
|
||||||
|
constexpr std::uint64_t fix_exponent{0x3FF0000000000000};
|
||||||
|
|
||||||
|
const auto mantissa = (random64() >> 12U);
|
||||||
|
|
||||||
|
return std::bit_cast<double>(mantissa | fix_exponent) - 1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static double normal_tail(bool is_negative) noexcept
|
||||||
|
{
|
||||||
|
double x{};
|
||||||
|
double y{};
|
||||||
|
std::size_t guard{};
|
||||||
|
do {
|
||||||
|
x = std::log(uniform01()) / R;
|
||||||
|
y = std::log(uniform01());
|
||||||
|
|
||||||
|
RAYCHEL_ASSERT(++guard != 16U);
|
||||||
|
} while (-2 * y < sq(x));
|
||||||
|
|
||||||
|
if (is_negative) {
|
||||||
|
return x - R;
|
||||||
|
}
|
||||||
|
return R - x;
|
||||||
|
}
|
||||||
|
} // namespace details
|
||||||
|
|
||||||
|
double uniform_random() noexcept
|
||||||
|
{
|
||||||
|
return details::uniform01() * 2 - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
double ziggurat_normal() noexcept
|
||||||
|
{
|
||||||
|
//The guard variable is just to prevent an infinte loop. It's not gonna matter in practice
|
||||||
|
for (std::size_t guard{}; guard != 16U; ++guard) {
|
||||||
|
const auto u = 2.0 * details::uniform01() - 1.0;
|
||||||
|
const auto i = details::random32() & 0x7FU;
|
||||||
|
/* first try the rectangular boxes */
|
||||||
|
if (std::abs(u) < details::R_table.at(i)) {
|
||||||
|
return u * details::X_table.at(i);
|
||||||
|
}
|
||||||
|
/* bottom box: sample from the tail */
|
||||||
|
if (i == 0) {
|
||||||
|
return details::normal_tail(u < 0);
|
||||||
|
}
|
||||||
|
/* is this a sample from the wedges? */
|
||||||
|
const auto x = u * details::X_table.at(i);
|
||||||
|
const auto f0 = std::exp(-0.5 * (sq(details::X_table.at(i)) - sq(x)));
|
||||||
|
const auto f1 = std::exp(-0.5 * (sq(details::X_table.at(i + 1)) - sq(x)));
|
||||||
|
if (f1 + details::uniform01() * (f0 - f1) < 1.0) {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
RAYCHEL_ASSERT_NOT_REACHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace Raychel
|
||||||
389
src/Render/Denoise.cpp
Normal file
389
src/Render/Denoise.cpp
Normal file
@@ -0,0 +1,389 @@
|
|||||||
|
/**
|
||||||
|
* \file Denoise.cpp
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Implementation of the Ray Histogram Fusion algorithm by Mauricio Delbracio et al.
|
||||||
|
* \date 2022-05-02
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Raychel/Render/Denoise.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <functional>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
static std::size_t to_index(std::size_t x, std::size_t y, std::size_t image_width)
|
||||||
|
{
|
||||||
|
return x + (y * image_width);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::size_t safe_sub(std::size_t a, std::size_t b)
|
||||||
|
{
|
||||||
|
if (b > a) {
|
||||||
|
return 0U;
|
||||||
|
}
|
||||||
|
return a - b;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t NumBins>
|
||||||
|
static double chi_squared_distance(const std::array<double, NumBins>& a, const std::array<double, NumBins>& b)
|
||||||
|
{
|
||||||
|
double sum{};
|
||||||
|
double num_nonempty_bins{};
|
||||||
|
|
||||||
|
for (std::size_t i{}; i != NumBins; ++i) {
|
||||||
|
const auto divisor = a[i] + b[i];
|
||||||
|
if (divisor != 0.0) {
|
||||||
|
sum += sq(a[i] - b[i]) / divisor;
|
||||||
|
++num_nonempty_bins;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (num_nonempty_bins == 0.0) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return sum / num_nonempty_bins;
|
||||||
|
}
|
||||||
|
|
||||||
|
template <std::size_t NumBins>
|
||||||
|
static auto chi_squared_distance(const RayHistogram<NumBins>& a, const RayHistogram<NumBins>& b)
|
||||||
|
{
|
||||||
|
//TODO: process the three channels in one loop
|
||||||
|
return Tuple{
|
||||||
|
chi_squared_distance(a.red_channel(), b.red_channel()),
|
||||||
|
chi_squared_distance(a.green_channel(), b.green_channel()),
|
||||||
|
chi_squared_distance(a.blue_channel(), b.blue_channel())};
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SearchWindow
|
||||||
|
{
|
||||||
|
[[nodiscard]] std::size_t width() const noexcept
|
||||||
|
{
|
||||||
|
return end_x - start_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::size_t height() const noexcept
|
||||||
|
{
|
||||||
|
return end_y - start_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] std::size_t area() const noexcept
|
||||||
|
{
|
||||||
|
return width() * height();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::size_t start_x{}, start_y{}, end_x{}, end_y{};
|
||||||
|
};
|
||||||
|
|
||||||
|
using Patch = SearchWindow;
|
||||||
|
|
||||||
|
static SearchWindow
|
||||||
|
search_window_for_pixel(std::size_t x, std::size_t y, Size2D image_size, std::size_t half_search_window_size) noexcept
|
||||||
|
{
|
||||||
|
const auto start_x = safe_sub(x, half_search_window_size);
|
||||||
|
const auto start_y = safe_sub(y, half_search_window_size);
|
||||||
|
|
||||||
|
const auto end_x = std::min(x + half_search_window_size, image_size.x());
|
||||||
|
const auto end_y = std::min(y + half_search_window_size, image_size.y());
|
||||||
|
|
||||||
|
return {start_x, start_y, end_x, end_y};
|
||||||
|
}
|
||||||
|
|
||||||
|
static Patch patch_for_pixel(std::size_t x, std::size_t y, Size2D image_size, std::size_t half_patch_size) noexcept
|
||||||
|
{
|
||||||
|
return search_window_for_pixel(x, y, image_size, half_patch_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::vector<color> get_denoised_patch_with_search_window(
|
||||||
|
const SearchWindow& search_window, const Patch& this_patch, const std::vector<FatPixel>& input_pixels,
|
||||||
|
const Size2D image_size, const DenoisingOptions& options) noexcept
|
||||||
|
{
|
||||||
|
std::vector<Tuple<double, 3>> c{this_patch.area()};
|
||||||
|
std::vector<color> V(this_patch.area());
|
||||||
|
|
||||||
|
for (auto search_y = search_window.start_y; search_y != search_window.end_y; ++search_y) {
|
||||||
|
for (auto search_x = search_window.start_x; search_x != search_window.end_x; ++search_x) {
|
||||||
|
|
||||||
|
const auto& other_patch = patch_for_pixel(search_x, search_y, image_size, options.half_patch_size);
|
||||||
|
|
||||||
|
for (auto this_y = this_patch.start_y; this_y != this_patch.end_y; ++this_y) {
|
||||||
|
for (auto this_x = this_patch.start_x; this_x != this_patch.end_x; ++this_x) {
|
||||||
|
const auto index_in_image = to_index(this_x, this_y, image_size.x());
|
||||||
|
const auto& this_pixel = input_pixels.at(index_in_image);
|
||||||
|
const auto index_in_patch =
|
||||||
|
to_index(this_x - this_patch.start_x, this_y - this_patch.start_y, this_patch.width());
|
||||||
|
|
||||||
|
for (auto other_y = other_patch.start_y; other_y != other_patch.end_y; ++other_y) {
|
||||||
|
for (auto other_x = other_patch.start_x; other_x != other_patch.end_x; ++other_x) {
|
||||||
|
|
||||||
|
const auto& other_pixel = input_pixels.at(to_index(other_x, other_y, image_size.x()));
|
||||||
|
const auto d = chi_squared_distance(this_pixel.histogram, other_pixel.histogram);
|
||||||
|
|
||||||
|
for (std::size_t i{}; i != 3U; ++i) {
|
||||||
|
if (d[i] < options.distance_threshold) {
|
||||||
|
V.at(index_in_patch)[i] += other_pixel.noisy_color[i];
|
||||||
|
++c.at(index_in_patch)[i];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::size_t i{}; i != V.size(); ++i) {
|
||||||
|
for (std::size_t j{}; j != 3U; ++j) {
|
||||||
|
if (c.at(i)[j] != 0) {
|
||||||
|
V.at(i)[j] /= c.at(i)[j];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return V;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void denoise_part(
|
||||||
|
std::vector<color>& output, Size2D begin, Size2D end, const std::vector<FatPixel>& input_pixels, Size2D image_size,
|
||||||
|
DenoisingOptions options) noexcept
|
||||||
|
{
|
||||||
|
std::vector<double> N(input_pixels.size());
|
||||||
|
|
||||||
|
for (auto y = begin.y(); y != end.y(); ++y) {
|
||||||
|
for (auto x = begin.x(); x != end.x(); ++x) {
|
||||||
|
const auto this_patch = patch_for_pixel(x, y, image_size, options.half_patch_size);
|
||||||
|
const auto search_window = search_window_for_pixel(x, y, image_size, options.half_search_window_size);
|
||||||
|
|
||||||
|
const auto V =
|
||||||
|
get_denoised_patch_with_search_window(search_window, this_patch, input_pixels, image_size, options);
|
||||||
|
|
||||||
|
for (auto patch_y = this_patch.start_y; patch_y != this_patch.end_y; ++patch_y) {
|
||||||
|
for (auto patch_x = this_patch.start_x; patch_x != this_patch.end_x; ++patch_x) {
|
||||||
|
const auto index_in_patch =
|
||||||
|
to_index(patch_x - this_patch.start_x, patch_y - this_patch.start_y, this_patch.width());
|
||||||
|
const auto index_in_image = to_index(patch_x, patch_y, image_size.x());
|
||||||
|
|
||||||
|
++N.at(index_in_image);
|
||||||
|
|
||||||
|
auto& p = output.at(index_in_image);
|
||||||
|
p += (V.at(index_in_patch) - p) / N.at(index_in_image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template <
|
||||||
|
typename Pixel, std::invocable<Pixel, Pixel> Add = std::plus<Pixel>,
|
||||||
|
std::invocable<Pixel, std::size_t> Divide = std::divides<void>>
|
||||||
|
details::BasicFramebuffer<Pixel> gaussian_subsample(
|
||||||
|
const details::BasicFramebuffer<Pixel>& input_pixels, std::size_t scale, Add&& adder = {}, Divide&& divider = {})
|
||||||
|
{
|
||||||
|
//Because we use operator<<, specifying a scale larger than the number of bits in an unsigned int would result in UB.
|
||||||
|
RAYCHEL_ASSERT(scale < (sizeof(unsigned int) * 8));
|
||||||
|
|
||||||
|
if (scale == 0U) {
|
||||||
|
return input_pixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto half_sample_window_size = (1U << (scale - 1));
|
||||||
|
const auto scaling_factor = std::pow(0.5, static_cast<double>(scale));
|
||||||
|
const auto pixel_step = 1U << scale;
|
||||||
|
const auto scaled_size = input_pixels.size * scaling_factor;
|
||||||
|
|
||||||
|
if (scaled_size == Size2D{}) {
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<Pixel> output_pixels{scaled_size.x() * scaled_size.y(), Pixel{}};
|
||||||
|
|
||||||
|
for (std::size_t y{}; y != scaled_size.y(); ++y) {
|
||||||
|
for (std::size_t x{}; x != scaled_size.x(); ++x) {
|
||||||
|
const auto sample_patch =
|
||||||
|
patch_for_pixel(x * pixel_step, y * pixel_step, input_pixels.size, half_sample_window_size);
|
||||||
|
|
||||||
|
std::size_t num_samples{};
|
||||||
|
Pixel output_pixel{};
|
||||||
|
|
||||||
|
for (auto patch_y = sample_patch.start_y; patch_y != sample_patch.end_y; ++patch_y) {
|
||||||
|
for (auto patch_x = sample_patch.start_x; patch_x != sample_patch.end_x; ++patch_x) {
|
||||||
|
++num_samples;
|
||||||
|
const auto& sample_pixel = input_pixels.pixel_data.at(to_index(patch_x, patch_y, input_pixels.size.x()));
|
||||||
|
|
||||||
|
output_pixel = adder(output_pixel, sample_pixel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
output_pixel = divider(output_pixel, num_samples);
|
||||||
|
|
||||||
|
output_pixels.at(to_index(x, y, scaled_size.x())) = output_pixel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return {scaled_size, output_pixels};
|
||||||
|
}
|
||||||
|
|
||||||
|
static void add_scaled_part(
|
||||||
|
const Raychel::Framebuffer& scaled_input, std::vector<Raychel::color>& output, std::size_t scale, std::size_t num_scales)
|
||||||
|
{
|
||||||
|
using namespace Raychel;
|
||||||
|
RAYCHEL_ASSERT(scale < (sizeof(unsigned int) * 8));
|
||||||
|
|
||||||
|
if (scale == 0U) {
|
||||||
|
std::copy(scaled_input.pixel_data.begin(), scaled_input.pixel_data.end(), output.begin());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto scaling_factor = 1U << scale;
|
||||||
|
const auto step_size = std::pow(0.5, static_cast<double>(scale));
|
||||||
|
const auto correction_factor = 0.5 / static_cast<double>(num_scales);
|
||||||
|
|
||||||
|
for (std::size_t y{}; y != scaled_input.size.y(); ++y) {
|
||||||
|
for (std::size_t x{}; x != scaled_input.size.x(); ++x) {
|
||||||
|
const auto next_x = std::min(x + 1U, scaled_input.size.x() - 1U);
|
||||||
|
const auto next_y = std::min(y + 1U, scaled_input.size.y() - 1U);
|
||||||
|
|
||||||
|
const auto top_left = scaled_input.at(x, y);
|
||||||
|
const auto top_right = scaled_input.at(next_x, y);
|
||||||
|
const auto bottom_left = scaled_input.at(x, next_y);
|
||||||
|
const auto bottom_right = scaled_input.at(next_x, next_y);
|
||||||
|
|
||||||
|
const auto large_x = x * scaling_factor;
|
||||||
|
const auto large_y = y * scaling_factor;
|
||||||
|
|
||||||
|
for (std::size_t part_y{}; part_y != scaling_factor; ++part_y) {
|
||||||
|
for (std::size_t part_x{}; part_x != scaling_factor; ++part_x) {
|
||||||
|
const auto relative_x = static_cast<double>(part_x) * step_size;
|
||||||
|
const auto relative_y = static_cast<double>(part_y) * step_size;
|
||||||
|
|
||||||
|
const auto right_weight = std::fmod(relative_x * static_cast<double>(scaling_factor), 1.0);
|
||||||
|
const auto left_weight = 1.0 - right_weight;
|
||||||
|
const auto bottom_weight = std::fmod(relative_y * static_cast<double>(scaling_factor), 1.0);
|
||||||
|
const auto top_weight = 1.0 - bottom_weight;
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
output.at(to_index(large_x + part_x, large_y + part_y, scaled_input.size.x() * scaling_factor)) += (
|
||||||
|
(top_left * (top_weight * left_weight)) +
|
||||||
|
(top_right * (top_weight * right_weight)) +
|
||||||
|
(bottom_left * (top_weight * left_weight)) +
|
||||||
|
(bottom_right * (bottom_weight * right_weight))
|
||||||
|
) * correction_factor;
|
||||||
|
// clang-format on
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void denoise_threaded(
|
||||||
|
std::vector<color>& output, const FatFramebuffer& input_pixels, unsigned int num_threads,
|
||||||
|
DenoisingOptions options) noexcept
|
||||||
|
{
|
||||||
|
constexpr Size2D patch_size{128, 128};
|
||||||
|
|
||||||
|
std::vector<std::jthread> thread_pool{};
|
||||||
|
thread_pool.reserve(num_threads);
|
||||||
|
|
||||||
|
std::atomic_size_t current_patch_index{0U};
|
||||||
|
|
||||||
|
const auto num_patches_x =
|
||||||
|
static_cast<std::size_t>(std::ceil(static_cast<double>(input_pixels.size.x()) / static_cast<double>(patch_size.x())));
|
||||||
|
const auto num_patches_y =
|
||||||
|
static_cast<std::size_t>(std::ceil(static_cast<double>(input_pixels.size.y()) / static_cast<double>(patch_size.y())));
|
||||||
|
const auto max_patch_index = num_patches_x * num_patches_y - 1U;
|
||||||
|
|
||||||
|
for (std::size_t i{}; i != num_threads; ++i) {
|
||||||
|
thread_pool.emplace_back([&, i] {
|
||||||
|
std::size_t patch_index{};
|
||||||
|
do {
|
||||||
|
patch_index = current_patch_index.fetch_add(1U);
|
||||||
|
|
||||||
|
const auto x_index = patch_index % num_patches_x;
|
||||||
|
const auto y_index = patch_index / num_patches_x;
|
||||||
|
|
||||||
|
const Size2D patch_begin{
|
||||||
|
std::min(x_index * patch_size.x(), input_pixels.size.x()),
|
||||||
|
std::min(y_index * patch_size.y(), input_pixels.size.y())};
|
||||||
|
const Size2D patch_end{
|
||||||
|
std::min(patch_begin.x() + patch_size.x(), input_pixels.size.x()),
|
||||||
|
std::min(patch_begin.y() + patch_size.y(), input_pixels.size.y())};
|
||||||
|
|
||||||
|
Logger::debug("Thread ", i, " got patch ", patch_index, " from ", patch_begin, " to ", patch_end, '\n');
|
||||||
|
|
||||||
|
denoise_part(output, patch_begin, patch_end, input_pixels.pixel_data, input_pixels.size, options);
|
||||||
|
} while (patch_index < max_patch_index);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void denoise_internal(std::vector<color>& output, const FatFramebuffer& input_pixels, DenoisingOptions options) noexcept
|
||||||
|
{
|
||||||
|
const auto num_threads = std::thread::hardware_concurrency();
|
||||||
|
|
||||||
|
if (num_threads == 0U) {
|
||||||
|
denoise_part(output, Size2D{}, input_pixels.size, input_pixels.pixel_data, input_pixels.size, options);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
denoise_threaded(output, input_pixels, num_threads, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
Framebuffer denoise_single_scale(const FatFramebuffer& input_pixels, DenoisingOptions options) noexcept
|
||||||
|
{
|
||||||
|
std::vector<color> output(input_pixels.pixel_data.size());
|
||||||
|
|
||||||
|
denoise_internal(output, input_pixels, options);
|
||||||
|
|
||||||
|
return {input_pixels.size, std::move(output)};
|
||||||
|
}
|
||||||
|
|
||||||
|
Framebuffer denoise_multiscale(const FatFramebuffer& input_pixels, DenoisingOptions options) noexcept
|
||||||
|
{
|
||||||
|
if (options.num_scales == 1U) {
|
||||||
|
return denoise_single_scale(input_pixels, options);
|
||||||
|
}
|
||||||
|
|
||||||
|
RAYCHEL_TODO("Multiscale denoising");
|
||||||
|
|
||||||
|
auto scale = options.num_scales - 1U;
|
||||||
|
|
||||||
|
std::vector<color> u_old{};
|
||||||
|
|
||||||
|
while (scale != 0) {
|
||||||
|
//uS = Ds(u)
|
||||||
|
const auto scaled_input = gaussian_subsample(input_pixels, scale);
|
||||||
|
//TODO: verify that the number of samples is still the same
|
||||||
|
|
||||||
|
std::vector<color> scaled_output{scaled_input.pixel_data.size(), color{}};
|
||||||
|
denoise_internal(scaled_output, scaled_input, options);
|
||||||
|
|
||||||
|
add_scaled_part({input_pixels.size, scaled_output}, u_old, scale, options.num_scales);
|
||||||
|
|
||||||
|
--scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {input_pixels.size, std::move(u_old)};
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Raychel
|
||||||
260
src/Render/RenderUtils.cpp
Normal file
260
src/Render/RenderUtils.cpp
Normal file
@@ -0,0 +1,260 @@
|
|||||||
|
/**
|
||||||
|
* \file RenderUtils.cpp
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Implementation file for RenderUtils class
|
||||||
|
* \date 2022-04-12
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Raychel/Render/RenderUtils.h"
|
||||||
|
#include <iterator>
|
||||||
|
#include "Raychel/Core/Raymarch.h"
|
||||||
|
#include "Raychel/Core/ZigguratNormal.h"
|
||||||
|
|
||||||
|
#include "RaychelMath/vector.h"
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
color get_shaded_color(const RenderData& data) noexcept
|
||||||
|
{
|
||||||
|
const auto get_background_color = [&] {
|
||||||
|
if (data.state.get_background) {
|
||||||
|
return data.state.get_background(data);
|
||||||
|
}
|
||||||
|
return color{data.direction.x(), data.direction.y(), data.direction.z()};
|
||||||
|
};
|
||||||
|
|
||||||
|
if (data.recursion_depth >= data.state.options.max_recursion_depth) {
|
||||||
|
return get_background_color();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& [surfaces, materials, _, options] = data.state;
|
||||||
|
const auto result = raymarch(
|
||||||
|
data.origin, data.direction, surfaces, {options.max_ray_steps, options.max_ray_depth, options.surface_epsilon});
|
||||||
|
|
||||||
|
if (result.hit_index == no_hit) {
|
||||||
|
return get_background_color();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto surface_normal = get_normal(result.point, surfaces[result.hit_index], options.normal_epsilon);
|
||||||
|
RAYCHEL_ASSERT(equivalent(mag_sq(surface_normal), 1.0));
|
||||||
|
|
||||||
|
return data.state.materials[result.hit_index].get_surface_color(
|
||||||
|
{.position = result.point + surface_normal * options.shading_epsilon,
|
||||||
|
.normal = surface_normal,
|
||||||
|
.incoming_direction = data.direction,
|
||||||
|
.state = data.state,
|
||||||
|
.recursion_depth = data.recursion_depth + 1U});
|
||||||
|
}
|
||||||
|
|
||||||
|
static vec3 get_random_direction_on_weighted_hemisphere(const vec3& normal) noexcept
|
||||||
|
{
|
||||||
|
vec3 test{};
|
||||||
|
do {
|
||||||
|
test = normal + vec3{ziggurat_normal(), ziggurat_normal(), ziggurat_normal()};
|
||||||
|
} while (test == vec3{});
|
||||||
|
|
||||||
|
test = normalize(test);
|
||||||
|
|
||||||
|
if (dot(test, normal) < 0.0) {
|
||||||
|
test *= -1;
|
||||||
|
}
|
||||||
|
return test;
|
||||||
|
}
|
||||||
|
|
||||||
|
color get_diffuse_lighting(const ShadingData& data) noexcept
|
||||||
|
{
|
||||||
|
const auto direction = get_random_direction_on_weighted_hemisphere(data.normal);
|
||||||
|
return get_shaded_color(RenderData{
|
||||||
|
.origin = data.position,
|
||||||
|
.direction = direction,
|
||||||
|
.state = data.state,
|
||||||
|
.recursion_depth = std::max(
|
||||||
|
data.state.options.max_recursion_depth - data.state.options.max_lighting_bounces, data.recursion_depth)}) *
|
||||||
|
dot(direction, data.normal);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static double fresnel(const vec3& direction, vec3 normal, double interior_ior, double exterior_ior)
|
||||||
|
{
|
||||||
|
RAYCHEL_ASSERT(equivalent(mag_sq(direction), 1.0));
|
||||||
|
RAYCHEL_ASSERT(equivalent(mag_sq(normal), 1.0));
|
||||||
|
|
||||||
|
auto cosi = dot(direction, normal);
|
||||||
|
|
||||||
|
if (cosi > 0.0) {
|
||||||
|
std::swap(interior_ior, exterior_ior);
|
||||||
|
normal *= -1.0;
|
||||||
|
} else {
|
||||||
|
cosi *= -1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto etai = exterior_ior;
|
||||||
|
const auto etat = interior_ior;
|
||||||
|
|
||||||
|
const auto sint = (exterior_ior / interior_ior) * std::sqrt(std::max(0.0, 1.0 - sq(cosi)));
|
||||||
|
|
||||||
|
if (sint >= 1.0) {
|
||||||
|
return 1.0; //TIR
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto cost = std::sqrt(1.0 - sq(sint));
|
||||||
|
|
||||||
|
const auto Rs = ((etat * cosi) - (etai * cost)) / ((etat * cosi) + (etai * cost));
|
||||||
|
const auto Rp = ((etai * cosi) - (etat * cost)) / ((etai * cosi) + (etat * cost));
|
||||||
|
|
||||||
|
return std::clamp((sq(Rs) + sq(Rp)) * 0.5, 0.0, 1.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static vec3 refract(const vec3& direction, vec3 normal, double interior_ior, double exterior_ior)
|
||||||
|
{
|
||||||
|
RAYCHEL_ASSERT(equivalent(mag_sq(direction), 1.0));
|
||||||
|
RAYCHEL_ASSERT(equivalent(mag_sq(normal), 1.0));
|
||||||
|
|
||||||
|
auto cosi = dot(direction, normal);
|
||||||
|
|
||||||
|
if (cosi > 0.0) {
|
||||||
|
std::swap(interior_ior, exterior_ior);
|
||||||
|
normal *= -1.0;
|
||||||
|
} else {
|
||||||
|
cosi *= -1.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto eta = exterior_ior / interior_ior;
|
||||||
|
|
||||||
|
const auto k = 1.0 - sq(eta) * (1.0 - sq(cosi));
|
||||||
|
if (k < 0.0) {
|
||||||
|
return vec3{}; //TIR
|
||||||
|
}
|
||||||
|
return normalize((direction * eta) + (normal * (eta * cosi - std::sqrt(k))));
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::size_t get_surrounding_object(const std::vector<SDFContainer>& surfaces, const vec3& point) noexcept
|
||||||
|
{
|
||||||
|
std::size_t hit_index{no_hit};
|
||||||
|
double min_distance{-1e9};
|
||||||
|
for (std::size_t i{}; i != surfaces.size(); ++i) {
|
||||||
|
const auto object_distance = surfaces[i].evaluate(point);
|
||||||
|
if (object_distance < 0.0 && object_distance > min_distance) {
|
||||||
|
min_distance = object_distance;
|
||||||
|
hit_index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return hit_index;
|
||||||
|
}
|
||||||
|
|
||||||
|
static double get_surrounding_ior(
|
||||||
|
const vec3& surface_point, const std::vector<SDFContainer>& surfaces, const std::vector<MaterialContainer>& materials)
|
||||||
|
{
|
||||||
|
const auto closest_object_index = get_surrounding_object(surfaces, surface_point);
|
||||||
|
if (closest_object_index == no_hit) {
|
||||||
|
return 1.0;
|
||||||
|
}
|
||||||
|
return materials[closest_object_index].get_material_ior();
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static color get_reflective_component(const RefractionData& data, double reflection_factor) noexcept
|
||||||
|
{
|
||||||
|
if (reflection_factor < 0.01) {
|
||||||
|
return color{};
|
||||||
|
}
|
||||||
|
return get_shaded_color(
|
||||||
|
{.origin = data.surface_point,
|
||||||
|
.direction = reflect(data.incoming_direction, data.normal),
|
||||||
|
.state = data.state,
|
||||||
|
.recursion_depth = data.recursion_depth}) *
|
||||||
|
reflection_factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static color get_refractive_component(const RefractionData& data, double ior_factor, double outer_ior) noexcept
|
||||||
|
{
|
||||||
|
const auto trace_direction = refract(data.incoming_direction, data.normal, data.material_ior * ior_factor, outer_ior);
|
||||||
|
const auto trace_origin = data.surface_point - ((2.0 * data.state.options.shading_epsilon) * data.normal);
|
||||||
|
|
||||||
|
if (trace_direction == vec3{}) {
|
||||||
|
Logger::warn("Did not expect to reach ", __FILE__, ':', __LINE__, '\n');
|
||||||
|
return color{0};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto& options = data.state.options;
|
||||||
|
const auto result = raymarch(
|
||||||
|
trace_origin,
|
||||||
|
trace_direction,
|
||||||
|
data.state.surfaces,
|
||||||
|
{options.max_ray_steps, options.max_ray_depth, options.surface_epsilon});
|
||||||
|
|
||||||
|
// RAYCHEL_ASSERT(result.hit_index != no_hit)
|
||||||
|
if (result.hit_index == no_hit) {
|
||||||
|
return color{0};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto opposite_normal = get_normal(result.point, data.state.surfaces[result.hit_index], options.normal_epsilon);
|
||||||
|
const auto opposite_shading_point = result.point + (opposite_normal * options.shading_epsilon);
|
||||||
|
const auto out_direction = refract(
|
||||||
|
trace_direction,
|
||||||
|
opposite_normal,
|
||||||
|
data.material_ior,
|
||||||
|
get_surrounding_ior(data.surface_point, data.state.surfaces, data.state.materials));
|
||||||
|
|
||||||
|
//Total internal reflection
|
||||||
|
if (out_direction == vec3{}) {
|
||||||
|
return get_shaded_color(RenderData{
|
||||||
|
.origin = opposite_shading_point,
|
||||||
|
.direction = reflect(trace_direction, opposite_normal),
|
||||||
|
.state = data.state,
|
||||||
|
.recursion_depth = data.recursion_depth});
|
||||||
|
}
|
||||||
|
|
||||||
|
return get_shaded_color(RenderData{
|
||||||
|
.origin = opposite_shading_point,
|
||||||
|
.direction = out_direction,
|
||||||
|
.state = data.state,
|
||||||
|
.recursion_depth = data.recursion_depth});
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static color get_refractive_component(const RefractionData& data, double refraction_factor) noexcept
|
||||||
|
{
|
||||||
|
if (refraction_factor < 0.01) {
|
||||||
|
return color{};
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto outer_ior = get_surrounding_ior(data.surface_point, data.state.surfaces, data.state.materials);
|
||||||
|
if (data.ior_variation == 0.0) {
|
||||||
|
return get_refractive_component(data, 1.0, outer_ior) * refraction_factor;
|
||||||
|
}
|
||||||
|
return color{
|
||||||
|
get_refractive_component(data, 1.0 - data.ior_variation, outer_ior).r(),
|
||||||
|
get_refractive_component(data, 1, outer_ior).g(),
|
||||||
|
get_refractive_component(data, 1.0 + data.ior_variation, outer_ior).b(),
|
||||||
|
} *
|
||||||
|
refraction_factor;
|
||||||
|
}
|
||||||
|
|
||||||
|
color get_refraction(const RefractionData& data) noexcept
|
||||||
|
{
|
||||||
|
const auto outer_ior = get_surrounding_ior(data.surface_point, data.state.surfaces, data.state.materials);
|
||||||
|
|
||||||
|
const auto reflection_factor = fresnel(data.incoming_direction, data.normal, data.material_ior, outer_ior);
|
||||||
|
|
||||||
|
return get_reflective_component(data, reflection_factor) + get_refractive_component(data, 1.0 - reflection_factor);
|
||||||
|
}
|
||||||
|
} // namespace Raychel
|
||||||
234
src/Render/Renderer.cpp
Normal file
234
src/Render/Renderer.cpp
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
/**
|
||||||
|
* \file Renderer.cpp
|
||||||
|
* \author Weckyy702 (weckyy702@gmail.com)
|
||||||
|
* \brief Implementation file for Renderer class
|
||||||
|
* \date 2022-04-11
|
||||||
|
*
|
||||||
|
* MIT License
|
||||||
|
* Copyright (c) [2022] [Weckyy702 (weckyy702@gmail.com | https://github.com/Weckyy702)]
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "Raychel/Render/Renderer.h"
|
||||||
|
#include "Raychel/Core/Scene.h"
|
||||||
|
#include "Raychel/Core/ZigguratNormal.h"
|
||||||
|
#include "Raychel/Render/FatPixel.h"
|
||||||
|
#include "Raychel/Render/RenderUtils.h"
|
||||||
|
|
||||||
|
#include "RaychelCore/ScopedTimer.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <atomic>
|
||||||
|
#include <execution>
|
||||||
|
#include <fstream>
|
||||||
|
#include <map>
|
||||||
|
#include <random>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace Raychel {
|
||||||
|
|
||||||
|
struct CacheComparator
|
||||||
|
{
|
||||||
|
constexpr bool operator()(const auto& lhs, const auto& rhs) const
|
||||||
|
{
|
||||||
|
if (lhs.first < rhs.first) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return std::lexicographical_compare(lhs.second.begin(), lhs.second.end(), rhs.second.begin(), rhs.second.end());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct CacheResult
|
||||||
|
{
|
||||||
|
std::vector<vec3>& data;
|
||||||
|
bool data_was_in_cache{false};
|
||||||
|
};
|
||||||
|
|
||||||
|
static CacheResult get_cached_ray_data(const Camera& camera, const RenderOptions options) noexcept
|
||||||
|
{
|
||||||
|
using Key = std::pair<double, Size2D>;
|
||||||
|
|
||||||
|
static std::map<Key, std::vector<vec3>, CacheComparator> ray_cache{};
|
||||||
|
|
||||||
|
const Key cache_key{camera.zoom, options.output_size};
|
||||||
|
|
||||||
|
if (const auto it = ray_cache.find(cache_key); it != ray_cache.end()) {
|
||||||
|
Logger::debug("Found cached ray data for zoom=", camera.zoom, ", size=", options.output_size, '\n');
|
||||||
|
return {it->second, true};
|
||||||
|
}
|
||||||
|
|
||||||
|
Logger::debug("Cache not populated. Generating rays for ", options.output_size, " plane\n");
|
||||||
|
return {ray_cache[cache_key], false};
|
||||||
|
}
|
||||||
|
|
||||||
|
static void generate_rays_internal(std::vector<vec3>& rays, const Camera& camera, const RenderOptions options) noexcept
|
||||||
|
{
|
||||||
|
using vec2 = basic_vec2<double>;
|
||||||
|
constexpr vec3 right{1, 0, 0};
|
||||||
|
constexpr vec3 up{0, 1, 0};
|
||||||
|
constexpr vec3 forward{0, 0, 1};
|
||||||
|
|
||||||
|
const auto get_relative_coordinates = [&options](const vec2 pixel_coordinate) {
|
||||||
|
const auto [plane_x, plane_y] = options.output_size;
|
||||||
|
|
||||||
|
const auto aspect_ratio = static_cast<double>(plane_x) / static_cast<double>(plane_y);
|
||||||
|
|
||||||
|
const auto raw_relative_x = pixel_coordinate.x() / static_cast<double>(plane_x) - 0.5;
|
||||||
|
const auto raw_relative_y = pixel_coordinate.y() / static_cast<double>(plane_y) - 0.5;
|
||||||
|
if (aspect_ratio > 1.0) {
|
||||||
|
return vec2{raw_relative_x * aspect_ratio, raw_relative_y};
|
||||||
|
}
|
||||||
|
return vec2{raw_relative_x, raw_relative_y / aspect_ratio};
|
||||||
|
};
|
||||||
|
|
||||||
|
rays.reserve(options.output_size.y() * options.output_size.x());
|
||||||
|
|
||||||
|
for (std::size_t y{options.output_size.y()}; y != 0U; --y) {
|
||||||
|
for (std::size_t x{}; x != options.output_size.x(); ++x) {
|
||||||
|
const auto [relative_x, relative_y] = get_relative_coordinates(vec2{x, y});
|
||||||
|
|
||||||
|
// clang-format off
|
||||||
|
const auto direction = normalize( (right * relative_x) +
|
||||||
|
(up * relative_y) +
|
||||||
|
(forward * camera.zoom));
|
||||||
|
// clang-format on
|
||||||
|
|
||||||
|
rays.emplace_back(direction);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static const std::vector<vec3>& generate_rays(const Camera& camera, const RenderOptions& options) noexcept
|
||||||
|
{
|
||||||
|
const auto [rays, was_cached] = get_cached_ray_data(camera, options);
|
||||||
|
|
||||||
|
if (was_cached) {
|
||||||
|
return rays;
|
||||||
|
}
|
||||||
|
|
||||||
|
generate_rays_internal(rays, camera, options);
|
||||||
|
|
||||||
|
return rays;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static vec3 get_direction_with_aa(const vec3& direction, const Size2D& output_size) noexcept
|
||||||
|
{
|
||||||
|
const vec3 jitter{
|
||||||
|
uniform_random() / static_cast<double>(output_size.x()), uniform_random() / static_cast<double>(output_size.y())};
|
||||||
|
|
||||||
|
return normalize(direction + jitter);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[maybe_unused]] static void
|
||||||
|
write_framebuffer(const std::string& file_name, Size2D size, const std::vector<FatPixel>& pixel_data) noexcept
|
||||||
|
{
|
||||||
|
std::ofstream output_image{file_name};
|
||||||
|
if (!output_image) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
output_image << "P6\n" << size.x() << ' ' << size.y() << '\n' << "255\n";
|
||||||
|
for (const auto& pixel : pixel_data) {
|
||||||
|
const auto pixel_rgb = convert_color<std::uint8_t>(pixel.noisy_color);
|
||||||
|
if (!output_image.write(reinterpret_cast<const char*>(&pixel_rgb), sizeof(pixel_rgb)).good()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static std::vector<FatPixel>
|
||||||
|
render_fat_pixels(const Scene& scene, const Camera& camera, const RenderOptions& options) noexcept
|
||||||
|
{
|
||||||
|
const auto& rays = generate_rays(camera, options);
|
||||||
|
std::vector<FatPixel> fat_pixels{rays.size()};
|
||||||
|
|
||||||
|
ScopedTimer<std::chrono::milliseconds> timer{"Render time"};
|
||||||
|
|
||||||
|
std::atomic_size_t pixels_rendered{};
|
||||||
|
|
||||||
|
std::jthread notifier{[&pixels_rendered, pixel_count = rays.size(), &fat_pixels, options] {
|
||||||
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
[[maybe_unused]] std::chrono::high_resolution_clock::time_point last_check_point{};
|
||||||
|
std::size_t pixels_so_far{};
|
||||||
|
do {
|
||||||
|
const auto previous = pixels_so_far;
|
||||||
|
pixels_so_far = pixels_rendered.load();
|
||||||
|
|
||||||
|
const auto percentage = (pixels_so_far * 100) / pixel_count; //beware of overflow *spooky noises*
|
||||||
|
const auto pixels_per_second = (pixels_so_far - previous) * 1'000 / 30;
|
||||||
|
|
||||||
|
Logger::info(
|
||||||
|
"Rendered ",
|
||||||
|
pixels_so_far,
|
||||||
|
"/",
|
||||||
|
pixel_count,
|
||||||
|
" pixels (",
|
||||||
|
percentage,
|
||||||
|
"%) ~",
|
||||||
|
pixels_per_second,
|
||||||
|
" pixels per second \r"); //big brain padding
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
const auto now = std::chrono::high_resolution_clock::now();
|
||||||
|
if (duration_cast<std::chrono::seconds>(now-last_check_point).count() >= 1) {
|
||||||
|
last_check_point = now;
|
||||||
|
write_framebuffer("progress.ppm", options.output_size, fat_pixels);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
std::this_thread::sleep_for(30ms);
|
||||||
|
} while (pixels_so_far != pixel_count);
|
||||||
|
Logger::log('\n');
|
||||||
|
}};
|
||||||
|
|
||||||
|
const RenderState state{scene.objects(), scene.materials(), scene.background_function(), options};
|
||||||
|
|
||||||
|
std::transform(std::execution::par, rays.begin(), rays.end(), fat_pixels.begin(), [&](const vec3& ray_direction) {
|
||||||
|
const auto get_direction = [&] {
|
||||||
|
if (options.do_aa) {
|
||||||
|
return get_direction_with_aa(ray_direction, options.output_size) * camera.transform.rotation;
|
||||||
|
}
|
||||||
|
return ray_direction * camera.transform.rotation;
|
||||||
|
};
|
||||||
|
|
||||||
|
FatPixel::Histogram histogram;
|
||||||
|
color pixel_color{};
|
||||||
|
|
||||||
|
for (std::size_t i{}; i != options.samples_per_pixel; ++i) {
|
||||||
|
const auto sample_direction = get_direction();
|
||||||
|
const auto sample = get_shaded_color(RenderData{camera.transform.offset, sample_direction, state, 0U});
|
||||||
|
histogram.add_sample(sample);
|
||||||
|
pixel_color += (sample / options.samples_per_pixel);
|
||||||
|
}
|
||||||
|
|
||||||
|
++pixels_rendered;
|
||||||
|
|
||||||
|
return FatPixel{pixel_color, histogram};
|
||||||
|
});
|
||||||
|
|
||||||
|
return fat_pixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
FatFramebuffer render_scene(const Scene& scene, const Camera& camera, const RenderOptions& options) noexcept
|
||||||
|
{
|
||||||
|
return {options.output_size, render_fat_pixels(scene, camera, options)};
|
||||||
|
}
|
||||||
|
|
||||||
|
} //namespace Raychel
|
||||||
23
test/CMakeLists.txt
Normal file
23
test/CMakeLists.txt
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
if(NOT RAYCHEL_LOGGER_EXTERNAL)
|
||||||
|
find_package(RaychelLogger REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT RAYCHEL_CORE_EXTERNAL)
|
||||||
|
find_package(RaychelCore REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
if(NOT RAYCHEL_MATH_EXTERNAL)
|
||||||
|
find_package(RaychelMath REQUIRED)
|
||||||
|
endif()
|
||||||
|
|
||||||
|
file(GLOB RAYCHEL_TEST_SOURCES "*.test.cpp")
|
||||||
|
|
||||||
|
add_executable(Raychel_test
|
||||||
|
${RAYCHEL_TEST_SOURCES}
|
||||||
|
)
|
||||||
|
|
||||||
|
target_compile_features(Raychel_test PUBLIC cxx_std_20)
|
||||||
|
|
||||||
|
target_link_libraries(Raychel_test PUBLIC
|
||||||
|
Raychel
|
||||||
|
)
|
||||||
29
test/Ziggurat.test.cpp
Normal file
29
test/Ziggurat.test.cpp
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
#include "Raychel/Core/ZigguratNormal.h"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
int zig_main()
|
||||||
|
{
|
||||||
|
constexpr auto scale = 25.0;
|
||||||
|
constexpr std::size_t n{100'000'000};
|
||||||
|
constexpr auto expected0 = static_cast<std::size_t>(n * 0.04 / 100.0);
|
||||||
|
|
||||||
|
std::map<std::int32_t, std::size_t> hist{};
|
||||||
|
|
||||||
|
for (std::size_t i{}; i != n; ++i) {
|
||||||
|
++hist[static_cast<std::int32_t>(std::round(Raychel::ziggurat_normal() * scale))];
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& [value, count] : hist) {
|
||||||
|
const auto actual_value = static_cast<double>(value) / scale;
|
||||||
|
if (actual_value >= 0) {
|
||||||
|
std::cout << ' ';
|
||||||
|
}
|
||||||
|
std::cout << std::fixed << std::setprecision(3) << actual_value << ": " << std::string(count / expected0, '*') << ">\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
283
test/main.test.cpp
Normal file
283
test/main.test.cpp
Normal file
@@ -0,0 +1,283 @@
|
|||||||
|
#include "Raychel/Core/Deserialize.h"
|
||||||
|
#include "Raychel/Core/Raymarch.h"
|
||||||
|
#include "Raychel/Core/SDFBooleans.h"
|
||||||
|
#include "Raychel/Core/SDFModifiers.h"
|
||||||
|
#include "Raychel/Core/SDFPrimitives.h"
|
||||||
|
#include "Raychel/Core/SDFTransforms.h"
|
||||||
|
#include "Raychel/Core/Scene.h"
|
||||||
|
#include "Raychel/Core/Types.h"
|
||||||
|
#include "Raychel/Render/RenderUtils.h"
|
||||||
|
#include "Raychel/Render/Renderer.h"
|
||||||
|
|
||||||
|
#include "RaychelMath/vector.h"
|
||||||
|
|
||||||
|
#include <RaychelMath/Quaternion.h>
|
||||||
|
#include <RaychelMath/color.h>
|
||||||
|
#include <RaychelMath/constants.h>
|
||||||
|
#include <fstream>
|
||||||
|
#include <iomanip>
|
||||||
|
#include <iostream>
|
||||||
|
#include <sstream>
|
||||||
|
|
||||||
|
struct FlatMaterial
|
||||||
|
{
|
||||||
|
Raychel::color surface_color{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ReflectiveMaterial
|
||||||
|
{
|
||||||
|
Raychel::color reflectivity{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DiffuseMaterial
|
||||||
|
{
|
||||||
|
Raychel::color surface_color{};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct TransparentMaterial
|
||||||
|
{
|
||||||
|
Raychel::color transparency{};
|
||||||
|
double ior{1.0};
|
||||||
|
double ior_variation{.1};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct DebugMaterial
|
||||||
|
{};
|
||||||
|
|
||||||
|
template <>
|
||||||
|
struct Raychel::is_transparent_material<TransparentMaterial> : std::true_type
|
||||||
|
{};
|
||||||
|
|
||||||
|
Raychel::color get_surface_color(const FlatMaterial& material, const Raychel::ShadingData& /*unused*/) noexcept
|
||||||
|
{
|
||||||
|
return material.surface_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
Raychel::color get_surface_color(const ReflectiveMaterial& material, const Raychel::ShadingData& data) noexcept
|
||||||
|
{
|
||||||
|
return Raychel::get_shaded_color(Raychel::RenderData{
|
||||||
|
.origin = data.position,
|
||||||
|
.direction = reflect(data.incoming_direction, data.normal),
|
||||||
|
.state = data.state,
|
||||||
|
.recursion_depth = data.recursion_depth}) *
|
||||||
|
material.reflectivity;
|
||||||
|
}
|
||||||
|
|
||||||
|
Raychel::color get_surface_color(const DiffuseMaterial& material, const Raychel::ShadingData& data) noexcept
|
||||||
|
{
|
||||||
|
return Raychel::get_diffuse_lighting(data) * material.surface_color;
|
||||||
|
}
|
||||||
|
|
||||||
|
Raychel::color get_surface_color(const TransparentMaterial& material, const Raychel::ShadingData& data) noexcept
|
||||||
|
{
|
||||||
|
return Raychel::get_refraction(Raychel::RefractionData{
|
||||||
|
.surface_point = data.position,
|
||||||
|
.incoming_direction = data.incoming_direction,
|
||||||
|
.normal = data.normal,
|
||||||
|
.material_ior = material.ior,
|
||||||
|
.ior_variation = material.ior_variation,
|
||||||
|
.state = data.state,
|
||||||
|
.recursion_depth = data.recursion_depth}) *
|
||||||
|
material.transparency;
|
||||||
|
}
|
||||||
|
|
||||||
|
Raychel::color get_surface_color(const DebugMaterial& /*unused*/, const Raychel::ShadingData& data) noexcept
|
||||||
|
{
|
||||||
|
const auto [x, y, z] = data.normal;
|
||||||
|
return Raychel::color{std::abs(x), std::abs(y), std::abs(z)};
|
||||||
|
}
|
||||||
|
|
||||||
|
double get_material_ior(const TransparentMaterial& material) noexcept
|
||||||
|
{
|
||||||
|
return material.ior;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[maybe_unused]] static void write_framebuffer(const std::string& file_name, const Raychel::Framebuffer& framebuffer) noexcept
|
||||||
|
{
|
||||||
|
//Don't bother writing an empty framebuffer
|
||||||
|
if (framebuffer.pixel_data.empty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto label = Logger::startTimer("Write time");
|
||||||
|
std::ofstream output_image{file_name};
|
||||||
|
if (!output_image) {
|
||||||
|
Logger::error("Unable to open output file '", file_name, "'\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
output_image << "P6\n" << framebuffer.size.x() << ' ' << framebuffer.size.y() << '\n' << "255\n";
|
||||||
|
for (const auto& pixel : framebuffer.pixel_data) {
|
||||||
|
const auto pixel_rgb = convert_color<std::uint8_t>(pixel);
|
||||||
|
if (!output_image.write(reinterpret_cast<const char*>(&pixel_rgb), sizeof(pixel_rgb)).good()) {
|
||||||
|
Logger::error("Error while writing output file!\n");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Logger::logDuration(label);
|
||||||
|
}
|
||||||
|
|
||||||
|
[[maybe_unused]] static Raychel::Framebuffer fat_pixels_to_regular(const Raychel::FatFramebuffer& input) noexcept
|
||||||
|
{
|
||||||
|
std::vector<Raychel::color> output{};
|
||||||
|
output.reserve(input.size.x() * input.size.y());
|
||||||
|
for (const auto& pixel : input) {
|
||||||
|
output.emplace_back(pixel.noisy_color);
|
||||||
|
}
|
||||||
|
return {input.size, std::move(output)};
|
||||||
|
}
|
||||||
|
|
||||||
|
[[maybe_unused]] static void build_cornell_box(Raychel::Scene& scene)
|
||||||
|
{
|
||||||
|
using namespace Raychel;
|
||||||
|
|
||||||
|
constexpr auto room_size = 1.0;
|
||||||
|
constexpr auto box_size = room_size * 1.1;
|
||||||
|
constexpr auto slim = 0.1;
|
||||||
|
|
||||||
|
//Floor
|
||||||
|
scene.add_object(Translate{Box{vec3{box_size, slim, box_size}}, vec3{0, -room_size, 0}}, DiffuseMaterial{color{1, 1, 1}});
|
||||||
|
//Ceiling
|
||||||
|
scene.add_object(Translate{Box{vec3{box_size, slim, box_size}}, vec3{0, room_size, 0}}, FlatMaterial{color{1, 1, .9} * 2.5});
|
||||||
|
|
||||||
|
//Left wall
|
||||||
|
scene.add_object(
|
||||||
|
Translate{Box{vec3{slim, box_size, box_size}}, vec3{-room_size * 1.01, 0, 0}}, DiffuseMaterial{color{1, 0, 0}});
|
||||||
|
//Right wall
|
||||||
|
scene.add_object(Translate{Box{vec3{slim, box_size, box_size}}, vec3{room_size, 0, 0}}, DiffuseMaterial{color{0, 1, 0}});
|
||||||
|
//Back wall
|
||||||
|
scene.add_object(Translate{Box{vec3{box_size, box_size, slim}}, vec3{0, 0, room_size}}, DiffuseMaterial{color{1, 1, 1}});
|
||||||
|
|
||||||
|
//Back sphere
|
||||||
|
scene.add_object(
|
||||||
|
Translate{Sphere{0.5}, vec3{-room_size + slim + 0.5, -room_size + 1.1 * slim + 0.5, room_size - 2 * slim - 0.5}},
|
||||||
|
ReflectiveMaterial{color_from_hex<double>(0xFF5733) * 0.95});
|
||||||
|
|
||||||
|
//Front box
|
||||||
|
scene.add_object(
|
||||||
|
Translate{
|
||||||
|
Rotate{Sphere{.25}, rotate_around(vec3{0, 1, 0}, 60 * deg_to_rad<double>)},
|
||||||
|
vec3{room_size - slim - .5625, -room_size + slim + .25, -room_size + .375}},
|
||||||
|
TransparentMaterial{.transparency = color_from_hex<double>(0xa8ccd7), .ior = 1.5});
|
||||||
|
}
|
||||||
|
|
||||||
|
bool do_serialize(std::ostream& os, const FlatMaterial& material) noexcept
|
||||||
|
{
|
||||||
|
os << material.surface_color << '\n';
|
||||||
|
return os.good();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool do_serialize(std::ostream& os, const ReflectiveMaterial& material) noexcept
|
||||||
|
{
|
||||||
|
os << material.reflectivity << '\n';
|
||||||
|
return os.good();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool do_serialize(std::ostream& os, const DiffuseMaterial& material) noexcept
|
||||||
|
{
|
||||||
|
os << material.surface_color << '\n';
|
||||||
|
return os.good();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<DiffuseMaterial> do_deserialize(std::istream& is, Raychel::DeserializationTag<DiffuseMaterial>) noexcept
|
||||||
|
{
|
||||||
|
Raychel::color c{};
|
||||||
|
if (!(is >> c))
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return DiffuseMaterial{c};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<FlatMaterial> do_deserialize(std::istream& is, Raychel::DeserializationTag<FlatMaterial>) noexcept
|
||||||
|
{
|
||||||
|
Raychel::color c{};
|
||||||
|
if (!(is >> c))
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return FlatMaterial{c};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<ReflectiveMaterial> do_deserialize(std::istream& is, Raychel::DeserializationTag<ReflectiveMaterial>) noexcept
|
||||||
|
{
|
||||||
|
Raychel::color c{};
|
||||||
|
if (!(is >> c))
|
||||||
|
return std::nullopt;
|
||||||
|
|
||||||
|
return ReflectiveMaterial{c};
|
||||||
|
}
|
||||||
|
|
||||||
|
template <Raychel::Arithmetic T, std::convertible_to<T> T_>
|
||||||
|
constexpr Raychel::basic_color<T> lerp(const Raychel::basic_color<T>& a, const Raychel::basic_color<T>& b, T_ x)
|
||||||
|
{
|
||||||
|
return (b * x) + (a * (1.0 - x));
|
||||||
|
}
|
||||||
|
|
||||||
|
int main()
|
||||||
|
{
|
||||||
|
Logger::setMinimumLogLevel(Logger::LogLevel::debug);
|
||||||
|
|
||||||
|
using namespace Raychel;
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
Scene scene;
|
||||||
|
build_cornell_box(scene);
|
||||||
|
|
||||||
|
std::ofstream out_stream{"cornell_box.txt"};
|
||||||
|
|
||||||
|
serialize_scene(scene, out_stream);
|
||||||
|
#endif
|
||||||
|
#if 0
|
||||||
|
std::ifstream in_file{"cornell_box.txt"};
|
||||||
|
|
||||||
|
auto scene = deserialize_scene(
|
||||||
|
in_file,
|
||||||
|
object_deserializers<Translate<>, Rotate<>, Sphere, Box>(),
|
||||||
|
material_deserializers<DiffuseMaterial, FlatMaterial, ReflectiveMaterial>());
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
Scene scene;
|
||||||
|
//scene.add_object(Translate{Sphere{.75}, vec3{0, -.25, 0}}, TransparentMaterial{color{1, .65, .45}, 1.5, .05});
|
||||||
|
//scene.add_object(Translate{Plane{vec3{0, 1, 0}}, vec3{0, -1.01, 0}}, DiffuseMaterial{color{1}});
|
||||||
|
//scene.add_object(Translate{Box{vec3{.1, 2, 2}}, vec3{-2, 0, 0}}, FlatMaterial{color{10}});
|
||||||
|
|
||||||
|
scene.add_object(Translate{Sphere{.5}, vec3{-3.5, 2.5, -1.5}}, FlatMaterial{(color_from_hex<double>(0x2FE3E0)) * 10});
|
||||||
|
scene.add_object(Translate{Sphere{.5}, vec3{2.5, 2.5, 1.5}}, FlatMaterial{color_from_hex<double>(0xD01C1F) * 10});
|
||||||
|
scene.add_object(Translate{Sphere{.05}, vec3{0, .8, 0}}, FlatMaterial{color{1, .75, .5625} * 5});
|
||||||
|
|
||||||
|
scene.add_object(
|
||||||
|
Difference{Translate{Sphere{.5}, vec3{0, .85, 0}}, Rounded{Box{vec3{1, 1, 1}}, 0.1}}, DiffuseMaterial{color{1, 1, 1}});
|
||||||
|
scene.set_background_function([](const RenderData& data) {
|
||||||
|
/* if (data.direction.z() < -0.99) {
|
||||||
|
return color_from_hex<double>(0xfdd835) * 15;
|
||||||
|
}
|
||||||
|
return lerp(
|
||||||
|
color_from_hex<double>(0xFFC922),
|
||||||
|
color_from_hex<double>(0x87CEEB),
|
||||||
|
std::pow(std::abs(data.direction.y()) + 0.2, 1.0 / 3.0)) *
|
||||||
|
0.75;*/
|
||||||
|
(void)data;
|
||||||
|
return color{};
|
||||||
|
});
|
||||||
|
#endif
|
||||||
|
|
||||||
|
for (const auto& obj : scene.objects()) {
|
||||||
|
obj.unsafe_impl()->debug_log();
|
||||||
|
}
|
||||||
|
|
||||||
|
const auto rendered_image = render_scene(
|
||||||
|
scene,
|
||||||
|
Camera{
|
||||||
|
.transform = {.offset = vec3{0, 2.5, -2.5}, .rotation = rotate_around(vec3{1, 0, 0}, quarter_pi<double>)},
|
||||||
|
.zoom = 1.0},
|
||||||
|
RenderOptions{
|
||||||
|
.output_size = Size2D{1920, 1080} / 2U,
|
||||||
|
.max_ray_steps = 4096,
|
||||||
|
.max_recursion_depth = 100,
|
||||||
|
.samples_per_pixel = 1U << 10U,
|
||||||
|
});
|
||||||
|
|
||||||
|
write_framebuffer("out.ppm", fat_pixels_to_regular(rendered_image));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user