Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 0 additions & 35 deletions src/include/rasterizer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,41 +33,6 @@ class Rasterizer {
std::vector<Fragment> Rasterize(const Vertex& v0, const Vertex& v1,
const Vertex& v2);

/**
* @brief 非分配版本:将片段直接写入调用方提供的容器
*
* 可选的裁剪区域为半开区间 [x0, x1) × [y0, y1)
* 用于 TBR:将光栅化限制在 tile 边界内,便于复用外部 scratch 容器
*
* @param v0 三角形第一个顶点
* @param v1 三角形第二个顶点
* @param v2 三角形第三个顶点
* @param x0 裁剪区域左边界(包含)
* @param y0 裁剪区域上边界(包含)
* @param x1 裁剪区域右边界(不包含)
* @param y1 裁剪区域下边界(不包含)
* @param out 输出片段容器
*/
void RasterizeTo(const Vertex& v0, const Vertex& v1, const Vertex& v2,
int x0, int y0, int x1, int y1,
std::vector<Fragment>& out);

/**
* @brief SoA 版本:按顶点索引从 SoA 读取三角形三顶点
* @param soa 结构体数组格式的顶点数据
* @param i0 三角形第一个顶点索引
* @param i1 三角形第二个顶点索引
* @param i2 三角形第三个顶点索引
* @param x0 裁剪区域左边界(包含)
* @param y0 裁剪区域上边界(包含)
* @param x1 裁剪区域右边界(不包含)
* @param y1 裁剪区域下边界(不包含)
* @param out 输出片段容器
*/
void RasterizeTo(const VertexSoA& soa, size_t i0, size_t i1, size_t i2,
int x0, int y0, int x1, int y1,
std::vector<Fragment>& out);

private:
size_t width_, height_;

Expand Down
7 changes: 4 additions & 3 deletions src/include/renderer.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ namespace simple_renderer {
* - DEFERRED: 延迟渲染(片段收集后再着色)
*/
enum class RenderingMode {
PER_TRIANGLE, //!< 逐三角形(triangle-major)
TILE_BASED, //!< 基于 tile(tile-major)
DEFERRED //!< 延迟渲染
PER_TRIANGLE, //!< 逐三角形(triangle-major)
TILE_BASED, //!< 基于 tile(tile-major)
DEFERRED, //!< 延迟渲染
TILE_BASED_DEFERRED //!< 基于 tile 的延迟着色(TBDR)
};

/**
Expand Down
59 changes: 59 additions & 0 deletions src/include/renderers/tile_based_deferred_renderer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
#ifndef SIMPLERENDER_SRC_INCLUDE_RENDERERS_TILE_BASED_DEFERRED_RENDERER_HPP_
#define SIMPLERENDER_SRC_INCLUDE_RENDERERS_TILE_BASED_DEFERRED_RENDERER_HPP_

#include "renderers/renderer_base.hpp"
#include "renderers/tile_based_renderer.hpp" // 复用 TileTriangleRef / TileGridContext 定义

namespace simple_renderer {

/**
* @brief 基于 Tile 的延迟渲染器(Tile‑Based Deferred Renderer, TBDR)
*
* 设计要点:
* - SoA 顶点布局 + 三角形分箱(binning)→ 与 TBR 一致;
* - 以 Tile 为并行单元,避免跨 Tile 写冲突;
* - 2‑Pass(同现代 TBDR 思路):
* 1) Z 预通过(深度决胜):仅更新每像素最小深度与胜出三角形索引,并缓存透视矫正重心;
* 2) 延迟着色:仅对胜者像素执行一次片元着色,写入 tile 局部缓冲,最后整 Tile 拷贝到全局。
*
* 优势:显著减少overdraw场景中的无效着色(FragmentShader 调用次数近似等于胜出像素数)。
*/
class TileBasedDeferredRenderer final : public RendererBase {
public:
TileBasedDeferredRenderer(size_t width, size_t height, size_t tile_size = 64)
: RendererBase(width, height), tile_size_(tile_size) {}

bool Render(const Model& model, const Shader& shader, uint32_t* out_color) override;

private:
void TriangleTileBinning(const Model& model,
const TileGridContext& grid,
std::vector<std::vector<TileTriangleRef>>& tile_triangles);

void ProcessTriangleForTileBinning(size_t tri_idx, bool count_only,
const Model& model,
const TileGridContext& grid,
std::vector<size_t>& tile_counts,
std::vector<std::vector<TileTriangleRef>>& tile_triangles);

void RasterizeTileDeferred(size_t tile_id,
const std::vector<TileTriangleRef>& triangles,
const TileGridContext& grid,
float* tile_depth_buffer, uint32_t* tile_color_buffer,
std::unique_ptr<float[]>& global_depth_buffer,
std::unique_ptr<uint32_t[]>& global_color_buffer,
const Shader& shader,
uint64_t* out_tested, uint64_t* out_covered,
uint64_t* out_winners, uint64_t* out_shaded);

private:
// 深度与颜色清除默认值(与 TBR 保持一致)
static constexpr float kDepthClear = 1.0f;
static constexpr uint32_t kColorClear = 0u;

const size_t tile_size_;
};

} // namespace simple_renderer

#endif // SIMPLERENDER_SRC_INCLUDE_RENDERERS_TILE_BASED_DEFERRED_RENDERER_HPP_
2 changes: 0 additions & 2 deletions src/include/renderers/tile_based_renderer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,7 +103,6 @@ class TileBasedRenderer final : public RendererBase {
* @param soa 经过变换后的 SoA 顶点数据
* @param shader 着色器
* @param use_early_z 是否启用 Early‑Z
* @param scratch_fragments 可复用片段临时容器
*/
void RasterizeTile(size_t tile_id,
const std::vector<TileTriangleRef> &triangles,
Expand All @@ -113,7 +112,6 @@ class TileBasedRenderer final : public RendererBase {
std::unique_ptr<uint32_t[]> &global_color_buffer,
const Shader& shader,
bool use_early_z,
std::vector<Fragment>* scratch_fragments,
TileMaskStats* out_stats);

private:
Expand Down
17 changes: 12 additions & 5 deletions src/include/shader.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include <shared_mutex>
#include <unordered_map>
#include <variant>
#include <vector>

#include "light.h"
#include "material.hpp"
Expand All @@ -14,7 +15,8 @@
namespace simple_renderer {

using UniformValue = std::variant<int, float, Vector2f, Vector3f, Vector4f,
Matrix3f, Matrix4f, Material, Light>;
Matrix3f, Matrix4f, Material, Light,
std::vector<Light>>;

inline constexpr size_t kSpecularLutResolution = 256;

Expand All @@ -27,7 +29,7 @@ class UniformBuffer {
std::is_same_v<T, Vector2f> || std::is_same_v<T, Vector3f> ||
std::is_same_v<T, Vector4f> || std::is_same_v<T, Matrix3f> ||
std::is_same_v<T, Matrix4f> || std::is_same_v<T, Material> ||
std::is_same_v<T, Light>,
std::is_same_v<T, Light> || std::is_same_v<T, std::vector<Light>>,
"Type not supported by UniformValue");
uniforms_[name] = value;
}
Expand Down Expand Up @@ -83,10 +85,10 @@ struct VertexUniformCache {
};

struct FragmentUniformCache {
Light light{};
std::vector<Light> lights{}; // 支持多光源
Vector3f camera_pos = Vector3f(0.0f);
Vector3f light_dir_normalized = Vector3f(0.0f);
bool has_light = false;
std::vector<Vector3f> light_dirs_normalized{};
bool has_lights = false;
bool has_camera = false;
bool derived_valid = false;
};
Expand Down Expand Up @@ -142,6 +144,7 @@ class Shader {

void UpdateMatrixCache(const std::string &name, const Matrix4f &value);
void UpdateFragmentCache(const std::string &name, const Light &value);
void UpdateFragmentCache(const std::string &name, const std::vector<Light> &value);
void UpdateFragmentCache(const std::string &name, const Vector3f &value);
void RecalculateDerivedMatrices();
void RecalculateFragmentDerived();
Expand All @@ -155,6 +158,10 @@ class Shader {

Color SampleTexture(const Texture &texture, const Vector2f &uv) const;
Color ClampColor(const Color color) const;

public:
// 便捷接口:设置多光源
void SetLights(const std::vector<Light>& lights) { SetUniform("lights", lights); }
};

uint8_t FloatToUint8_t(float val);
Expand Down
153 changes: 0 additions & 153 deletions src/rasterizer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,159 +81,6 @@ std::vector<Fragment> Rasterizer::Rasterize(const Vertex& v0, const Vertex& v1,
return fragments;
}

void Rasterizer::RasterizeTo(const Vertex& v0, const Vertex& v1, const Vertex& v2,
int x0, int y0, int x1, int y1,
std::vector<Fragment>& out) {
// 获取三角形的最小 box(屏幕空间)
const Vector4f p0 = v0.GetPosition();
const Vector4f p1 = v1.GetPosition();
const Vector4f p2 = v2.GetPosition();

Vector2f a(p0.x, p0.y);
Vector2f b(p1.x, p1.y);
Vector2f c(p2.x, p2.y);

Vector2f bboxMin = Vector2f{std::min({a.x, b.x, c.x}), std::min({a.y, b.y, c.y})};
Vector2f bboxMax = Vector2f{std::max({a.x, b.x, c.x}), std::max({a.y, b.y, c.y})};

// Clamp 到屏幕尺寸
float minx = std::max(0.0f, bboxMin.x);
float miny = std::max(0.0f, bboxMin.y);
float maxx = std::min(float(width_ - 1), bboxMax.x);
float maxy = std::min(float(height_ - 1), bboxMax.y);

// 与外部提供的裁剪区域相交(半开区间) -> 闭区间扫描
int sx = std::max(x0, static_cast<int>(std::floor(minx)));
int sy = std::max(y0, static_cast<int>(std::floor(miny)));
int ex = std::min(x1 - 1, static_cast<int>(std::floor(maxx)));
int ey = std::min(y1 - 1, static_cast<int>(std::floor(maxy)));
if (sx > ex || sy > ey) return;

for (int x = sx; x <= ex; ++x) {
for (int y = sy; y <= ey; ++y) {
auto [is_inside, bary] = GetBarycentricCoord(
Vector3f(p0.x, p0.y, p0.z), Vector3f(p1.x, p1.y, p1.z), Vector3f(p2.x, p2.y, p2.z),
Vector3f(static_cast<float>(x), static_cast<float>(y), 0));
if (!is_inside) continue;

// 透视矫正插值
auto perspective_result = PerformPerspectiveCorrection(
p0.w, p1.w, p2.w,
p0.z, p1.z, p2.z,
bary);

const Vector3f& corrected_bary = perspective_result.corrected_barycentric;
float z = perspective_result.interpolated_z;

Fragment frag; // material 指针由调用方填写
frag.screen_coord = {x, y};
frag.normal = Interpolate(v0.GetNormal(), v1.GetNormal(), v2.GetNormal(), corrected_bary);
frag.uv = Interpolate(v0.GetTexCoords(), v1.GetTexCoords(), v2.GetTexCoords(), corrected_bary);
frag.color = InterpolateColor(v0.GetColor(), v1.GetColor(), v2.GetColor(), corrected_bary);
frag.depth = z;

out.push_back(frag);
}
}
}

void Rasterizer::RasterizeTo(const VertexSoA& soa, size_t i0, size_t i1, size_t i2,
int x0, int y0, int x1, int y1,
std::vector<Fragment>& out) {
// 读取三顶点的屏幕空间位置
const Vector4f& p0 = soa.pos_screen[i0];
const Vector4f& p1 = soa.pos_screen[i1];
const Vector4f& p2 = soa.pos_screen[i2];

// 为BarycentricCoord预构造Vec3f,避免循环内重复构造
const Vector3f sp0(p0.x, p0.y, p0.z);
const Vector3f sp1(p1.x, p1.y, p1.z);
const Vector3f sp2(p2.x, p2.y, p2.z);

// 计算屏幕空间AABB包围盒
const float minx_f = std::max(0.0f, std::min({p0.x, p1.x, p2.x}));
const float miny_f = std::max(0.0f, std::min({p0.y, p1.y, p2.y}));
const float maxx_f = std::min(float(width_ - 1), std::max({p0.x, p1.x, p2.x}));
const float maxy_f = std::min(float(height_ - 1), std::max({p0.y, p1.y, p2.y}));

// 与外部提供的裁剪区域相交(半开区间) -> 闭区间扫描
int sx = std::max(x0, static_cast<int>(std::floor(minx_f)));
int sy = std::max(y0, static_cast<int>(std::floor(miny_f)));
int ex = std::min(x1 - 1, static_cast<int>(std::floor(maxx_f)));
int ey = std::min(y1 - 1, static_cast<int>(std::floor(maxy_f)));
if (sx > ex || sy > ey) return;

// 预计算边函数系数:E(x,y) = A*x + B*y + C
// 使用相对坐标的边函数定义,避免大常数项导致的数值不稳定
// 如使用绝对形式Ax+By+C会由于常数C的量级过大,造成浮点抵消,有效位丢失不稳定
auto cross2 = [](float ax, float ay, float bx, float by) {
return ax * by - ay * bx;
};
// 边向量
const float e01x = p1.x - p0.x, e01y = p1.y - p0.y; // (p0->p1)
const float e12x = p2.x - p1.x, e12y = p2.y - p1.y; // (p1->p2)
const float e20x = p0.x - p2.x, e20y = p0.y - p2.y; // (p2->p0)

// 有向面积(两倍),用相对面积定义:area2 = cross(p1 - p0, p2 - p0)
float area2 = cross2(e01x, e01y, p2.x - p0.x, p2.y - p0.y);
if (std::abs(area2) < 1e-6f) return; // 退化三角形
const float inv_area2 = 1.0f / area2;
const bool positive = (area2 > 0.0f);

// 行优先遍历:有利于 cache 与向量化
#pragma omp simd
for (int y = sy; y <= ey; ++y) {
const float yf = static_cast<float>(y);

// 注意:此处存在对 out.push_back 的写入,属于有副作用操作,不适合使用
// omp simd 进行强制向量化,否则可能导致不符合预期的行为(如周期性伪影)。
// 先保持标量内层,后续如切换为“直写像素回调”再考虑安全的 SIMD 化。
for (int x = sx; x <= ex; ++x) {
const float xf = static_cast<float>(x);

// 相对坐标边函数:
// E01(p) = cross(p1 - p0, p - p0)
// E12(p) = cross(p2 - p1, p - p1)
// E20(p) = cross(p0 - p2, p - p2)
const float E01 = cross2(e01x, e01y, xf - p0.x, yf - p0.y);
const float E12 = cross2(e12x, e12y, xf - p1.x, yf - p1.y);
const float E20 = cross2(e20x, e20y, xf - p2.x, yf - p2.y);

// 半空间测试(根据朝向选择符号)
const bool inside = positive ? (E01 >= 0.0f && E12 >= 0.0f && E20 >= 0.0f)
: (E01 <= 0.0f && E12 <= 0.0f && E20 <= 0.0f);
if (!inside) continue;

// 重心权重映射:
// b0 对应 v0,取与对边 (v1,v2) 的子面积 → E12
// b1 对应 v1 → E20
// b2 对应 v2 → E01
const float b0 = E12 * inv_area2;
const float b1 = E20 * inv_area2;
const float b2 = E01 * inv_area2;
const Vector3f bary(b0, b1, b2);

// 透视矫正插值
auto perspective_result = PerformPerspectiveCorrection(
p0.w, p1.w, p2.w,
p0.z, p1.z, p2.z,
bary);

const Vector3f& corrected_bary = perspective_result.corrected_barycentric;
const float z = perspective_result.interpolated_z;

Fragment frag; // Note: material 指针由调用方填写
frag.screen_coord = {x, y};
frag.normal = Interpolate(soa.normal[i0], soa.normal[i1], soa.normal[i2], corrected_bary);
frag.uv = Interpolate(soa.uv[i0], soa.uv[i1], soa.uv[i2], corrected_bary);
frag.color = InterpolateColor(soa.color[i0], soa.color[i1], soa.color[i2], corrected_bary);
frag.depth = z;

out.push_back(frag);
}
}
}

std::pair<bool, Vector3f> Rasterizer::GetBarycentricCoord(const Vector3f& p0,
const Vector3f& p1,
const Vector3f& p2,
Expand Down
7 changes: 7 additions & 0 deletions src/renderer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
#include "renderers/per_triangle_renderer.hpp"
#include "renderers/tile_based_renderer.hpp"
#include "renderers/deferred_renderer.hpp"
#include "renderers/tile_based_deferred_renderer.hpp"

namespace simple_renderer {

Expand All @@ -14,6 +15,7 @@ std::string RenderingModeToString(RenderingMode mode) {
case RenderingMode::PER_TRIANGLE: return "PER_TRIANGLE";
case RenderingMode::TILE_BASED: return "TILE_BASED";
case RenderingMode::DEFERRED: return "DEFERRED";
case RenderingMode::TILE_BASED_DEFERRED: return "TILE_BASED_DEFERRED";
}
return "PER_TRIANGLE";
}
Expand Down Expand Up @@ -77,6 +79,11 @@ void SimpleRenderer::EnsureRenderer() {
renderer_ = std::move(r);
break;
}
case RenderingMode::TILE_BASED_DEFERRED: {
auto r = std::make_unique<TileBasedDeferredRenderer>(width_, height_, tbr_tile_size_);
renderer_ = std::move(r);
break;
}
}
}

Expand Down
Loading
Loading