铺垫 DepthBuffer
- 将 Triangle2D 扩展到 Triangle,能支持三维的坐标 - 简单填充了深度缓冲的类实现 - 将 main 中进行测试的正方体的面打散成三角形
This commit is contained in:
parent
b5f81958df
commit
cc203c40f3
|
|
@ -0,0 +1,110 @@
|
|||
# CPU 软件渲染器项目约定
|
||||
|
||||
本文档用于记录当前项目已经采用的坐标系、矩阵、相机和屏幕空间约定,避免后续开发时在符号、方向和乘法顺序上产生混乱。
|
||||
|
||||
## 1. 通用约定
|
||||
|
||||
- 项目使用右手坐标系。
|
||||
- `Vector3::cross(a, b)` 遵循右手定则。
|
||||
- 除非特别说明,向量按列向量理解。
|
||||
- 变换写法按 `M * v` 解释。
|
||||
|
||||
## 2. 世界空间与局部空间
|
||||
|
||||
世界空间与物体局部空间目前使用同一套方向命名:
|
||||
|
||||
- `+X`:右
|
||||
- `+Y`:上
|
||||
- `+Z`:前
|
||||
|
||||
`Scene::Transform` 也遵循这套约定:
|
||||
|
||||
- `get_right()`:旋转后的局部右方向
|
||||
- `get_up()`:旋转后的局部上方向
|
||||
- `get_forward()`:旋转后的局部前方向
|
||||
- `get_left()`、`get_down()`、`get_back()`:分别为对应反方向
|
||||
|
||||
也就是说,`Transform` 中的 `forward` 明确定义为 `+Z`。
|
||||
|
||||
## 3. 旋转约定
|
||||
|
||||
- 欧拉角使用弧度制。
|
||||
- `rotation.x`:绕 `X` 轴旋转
|
||||
- `rotation.y`:绕 `Y` 轴旋转
|
||||
- `rotation.z`:绕 `Z` 轴旋转
|
||||
- 组合旋转顺序为 `Rz * Ry * Rx`
|
||||
|
||||
当前项目里,方向向量的旋转方式是先构造旋转矩阵,再去变换基础方向轴。
|
||||
|
||||
## 4. 矩阵约定
|
||||
|
||||
`Math::Matrix4x4` 当前采用以下规则:
|
||||
|
||||
- 语义上按列向量使用,写法为 `M * v`
|
||||
- 元素访问方式为 `matrix[row][col]`
|
||||
- `data()` 暴露的是连续的 row-major 内存
|
||||
- 平移分量存放在最后一列
|
||||
|
||||
因此,常见的组合变换阅读顺序是从右往左:
|
||||
|
||||
- `worldPosition = Translation * Rotation * Scale * localPosition`
|
||||
|
||||
## 5. 相机与视图空间
|
||||
|
||||
相机目前由 `Scene::Camera::transform` 驱动。
|
||||
|
||||
相机自身局部方向定义为:
|
||||
|
||||
- 相机右方向:`transform.get_right()`
|
||||
- 相机上方向:`transform.get_up()`
|
||||
- 相机前方向:`transform.get_forward()`
|
||||
|
||||
但进入视图空间后,当前项目采用的是常见的相机空间约定:
|
||||
|
||||
- 位于相机前方的点,其 view-space `z` 为负值
|
||||
- 视图矩阵第三行存的是相机 backward,而不是 forward
|
||||
|
||||
这和当前透视投影矩阵实现是一致的,因为那里对应的是 `clip.w = -viewZ` 这套约定。
|
||||
|
||||
## 6. 投影与 NDC
|
||||
|
||||
当前透视投影相关约定如下:
|
||||
|
||||
- 规范化设备坐标(NDC)可见范围为 `[-1, 1]`
|
||||
- `x`、`y`、`z` 都会在映射到 viewport 之前做范围检查
|
||||
- 当前测试代码里,如果点超出 NDC,可能会直接判为不可见,而不是继续做线段裁剪
|
||||
|
||||
这意味着:
|
||||
|
||||
- 当前 demo 还没有实现完整的视锥裁剪
|
||||
- 如果一条线段只有一部分还在屏幕内,但端点已经越出 NDC,整条线仍可能被直接丢弃
|
||||
|
||||
## 7. 屏幕与像素坐标
|
||||
|
||||
屏幕/像素坐标使用左上角为原点的约定:
|
||||
|
||||
- 原点在左上角
|
||||
- `x` 向右增大
|
||||
- `y` 向下增大
|
||||
|
||||
`Core::FrameBuffer` 当前行为如下:
|
||||
|
||||
- 有效像素范围:`x in [0, width)`
|
||||
- 有效像素范围:`y in [0, height)`
|
||||
- 越界写入会被直接忽略
|
||||
- 像素缓冲按 row-major 排列
|
||||
- 内存中的第一行对应 `y = 0`
|
||||
|
||||
`Camera::get_viewport_matrix()` 里也对 `Y` 做了翻转,因此 NDC 的“向上”为正,最终会映射成屏幕坐标“向下”为正。
|
||||
|
||||
## 8. Demo 中的可见性规则
|
||||
|
||||
`main.cpp` 里的旋转立方体示例,目前采用的是一个简化版隐藏线规则:
|
||||
|
||||
- 先在 view space 中判断哪些面朝向相机
|
||||
- 再把这些可见面的边标记为可绘制
|
||||
- 最后只绘制被标记出来的边
|
||||
|
||||
这只是一个 demo 级别的近似方案,不是完整的隐藏线消除算法。
|
||||
|
||||
后续如果项目要加入更严格的裁剪、剔除或可见性规则,应当以代码实现为准,并同步更新本文档。
|
||||
|
|
@ -157,7 +157,7 @@
|
|||
<ItemGroup>
|
||||
<None Include="..\README.md" />
|
||||
<None Include="..\TODO.md" />
|
||||
<None Include="TODO.md" />
|
||||
<None Include="CONVENTIONS.md" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Asset\ObjLoader.h" />
|
||||
|
|
|
|||
|
|
@ -77,11 +77,11 @@
|
|||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="TODO.md">
|
||||
<Filter>资源文件</Filter>
|
||||
</None>
|
||||
<None Include="..\README.md" />
|
||||
<None Include="..\TODO.md" />
|
||||
<None Include="CONVENTIONS.md">
|
||||
<Filter>资源文件</Filter>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Core\DepthBuffer.h">
|
||||
|
|
|
|||
|
|
@ -0,0 +1,23 @@
|
|||
#include "DepthBuffer.h"
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
|
||||
namespace Core
|
||||
{
|
||||
void DepthBuffer::clear(const uint8_t depth)
|
||||
{
|
||||
std::fill(buffer.begin(), buffer.end(), depth);
|
||||
}
|
||||
|
||||
|
||||
void DepthBuffer::set_pixel(const int32_t x, const int32_t y, const uint8_t depth)
|
||||
{
|
||||
if (x < 0 || x >= width || y < 0 || y >= height)
|
||||
{
|
||||
return;
|
||||
}
|
||||
// Row-major layout with y = 0 on the first row, matching a top-left screen origin.
|
||||
size_t index = static_cast<size_t>(y) * width + x;
|
||||
buffer.at(index) = depth;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,7 +1,36 @@
|
|||
#pragma once
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include "Color.h"
|
||||
#include "Vector2.h"
|
||||
|
||||
namespace Core
|
||||
{
|
||||
class DepthBuffer
|
||||
{};
|
||||
}
|
||||
{
|
||||
private:
|
||||
int32_t width;
|
||||
int32_t height;
|
||||
std::vector<uint8_t> buffer;
|
||||
|
||||
public:
|
||||
int32_t get_width() const { return width; }
|
||||
|
||||
int32_t get_height() const { return height; }
|
||||
|
||||
size_t total_pixels() const { return buffer.size(); }
|
||||
|
||||
void* get_buffer() const { return (void*)buffer.data(); }
|
||||
|
||||
DepthBuffer(int32_t width, int32_t height) :width(width), height(height), buffer(std::vector<uint8_t>(width* height, 0)) {}
|
||||
|
||||
void clear(const uint8_t depth);
|
||||
|
||||
void set_pixel(const Math::Vector2Int position, const uint8_t depth)
|
||||
{
|
||||
set_pixel(position.x, position.y, depth);
|
||||
}
|
||||
|
||||
void set_pixel(const int32_t x, const int32_t y, const uint8_t depth);
|
||||
};
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
#include "FrameBuffer.h"
|
||||
#include <cstdint>
|
||||
#include <iostream>
|
||||
#include <algorithm>
|
||||
using namespace Math;
|
||||
|
||||
namespace Core
|
||||
{
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
namespace Rasterizer
|
||||
{
|
||||
void TriangleRasterizer::DrawTriangle2D(const RenderData::Triangle2D triangle, const RenderData::Color color)
|
||||
void TriangleRasterizer::DrawTriangle2D(const RenderData::Triangle& triangle, const RenderData::Color color)
|
||||
{
|
||||
auto boundingBox = triangle.get_boundingBox();
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ namespace Rasterizer
|
|||
{
|
||||
for (int y = minY; y <= maxY; y++)
|
||||
{
|
||||
if (triangle.ContainsPixel(Math::Vector2(x, y)))
|
||||
if (triangle.ContainsPixel(Math::Vector2Int(x, y)))
|
||||
{
|
||||
frameBuffer.set_pixel(Math::Vector2Int(x, y), color.to_rgba());
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,6 @@ namespace Rasterizer
|
|||
public:
|
||||
explicit TriangleRasterizer(Core::FrameBuffer& frameBuffer) :frameBuffer(frameBuffer) {};
|
||||
|
||||
void DrawTriangle2D(const RenderData::Triangle2D triangle, const RenderData::Color color);
|
||||
void DrawTriangle2D(const RenderData::Triangle& triangle, const RenderData::Color color);
|
||||
};
|
||||
}
|
||||
|
|
@ -1,28 +1,30 @@
|
|||
#pragma once
|
||||
#include "Vector2.h"
|
||||
#include "BoundingBox.h"
|
||||
#include "Vertex.h"
|
||||
#include <cstdint>
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <Vector3.h>
|
||||
|
||||
namespace RenderData
|
||||
{
|
||||
struct Triangle2D
|
||||
struct Triangle
|
||||
{
|
||||
Math::Vector2 v0;
|
||||
Math::Vector2 v1;
|
||||
Math::Vector2 v2;
|
||||
Triangle2D() : v0(), v1(), v2() {}
|
||||
Triangle2D(const Math::Vector2 a, const Math::Vector2 b, const Math::Vector2 c) : v0(a), v1(b), v2(c) {}
|
||||
Scene::Vertex v0;
|
||||
Scene::Vertex v1;
|
||||
Scene::Vertex v2;
|
||||
Triangle() : v0(), v1(), v2() {}
|
||||
Triangle(const Scene::Vertex a, const Scene::Vertex b, const Scene::Vertex c) : v0(a), v1(b), v2(c) {}
|
||||
|
||||
BoundingBox2D get_boundingBox() const
|
||||
{
|
||||
using namespace Math;
|
||||
|
||||
int32_t minX = static_cast<int32_t>(std::floor(std::min({ v0.x, v1.x, v2.x })));
|
||||
int32_t maxX = static_cast<int32_t>(std::ceil(std::max({ v0.x, v1.x, v2.x })));
|
||||
int32_t minY = static_cast<int32_t>(std::floor(std::min({ v0.y, v1.y, v2.y })));
|
||||
int32_t maxY = static_cast<int32_t>(std::ceil(std::max({ v0.y, v1.y, v2.y })));
|
||||
int32_t minX = static_cast<int32_t>(std::floor(std::min({ v0.position.x, v1.position.x, v2.position.x })));
|
||||
int32_t maxX = static_cast<int32_t>(std::ceil(std::max({ v0.position.x, v1.position.x, v2.position.x })));
|
||||
int32_t minY = static_cast<int32_t>(std::floor(std::min({ v0.position.y, v1.position.y, v2.position.y })));
|
||||
int32_t maxY = static_cast<int32_t>(std::ceil(std::max({ v0.position.y, v1.position.y, v2.position.y })));
|
||||
|
||||
Vector2Int min(minX, minY);
|
||||
Vector2Int max(maxX, maxY);
|
||||
|
|
@ -30,16 +32,17 @@ namespace RenderData
|
|||
return BoundingBox2D(min, max);
|
||||
}
|
||||
|
||||
bool ContainsPixel(const Math::Vector2 point) const
|
||||
bool ContainsPixel(const Math::Vector2Int point) const
|
||||
{
|
||||
using namespace Math;
|
||||
using namespace Scene;
|
||||
|
||||
auto cross = [](const Vector2& p1, const Vector2& p2, const Vector2& p3) -> float
|
||||
auto cross = [](const Vertex& v1, const Vertex& v2, const Math::Vector2Int& point) -> float
|
||||
{
|
||||
const float x1 = p2.x - p1.x;
|
||||
const float y1 = p2.y - p1.y;
|
||||
const float x2 = p3.x - p1.x;
|
||||
const float y2 = p3.y - p1.y;
|
||||
Scene::Vertex v3(Math::Vector3(point.x, point.y, 0));
|
||||
const float x1 = v2.position.x - v1.position.x;
|
||||
const float y1 = v2.position.y - v1.position.y;
|
||||
const float x2 = v3.position.x - v1.position.x;
|
||||
const float y2 = v3.position.y - v1.position.y;
|
||||
return x1 * y2 - y1 * x2;
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -14,11 +14,15 @@
|
|||
#include "Color.h"
|
||||
#include "FrameBuffer.h"
|
||||
#include "Rasterizer.h"
|
||||
#include "TriangleRasterizer.h"
|
||||
#include "Triangle.h"
|
||||
#include "Camera.h"
|
||||
#include "SDL_events.h"
|
||||
#include "SDL_keycode.h"
|
||||
#include "SDL_timer.h"
|
||||
#include <cstdlib>
|
||||
#include <algorithm>
|
||||
#include "Vertex.h"
|
||||
|
||||
const uint32_t SDL_INIT_FLAGS = SDL_INIT_VIDEO;
|
||||
const int32_t width = 800;
|
||||
|
|
@ -117,14 +121,24 @@ static bool EnsureTexture()
|
|||
|
||||
struct ProjectedVertex
|
||||
{
|
||||
Math::Vector2Int screen;
|
||||
Math::Vector3 screen;
|
||||
bool visible = false;
|
||||
};
|
||||
|
||||
struct CubeFace
|
||||
{
|
||||
std::array<int, 4> vertices;
|
||||
std::array<int, 4> edges;
|
||||
};
|
||||
|
||||
struct CubeTriangle
|
||||
{
|
||||
std::array<int, 3> vertices;
|
||||
};
|
||||
|
||||
struct TriangleDrawCommand
|
||||
{
|
||||
RenderData::Triangle triangle;
|
||||
float averageViewSpaceZ = 0.0f;
|
||||
};
|
||||
|
||||
static ProjectedVertex ProjectToScreen(
|
||||
|
|
@ -151,10 +165,7 @@ static ProjectedVertex ProjectToScreen(
|
|||
}
|
||||
|
||||
const Vector4 screen = viewport * Vector4(ndcX, ndcY, ndcZ, 1.0f);
|
||||
const float screenX = screen.x;
|
||||
const float screenY = screen.y;
|
||||
|
||||
return { Vector2(screenX, screenY).to_vector2Int(), true };
|
||||
return { Math::Vector3(screen.x, screen.y, screen.z), true };
|
||||
}
|
||||
|
||||
static bool IsFaceVisible(const CubeFace& face, const std::array<Math::Vector3, 8>& viewSpaceVertices)
|
||||
|
|
@ -174,6 +185,19 @@ static bool IsFaceVisible(const CubeFace& face, const std::array<Math::Vector3,
|
|||
return faceNormal.dot(faceCenter) > 0.0f;
|
||||
}
|
||||
|
||||
static bool IsTriangleVisible(const CubeTriangle& triangle, const std::array<Math::Vector3, 8>& viewSpaceVertices)
|
||||
{
|
||||
using namespace Math;
|
||||
|
||||
const Vector3& v0 = viewSpaceVertices[triangle.vertices[0]];
|
||||
const Vector3& v1 = viewSpaceVertices[triangle.vertices[1]];
|
||||
const Vector3& v2 = viewSpaceVertices[triangle.vertices[2]];
|
||||
const Vector3 faceNormal = (v1 - v0).cross(v2 - v0);
|
||||
const Vector3 faceCenter = (v0 + v1 + v2) / 3.0f;
|
||||
|
||||
return faceNormal.dot(faceCenter) > 0.0f;
|
||||
}
|
||||
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
if (!EnsureSDLInitialized()) return -1;
|
||||
|
|
@ -186,6 +210,7 @@ int main(int argc, char* argv[])
|
|||
|
||||
Core::FrameBuffer frameBuffer(width, height);
|
||||
Rasterizer::Rasterizer rasterizer(frameBuffer);
|
||||
Rasterizer::TriangleRasterizer triangleRasterizer(frameBuffer);
|
||||
|
||||
Scene::Camera camera;
|
||||
camera.transform.position = Math::Vector3(0.0f, 0.0f, 3.0f);
|
||||
|
|
@ -202,18 +227,21 @@ int main(int argc, char* argv[])
|
|||
Math::Vector3(-0.5f, 0.5f, 0.5f)
|
||||
};
|
||||
|
||||
const std::array<std::pair<int, int>, 12> cubeEdges = {
|
||||
std::pair<int, int>(0, 1), std::pair<int, int>(1, 2), std::pair<int, int>(2, 3), std::pair<int, int>(3, 0),
|
||||
std::pair<int, int>(4, 5), std::pair<int, int>(5, 6), std::pair<int, int>(6, 7), std::pair<int, int>(7, 4),
|
||||
std::pair<int, int>(0, 4), std::pair<int, int>(1, 5), std::pair<int, int>(2, 6), std::pair<int, int>(3, 7)
|
||||
};
|
||||
const std::array<CubeFace, 6> 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 } }
|
||||
CubeFace{ { 0, 3, 2, 1 } },
|
||||
CubeFace{ { 4, 5, 6, 7 } },
|
||||
CubeFace{ { 0, 4, 7, 3 } },
|
||||
CubeFace{ { 1, 2, 6, 5 } },
|
||||
CubeFace{ { 0, 1, 5, 4 } },
|
||||
CubeFace{ { 3, 7, 6, 2 } }
|
||||
};
|
||||
const std::array<CubeTriangle, 12> cubeTriangles = {
|
||||
CubeTriangle{ { 0, 3, 2 } }, CubeTriangle{ { 0, 2, 1 } },
|
||||
CubeTriangle{ { 4, 5, 6 } }, CubeTriangle{ { 4, 6, 7 } },
|
||||
CubeTriangle{ { 0, 4, 7 } }, CubeTriangle{ { 0, 7, 3 } },
|
||||
CubeTriangle{ { 1, 2, 6 } }, CubeTriangle{ { 1, 6, 5 } },
|
||||
CubeTriangle{ { 0, 1, 5 } }, CubeTriangle{ { 0, 5, 4 } },
|
||||
CubeTriangle{ { 3, 7, 6 } }, CubeTriangle{ { 3, 6, 2 } }
|
||||
};
|
||||
|
||||
const RenderData::Color clearColor(18, 18, 24, 255);
|
||||
|
|
@ -257,36 +285,80 @@ int main(int argc, char* argv[])
|
|||
projectedVertices[i] = ProjectToScreen(cubeVertices[i], mvp, viewport);
|
||||
}
|
||||
|
||||
std::array<bool, 12> visibleEdges = {};
|
||||
for (const CubeFace& face : cubeFaces)
|
||||
std::array<bool, 6> visibleFaces = {};
|
||||
for (size_t faceIndex = 0; faceIndex < cubeFaces.size(); ++faceIndex)
|
||||
{
|
||||
if (!IsFaceVisible(face, viewSpaceVertices))
|
||||
visibleFaces[faceIndex] = IsFaceVisible(cubeFaces[faceIndex], viewSpaceVertices);
|
||||
}
|
||||
|
||||
std::array<TriangleDrawCommand, 12> drawCommands;
|
||||
size_t drawCommandCount = 0;
|
||||
for (const CubeTriangle& cubeTriangle : cubeTriangles)
|
||||
{
|
||||
if (!IsTriangleVisible(cubeTriangle, viewSpaceVertices))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
for (const int edgeIndex : face.edges)
|
||||
{
|
||||
visibleEdges[edgeIndex] = true;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t edgeIndex = 0; edgeIndex < cubeEdges.size(); ++edgeIndex)
|
||||
{
|
||||
if (!visibleEdges[edgeIndex])
|
||||
const ProjectedVertex& v0 = projectedVertices[cubeTriangle.vertices[0]];
|
||||
const ProjectedVertex& v1 = projectedVertices[cubeTriangle.vertices[1]];
|
||||
const ProjectedVertex& v2 = projectedVertices[cubeTriangle.vertices[2]];
|
||||
if (!v0.visible || !v1.visible || !v2.visible)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const auto& edge = cubeEdges[edgeIndex];
|
||||
const ProjectedVertex& start = projectedVertices[edge.first];
|
||||
const ProjectedVertex& end = projectedVertices[edge.second];
|
||||
const Math::Vector3& viewV0 = viewSpaceVertices[cubeTriangle.vertices[0]];
|
||||
const Math::Vector3& viewV1 = viewSpaceVertices[cubeTriangle.vertices[1]];
|
||||
const Math::Vector3& viewV2 = viewSpaceVertices[cubeTriangle.vertices[2]];
|
||||
|
||||
drawCommands[drawCommandCount++] = TriangleDrawCommand{
|
||||
RenderData::Triangle(
|
||||
Scene::Vertex(v0.screen),
|
||||
Scene::Vertex(v1.screen),
|
||||
Scene::Vertex(v2.screen)
|
||||
),
|
||||
(viewV0.z + viewV1.z + viewV2.z) / 3.0f
|
||||
};
|
||||
}
|
||||
|
||||
std::sort(
|
||||
drawCommands.begin(),
|
||||
drawCommands.begin() + drawCommandCount,
|
||||
[](const TriangleDrawCommand& a, const TriangleDrawCommand& b)
|
||||
{
|
||||
return a.averageViewSpaceZ < b.averageViewSpaceZ;
|
||||
});
|
||||
|
||||
for (size_t i = 0; i < drawCommandCount; ++i)
|
||||
{
|
||||
triangleRasterizer.DrawTriangle2D(drawCommands[i].triangle, cubeColor);
|
||||
}
|
||||
|
||||
for (size_t faceIndex = 0; faceIndex < cubeFaces.size(); ++faceIndex)
|
||||
{
|
||||
if (!visibleFaces[faceIndex])
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
const CubeFace& face = cubeFaces[faceIndex];
|
||||
for (size_t edgeOffset = 0; edgeOffset < face.vertices.size(); ++edgeOffset)
|
||||
{
|
||||
const int startIndex = face.vertices[edgeOffset];
|
||||
const int endIndex = face.vertices[(edgeOffset + 1) % face.vertices.size()];
|
||||
const ProjectedVertex& start = projectedVertices[startIndex];
|
||||
const ProjectedVertex& end = projectedVertices[endIndex];
|
||||
if (!start.visible || !end.visible)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
rasterizer.DrawLine(start.screen, end.screen, cubeColor);
|
||||
rasterizer.DrawLine(
|
||||
Math::Vector2(start.screen.x, start.screen.y).to_vector2Int(),
|
||||
Math::Vector2(end.screen.x, end.screen.y).to_vector2Int(),
|
||||
clearColor);
|
||||
}
|
||||
}
|
||||
|
||||
SDL_UpdateTexture(texture, nullptr, frameBuffer.get_buffer(), width * sizeof(uint32_t));
|
||||
|
|
|
|||
Loading…
Reference in New Issue