跳过正文
  1. 计算机图形学/

3D 软渲染程序 sRenderer

·517 字·3 分钟
目录

sRenderer 是一个纯软件渲染器——每一个像素都完全在 CPU 上计算完成,不依赖任何 GPU。没有 OpenGL,没有 Vulkan,没有 DirectX。唯一的外部依赖是 SFML,它仅用于创建窗口和显示画面。从向量运算到透视投影再到纹理映射,其他所有功能都是从零开始构建的。

项目地址: https://github.com/Oh-noodles/sRenderer

African Head 模型 鲨鱼 树木

技术栈
#

类别 技术 说明
编程语言 C++11 不依赖任何外部数学库(Eigen、glm 等)
构建系统 CMake 3.11+ 使用 FetchContent 自动获取 SFML
界面 / 窗口 SFML 3.0.0 仅用于创建窗口和纹理显示
模型格式 Wavefront .obj 解析顶点、纹理坐标、法线和面
纹理格式 TGA (Targa) 手写的 TGA 加载器,支持 RGBA
编译器 g++ / clang++ 在 Linux 和 macOS 上测试

特性
#

核心渲染
#

  • 纯软件光栅化器 —— 每个像素在 CPU 上着色,无需 GPU
  • 透视投影(Perspective Projection) —— 完整的 4x4 矩阵管线:视图矩阵(View)→ 投影矩阵(Project)→ 视口矩阵(Viewport)
  • Z 缓冲深度测试(Z-Buffer) —— 逐像素深度比较以处理遮挡关系
  • 透视正确插值(Perspective-Correct Interpolation) —— 使用 1/z 插值技术实现正确的纹理映射
  • 漫反射光照(Diffuse / Lambertian Lighting) —— 表面法线与光线方向的点积,附带环境光项
  • 纹理映射(Texture Mapping) —— 带透视校正的 UV 坐标插值
  • 线框渲染(Wireframe) —— 类似 Bresenham 的三角线绘制算法
  • 着色器抽象(Shader Abstraction) —— IShader 接口,包含顶点阶段和片段阶段(类似 GPU 着色器模型)

数学库(从零实现)
#

  • Vec<T, DIM> —— 泛型 N 维向量,支持运算符(+、-、*、点积、叉积、归一化)
  • Matrix44f —— 完整 4x4 浮点矩阵,支持乘法、逆矩阵和转置
  • lookAt() —— 相机变换矩阵
  • edgeFunction() —— 三角形光栅化辅助函数
  • embed<>() —— 齐次坐标的维度嵌入

模型与图像加载
#

  • Wavefront .obj 解析器(顶点、纹理坐标、法线、三角形/四边形面)
  • 四边形到三角形的扇形转换(支持任意多边形面)
  • TGA 图像加载器,支持 RGBA
  • 内置模型切换器(运行时可选择 african_head、shark 或 tree)

实时渲染循环
#

  • 自动旋转轨道相机(基于时间的动画)
  • 使用 SFML 的 60 FPS 渲染循环
  • 通过 SFML RenderTexture + Sprite 管线实时显示

渲染管线
#

渲染管线遵循经典 GPU 管线的简化版。以下是每个三角形的处理流程:

OBJ 模型
顶点处理(Vertex Processing)
   │  将顶点嵌入为 Vec4f(齐次坐标)
   │  依次应用 View × Project × ViewPort 矩阵变换
   │  执行透视除法(w-divide)
光栅化(Rasterization)
   │  计算三角形包围盒
   │  逐像素进行点在三角形内测试(边函数)
   │  透视正确插值(1/z 方法)
片段着色(Fragment Shading)
   │  在插值后的 UV 坐标处采样纹理
   │  计算漫反射光照(法线 · 光线方向 + 环境光)
   │  将光照应用到纹理颜色
深度测试与写入(Depth Test & Write)
   │  将插值后的 z 值与 Z 缓冲进行比较
   │  将像素写入帧缓冲
SFML 显示
      将帧缓冲上传为 GPU 纹理(通过 SFML)
      渲染精灵到窗口

矩阵变换
#

三个主要矩阵的构建方式如下:

  1. 视图矩阵(View Matrix) —— lookAt(eye, target, up) 的逆矩阵
    • 将世界空间坐标转换为相机空间
  2. 投影矩阵(Projection Matrix) —— 对称透视投影
    • 将相机空间映射到裁剪空间:near=1、far=30、视场角约 45°
  3. 视口矩阵(Viewport Matrix) —— 将归一化设备坐标映射到屏幕像素
    • 按帧缓冲维度进行缩放和平移

组合变换为:ViewPort × Project × View × vertex

透视正确插值
#

这是避免纹理映射中常见"扭曲"伪影的关键技术。算法不是直接线性插值 UV 坐标,而是先对 UV/z 值进行插值,然后除以插值后的 1/z 来获得透视正确的结果:

1/z = w0/z0 + w1/z1 + w2/z2
uv_corrected = z * (w0*uv0/z0 + w1*uv1/z1 + w2*uv2/z2)

项目结构
#

sRenderer/
├── inc/                        # 头文件
│   ├── geometry.hpp            # Vec<T,DIM>、Matrix44f、数学运算符
│   ├── model.hpp               # OBJ 模型加载器(顶点、纹理、法线)
│   ├── renderer.hpp            # 主 Renderer 类(光栅化循环)
│   ├── shader.hpp              # IShader 接口 + SimpleShader 实现
│   ├── tgaImage.hpp            # TGA 图像加载器 + TGAColor + TGAImage
│   └── SFML/                   # SFML 头文件(vendored)
├── src/                        # 源文件
│   ├── main.cpp                # 入口点、模型选择、渲染循环
│   ├── geometry.cpp            # 矩阵实现、lookAt、直线数学
│   ├── model.cpp               # OBJ 文件解析器
│   ├── renderer.cpp            # Renderer::render() —— 完整光栅化管线
│   ├── shader.cpp              # 增量式直线绘制
│   └── tgaImage.cpp            # TGA 读写(含 RLE 支持)
├── obj/                        # Wavefront 模型文件
│   ├── african_head.obj
│   ├── shark.obj
│   ├── tree.obj
│   ├── blender_cube.obj
│   ├── cube.obj
│   ├── frustum.obj
│   └── teapot.obj
├── texture/                    # TGA 纹理贴图
│   ├── african_head_diffuse.tga
│   ├── shark_diffuse.tga
│   ├── tree_diffuse.tga
│   └── blender_cube_diffuse.tga
├── doc/assets/                 # 演示 GIF
│   ├── head.gif
│   ├── shark.gif
│   └── tree.gif
├── CMakeLists.txt              # CMake 构建配置
├── Makefile                    # 使用 make 的备选构建方式
├── compile_flags.txt           # 用于 clangd 的编译选项
├── README.md                   # 本文件(英文版)
└── README_zh.md                # 中文版

编译与运行
#

环境要求
#

  • Linux 或 macOS(Windows 可能可以工作但未经测试)
  • g++clang++,需支持 C++11
  • cmake(版本 3.11 或更高)

编译步骤
#

# 1. 克隆仓库(或进入项目目录)
cd sRenderer

# 2. 配置构建(CMake 会自动获取 SFML 3.0.0)
cmake -B build

# 3. 编译
cmake --build build

# 4. 运行程序
./build/sRenderer

使用说明
#

启动后,程序会提示选择一个模型:

Choose a model below:
0. head
1. shark
2. tree
Your choice [0-2]:

选择后,模型将以自动旋转的相机轨道进行渲染。