diff --git a/.gitignore b/.gitignore index b969363..f0f58df 100644 --- a/.gitignore +++ b/.gitignore @@ -1,49 +1,47 @@ -# Visual Studio user-specific files -*.user -*.suo -*.userosscache -*.sln.docstates - -# Visual Studio build artifacts -.vs/ -[Dd]ebug/ -[Rr]elease/ -x64/ -x86/ -[Aa][Rr][Mm]/ -[Aa][Rr][Mm]64/ -out/ -bin/ -obj/ - -# MSVC intermediate and debug files -*.obj -*.ipdb -*.iobj -*.ilk -*.pdb -*.idb -*.tlog -*.lastbuildstate -*.recipe -*.log - -# CMake / generated project files -CMakeFiles/ -CMakeCache.txt -cmake-build-*/ -compile_commands.json - -# Test and coverage output -*.gcno -*.gcda -*.profraw -*.profdata -*.coverage - -# OS / editor noise -.DS_Store -Thumbs.db -SDL2-2.32.10-win32-x64.zip -SDL2-devel-2.32.10-VC.zip -CPU-Software-Renderer/TODO.md +# Visual Studio user-specific files +*.user +*.suo +*.userosscache +*.sln.docstates + +# Visual Studio build artifacts +.vs/ +[Dd]ebug/ +[Rr]elease/ +[Aa][Rr][Mm]/ +[Aa][Rr][Mm]64/ +out/ +bin/ +obj/ + +# MSVC intermediate and debug files +*.obj +*.ipdb +*.iobj +*.ilk +*.pdb +*.idb +*.tlog +*.lastbuildstate +*.recipe +*.log + +# CMake / generated project files +CMakeFiles/ +CMakeCache.txt +cmake-build-*/ +compile_commands.json + +# Test and coverage output +*.gcno +*.gcda +*.profraw +*.profdata +*.coverage + +# OS / editor noise +.DS_Store +Thumbs.db +SDL2-2.32.10-win32-x64.zip +SDL2-devel-2.32.10-VC.zip +CPU-Software-Renderer/TODO.md diff --git a/CPU-Software-Renderer/Math/MathUtil.h b/CPU-Software-Renderer/Math/MathUtil.h index 996267a..b689050 100644 --- a/CPU-Software-Renderer/Math/MathUtil.h +++ b/CPU-Software-Renderer/Math/MathUtil.h @@ -16,7 +16,7 @@ namespace Math 1, 0, 0, translation.x, 0, 1, 0, translation.y, 0, 0, 1, translation.z, - 0, 0, 0, 0 + 0, 0, 0, 1 ); } @@ -98,4 +98,4 @@ namespace Math return vec1.cross(vec2); } }; -} \ No newline at end of file +} diff --git a/CPU-Software-Renderer/Scene/Camera.cpp b/CPU-Software-Renderer/Scene/Camera.cpp index 0879549..3e2cda1 100644 --- a/CPU-Software-Renderer/Scene/Camera.cpp +++ b/CPU-Software-Renderer/Scene/Camera.cpp @@ -23,16 +23,7 @@ namespace Scene ); } - Math::Matrix4x4 Camera::get_orthographic_projection_matrix(float aspectRatio) const - { - using namespace Math; - - return Matrix4x4( - - ); - } - - Math::Matrix4x4 Camera::get_perspective_projection_matrix(float width, float height) const + Math::Matrix4x4 Camera::get_orthographic_projection_matrix(float width, float height) const { using namespace Math; @@ -42,13 +33,27 @@ namespace Scene const float t = height * 0.5f; const float n = nearPlane; const float f = farPlane; - const float length = n - f; return Matrix4x4( - 2.0f / width, 0, 0, -(r + l) / (r - l) + 1, - 0, 2.0f / height, 0, -(t + b) / (t - b) + 1, - 0, 0, 2.0f / length, -(n + f) / (n - f) + 1, + 2.0f / (r - l), 0, 0, -(r + l) / (r - l), + 0, 2.0f / (t - b), 0, -(t + b) / (t - b), + 0, 0, 2.0f / (n - f), -(n + f) / (f - n), 0, 0, 0, 1 ); } + + Math::Matrix4x4 Camera::get_perspective_projection_matrix(float aspectRatio) const + { + using namespace Math; + + const float n = nearPlane; + const float f = farPlane; + + return Matrix4x4( + 1.0f / (aspectRatio * std::tan(verticalFovRadians / 2)), 0, 0, 0, + 0, 1.0f / std::tan(verticalFovRadians / 2), 0, 0, + 0, 0, -(f + n) / (f - n), -(2 * f * n) / (f - n), + 0, 0, -1, 0 + ); + } } \ No newline at end of file diff --git a/CPU-Software-Renderer/Scene/Camera.h b/CPU-Software-Renderer/Scene/Camera.h index f61a089..9dc5ea5 100644 --- a/CPU-Software-Renderer/Scene/Camera.h +++ b/CPU-Software-Renderer/Scene/Camera.h @@ -38,8 +38,8 @@ namespace Scene Math::Matrix4x4 get_view_matrix() const; - Math::Matrix4x4 get_orthographic_projection_matrix(float aspectRatio) const; + Math::Matrix4x4 get_orthographic_projection_matrix(float width, float height) const; - Math::Matrix4x4 get_perspective_projection_matrix(float width, float height) const; + Math::Matrix4x4 get_perspective_projection_matrix(float aspectRatio) const; }; } diff --git a/CPU-Software-Renderer/libs/SDL2/lib/.gitkeep b/CPU-Software-Renderer/libs/SDL2/lib/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/CPU-Software-Renderer/libs/SDL2/lib/x64/SDL2.dll b/CPU-Software-Renderer/libs/SDL2/lib/x64/SDL2.dll new file mode 100644 index 0000000..a83d4ba Binary files /dev/null and b/CPU-Software-Renderer/libs/SDL2/lib/x64/SDL2.dll differ diff --git a/CPU-Software-Renderer/libs/SDL2/lib/x64/SDL2.lib b/CPU-Software-Renderer/libs/SDL2/lib/x64/SDL2.lib new file mode 100644 index 0000000..325dbd5 Binary files /dev/null and b/CPU-Software-Renderer/libs/SDL2/lib/x64/SDL2.lib differ diff --git a/CPU-Software-Renderer/libs/SDL2/lib/x64/SDL2main.lib b/CPU-Software-Renderer/libs/SDL2/lib/x64/SDL2main.lib new file mode 100644 index 0000000..d5692e4 Binary files /dev/null and b/CPU-Software-Renderer/libs/SDL2/lib/x64/SDL2main.lib differ diff --git a/CPU-Software-Renderer/libs/SDL2/lib/x64/SDL2test.lib b/CPU-Software-Renderer/libs/SDL2/lib/x64/SDL2test.lib new file mode 100644 index 0000000..a26b080 Binary files /dev/null and b/CPU-Software-Renderer/libs/SDL2/lib/x64/SDL2test.lib differ diff --git a/CPU-Software-Renderer/libs/SDL2/lib/x86/SDL2.dll b/CPU-Software-Renderer/libs/SDL2/lib/x86/SDL2.dll new file mode 100644 index 0000000..49d566b Binary files /dev/null and b/CPU-Software-Renderer/libs/SDL2/lib/x86/SDL2.dll differ diff --git a/CPU-Software-Renderer/libs/SDL2/lib/x86/SDL2.lib b/CPU-Software-Renderer/libs/SDL2/lib/x86/SDL2.lib new file mode 100644 index 0000000..5eb3674 Binary files /dev/null and b/CPU-Software-Renderer/libs/SDL2/lib/x86/SDL2.lib differ diff --git a/CPU-Software-Renderer/libs/SDL2/lib/x86/SDL2main.lib b/CPU-Software-Renderer/libs/SDL2/lib/x86/SDL2main.lib new file mode 100644 index 0000000..31d8cdb Binary files /dev/null and b/CPU-Software-Renderer/libs/SDL2/lib/x86/SDL2main.lib differ diff --git a/CPU-Software-Renderer/libs/SDL2/lib/x86/SDL2test.lib b/CPU-Software-Renderer/libs/SDL2/lib/x86/SDL2test.lib new file mode 100644 index 0000000..f60501c Binary files /dev/null and b/CPU-Software-Renderer/libs/SDL2/lib/x86/SDL2test.lib differ diff --git a/CPU-Software-Renderer/main.cpp b/CPU-Software-Renderer/main.cpp index 92635fb..40b32c4 100644 --- a/CPU-Software-Renderer/main.cpp +++ b/CPU-Software-Renderer/main.cpp @@ -1,13 +1,20 @@ -#include +#include +#include +#include #include #include "SDL_video.h" #include "SDL_render.h" #include #include "SDL_error.h" #include "Vector2.h" +#include "Vector3.h" +#include "Vector4.h" +#include "Matrix4x4.h" +#include "MathUtil.h" #include "Color.h" #include "FrameBuffer.h" #include "Rasterizer.h" +#include "Camera.h" #include "SDL_events.h" #include "SDL_keycode.h" @@ -106,6 +113,64 @@ static bool EnsureTexture() return true; } +struct ProjectedVertex +{ + Math::Vector2Int screen; + bool visible = false; +}; + +struct CubeFace +{ + std::array vertices; + std::array edges; +}; + +static ProjectedVertex ProjectToScreen( + const Math::Vector3& vertex, + const Math::Matrix4x4& mvp, + const int32_t screenWidth, + const int32_t screenHeight) +{ + using namespace Math; + + const Vector4 clip = mvp * Vector4::Point(vertex); + if (std::abs(clip.w) < 1e-5f) + { + return {}; + } + + const float invW = 1.0f / clip.w; + const float ndcX = clip.x * invW; + const float ndcY = clip.y * invW; + const float ndcZ = clip.z * invW; + + if (ndcX < -1.0f || ndcX > 1.0f || ndcY < -1.0f || ndcY > 1.0f || ndcZ < -1.0f || ndcZ > 1.0f) + { + return {}; + } + + const float screenX = (ndcX * 0.5f + 0.5f) * static_cast(screenWidth - 1); + const float screenY = (1.0f - (ndcY * 0.5f + 0.5f)) * static_cast(screenHeight - 1); + + return { Vector2(screenX, screenY).to_vector2Int(), true }; +} + +static bool IsFaceVisible(const CubeFace& face, const std::array& viewSpaceVertices) +{ + using namespace Math; + + const Vector3& v0 = viewSpaceVertices[face.vertices[0]]; + const Vector3& v1 = viewSpaceVertices[face.vertices[1]]; + const Vector3& v2 = viewSpaceVertices[face.vertices[2]]; + const Vector3 faceNormal = (v1 - v0).cross(v2 - v0); + const Vector3 faceCenter = + (viewSpaceVertices[face.vertices[0]] + + viewSpaceVertices[face.vertices[1]] + + viewSpaceVertices[face.vertices[2]] + + viewSpaceVertices[face.vertices[3]]) / 4.0f; + + return faceNormal.dot(faceCenter) < 0.0f; +} int main(int argc, char* argv[]) { @@ -114,21 +179,47 @@ int main(int argc, char* argv[]) if (!EnsureSDLWindow()) return -1; if (!EnsureRenderer()) return -1; - + if (!EnsureTexture()) return -1; Core::FrameBuffer frameBuffer(width, height); Rasterizer::Rasterizer rasterizer(frameBuffer); - Math::Vector2Int v0(100, 100); - Math::Vector2Int v1(300, 100); - Math::Vector2Int v2(300, 400); - RenderData::Color color0(255, 0, 0, 255); - RenderData::Color color1(0, 255, 0, 255); - RenderData::Color color2(0, 0, 255, 255); - rasterizer.DrawLine(v0, v1, color0); - rasterizer.DrawLine(v1, v2, color1); - rasterizer.DrawLine(v2, v0, color2); + Scene::Camera camera; + camera.set_position(Math::Vector3(0.0f, 0.0f, 3.0f)); + camera.set_target(Math::Vector3(0.0f, 0.0f, 0.0f)); + camera.set_up(Math::Vector3(0.0f, 1.0f, 0.0f)); + camera.set_near_plane(0.1f); + camera.set_far_plane(100.0f); + + const std::array cubeVertices = { + Math::Vector3(-0.5f, -0.5f, -0.5f), + Math::Vector3(0.5f, -0.5f, -0.5f), + Math::Vector3(0.5f, 0.5f, -0.5f), + Math::Vector3(-0.5f, 0.5f, -0.5f), + Math::Vector3(-0.5f, -0.5f, 0.5f), + Math::Vector3(0.5f, -0.5f, 0.5f), + Math::Vector3(0.5f, 0.5f, 0.5f), + Math::Vector3(-0.5f, 0.5f, 0.5f) + }; + + const std::array, 12> cubeEdges = { + std::pair(0, 1), std::pair(1, 2), std::pair(2, 3), std::pair(3, 0), + std::pair(4, 5), std::pair(5, 6), std::pair(6, 7), std::pair(7, 4), + std::pair(0, 4), std::pair(1, 5), std::pair(2, 6), std::pair(3, 7) + }; + const std::array cubeFaces = { + CubeFace{ { 0, 3, 2, 1 }, { 0, 1, 2, 3 } }, + CubeFace{ { 4, 5, 6, 7 }, { 4, 5, 6, 7 } }, + CubeFace{ { 0, 4, 7, 3 }, { 8, 7, 11, 3 } }, + CubeFace{ { 1, 2, 6, 5 }, { 1, 10, 5, 9 } }, + CubeFace{ { 0, 1, 5, 4 }, { 0, 9, 4, 8 } }, + CubeFace{ { 3, 7, 6, 2 }, { 11, 6, 10, 2 } } + }; + + const RenderData::Color clearColor(18, 18, 24, 255); + const RenderData::Color cubeColor(240, 240, 240, 255); + const float aspectRatio = static_cast(width) / static_cast(height); bool isRuning = true; while (isRuning) @@ -147,6 +238,57 @@ int main(int argc, char* argv[]) } } + frameBuffer.clear(clearColor); + + const float timeSeconds = static_cast(SDL_GetTicks()) * 0.001f; + const Math::Matrix4x4 model = + Math::MathUtil::get_rotation_matrix_y(timeSeconds) * + Math::MathUtil::get_rotation_matrix_x(timeSeconds * 0.6f); + const Math::Matrix4x4 view = camera.get_view_matrix(); + const Math::Matrix4x4 modelView = view * model; + const Math::Matrix4x4 projection = camera.get_perspective_projection_matrix(aspectRatio); + const Math::Matrix4x4 mvp = projection * modelView; + + std::array viewSpaceVertices; + std::array projectedVertices; + for (size_t i = 0; i < cubeVertices.size(); ++i) + { + viewSpaceVertices[i] = (modelView * Math::Vector4::Point(cubeVertices[i])).to_vector3(); + projectedVertices[i] = ProjectToScreen(cubeVertices[i], mvp, width, height); + } + + std::array visibleEdges = {}; + for (const CubeFace& face : cubeFaces) + { + if (!IsFaceVisible(face, viewSpaceVertices)) + { + continue; + } + + for (const int edgeIndex : face.edges) + { + visibleEdges[edgeIndex] = true; + } + } + + for (size_t edgeIndex = 0; edgeIndex < cubeEdges.size(); ++edgeIndex) + { + if (!visibleEdges[edgeIndex]) + { + continue; + } + + const auto& edge = cubeEdges[edgeIndex]; + const ProjectedVertex& start = projectedVertices[edge.first]; + const ProjectedVertex& end = projectedVertices[edge.second]; + if (!start.visible || !end.visible) + { + continue; + } + + rasterizer.DrawLine(start.screen, end.screen, cubeColor); + } + SDL_UpdateTexture(texture, nullptr, frameBuffer.get_buffer(), width * sizeof(uint32_t)); SDL_RenderClear(renderer); SDL_RenderCopy(renderer, texture, nullptr, nullptr); @@ -156,4 +298,3 @@ int main(int argc, char* argv[]) ClearSDLResources(); return 0; } -