diff --git a/.github/workflows/workflow.yml b/.github/workflows/workflow.yml index abedbab..6d24737 100755 --- a/.github/workflows/workflow.yml +++ b/.github/workflows/workflow.yml @@ -26,7 +26,7 @@ jobs: run: | sudo apt update sudo apt install --fix-missing -y doxygen graphviz clang-format clang-tidy cppcheck lcov - sudo apt install --fix-missing -y gcc g++ libsdl2-dev libsdl2-ttf-dev libomp-dev libspdlog-dev + sudo apt install --fix-missing -y gcc g++ libsdl2-dev libsdl2-ttf-dev libomp-dev libspdlog-dev libassimp-dev - name: Build run: | @@ -48,7 +48,7 @@ jobs: run: | sudo apt update sudo apt install --fix-missing -y doxygen graphviz clang-format clang-tidy cppcheck lcov - sudo apt install --fix-missing -y gcc g++ libsdl2-dev libsdl2-ttf-dev libomp-dev libspdlog-dev + sudo apt install --fix-missing -y gcc g++ libsdl2-dev libsdl2-ttf-dev libomp-dev libspdlog-dev libassimp-dev - name: Build run: | diff --git a/.gitignore b/.gitignore index f646857..c52ec00 100755 --- a/.gitignore +++ b/.gitignore @@ -14,4 +14,4 @@ tools/opensbi/build .idea 3rd Doxyfile -src/include/config.h +src/include/config.h \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt index 31556e8..5b5d780 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,6 +7,7 @@ # 设置最小 cmake 版本 cmake_minimum_required(VERSION 3.27 FATAL_ERROR) + # 设置项目名与版本 project(SimpleRenderer VERSION 0.0.1) @@ -53,4 +54,4 @@ configure_file( # 添加要编译的目录 add_subdirectory(src) add_subdirectory(test) -add_subdirectory(doc) +add_subdirectory(doc) \ No newline at end of file diff --git a/CMakePresets.json b/CMakePresets.json index cdab07d..6976ac5 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -1,71 +1,84 @@ { - "version": 6, - "cmakeMinimumRequired": { - "major": 3, - "minor": 27, - "patch": 0 - }, - "configurePresets": [ - { - "name": "host", - "description": "Linux Only", - "hidden": true, - "condition": { - "type": "equals", - "lhs": "${hostSystemName}", - "rhs": "Linux" - } - }, - { - "name": "std", - "description": "This preset makes sure the project actually builds with at least the specified standard", - "hidden": true, - "cacheVariables": { - "CMAKE_C_EXTENSIONS": "OFF", - "CMAKE_C_STANDARD": "23", - "CMAKE_C_STANDARD_REQUIRED": "ON", - "CMAKE_CXX_EXTENSIONS": "OFF", - "CMAKE_CXX_STANDARD": "23", - "CMAKE_CXX_STANDARD_REQUIRED": "ON" - } + "version": 6, + "cmakeMinimumRequired": { + "major": 3, + "minor": 23, + "patch": 0 }, - { - "name": "configurePresets_base", - "hidden": true, - "inherits": [ - "host", - "std" - ], - "displayName": "configurePresets_base", - "description": "base configurePresets", - "binaryDir": "${sourceDir}/build", - "cacheVariables": { - "CMAKE_EXPORT_COMPILE_COMMANDS": { - "type": "BOOL", - "value": "ON" - }, - "EXECUTABLE_OUTPUT_PATH": { - "type": "STRING", - "value": "${sourceDir}/build/bin" - }, - "LIBRARY_OUTPUT_PATH": { - "type": "STRING", - "value": "${sourceDir}/build/lib" - }, - "COVERAGE_OUTPUT_DIR": { - "type": "STRING", - "value": "${sourceDir}/build/coverage" + "configurePresets": [ + { + "name": "std", + "description": "This preset makes sure the project actually builds with at least the specified standard", + "hidden": true, + "cacheVariables": { + "CMAKE_C_EXTENSIONS": "OFF", + "CMAKE_C_STANDARD": "23", + "CMAKE_C_STANDARD_REQUIRED": "ON", + "CMAKE_CXX_EXTENSIONS": "OFF", + "CMAKE_CXX_STANDARD": "23", + "CMAKE_CXX_STANDARD_REQUIRED": "ON" } + }, + { + "name": "config-base", + "hidden": true, + "inherits": [ "std" ], + "displayName": "config-base", + "description": "base configurePresets", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_EXPORT_COMPILE_COMMANDS": { + "type": "BOOL", + "value": "ON" + }, + "EXECUTABLE_OUTPUT_PATH": { + "type": "STRING", + "value": "${sourceDir}/build/bin" + }, + "LIBRARY_OUTPUT_PATH": { + "type": "STRING", + "value": "${sourceDir}/build/lib" + }, + "COVERAGE_OUTPUT_DIR": { + "type": "STRING", + "value": "${sourceDir}/build/coverage" + } + } + }, + { + "name": "config-macos", + "hidden": true, + "inherits": [ "config-base" ], + "displayName": "config-base", + "description": "macOS configurePresets", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Darwin" + }, + "cacheVariables": { + "CMAKE_MACOSX_RPATH": "1", + "CMAKE_INSTALL_RPATH": "/Library/Frameworks", + "CMAKE_BUILD_WITH_INSTALL_RPATH": "TRUE" + } + }, + { + "name": "build", + "hidden": false, + "inherits": [ + "config-base" + ], + "displayName": "build-base", + "description": "build base configurePresets" + }, + { + "name": "build-macos", + "hidden": false, + "inherits": [ + "config-macos" + ], + "displayName": "build-macos", + "description": "macOS build configurePresets" } - }, - { - "name": "build", - "hidden": false, - "inherits": [ - "configurePresets_base" - ], - "displayName": "build", - "description": "build" - } - ] -} \ No newline at end of file + ] + } \ No newline at end of file diff --git a/README.md b/README.md index 1ccd0cd..8339ee0 100755 --- a/README.md +++ b/README.md @@ -52,7 +52,7 @@ A software renderer ## 依赖 ```shell -sudo apt install doxygen graphviz clang-format clang-tidy cppcheck lcov gcc g++ libsdl2-dev libsdl2-ttf-dev libomp-dev libspdlog-dev cmake vim +sudo apt install doxygen graphviz clang-format clang-tidy cppcheck lcov gcc g++ libsdl2-dev libsdl2-ttf-dev libomp-dev libspdlog-dev cmake vim libassimp-dev ``` ## 使用 diff --git a/cmake/3rd.cmake b/cmake/3rd.cmake index 496cbb8..9c3fe69 100644 --- a/cmake/3rd.cmake +++ b/cmake/3rd.cmake @@ -46,54 +46,35 @@ endif () include(${CPM_DOWNLOAD_LOCATION}) # -------- get_cpm.cmake -------- -# https://github.com/google/googletest CPMAddPackage( NAME googletest - GITHUB_REPOSITORY google/googletest - GIT_TAG v1.14.0 - VERSION 1.14.0 + GIT_REPOSITORY https://github.com/google/googletest.git + GIT_TAG v1.15.2 + VERSION 1.15.2 OPTIONS "INSTALL_GTEST OFF" "gtest_force_shared_crt ON" ) -# https://github.com/aminosbh/sdl2-cmake-modules.git +# https://github.com/libsdl-org/SDL CPMAddPackage( - NAME sdl2-cmake-modules - GIT_REPOSITORY https://github.com/aminosbh/sdl2-cmake-modules.git - GIT_TAG ad006a3daae65a612ed87415037e32188b81071e - DOWNLOAD_ONLY True + NAME SDL2 + GITHUB_REPOSITORY libsdl-org/SDL + GIT_TAG release-2.30.6 + OPTIONS + "SDL2_DISABLE_INSTALL ON" + "SDL_SHARED OFF" + "SDL_STATIC ON" + "SDL_STATIC_PIC ON" + "SDL_WERROR OFF" ) -if (sdl2-cmake-modules_ADDED) - list(APPEND CMAKE_MODULE_PATH ${sdl2-cmake-modules_SOURCE_DIR}) -endif () -## https://github.com/freetype/freetype -#CPMAddPackage( -# NAME freetype -# GIT_REPOSITORY https://github.com/freetype/freetype.git -# GIT_TAG VER-2-13-0 -# VERSION 2.13.0 -#) -#if (freetype_ADDED) -# add_library(Freetype::Freetype ALIAS freetype) -#endif () - -# https://github.com/tinyobjloader/tinyobjloader.git +# https://github.com/g-truc/glm CPMAddPackage( - NAME tinyobjloader - GIT_REPOSITORY https://github.com/tinyobjloader/tinyobjloader.git - GIT_TAG 853f059d778058a43c954850e561a231934b33a7 - DOWNLOAD_ONLY True + NAME glm + GITHUB_REPOSITORY g-truc/glm + GIT_TAG 1.0.1 ) -if (tinyobjloader_ADDED) - add_library(tinyobjloader INTERFACE) - target_sources(tinyobjloader INTERFACE - FILE_SET HEADERS - BASE_DIRS ${tinyobjloader_SOURCE_DIR} - FILES tiny_obj_loader.h - ) -endif () # https://github.com/nothings/stb.git CPMAddPackage( @@ -111,19 +92,6 @@ if (stb_ADDED) ) endif () -# https://gitlab.com/libeigen/eigen.git -CPMAddPackage( - NAME Eigen - GIT_REPOSITORY https://gitlab.com/libeigen/eigen.git - GIT_TAG 3.4.0 - VERSION 3.4.0 - DOWNLOAD_ONLY True -) -if (Eigen_ADDED) - add_library(Eigen INTERFACE IMPORTED) - target_include_directories(Eigen INTERFACE ${Eigen_SOURCE_DIR}) -endif () - # http://wenq.org/wqy2/index.cgi?ZenHei CPMAddPackage( NAME wqy_font @@ -235,20 +203,24 @@ if (NOT LCOV_EXE) "Following https://github.com/linux-test-project/lcov to install.") endif () -find_package(SDL2 REQUIRED) -if (NOT SDL2_FOUND) - message(FATAL_ERROR "sdl2 not found.\n" - "Following https://github.com/libsdl-org/SDL to install.") -endif () - -find_package(OpenMP REQUIRED) -if (NOT OpenMP_FOUND) - message(FATAL_ERROR "OpenMP not found.\n" - "Following https://www.openmp.org to install.") -endif () - find_package(spdlog REQUIRED) if (NOT spdlog_FOUND) message(FATAL_ERROR "spdlog not found.\n" "Following https://github.com/gabime/spdlog to install.") endif () + +find_package(assimp REQUIRED) +if (NOT assimp_FOUND) + message(FATAL_ERROR "assimp not found.\n" + "Following https://github.com/assimp/assimp to install.") +endif () + +find_package(OpenMP REQUIRED) +if (APPLE) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}") + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}") + set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -L${OPENMP_LIBRARIES} -lomp") +endif () +if (NOT OpenMP_FOUND) + message(FATAL_ERROR "OpenMP not found. Please install OpenMP.") +endif () \ No newline at end of file diff --git a/cmake/add_header.cmake b/cmake/add_header.cmake index 0e95eac..54104a8 100644 --- a/cmake/add_header.cmake +++ b/cmake/add_header.cmake @@ -6,6 +6,5 @@ # 将头文件路径添加到 _target 的搜索路径中 function(add_header_3rd _target) - target_include_directories(${_target} PRIVATE - ${tinyobjloader_SOURCE_DIR}) + target_include_directories(${_target} PRIVATE) endfunction() diff --git a/cmake/compile_config.cmake b/cmake/compile_config.cmake index 330a14a..0cdec5a 100644 --- a/cmake/compile_config.cmake +++ b/cmake/compile_config.cmake @@ -16,9 +16,9 @@ list(APPEND DEFAULT_COMPILE_OPTIONS list(APPEND DEFAULT_LINK_LIB spdlog::spdlog stb - tinyobjloader - Eigen + glm::glm ${glog_LIBRARIES} - SDL2::Main + SDL2::SDL2 OpenMP::OpenMP_CXX + assimp ) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2fbf564..5b4fe72 100755 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -5,31 +5,21 @@ # CMakeLists.txt for Simple-XX/SimpleRenderer. # 生成静态库 -add_library(${PROJECT_NAME} STATIC - log_system.cpp - color.cpp - include/vector.hpp - include/matrix.hpp - include/model.hpp - include/shader_base.h - include/default_shader.h - include/light.h - model.cpp - light.cpp - shader_base.cpp - default_shader.cpp - simple_renderer.cpp +file(GLOB_RECURSE SRC_FILES CONFIGURE_DEPENDS + "*.cpp" + "*.c" ) +add_library(${PROJECT_NAME} STATIC ${SRC_FILES}) target_include_directories(${PROJECT_NAME} PRIVATE - $ - $ + $ + $ ) target_compile_options(${PROJECT_NAME} PRIVATE - ${DEFAULT_COMPILE_OPTIONS} + ${DEFAULT_COMPILE_OPTIONS} ) target_link_libraries(${PROJECT_NAME} PRIVATE - ${DEFAULT_LINK_LIB} -) + ${DEFAULT_LINK_LIB} +) \ No newline at end of file diff --git a/src/default_shader.cpp b/src/default_shader.cpp index 4a90f8a..ba1c34e 100644 --- a/src/default_shader.cpp +++ b/src/default_shader.cpp @@ -23,23 +23,23 @@ auto DefaultShader::InterpolateColor( const Vector3f &barycentric_coord) -> Color { return Color( static_cast(static_cast(color0[Color::kColorIndexRed]) * - barycentric_coord.x() + + barycentric_coord.x + static_cast(color1[Color::kColorIndexRed]) * - barycentric_coord.y() + + barycentric_coord.y + static_cast(color2[Color::kColorIndexRed]) * - barycentric_coord.z()), + barycentric_coord.z), static_cast(static_cast(color0[Color::kColorIndexGreen]) * - barycentric_coord.x() + + barycentric_coord.x + static_cast(color1[Color::kColorIndexGreen]) * - barycentric_coord.y() + + barycentric_coord.y + static_cast(color2[Color::kColorIndexGreen]) * - barycentric_coord.z()), + barycentric_coord.z), static_cast(static_cast(color0[Color::kColorIndexBlue]) * - barycentric_coord.x() + + barycentric_coord.x + static_cast(color1[Color::kColorIndexBlue]) * - barycentric_coord.y() + + barycentric_coord.y + static_cast(color2[Color::kColorIndexBlue]) * - barycentric_coord.z())); + barycentric_coord.z)); } /// @todo 巨大性能开销 @@ -47,8 +47,8 @@ auto DefaultShader::Vertex(const ShaderVertexIn &shader_vertex_in) const -> ShaderVertexOut { auto face(shader_vertex_in.face_); - face = face * shader_data_.model_matrix_ * shader_data_.view_matrix_ * - shader_data_.project_matrix_; + face.transform(shader_data_.project_matrix_ * shader_data_.view_matrix_ * + shader_data_.model_matrix_); /// @todo 变换贴图 return ShaderVertexOut(face); @@ -56,7 +56,8 @@ auto DefaultShader::Vertex(const ShaderVertexIn &shader_vertex_in) const auto DefaultShader::Fragment(const ShaderFragmentIn &shader_fragment_in) const -> ShaderFragmentOut { - auto intensity = (shader_fragment_in.normal_.dot(shader_fragment_in.light_)); + auto intensity = + glm::dot(shader_fragment_in.normal_, shader_fragment_in.light_); auto is_need_draw = true; // 光照方向为正,不绘制背面 if (intensity <= 0) { diff --git a/src/include/face.hpp b/src/include/face.hpp new file mode 100644 index 0000000..bd5de4e --- /dev/null +++ b/src/include/face.hpp @@ -0,0 +1,81 @@ +#ifndef SIMPLERENDER_SRC_INCLUDE_FACE_HPP_ +#define SIMPLERENDER_SRC_INCLUDE_FACE_HPP_ + +#include + +#include "material.hpp" +#include "vertex.hpp" + +namespace simple_renderer { + +class Face { + public: + // Default constructor + // 默认构造函数 + Face() = default; + + // Copy constructor + // 拷贝构造函数 + Face(const Face& face) = default; + // Copy assignment operator + // 拷贝赋值操作符 + Face& operator=(const Face& face) = default; + + // Move constructor + // 移动构造函数 + Face(Face&& face) = default; + // Move assignment operator + // 移动赋值操作符 + Face& operator=(Face&& face) = default; + + // Destructor + // 析构函数 + ~Face() = default; + + // Constructor that initializes the Face with three vertices and a material + // 使用三个顶点和材质初始化 Face 的构造函数 + explicit Face(const Vertex& v0, const Vertex& v1, const Vertex& v2, + Material material) + : vertices_{v0, v1, v2}, material_(std::move(material)) { + // Calculate the normal vector when the face is created + // 创建 Face 时计算法向量 + calculateNormal(); + } + + // Apply a transformation matrix to the vertices + // 对顶点应用变换矩阵 + void transform(const Matrix4f& tran) { + vertices_[0].transform(tran); + vertices_[1].transform(tran); + vertices_[2].transform(tran); + } + + // Get functions + // 获取函数 + const std::array& vertices() const { return vertices_; } + const Vertex& vertex(size_t index) const { return vertices_[index]; } + const Vector3f& normal() const { return normal_; } + const Material& material() const { return material_; } + + private: + std::array vertices_; + Vector3f normal_; + Material material_; + + // Calculate the normal vector based on the vertices + // 根据顶点计算法向量 + void calculateNormal() { + Vector3f edge1 = + Vector3f(vertices_[1].position()) - Vector3f(vertices_[0].position()); + Vector3f edge2 = + Vector3f(vertices_[2].position()) - Vector3f(vertices_[0].position()); + normal_ = glm::normalize( + // Normalize the cross product to get the + // normal 归一化叉积以获得法向量 + glm::cross(edge1, edge2)); + } +}; + +} // namespace simple_renderer + +#endif \ No newline at end of file diff --git a/src/include/light.h b/src/include/light.h index f566308..81fde8e 100644 --- a/src/include/light.h +++ b/src/include/light.h @@ -21,7 +21,7 @@ #include #include "color.h" -#include "vector.hpp" +#include "math.hpp" namespace simple_renderer { diff --git a/src/include/material.hpp b/src/include/material.hpp new file mode 100644 index 0000000..c637548 --- /dev/null +++ b/src/include/material.hpp @@ -0,0 +1,42 @@ +#ifndef SIMPLERENDER_SRC_INCLUDE_MATERIAL_HPP_ +#define SIMPLERENDER_SRC_INCLUDE_MATERIAL_HPP_ + +#include + +namespace simple_renderer { + +class Material { + public: + // Default constructor + // 默认构造函数 + Material() = default; + + // Copy constructor + // 拷贝构造函数 + Material(const Material& material) = default; + + // Copy assignment operator + // 拷贝赋值操作符 + Material& operator=(const Material& material) = default; + + // Move constructor + // 移动构造函数 + Material(Material&& material) = default; + + // Move assignment operator + // 移动赋值操作符 + Material& operator=(Material&& material) = default; + + // Destructor + // 析构函数 + ~Material() = default; + + float shininess = 0.0f; + Vector3f ambient; + Vector3f diffuse; + Vector3f specular; +}; + +} // namespace simple_renderer + +#endif \ No newline at end of file diff --git a/src/include/math.hpp b/src/include/math.hpp new file mode 100644 index 0000000..6f5a8eb --- /dev/null +++ b/src/include/math.hpp @@ -0,0 +1,61 @@ +#ifndef SIMPLERENDER_SRC_INCLUDE_MATH_HPP_ +#define SIMPLERENDER_SRC_INCLUDE_MATH_HPP_ + +#include + +#define GLM_ENABLE_EXPERIMENTAL +#include + +#include "log_system.h" + +namespace simple_renderer { +using Vector2f = glm::vec2; +using Vector3f = glm::vec3; +using Vector4f = glm::vec4; +using Matrix4f = glm::mat4; +} // namespace simple_renderer + +/** + * spdlog 输出 Vector3f 实现 + */ +template <> +struct fmt::formatter : fmt::formatter { + auto format(const simple_renderer::Vector3f &vector, + fmt::format_context &ctx) const -> decltype(ctx.out()) { + std::string vector_string = glm::to_string(vector); + + // Format and output the string + return fmt::format_to(ctx.out(), "\n{}", vector_string); + } +}; + +/** + * spdlog 输出 Vector4f 实现 + */ +template <> +struct fmt::formatter : fmt::formatter { + auto format(const simple_renderer::Vector4f &vector, + fmt::format_context &ctx) const -> decltype(ctx.out()) { + std::string vector_string = glm::to_string(vector); + + // Format and output the string + return fmt::format_to(ctx.out(), "\n{}", vector_string); + } +}; + +/** + * spdlog 输出矩阵实现 + */ +template <> +struct fmt::formatter : fmt::formatter { + auto format(const simple_renderer::Matrix4f &matrix, + fmt::format_context &ctx) const -> decltype(ctx.out()) { + // Convert the Matrix4f to a string using glm::to_string + std::string matrix_str = glm::to_string(matrix); + + // Format and output the string + return fmt::format_to(ctx.out(), "\n{}", matrix_str); + } +}; + +#endif diff --git a/src/include/matrix.hpp b/src/include/matrix.hpp deleted file mode 100755 index 30a1e1b..0000000 --- a/src/include/matrix.hpp +++ /dev/null @@ -1,43 +0,0 @@ - -/** - * @file matrix.hpp - * @brief 矩阵 - * @author Zone.N (Zone.Niuzh@hotmail.com) - * @version 1.0 - * @date 2022-09-07 - * @copyright MIT LICENSE - * https://github.com/Simple-XX/SimpleRenderer - * @par change log: - * - *
DateAuthorDescription - *
2022-09-07Zone.N迁移到 doxygen - *
- */ - -#ifndef SIMPLERENDER_SRC_INCLUDE_MATRIX_HPP_ -#define SIMPLERENDER_SRC_INCLUDE_MATRIX_HPP_ - -#include - -#include "log_system.h" - -namespace simple_renderer { - -using Matrix4f = Eigen::Matrix4f; - -} // namespace simple_renderer - -/** - * spdlog 输出矩阵实现 - */ -template <> -struct fmt::formatter : fmt::formatter { - auto format(simple_renderer::Matrix4f matrix, format_context &format_context) - const -> decltype(format_context.out()) { - std::stringstream buf; - buf << matrix; - return fmt::format_to(format_context.out(), "\n{}", buf.str()); - } -}; - -#endif /* SIMPLERENDER_SRC_INCLUDE_MATRIX_HPP_ */ diff --git a/src/include/model.hpp b/src/include/model.hpp index 635436e..de8924b 100755 --- a/src/include/model.hpp +++ b/src/include/model.hpp @@ -17,194 +17,84 @@ #ifndef SIMPLERENDER_SRC_INCLUDE_MODEL_HPP_ #define SIMPLERENDER_SRC_INCLUDE_MODEL_HPP_ -#include +#include #include #include #include "color.h" #include "config.h" +#include "face.hpp" #include "log_system.h" -#include "matrix.hpp" -#include "vector.hpp" +#include "math.hpp" +#include "vertex.hpp" namespace simple_renderer { -/** - * 模型 - */ class Model { public: - /// 顶点坐标 - using Coord = Vector3f; - /// 法向量 - using Normal = Vector3f; - /// 贴图 - using TextureCoord = Vector2f; - - class Material { - public: - /// 反光度 - float shininess = 0; - /// 环境光照 - Vector3f ambient; - /// 漫反射光照 - Vector3f diffuse; - /// 镜面光照 - Vector3f specular; - - /// @name 默认构造/析构函数 - /// @{ - Material() = default; - Material(const Material &material) = default; - Material(Material &&material) = default; - auto operator=(const Material &material) -> Material & = default; - auto operator=(Material &&material) -> Material & = default; - ~Material() = default; - /// @} - }; - - /** - * obj/mtl 文件的原始数据 - * @todo 直接保存太浪费内存了 - */ - class Vertex { - public: - /// 坐标 - Coord coord_; - /// 法线,顶点 v 的数量与 vn 的数量一样多 - Normal normal_; - /// 贴图(纹理),范围为 0~1,顶点 v 的个数不一定与纹理坐标 vt - /// 的个数一样多, 因为有可能很多顶点公用一个纹理坐标的像素。 - TextureCoord texture_coord_; - - /// 颜色,最终每个三角面的颜色,是由构成这个三角面的三个顶点进行插值计算 - /// 如果 obj 文件中没有指定则设为 1(白色) - /// 范围 [0, 1] - Color color_; - - /** - * 构造函数 - * @param coord 坐标 - * @param normal 法向量 - * @param texture_coord 贴图 - * @param color 颜色 - */ - explicit Vertex(Coord coord, Normal normal, TextureCoord texture_coord, - const Color &color); - - /// @name 默认构造/析构函数 - /// @{ - Vertex() = default; - Vertex(const Vertex &vertex) = default; - Vertex(Vertex &&vertex) = default; - auto operator=(const Vertex &vertex) -> Vertex & = default; - auto operator=(Vertex &&vertex) -> Vertex & = default; - ~Vertex() = default; - /// @} - - /** - * * 重载,对顶点应用变换矩阵 - * @param tran 要对顶点进行的变换矩阵 - * @return 结果 - */ - [[nodiscard]] auto operator*(const Matrix4f &tran) const -> Vertex; - }; - - /// @todo 直接保存太浪费内存了 - class Face { - public: - Vertex v0_; - Vertex v1_; - Vertex v2_; - /// 面的法向量为三个点的法向量矢量和 - Normal normal_; - // 面的颜色为三个点的颜色插值 - /// 材质信息 - Material material_; - - /** - * 构造函数 - * @param v0 第一个顶点 - * @param v1 第二个顶点 - * @param v2 第三个顶点 - * @param material 材质 - */ - explicit Face(const Vertex &v0, const Vertex &v1, const Vertex &v2, - Material material); - - /// @name 默认构造/析构函数 - /// @{ - Face() = default; - Face(const Face &face) = default; - Face(Face &&face) = default; - auto operator=(const Face &face) -> Face & = default; - auto operator=(Face &&face) -> Face & = default; - ~Face() = default; - /// @} - - /** - * * 重载,对面应用变换矩阵 - * @param tran 要对面进行的变换矩阵 - * @return 结果 - */ - [[nodiscard]] auto operator*(const Matrix4f &tran) const -> Face; - }; - - /// obj 文件路径 - std::string obj_path_ = ""; - /// mtl 路径 - std::string mtl_path_ = ""; - - /** - * 构造函数 - * @param obj_path obj 文件路径 - * @param mtl_path mtl 文件路径 - * @todo 顶点去重 - */ - explicit Model(const std::string &obj_path, const std::string &mtl_path = ""); - - /// @name 默认构造/析构函数 - /// @{ + // Default constructor + // 默认构造函数 Model() = default; - Model(const Model &model) = default; - Model(Model &&model) = default; - auto operator=(const Model &model) -> Model & = default; - auto operator=(Model &&model) -> Model & = default; + + // Default copy constructor + // 默认拷贝构造函数 + Model(const Model& model) = default; + // Default copy assignment operator + // 默认拷贝赋值运算符 + Model& operator=(const Model& model) = default; + + // Default move constructor + // 默认移动构造函数 + Model(Model&& model) = default; + // Default move assignment operator + // 默认移动赋值运算符 + Model& operator=(Model&& model) = default; + + // Destructor + // 析构函数 ~Model() = default; - /// @} - /** - * * 重载,对模型应用变换矩阵 - * @param tran 要对模型进行的变换矩阵 - * @return 结果 - */ - [[nodiscard]] auto operator*(const Matrix4f &tran) const -> Model; + // Constructor that loads a model from a file path + // 从文件路径加载模型的构造函数 + Model(const std::string& model_path); + + // Apply a transformation to the model + // 对模型应用变换 + void transform(const Matrix4f& tran); - /** - * 获取面 - * @return 所有面 - */ - [[nodiscard]] auto GetFace() const -> const std::vector &; + // Get functions + // 获取函数 + const std::vector& faces() const { return faces_; }; + const std::string& modelPath() const { return directory_; }; private: - /// 三角形顶点数 + // Number of vertices per triangle face + // 每个三角形面的顶点数 static constexpr const uint8_t kTriangleFaceVertexCount = 3; - - /// 保存模型的所有面 + // Directory where the model is located + // 模型所在的目录 + std::string directory_; + // List of faces(triangles) that make up the model + // 构成模型的面(三角形)列表 std::vector faces_; - /** - * 获取模型的 xyz 最大值/最小值 - */ - std::pair GetMaxMinXYX(); + // Load the model from the specified file path + // 从指定的文件路径加载模型 + void loadModel(const std::string& path); - /** - * 将模型归一化,所有坐标在 [-1, 1] 内 - */ - void Normalize(); -}; + // Process a node in the model + // 处理模型中的一个节点 + void processNode(aiNode* node, const aiScene* scene); + // Process a mesh in the model + // 处理模型中的一个网格 + void processMesh(aiMesh* mesh, const aiScene* scene); + + // Process the material of the model + // 处理模型的材质 + Material processMaterial(aiMaterial* material); +}; } // namespace simple_renderer -#endif /* SIMPLERENDER_SRC_INCLUDE_MODEL_HPP_ */ +#endif /* SIMPLERENDER_SRC_INCLUDE_MODEL_HPP_ */ \ No newline at end of file diff --git a/src/include/shader_base.h b/src/include/shader_base.h index aa04937..9f69382 100644 --- a/src/include/shader_base.h +++ b/src/include/shader_base.h @@ -17,9 +17,8 @@ #ifndef SIMPLERENDER_SRC_INCLUDE_SHADER_BASE_H_ #define SIMPLERENDER_SRC_INCLUDE_SHADER_BASE_H_ -#include "matrix.hpp" +#include "math.hpp" #include "model.hpp" -#include "vector.hpp" namespace simple_renderer { @@ -29,13 +28,13 @@ namespace simple_renderer { class ShaderVertexIn { public: /// 面信息 - Model::Face face_; + Face face_; /** * 构造函数 * @param face 面信息 */ - explicit ShaderVertexIn(const Model::Face &face); + explicit ShaderVertexIn(const Face &face); /// @name 默认构造/析构函数 /// @{ @@ -56,13 +55,13 @@ class ShaderVertexIn { class ShaderVertexOut { public: /// 面信息 - Model::Face face_; + Face face_; /** * 构造函数 * @param face 面信息 */ - explicit ShaderVertexOut(const Model::Face &face); + explicit ShaderVertexOut(const Face &face); /// @name 默认构造/析构函数 /// @{ @@ -160,11 +159,11 @@ class ShaderFragmentOut { class ShaderData { public: /// 模型变换矩阵 - Matrix4f model_matrix_ = Matrix4f().setIdentity(); + Matrix4f model_matrix_ = Matrix4f(1.0f); /// 视图变换矩阵 - Matrix4f view_matrix_ = Matrix4f().setIdentity(); + Matrix4f view_matrix_ = Matrix4f(1.0f); /// 正交变换矩阵 - Matrix4f project_matrix_ = Matrix4f().setIdentity(); + Matrix4f project_matrix_ = Matrix4f(1.0f); /** * 构造函数 diff --git a/src/include/simple_renderer.h b/src/include/simple_renderer.h index 9aa587f..44e2ca6 100755 --- a/src/include/simple_renderer.h +++ b/src/include/simple_renderer.h @@ -23,10 +23,9 @@ #include "light.h" #include "log_system.h" -#include "matrix.hpp" +#include "math.hpp" #include "model.hpp" #include "shader_base.h" -#include "vector.hpp" namespace simple_renderer { @@ -91,7 +90,7 @@ class SimpleRenderer { * @todo 多线程支持 */ void DrawTriangle(const ShaderBase &shader, const Light &light, - const Model::Normal &normal, const Model::Face &face); + const Vector3f &normal, const Face &face); /** * 绘制模型 diff --git a/src/include/vector.hpp b/src/include/vector.hpp deleted file mode 100755 index eab8995..0000000 --- a/src/include/vector.hpp +++ /dev/null @@ -1,58 +0,0 @@ - -/** - * @file vector.hpp - * @brief 向量模版 - * @author Zone.N (Zone.Niuzh@hotmail.com) - * @version 1.0 - * @date 2022-06-07 - * @copyright MIT LICENSE - * https://github.com/Simple-XX/SimpleRenderer - * @par change log: - * - *
DateAuthorDescription - *
2022-06-07Zone.N迁移到 doxygen - *
- */ - -#ifndef SIMPLERENDER_SRC_INCLUDE_VECTOR_HPP_ -#define SIMPLERENDER_SRC_INCLUDE_VECTOR_HPP_ - -#include - -#include "log_system.h" - -namespace simple_renderer { - -using Vector2f = Eigen::Vector2f; -using Vector3f = Eigen::Vector3f; -using Vector4f = Eigen::Vector4f; - -} // namespace simple_renderer - -/** - * spdlog 输出 Vector3f 实现 - */ -template <> -struct fmt::formatter : fmt::formatter { - auto format(simple_renderer::Vector3f vector, format_context &format_context) - const -> decltype(format_context.out()) { - std::stringstream buf; - buf << vector; - return fmt::format_to(format_context.out(), "\n{}", buf.str()); - } -}; - -/** - * spdlog 输出 Vector4f 实现 - */ -template <> -struct fmt::formatter : fmt::formatter { - auto format(simple_renderer::Vector4f vector, format_context &format_context) - const -> decltype(format_context.out()) { - std::stringstream buf; - buf << vector; - return fmt::format_to(format_context.out(), "\n{}", buf.str()); - } -}; - -#endif /* SIMPLERENDER_SRC_INCLUDE_VECTOR_HPP_ */ diff --git a/src/include/vertex.hpp b/src/include/vertex.hpp new file mode 100644 index 0000000..924d8fe --- /dev/null +++ b/src/include/vertex.hpp @@ -0,0 +1,69 @@ +#ifndef SIMPLERENDER_SRC_INCLUDE_VERTEX_HPP_ +#define SIMPLERENDER_SRC_INCLUDE_VERTEX_HPP_ + +#include + +#include "color.h" + +namespace simple_renderer { + +class Vertex { + public: + // Default constructor + // 默认构造函数 + Vertex() = default; + + // Copy constructor + // 拷贝构造函数 + Vertex(const Vertex& vertex) = default; + // Copy assignment operator + // 拷贝赋值操作符 + Vertex& operator=(const Vertex& vertex) = default; + + // Move constructor + // 移动构造函数 + Vertex(Vertex&& vertex) = default; + // Move assignment operator + // 移动赋值操作符 + Vertex& operator=(Vertex&& vertex) = default; + + // Destructor + // 析构函数 + ~Vertex() = default; + + // Constructor with parameters 带参数的构造函数 + explicit Vertex(const Vector4f& pos, const Vector3f& norm, + const Vector2f& tex, const Color& color_) + : position_(pos), normal_(norm), texCoords_(tex), color_(color_) {} + + // Transform the vertex with a matrix 使用矩阵变换顶点 + void transform(const Matrix4f& matrix) { position_ = matrix * position_; } + + // Perspective divide to convert from clip space to normalized device + // coordinates 透视除法,将裁剪空间转换为标准化设备坐标 + void perspectiveDivide() { + if (position_.w != 0) { + position_.x /= position_.w; + position_.y /= position_.w; + position_.z /= position_.w; + position_.w = 1.0f; // Homogenize, 齐次坐标 + } + } + + // Get functions + // 获取函数 + Vector4f position() const { return position_; } + Vector3f normal() const { return normal_; } + Vector2f texCoords() const { return texCoords_; } + Color color() const { return color_; } + + private: + Vector4f position_; // 3D position, 3D顶点坐标 + Vector3f normal_; // Normal vector, 顶点法向量 + Vector2f texCoords_; // Texture coordinates, 顶点纹理坐标 + Color color_; +}; + +} // namespace simple_renderer + +#endif \ No newline at end of file diff --git a/src/light.cpp b/src/light.cpp index 21c0254..f25fb4c 100644 --- a/src/light.cpp +++ b/src/light.cpp @@ -18,7 +18,7 @@ #include "color.h" #include "log_system.h" -#include "vector.hpp" +#include "math.hpp" namespace simple_renderer { diff --git a/src/log_system.cpp b/src/log_system.cpp index dc8cc66..e6e7218 100755 --- a/src/log_system.cpp +++ b/src/log_system.cpp @@ -22,15 +22,17 @@ #include #include +#include + namespace simple_renderer { LogSystem::LogSystem(const std::string &log_file_path, size_t lig_file_max_size, size_t log_file_max_count) { spdlog::init_thread_pool(65536, 1); + // std::string log_file_paths = "./logs/simple_renderer.log"; auto stdout_sink = std::make_shared(); auto rotating_sink = std::make_shared( log_file_path, lig_file_max_size, log_file_max_count); - std::vector sinks{stdout_sink, rotating_sink}; logger_ = std::make_shared( "multi_sink", sinks.begin(), sinks.end(), spdlog::thread_pool(), diff --git a/src/model.cpp b/src/model.cpp index 8ce949b..53b8bac 100755 --- a/src/model.cpp +++ b/src/model.cpp @@ -1,7 +1,6 @@ - /** * @file model.cpp - * @brief 模型抽象 + * @brief Model abstraction (模型抽象) * @author Zone.N (Zone.Niuzh@hotmail.com) * @version 1.0 * @date 2022-06-06 @@ -16,250 +15,147 @@ #include "model.hpp" -#include +#include +#include -#define TINYOBJLOADER_IMPLEMENTATION -#include +#include +#include +#include #include "log_system.h" namespace simple_renderer { -Model::Vertex::Vertex(Coord coord, Normal normal, TextureCoord texture_coord, - const Color &color) - : coord_(std::move(coord)), - normal_(std::move(normal)), - texture_coord_(std::move(texture_coord)), - color_(color) {} - -auto Model::Vertex::operator*(const Matrix4f &tran) const -> Model::Vertex { - auto vertex(*this); - - auto res4 = Vector4f(coord_.x(), coord_.y(), coord_.z(), 1); - auto ret4 = Vector4f(tran * res4); - - vertex.coord_.x() = ret4.x(); - vertex.coord_.y() = ret4.y(); - vertex.coord_.z() = ret4.z(); - - /// @todo 变换法线 - - return vertex; -} - -Model::Face::Face(const Model::Vertex &v0, const Model::Vertex &v1, - const Model::Vertex &v2, Material material) - : v0_(v0), v1_(v1), v2_(v2), material_(std::move(material)) { - // 计算法向量 - // 如果 obj 内包含法向量,直接使用即可 - if (v0.normal_.norm() != 0 && v1.normal_.norm() != 0 && - v2.normal_.norm() != 0) { - normal_ = (v0.normal_ + v1.normal_ + v2.normal_).normalized(); - } - // 手动计算 - else { - // 两条相临边的叉积 - auto v2v0 = v2.coord_ - v0.coord_; - auto v1v0 = v1.coord_ - v0.coord_; - normal_ = (v2v0.cross(v1v0)).normalized(); +// Constructor that loads a model from a file path +// 构造函数从文件路径加载模型 +Model::Model(const std::string& model_path) { loadModel(model_path); } + +// Load the model using Assimp and process its nodes and meshes +// 使用 Assimp 加载模型并处理其节点和网格 +void Model::loadModel(const std::string& path) { + Assimp::Importer importer; + const aiScene* scene = + importer.ReadFile(path, aiProcess_Triangulate | aiProcess_FlipUVs); + + // Check for errors in loading the model + // 检查加载模型时的错误 + if (!scene || scene->mFlags & AI_SCENE_FLAGS_INCOMPLETE || + !scene->mRootNode) { + SPDLOG_ERROR("Assimp Error: {}", importer.GetErrorString()); + throw std::runtime_error("Failed to load model with Assimp"); } -} -auto Model::Face::operator*(const Matrix4f &tran) const -> Model::Face { - auto face(*this); - face.v0_ = face.v0_ * tran; - face.v1_ = face.v1_ * tran; - face.v2_ = face.v2_ * tran; + // Store the directory path of the model + // 存储模型的目录路径 + directory_ = path.substr(0, path.find_last_of('/')); - /// @todo 通过矩阵变换法线 - auto v2v0 = face.v2_.coord_ - face.v0_.coord_; - auto v1v0 = face.v1_.coord_ - face.v0_.coord_; - face.normal_ = (v2v0.cross(v1v0)).normalized(); + SPDLOG_INFO( + "Loaded model path: {}, Directory: {}, with meshes: {}, materials: {}", + path, directory_, scene->mNumMeshes, scene->mNumMaterials); - return face; + // Process the root node recursively + // 递归处理根节点 + processNode(scene->mRootNode, scene); } -Model::Model(const std::string &obj_path, const std::string &mtl_path) - : obj_path_(obj_path), mtl_path_(mtl_path) { - SPDLOG_DEBUG(SRLOG, "obj_path: {}", obj_path_); - - tinyobj::ObjReader reader; - tinyobj::ObjReaderConfig config; - config.mtl_search_path = mtl_path_; - // 默认开启三角化 - auto ret = reader.ParseFromFile(obj_path_, config); - if (!ret) { - if (!reader.Error().empty()) { - SPDLOG_ERROR(reader.Error()); - throw std::runtime_error(reader.Error()); - } +// Recursively process nodes in the model +// 递归处理模型中的节点 +void Model::processNode(aiNode* node, const aiScene* scene) { + // Process each mesh in the node + // 处理节点中的每个网格 + for (unsigned int i = 0; i < node->mNumMeshes; i++) { + aiMesh* mesh = scene->mMeshes[node->mMeshes[i]]; + processMesh(mesh, scene); } - if (!reader.Warning().empty()) { - SPDLOG_WARN("TinyObjReader {}", reader.Warning()); + // Recursively process each child node + // 递归处理每个子节点 + for (unsigned int i = 0; i < node->mNumChildren; i++) { + processNode(node->mChildren[i], scene); } +} - const auto &attrib = reader.GetAttrib(); - const auto &shapes = reader.GetShapes(); - const auto &materials = reader.GetMaterials(); - - SPDLOG_INFO( - "加载模型: {}, 顶点数: {}, 法线数: {}, 颜色数: {}, UV数: {}, " - "子模型数: {}, 材质数: {}", - obj_path_, attrib.vertices.size() / 3, attrib.normals.size() / 3, - attrib.colors.size() / 3, attrib.texcoords.size() / 2, shapes.size(), - materials.size()); - - // 遍历所有 shape - for (size_t shape_index = 0; shape_index < shapes.size(); shape_index++) { - auto shape = shapes[shape_index]; - // Loop over faces(polygon) - size_t index_offset = 0; - for (size_t num_face_vertices_size = 0; - num_face_vertices_size < shape.mesh.num_face_vertices.size(); - num_face_vertices_size++) { - // 由于开启了三角化,所有的 shape 都是由三个点组成的,即 fv == 3 - auto num_face_vertices = - size_t(shape.mesh.num_face_vertices[num_face_vertices_size]); - if (num_face_vertices != kTriangleFaceVertexCount) { - SPDLOG_ERROR("num_face_vertices != kTriangleFaceVertexCount: {}, {}", - num_face_vertices, kTriangleFaceVertexCount); - throw std::runtime_error( - "num_face_vertices != kTriangleFaceVertexCount"); - } - - auto vertexes = std::array(); - // 遍历面上的顶点,这里 fv == 3 - for (size_t num_face_vertices_idx = 0; - num_face_vertices_idx < num_face_vertices; num_face_vertices_idx++) { - // 获取索引 - auto idx = shape.mesh.indices[index_offset + num_face_vertices_idx]; - - // 构造顶点信息并保存 - // 每组顶点信息有 xyz 三个分量,因此需要 3* - auto coord = Coord(attrib.vertices[3 * size_t(idx.vertex_index) + 0], - attrib.vertices[3 * size_t(idx.vertex_index) + 1], - attrib.vertices[3 * size_t(idx.vertex_index) + 2]); - - // 如果法线索引存在(即 idx.normal_index >= 0), - // 则构造并保存,否则设置为 0 - Normal normal = Normal::Zero(); - if (idx.normal_index >= 0) { - normal = Normal(attrib.normals[3 * size_t(idx.normal_index) + 0], - attrib.normals[3 * size_t(idx.normal_index) + 1], - attrib.normals[3 * size_t(idx.normal_index) + 2]); - } +// Process a single mesh and extract vertices, normals, and faces +// 处理单个网格并提取顶点、法线和面 +void Model::processMesh(aiMesh* mesh, const aiScene* scene) { + std::vector vertices; + + // Process vertices + // 处理顶点 + for (unsigned int i = 0; i < mesh->mNumVertices; ++i) { + Vector3f position(mesh->mVertices[i].x, mesh->mVertices[i].y, + mesh->mVertices[i].z); + Vector3f normal(mesh->mNormals[i].x, mesh->mNormals[i].y, + mesh->mNormals[i].z); + Vector2f texCoords(0.0f, 0.0f); + // Check if the mesh has texture coordinates + // 检查网格是否有纹理坐标 + if (mesh->mTextureCoords[0]) { + texCoords = + Vector2f(mesh->mTextureCoords[0][i].x, mesh->mTextureCoords[0][i].y); + } - // 如果贴图索引存在(即 idx.texcoord_index >= 0), - // 则构造并保存,否则设置为 0 - TextureCoord texture_coord = TextureCoord::Zero(); - if (idx.texcoord_index >= 0) { - texture_coord = TextureCoord( - attrib.texcoords[2 * size_t(idx.texcoord_index) + 0], - attrib.texcoords[2 * size_t(idx.texcoord_index) + 1]); - } + Color color(255.f, 255.f, 255.f, 255.f); // Default color (white) + // 默认颜色(白色) - // 顶点颜色,如果 obj 文件中没有指定则设为 1(白色),范围 [0, 1] - auto color = Color(attrib.colors[3 * size_t(idx.vertex_index) + 0], - attrib.colors[3 * size_t(idx.vertex_index) + 1], - attrib.colors[3 * size_t(idx.vertex_index) + 2]); - vertexes.at(num_face_vertices_idx) = - Vertex(coord, normal, texture_coord, color); - } - index_offset += num_face_vertices; + vertices.emplace_back(Vector4f(position, 1.0f), normal, texCoords, color); + } - // 如果材质不为空,加载材质信息 - auto material = Material(); - if (!materials.empty()) { - material.shininess = materials[shape_index].shininess; - material.ambient = Vector3f(materials[shape_index].ambient[0], - materials[shape_index].ambient[1], - materials[shape_index].ambient[2]); - material.diffuse = Vector3f(materials[shape_index].diffuse[0], - materials[shape_index].diffuse[1], - materials[shape_index].diffuse[2]); - material.specular = Vector3f(materials[shape_index].specular[0], - materials[shape_index].specular[1], - materials[shape_index].specular[2]); - } - // 添加到 face 中 - faces_.emplace_back(vertexes[0], vertexes[1], vertexes[2], material); + // Process faces (assuming triangulation, so each face has 3 vertices) + // 处理面(假设三角化,因此每个面有3个顶点) + for (unsigned int i = 0; i < mesh->mNumFaces; ++i) { + aiFace face = mesh->mFaces[i]; + if (face.mNumIndices == 3) { // Triangle, 三角形 + Vertex v0 = vertices[face.mIndices[0]]; + Vertex v1 = vertices[face.mIndices[1]]; + Vertex v2 = vertices[face.mIndices[2]]; + + // Process the material associated with this mesh + // 处理与此网格关联的材质 + Material material = + processMaterial(scene->mMaterials[mesh->mMaterialIndex]); + + // Create a Face object and store it + // 创建一个 Face 对象并存储它 + faces_.emplace_back(v0, v1, v2, std::move(material)); } } - - Normalize(); } -auto Model::operator*(const Matrix4f &tran) const -> Model { - auto model = Model(*this); +// Extract material properties from the Assimp material structure +// 从 Assimp 材质结构中提取材质属性 +Material Model::processMaterial(aiMaterial* mat) { + Material material; - for (auto &i : model.faces_) { - i = i * tran; + aiColor3D ambient(0.0f, 0.0f, 0.0f); + if (AI_SUCCESS == mat->Get(AI_MATKEY_COLOR_AMBIENT, ambient)) { + material.ambient = Vector3f(ambient.r, ambient.g, ambient.b); } - return model; -} - -auto Model::GetFace() const -> const std::vector & { - return faces_; -} - -std::pair Model::GetMaxMinXYX() { - auto max = Coord(std::numeric_limits::lowest(), - std::numeric_limits::lowest(), - std::numeric_limits::lowest()); - auto min = Coord(std::numeric_limits::max(), - std::numeric_limits::max(), - std::numeric_limits::max()); - - for (const auto &i : faces_) { - auto curr_max_x = std::max(i.v0_.coord_.x(), - std::max(i.v1_.coord_.x(), i.v2_.coord_.x())); - auto curr_max_y = std::max(i.v0_.coord_.y(), - std::max(i.v1_.coord_.y(), i.v2_.coord_.y())); - auto curr_max_z = std::max(i.v0_.coord_.z(), - std::max(i.v1_.coord_.z(), i.v2_.coord_.z())); - - max.x() = curr_max_x > max.x() ? curr_max_x : max.x(); - max.y() = curr_max_y > max.y() ? curr_max_y : max.y(); - max.z() = curr_max_z > max.z() ? curr_max_z : max.z(); - - auto curr_min_x = std::min(i.v0_.coord_.x(), - std::min(i.v1_.coord_.x(), i.v2_.coord_.x())); - auto curr_min_y = std::min(i.v0_.coord_.y(), - std::min(i.v1_.coord_.y(), i.v2_.coord_.y())); - auto curr_min_z = std::min(i.v0_.coord_.z(), - std::min(i.v1_.coord_.z(), i.v2_.coord_.z())); - - min.x() = curr_min_x < min.x() ? curr_min_x : min.x(); - min.y() = curr_min_y < min.y() ? curr_min_y : min.y(); - min.z() = curr_min_z < min.z() ? curr_min_z : min.z(); + aiColor3D diffuse(1.0f, 1.0f, 1.0f); + if (AI_SUCCESS == mat->Get(AI_MATKEY_COLOR_DIFFUSE, diffuse)) { + material.diffuse = Vector3f(diffuse.r, diffuse.g, diffuse.b); } - return {max, min}; -} - -void Model::Normalize() { - auto [max, min] = GetMaxMinXYX(); - - auto x = std::abs(max.x()) + std::abs(min.x()); - auto y = std::abs(max.y()) + std::abs(min.y()); - auto z = std::abs(max.z()) + std::abs(min.z()); - auto scale = 1.0f / std::max(x, std::max(y, z)); - auto scale_matrix = Matrix4f(Matrix4f::Identity()); - scale_matrix.diagonal() << scale, scale, scale, 1; - - auto center = Coord((max.x() + min.x()) / 2.f, (max.y() + min.y()) / 2.f, - (max.z() + min.z()) / 2.f); - auto translation_matrix = Matrix4f(Matrix4f::Identity()); - translation_matrix.col(translation_matrix.cols() - 1) << -center.x(), - -center.y(), -center.z(), 1; + aiColor3D specular(0.0f, 0.0f, 0.0f); + if (AI_SUCCESS == mat->Get(AI_MATKEY_COLOR_SPECULAR, specular)) { + material.specular = Vector3f(specular.r, specular.g, specular.b); + } - auto matrix = Matrix4f(scale_matrix * translation_matrix); + float shininess = 0.0f; + if (AI_SUCCESS == mat->Get(AI_MATKEY_SHININESS, shininess)) { + material.shininess = shininess; + } - SPDLOG_DEBUG("matrix: {}", matrix); + return material; +} - *this = *this * matrix; +// Apply a transformation matrix to all faces in the model +// 对模型中的所有面应用变换矩阵 +void Model::transform(const Matrix4f& tran) { + for (auto& face : faces_) { + face.transform(tran); + } } } // namespace simple_renderer diff --git a/src/scene.cpp b/src/scene.cpp deleted file mode 100644 index 49c48e9..0000000 --- a/src/scene.cpp +++ /dev/null @@ -1,43 +0,0 @@ - -/** - * @file scene.cpp - * @brief 场景抽象 - * @author Zone.N (Zone.Niuzh@hotmail.com) - * @version 1.0 - * @date 2022-12-15 - * @copyright MIT LICENSE - * https://github.com/Simple-XX/SimpleRenderer - * @par change log: - * - *
DateAuthorDescription - *
2022-12-15Zone.N创建文件 - *
- */ - -#include "scene.h" -#include "log_system.h" -#include "matrix.hpp" - -namespace simple_renderer { - -scene_t::scene_t(const std::string &_name, uint64_t _x, uint64_t _y, - uint64_t _z) - : name(_name), x(_x), y(_y), z(_z) {} - -void scene_t::add_model(const model_t &_model, const vector3f_t _pos) { - models.push_back(std::pair(_model, _pos)); - SPDLOG_LOGGER_INFO(SRLOG, "add_model: {} to: {}, total: {}", _model.obj_path, - name, models.size()); -} - -void scene_t::add_light(const light_t &_light) { - lights.push_back(_light); - SPDLOG_LOGGER_INFO(SRLOG, "add_light: {} to: {}", _light.name, name); -} - -void scene_t::add_camera(const SimpleRenderer::camera_t &_camera) { - camera = _camera; - SPDLOG_LOGGER_INFO(SRLOG, "add_camera: {} to: {}", _camera.name, name); -} - -} // namespace SimpleRenderer diff --git a/src/shader_base.cpp b/src/shader_base.cpp index c0e9a83..b5b1f39 100644 --- a/src/shader_base.cpp +++ b/src/shader_base.cpp @@ -18,9 +18,9 @@ namespace simple_renderer { -ShaderVertexIn::ShaderVertexIn(const Model::Face &face) : face_(face) {} +ShaderVertexIn::ShaderVertexIn(const Face &face) : face_(face) {} -ShaderVertexOut::ShaderVertexOut(const Model::Face &face) : face_(face) {} +ShaderVertexOut::ShaderVertexOut(const Face &face) : face_(face) {} ShaderFragmentIn::ShaderFragmentIn(const Vector3f &barycentric_coord, const Vector3f &normal, diff --git a/src/simple_renderer.cpp b/src/simple_renderer.cpp index c259899..fafbc15 100755 --- a/src/simple_renderer.cpp +++ b/src/simple_renderer.cpp @@ -58,7 +58,7 @@ SimpleRenderer::SimpleRenderer(size_t width, size_t height, uint32_t *buffer, } bool SimpleRenderer::render(const Model &model) { - SPDLOG_INFO("render model: {}", model.obj_path_); + SPDLOG_INFO("render model: {}", model.modelPath()); auto shader = DefaultShader(); auto light = Light(); DrawModel(shader, light, model, 1, 0); @@ -126,56 +126,59 @@ void SimpleRenderer::DrawLine(float x0, float y0, float x1, float y1, } void SimpleRenderer::DrawTriangle(const ShaderBase &shader, const Light &light, - const Model::Normal &normal, - const Model::Face &face) { - auto v0 = face.v0_; - auto v1 = face.v1_; - auto v2 = face.v2_; + const Vector3f &normal, const Face &face) { + auto v0 = face.vertex(0); + auto v1 = face.vertex(1); + auto v2 = face.vertex(2); // 获取三角形的最小 box - auto min = v0.coord_; - auto max = v1.coord_; - auto max_x = std::max(face.v0_.coord_.x(), - std::max(face.v1_.coord_.x(), face.v2_.coord_.x())); - auto max_y = std::max(face.v0_.coord_.y(), - std::max(face.v1_.coord_.y(), face.v2_.coord_.y())); - max.x() = max_x > max.x() ? max_x : max.x(); - max.y() = max_y > max.y() ? max_y : max.y(); - max.z() = 0; - auto min_x = std::min(face.v0_.coord_.x(), - std::min(face.v1_.coord_.x(), face.v2_.coord_.x())); - auto min_y = std::min(face.v0_.coord_.y(), - std::min(face.v1_.coord_.y(), face.v2_.coord_.y())); - min.x() = min_x < min.x() ? min_x : min.x(); - min.y() = min_y < min.y() ? min_y : min.y(); - min.z() = 0; + auto min = v0.position(); + auto max = v1.position(); + auto max_x = std::max( + face.vertex(0).position().x, + std::max(face.vertex(1).position().x, face.vertex(2).position().x)); + auto max_y = std::max( + face.vertex(0).position().y, + std::max(face.vertex(1).position().y, face.vertex(2).position().y)); + max.x = max_x > max.x ? max_x : max.x; + max.y = max_y > max.y ? max_y : max.y; + max.z = 0; + auto min_x = std::min( + face.vertex(0).position().x, + std::min(face.vertex(1).position().x, face.vertex(2).position().x)); + auto min_y = std::min( + face.vertex(0).position().y, + std::min(face.vertex(1).position().y, face.vertex(2).position().y)); + min.x = min_x < min.x ? min_x : min.x; + min.y = min_y < min.y ? min_y : min.y; + min.z = 0; #pragma omp parallel for num_threads(kNProc) collapse(2) default(none) \ shared(min, max, v0, v1, v2, shader) firstprivate(normal, light) - for (auto x = int32_t(min.x()); x <= int32_t(max.x()); x++) { - for (auto y = int32_t(min.y()); y <= int32_t(max.y()); y++) { + for (auto x = int32_t(min.x); x <= int32_t(max.x); x++) { + for (auto y = int32_t(min.y); y <= int32_t(max.y); y++) { /// @todo 这里要用裁剪替换掉 if ((unsigned)x >= width_ || (unsigned)y >= height_) { continue; } auto [is_inside, barycentric_coord] = GetBarycentricCoord( - v0.coord_, v1.coord_, v2.coord_, + v0.position(), v1.position(), v2.position(), Vector3f(static_cast(x), static_cast(y), 0)); // 如果点在三角形内再进行下一步 if (!is_inside) { continue; } // 计算该点的深度,通过重心坐标插值计算 - auto z = InterpolateDepth(v0.coord_.z(), v1.coord_.z(), v2.coord_.z(), - barycentric_coord); + auto z = InterpolateDepth(v0.position().z, v1.position().z, + v2.position().z, barycentric_coord); // 深度在已有颜色之上 if (z < depth_buffer_[y * width_ + x]) { continue; } // 构造着色器输入 auto shader_fragment_in = - ShaderFragmentIn(barycentric_coord, normal, light.dir, v0.color_, - v1.color_, v2.color_); + ShaderFragmentIn(barycentric_coord, normal, light.dir, v0.color(), + v1.color(), v2.color()); // 计算颜色,颜色为通过 shader 片段着色器计算 auto shader_fragment_out = shader.Fragment(shader_fragment_in); // 如果不需要绘制则跳过 @@ -194,29 +197,32 @@ void SimpleRenderer::DrawTriangle(const ShaderBase &shader, const Light &light, void SimpleRenderer::DrawModel(const ShaderBase &shader, const Light &light, const Model &model, bool draw_line, bool draw_triangle) { - SPDLOG_INFO("draw {}", model.obj_path_); + SPDLOG_INFO("draw {}", model.modelPath()); if (draw_line) { #pragma omp parallel for num_threads(kNProc) default(none) shared(shader) \ firstprivate(model) - for (const auto &f : model.GetFace()) { + for (const auto &f : model.faces()) { /// @todo 巨大性能开销 auto face = shader.Vertex(ShaderVertexIn(f)).face_; - DrawLine(face.v0_.coord_.x(), face.v0_.coord_.y(), face.v1_.coord_.x(), - face.v1_.coord_.y(), Color::kRed); - DrawLine(face.v1_.coord_.x(), face.v1_.coord_.y(), face.v2_.coord_.x(), - face.v2_.coord_.y(), Color::kGreen); - DrawLine(face.v2_.coord_.x(), face.v2_.coord_.y(), face.v0_.coord_.x(), - face.v0_.coord_.y(), Color::kBlue); + DrawLine(face.vertex(0).position().x, face.vertex(0).position().y, + face.vertex(1).position().x, face.vertex(1).position().y, + Color::kRed); + DrawLine(face.vertex(1).position().x, face.vertex(1).position().y, + face.vertex(2).position().x, face.vertex(2).position().y, + Color::kGreen); + DrawLine(face.vertex(2).position().x, face.vertex(2).position().y, + face.vertex(0).position().x, face.vertex(0).position().y, + Color::kBlue); } } if (draw_triangle) { #pragma omp parallel for num_threads(kNProc) default(none) shared(shader) \ firstprivate(model, light) - for (const auto &f : model.GetFace()) { + for (const auto &f : model.faces()) { /// @todo 巨大性能开销 auto face = shader.Vertex(ShaderVertexIn(f)).face_; - DrawTriangle(shader, light, face.normal_, face); + DrawTriangle(shader, light, face.normal(), face); } } } @@ -229,17 +235,17 @@ auto SimpleRenderer::GetBarycentricCoord(const Vector3f &p0, const Vector3f &p1, auto p2p0 = p2 - p0; auto pap0 = pa - p0; - auto deno = (p1p0.x() * p2p0.y() - p1p0.y() * p2p0.x()); + auto deno = (p1p0.x * p2p0.y - p1p0.y * p2p0.x); if (std::abs(deno) < std::numeric_limits::epsilon()) { return std::pair{false, Vector3f()}; } - auto s = (p2p0.y() * pap0.x() - p2p0.x() * pap0.y()) / deno; + auto s = (p2p0.y * pap0.x - p2p0.x * pap0.y) / deno; if ((s > 1) || (s < 0)) { return std::pair{false, Vector3f()}; } - auto t = (p1p0.x() * pap0.y() - p1p0.y() * pap0.x()) / deno; + auto t = (p1p0.x * pap0.y - p1p0.y * pap0.x) / deno; if ((t > 1) || (t < 0)) { return std::pair{false, Vector3f()}; } @@ -254,9 +260,9 @@ auto SimpleRenderer::GetBarycentricCoord(const Vector3f &p0, const Vector3f &p1, auto SimpleRenderer::InterpolateDepth(float depth0, float depth1, float depth2, const Vector3f &_barycentric_coord) -> float { - auto depth = depth0 * _barycentric_coord.x(); - depth += depth1 * _barycentric_coord.y(); - depth += depth2 * _barycentric_coord.z(); + auto depth = depth0 * _barycentric_coord.x; + depth += depth1 * _barycentric_coord.y; + depth += depth2 * _barycentric_coord.z; return depth; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index a568aeb..e8267a4 100755 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -14,30 +14,31 @@ enable_testing() include(GoogleTest) include_directories( - ${SimpleRenderer_SOURCE_DIR}/src/include + ${SimpleRenderer_SOURCE_DIR}/src/include ) list(APPEND DEFAULT_TEST_COMPILE_OPTIONS - ${DEFAULT_COMPILE_OPTIONS} - --coverage + ${DEFAULT_COMPILE_OPTIONS} + --coverage ) list(APPEND DEFAULT_TEST_LINK_OPTIONS - --coverage - $<$: - # -fsanitize=leak - > - # -fsanitize=address - -fno-omit-frame-pointer + --coverage + $<$: + # -fsanitize=leak + > + # -fsanitize=address + -fno-omit-frame-pointer ) link_libraries( - ${DEFAULT_LINK_LIB} - gtest_main - ${glog_LIBRARIES} - SimpleRenderer + ${DEFAULT_LINK_LIB} + gtest_main + ${glog_LIBRARIES} + SimpleRenderer ) + add_subdirectory(unit_test) #add_subdirectory(integration_test) add_subdirectory(system_test) @@ -47,4 +48,4 @@ add_coverage_target( SOURCE_DIR ${SimpleRenderer_SOURCE_DIR} BINARY_DIR ${SimpleRenderer_BINARY_DIR} EXCLUDE_DIR ${SimpleRenderer_SOURCE_DIR}/3rd/* -) +) \ No newline at end of file diff --git a/test/system_test/CMakeLists.txt b/test/system_test/CMakeLists.txt index 6d3b0f5..3e976e3 100755 --- a/test/system_test/CMakeLists.txt +++ b/test/system_test/CMakeLists.txt @@ -23,4 +23,4 @@ target_compile_options(system_test PRIVATE target_link_options(system_test PRIVATE ${DEFAULT_TEST_LINK_OPTIONS} -) +) \ No newline at end of file diff --git a/test/system_test/camera.h b/test/system_test/camera.h index ffaf55b..d64693d 100644 --- a/test/system_test/camera.h +++ b/test/system_test/camera.h @@ -21,7 +21,7 @@ #include #include "color.h" -#include "vector.hpp" +#include "math.hpp" namespace simple_renderer { diff --git a/test/system_test/main.cpp b/test/system_test/main.cpp index 6000cd2..d1024a8 100755 --- a/test/system_test/main.cpp +++ b/test/system_test/main.cpp @@ -26,8 +26,8 @@ /// @name 默认大小 /// @{ -static constexpr const size_t kWidth = 1920; -static constexpr const size_t kHeight = 1080; +static constexpr const size_t kWidth = 800; +static constexpr const size_t kHeight = 600; /// @} static void pixel(size_t x, size_t y, uint32_t color, uint32_t *buffer) { @@ -59,11 +59,18 @@ int main(int argc, char **argv) { // objs.emplace_back(obj_path + "/african_head.obj"); objs.emplace_back(obj_path + "/utah-teapot/utah-teapot.obj"); + auto matrix = simple_renderer::Matrix4f(1.0f); + simple_renderer::Matrix4f scale_matrix = + glm::scale(simple_renderer::Matrix4f(1.0f), + simple_renderer::Vector3f(10.0f, 10.0f, 10.0f)); - auto matrix = - simple_renderer::Matrix4f(simple_renderer::Matrix4f::Identity()); - matrix.diagonal() << 500, 500, 500, 1; - matrix.col(matrix.cols() - 1) << kWidth / 2, kHeight / 2, 0, 1; + // Translation matrix + simple_renderer::Matrix4f translation_matrix = glm::translate( + simple_renderer::Matrix4f(1.0f), + simple_renderer::Vector3f(kWidth / 2.0f, kHeight / 2.0f, 0.0f)); + + // Combined transformation matrix + matrix = translation_matrix * scale_matrix; // 矩阵运算的顺序 // 归一化 @@ -83,7 +90,7 @@ int main(int argc, char **argv) { for (auto &obj : objs) { // 添加到场景中 auto model = simple_renderer::Model(obj); - model = model * matrix; + model.transform(matrix); simple_renderer.render(model); } diff --git a/test/unit_test/matrix_test.cpp b/test/unit_test/matrix_test.cpp index 9cb7543..4ddf650 100644 --- a/test/unit_test/matrix_test.cpp +++ b/test/unit_test/matrix_test.cpp @@ -15,8 +15,7 @@ * */ -#include "matrix.hpp" - #include "gtest/gtest.h" +#include "math.hpp" TEST(ttt1, todo1) { EXPECT_EQ(nullptr, nullptr); }