经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » C++ » 查看文章
Chapter1 p2 vec
来源:cnblogs  作者:qiyuewuyi2333  时间:2024/6/12 23:01:47  对本文有异议

在上一小节中,我们完成了对BMPImage类的构建,成功实现了我们这个小小引擎的图像输出功能。

你已经完成了图像输出了,接着就开始路径追踪吧。。。
开个玩笑XD
对于曾经学习过一些图形学经典教材的人来说,下一步应当开始着手于画线算法了,但对于本文来说,肯定是要走一些不走寻常路的

所谓万事开头难,我决定先打好地基。编写一个鲁棒性,扩展性还不错的向量类。

由于我们不打算借助外部库,所以,适当的设计是必要的。在这里,我打算借鉴一下pbrt-v4的设计。
也就是利用模板基类来定制我们的数据结构。

这么做的好处是,我们可以按照元素数量划分父类

本章目标

构建基本的数据结构,包括点和向量。

需求

  • 类型可扩展,鲁棒性还可以
  • 易于使用

实现

class Tuple2
  1. /**
  2. * \brief every element's parent class who has two parameter
  3. * \tparam Child a template class contains one template parameter
  4. * \tparam T defined by the child's type of template parameter
  5. */
  6. template <template <typename> class Child, typename T> class Tuple2
  7. {
  8. public:
  9. static constexpr int nDimensions = 2;
  10. Tuple2() = default;
  11. Tuple2(T x, T y) : x(x), y(y)
  12. {
  13. }
  14. Tuple2(Child<T> c)
  15. {
  16. x = c.x;
  17. y = c.y;
  18. }
  19. Child<T>& operator=(Child<T> c)
  20. {
  21. x = c.x;
  22. y = c.y;
  23. return static_cast<Child<T>&>(*this);
  24. }
  25. template <typename U> auto operator+(Child<U> c) const -> Child<decltype(T{} + U{})>
  26. {
  27. return {x + c.x, y + c.y};
  28. }
  29. template <typename U> Child<T>& operator+=(Child<U> c)
  30. {
  31. x += c.x;
  32. y += c.y;
  33. return static_cast<Child<T>&>(*this);
  34. }
  35. template <typename U> auto operator-(Child<U> c) const -> Child<decltype(T{} - U{})>
  36. {
  37. return {x - c.x, y - c.y};
  38. }
  39. template <typename U> Child<T>& operator-=(Child<U> c)
  40. {
  41. x -= c.x;
  42. y -= c.y;
  43. return static_cast<Child<T>&>(*this);
  44. }
  45. bool operator==(Child<T> c) const
  46. {
  47. return x == c.x && y == c.y;
  48. }
  49. bool operator!=(Child<T> c) const
  50. {
  51. return x != c.x || y != c.y;
  52. }
  53. template <typename U> auto operator*(U s) const -> Child<decltype(T{} * U{})>
  54. {
  55. return {s * x, s * y};
  56. }
  57. template <typename U> Child<T>& operator*=(U s)
  58. {
  59. x *= s;
  60. y *= s;
  61. return static_cast<Child<T>&>(*this);
  62. }
  63. template <typename U> Child<decltype(T{} / U{})> operator/(U d) const
  64. {
  65. VEC_CHECK(d != 0);
  66. return {x / d, y / d};
  67. }
  68. template <typename U> [[nodiscard]] Child<T>& operator/=(U d)
  69. {
  70. VEC_CHECK(d != 0);
  71. x /= d;
  72. y /= d;
  73. return static_cast<Child<T>&>(*this);
  74. }
  75. [[nodiscard]] Child<T> operator-() const
  76. {
  77. return {-x, -y};
  78. }
  79. [[nodiscard]] T& operator[](const int i) const
  80. {
  81. VEC_CHECK(i >= 0 && i <= 1);
  82. return (i == 0) ? x : y;
  83. }
  84. [[nodiscard]] T& operator[](const int i)
  85. {
  86. VEC_CHECK(i >= 0 && i <= 1);
  87. return (i == 0) ? x : y;
  88. }
  89. [[nodiscard]] std::string toString() const
  90. {
  91. return std::to_string(x) + std::to_string(x);
  92. }
  93. T x{};
  94. T y{};
  95. };

在代码中,用到了一些C++的高级技巧,我将在下面一一解释给大家:

  1. VEC_CHECK()宏定义
    为了达成鲁棒性的需求,我在方法的定义中加入了仅在debug模式下生效的assert,同时封装进入宏变量中,提高了代码健壮性的同时,也不会影响性能。

  2. [[nodiscard]]声明
    该声明于C++17版本中加入,声明在使用该方法时,不应当遗弃返回值,否则会发出警告,在调试时,略有作用

  3. template <template <typename> class Child, typename T>
    作为Tuple2的模板参数声明,使用Child作为当基类进行继承时的模板特化
    这是一种二级的模板特化结构,子类对父类的继承仅指定了Child这一模板参数的值,如此实现,基类的模板方法会非常好写。
    而T只需要在编译时进行确定,就可以生成对应的类了。
    这是一种在C++中实现动态绑定的方式。

那么在Tuple2的基础上,再去实现Vec2和Point2就十分容易了。

  1. // 派生类 Vec2,继承自 Tuple2
  2. template <typename T> class Vec2 : public Tuple2<Vec2, T>
  3. {
  4. public:
  5. using Tuple2<Vec2, T>::x;
  6. using Tuple2<Vec2, T>::y;
  7. Vec2() : Tuple2<Vec2, T>()
  8. {
  9. }
  10. Vec2(T x, T y) : Tuple2<Vec2, T>(x, y)
  11. {
  12. }
  13. void print() const
  14. {
  15. std::cout << "Vec2: (" << this->x << ", " << this->y << ")\n";
  16. }
  17. };
  18. template <typename T> class Point2 : public Tuple2<Point2, T>
  19. {
  20. public:
  21. using Tuple2<Point2, T>::x;
  22. using Tuple2<Point2, T>::y;
  23. Point2() : Tuple2<Point2, T>()
  24. {
  25. }
  26. Point2(T x, T y) : Tuple2<Point2, T>(x, y)
  27. {
  28. }
  29. void print() const
  30. {
  31. std::cout << "Point2: (" << this->x << ", " << this->y << ")\n";
  32. }
  33. };

同理,只需要把元素数量改成3个,我们就可以得到Tuple3以及其子类

  1. // 基类模板 Tuple3
  2. template <template <typename> class Child, typename T> class Tuple3
  3. {
  4. public:
  5. static constexpr int nDimensions = 3;
  6. Tuple3() = default;
  7. Tuple3(T x, T y, T z) : x(x), y(y), z(z)
  8. {
  9. }
  10. Tuple3(Child<T> c)
  11. {
  12. x = c.x;
  13. y = c.y;
  14. z = c.z;
  15. }
  16. Child<T>& operator=(Child<T> c)
  17. {
  18. x = c.x;
  19. y = c.y;
  20. z = c.z;
  21. return static_cast<Child<T>&>(*this);
  22. }
  23. template <typename U> auto operator+(Child<U> c) const -> Child<decltype(T{} + U{})>
  24. {
  25. return {x + c.x, y + c.y, z + c.z};
  26. }
  27. template <typename U> Child<T>& operator+=(Child<U> c)
  28. {
  29. x += c.x;
  30. y += c.y;
  31. z += c.z;
  32. return static_cast<Child<T>&>(*this);
  33. }
  34. template <typename U> auto operator-(Child<U> c) const -> Child<decltype(T{} - U{})>
  35. {
  36. return {x - c.x, y - c.y, z - c.z};
  37. }
  38. template <typename U> Child<T>& operator-=(Child<U> c)
  39. {
  40. x -= c.x;
  41. y -= c.y;
  42. z -= c.z;
  43. return static_cast<Child<T>&>(*this);
  44. }
  45. bool operator==(Child<T> c) const
  46. {
  47. return x == c.x && y == c.y && z == c.z;
  48. }
  49. bool operator!=(Child<T> c) const
  50. {
  51. return x != c.x || y != c.y || z != c.z;
  52. }
  53. template <typename U> auto operator*(U s) const -> Child<decltype(T{} * U{})>
  54. {
  55. return {s * x, s * y, s * z};
  56. }
  57. template <typename U> Child<T>& operator*=(U s)
  58. {
  59. x *= s;
  60. y *= s;
  61. z *= s;
  62. return static_cast<Child<T>&>(*this);
  63. }
  64. template <typename U> Child<decltype(T{} / U{})> operator/(U d) const
  65. {
  66. VEC_CHECK(d != 0);
  67. return {x / d, y / d, z / d};
  68. }
  69. template <typename U> [[nodiscard]] Child<T>& operator/=(U d)
  70. {
  71. VEC_CHECK(d != 0);
  72. x /= d;
  73. y /= d;
  74. z /= d;
  75. return static_cast<Child<T>&>(*this);
  76. }
  77. [[nodiscard]] Child<T> operator-() const
  78. {
  79. return {-x, -y, -z};
  80. }
  81. [[nodiscard]] T& operator[](const int i) const
  82. {
  83. VEC_CHECK(i >= 0 && i <= 2);
  84. return (i == 0) ? x : (i == 1) ? y : z;
  85. }
  86. [[nodiscard]] T& operator[](const int i)
  87. {
  88. VEC_CHECK(i >= 0 && i <= 2);
  89. return (i == 0) ? x : (i == 1) ? y : z;
  90. }
  91. [[nodiscard]] std::string toString() const
  92. {
  93. return std::to_string(x) + ", " + std::to_string(y) + ", " + std::to_string(z);
  94. }
  95. T x{};
  96. T y{};
  97. T z{};
  98. };
  1. // 派生类 Vec3,继承自 Tuple3
  2. template <typename T> class Vec3 : public Tuple3<Vec3, T>
  3. {
  4. public:
  5. // 显式引入模板基类的属性
  6. using Tuple3<Vec3, T>::x;
  7. using Tuple3<Vec3, T>::y;
  8. using Tuple3<Vec3, T>::z;
  9. Vec3() : Tuple3<Vec3, T>()
  10. {
  11. }
  12. Vec3(T x, T y, T z) : Tuple3<Vec3, T>(x, y, z)
  13. {
  14. }
  15. void print() const
  16. {
  17. std::cout << "Vec3: (" << this->x << ", " << this->y << ", " << this->z << ")\n";
  18. }
  19. };
  1. template <typename T> class Point3 : public Tuple3<Point3, T>
  2. {
  3. public:
  4. using Tuple3<Point3, T>::x;
  5. using Tuple3<Point3, T>::y;
  6. using Tuple3<Point3, T>::z;
  7. Point3() : Tuple3<Point3, T>()
  8. {
  9. }
  10. Point3(T x, T y, T z) : Tuple3<Point3, T>(x, y, z)
  11. {
  12. }
  13. void print() const
  14. {
  15. std::cout << "Point3: (" << this->x << ", " << this->y << ", " << this->z << ")\n";
  16. }
  17. };

原文链接:https://www.cnblogs.com/qiyuewuyi/p/18244909

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号