添加 Transform 类并应用到 Camera 中
This commit is contained in:
parent
858c0b4d59
commit
55a96143fc
|
|
@ -156,6 +156,7 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\README.md" />
|
<None Include="..\README.md" />
|
||||||
|
<None Include="..\TODO.md" />
|
||||||
<None Include="TODO.md" />
|
<None Include="TODO.md" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
|
|
||||||
|
|
@ -81,6 +81,7 @@
|
||||||
<Filter>资源文件</Filter>
|
<Filter>资源文件</Filter>
|
||||||
</None>
|
</None>
|
||||||
<None Include="..\README.md" />
|
<None Include="..\README.md" />
|
||||||
|
<None Include="..\TODO.md" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ClInclude Include="Core\DepthBuffer.h">
|
<ClInclude Include="Core\DepthBuffer.h">
|
||||||
|
|
|
||||||
|
|
@ -53,9 +53,9 @@ namespace Math
|
||||||
const float sinValue = std::sin(radians);
|
const float sinValue = std::sin(radians);
|
||||||
|
|
||||||
return Matrix4x4(
|
return Matrix4x4(
|
||||||
cosValue, sinValue, 0, 0,
|
cosValue, 0, sinValue, 0,
|
||||||
0, 1, 0, 0,
|
0, 1, 0, 0,
|
||||||
-sinValue, cosValue, 0, 0,
|
-sinValue, 0, cosValue, 0,
|
||||||
0, 0, 0, 1
|
0, 0, 0, 1
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@
|
||||||
#include "Matrix4x4.h"
|
#include "Matrix4x4.h"
|
||||||
#include "Camera.h"
|
#include "Camera.h"
|
||||||
#include <Vector3.h>
|
#include <Vector3.h>
|
||||||
#include "MathUtil.h"
|
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
|
||||||
namespace Scene
|
namespace Scene
|
||||||
|
|
@ -11,10 +10,10 @@ namespace Scene
|
||||||
{
|
{
|
||||||
using namespace Math;
|
using namespace Math;
|
||||||
|
|
||||||
Vector3 backward = (position - target).normalized();
|
const Vector3 position = transform.position;
|
||||||
Vector3 right = MathUtil::cross(up, backward).normalized();
|
const Vector3 right = transform.get_right();
|
||||||
if (right.magnitude() < 1e-5) right = MathUtil::cross(Vector3(0, 1, 0), backward);
|
const Vector3 cameraUp = transform.get_up();
|
||||||
Vector3 cameraUp = MathUtil::cross(backward, right);
|
const Vector3 backward = -transform.get_forward();
|
||||||
|
|
||||||
return Matrix4x4(
|
return Matrix4x4(
|
||||||
right.x, right.y, right.z, -right.dot(position),
|
right.x, right.y, right.z, -right.dot(position),
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,20 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
#include "Vector3.h"
|
|
||||||
#include "Matrix4x4.h"
|
#include "Matrix4x4.h"
|
||||||
|
#include "Transform.h"
|
||||||
|
|
||||||
namespace Scene
|
namespace Scene
|
||||||
{
|
{
|
||||||
class Camera
|
class Camera
|
||||||
{
|
{
|
||||||
private:
|
public:
|
||||||
Math::Vector3 position = Math::Vector3(0.0f, 0.0f, 0.0f);
|
Camera() = default;
|
||||||
Math::Vector3 target = Math::Vector3(0.0f, 0.0f, -1.0f);
|
|
||||||
Math::Vector3 up = Math::Vector3(0.0f, 1.0f, 0.0f);
|
Transform transform;
|
||||||
|
|
||||||
float verticalFovRadians = 1.0471975512f;
|
float verticalFovRadians = 1.0471975512f;
|
||||||
float nearPlane = 0.1f;
|
float nearPlane = 0.1f;
|
||||||
float farPlane = 100.0f;
|
float farPlane = 100.0f;
|
||||||
|
|
||||||
public:
|
|
||||||
Camera() = default;
|
|
||||||
|
|
||||||
Math::Vector3 get_position() const { return position; }
|
|
||||||
void set_position(const Math::Vector3& pos) { position = pos; }
|
|
||||||
|
|
||||||
Math::Vector3 get_target() const { return target; }
|
|
||||||
void set_target(const Math::Vector3& t) { target = t; }
|
|
||||||
|
|
||||||
Math::Vector3 get_up() const { return up; }
|
|
||||||
void set_up(const Math::Vector3& u) { up = u; }
|
|
||||||
|
|
||||||
float get_vertical_fov_radians() const { return verticalFovRadians; }
|
|
||||||
void set_vertical_fov_radians(float fov) { verticalFovRadians = fov; }
|
|
||||||
|
|
||||||
float get_near_plane() const { return nearPlane; }
|
|
||||||
void set_near_plane(float nearP) { nearPlane = nearP; }
|
|
||||||
|
|
||||||
float get_far_plane() const { return farPlane; }
|
|
||||||
void set_far_plane(float farP) { farPlane = farP; }
|
|
||||||
|
|
||||||
Math::Matrix4x4 get_view_matrix() const;
|
Math::Matrix4x4 get_view_matrix() const;
|
||||||
|
|
||||||
Math::Matrix4x4 get_orthographic_projection_matrix(float width, float height) const;
|
Math::Matrix4x4 get_orthographic_projection_matrix(float width, float height) const;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,31 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "Vector3.h"
|
||||||
|
#include "MathUtil.h"
|
||||||
|
#include <Matrix4x4.h>
|
||||||
|
|
||||||
namespace Scene
|
namespace Scene
|
||||||
{
|
{
|
||||||
class Transform
|
class Transform
|
||||||
{};
|
{
|
||||||
|
public:
|
||||||
|
Transform() = default;
|
||||||
|
Transform(Math::Vector3 position, Math::Vector3 rotation, Math::Vector3 scale) : position(position), rotation(rotation), scale(scale) {}
|
||||||
|
|
||||||
|
Math::Vector3 position = Math::Vector3(0, 0, 0);
|
||||||
|
Math::Vector3 rotation = Math::Vector3(0, 0, 0);
|
||||||
|
Math::Vector3 scale = Math::Vector3(1, 1, 1);
|
||||||
|
|
||||||
|
Math::Vector3 transform_direction(const Math::Vector3& direction) const
|
||||||
|
{
|
||||||
|
const Math::Matrix4x4 rotationMatrix = Math::MathUtil::get_rotation_matrix(rotation);
|
||||||
|
return rotationMatrix.TransformDirection(direction).to_vector3().normalized();
|
||||||
|
}
|
||||||
|
|
||||||
|
Math::Vector3 get_right() const { return transform_direction(Math::Vector3(1.0f, 0.0f, 0.0f)); }
|
||||||
|
Math::Vector3 get_left() const { return transform_direction(Math::Vector3(-1.0f, 0.0f, 0.0f)); }
|
||||||
|
Math::Vector3 get_up() const { return transform_direction(Math::Vector3(0.0f, 1.0f, 0.0f)); }
|
||||||
|
Math::Vector3 get_down() const { return transform_direction(Math::Vector3(0.0f, -1.0f, 0.0f)); }
|
||||||
|
Math::Vector3 get_forward() const { return transform_direction(Math::Vector3(0.0f, 0.0f, 1.0f)); }
|
||||||
|
Math::Vector3 get_back() const { return transform_direction(Math::Vector3(0.0f, 0.0f, -1.0f)); }
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -1,7 +1,14 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
#include "Vector3.h"
|
||||||
|
|
||||||
namespace Scene
|
namespace Scene
|
||||||
{
|
{
|
||||||
class Vertex
|
class Vertex
|
||||||
{};
|
{
|
||||||
|
public:
|
||||||
|
Vertex() = default;
|
||||||
|
Vertex(Math::Vector3 position) :position(position) {}
|
||||||
|
|
||||||
|
Math::Vector3 position = Math::Vector3(0, 0, 0);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
@ -167,9 +167,9 @@ static bool IsFaceVisible(const CubeFace& face, const std::array<Math::Vector3,
|
||||||
const Vector3 faceNormal = (v1 - v0).cross(v2 - v0);
|
const Vector3 faceNormal = (v1 - v0).cross(v2 - v0);
|
||||||
const Vector3 faceCenter =
|
const Vector3 faceCenter =
|
||||||
(viewSpaceVertices[face.vertices[0]] +
|
(viewSpaceVertices[face.vertices[0]] +
|
||||||
viewSpaceVertices[face.vertices[1]] +
|
viewSpaceVertices[face.vertices[1]] +
|
||||||
viewSpaceVertices[face.vertices[2]] +
|
viewSpaceVertices[face.vertices[2]] +
|
||||||
viewSpaceVertices[face.vertices[3]]) / 4.0f;
|
viewSpaceVertices[face.vertices[3]]) / 4.0f;
|
||||||
|
|
||||||
return faceNormal.dot(faceCenter) < 0.0f;
|
return faceNormal.dot(faceCenter) < 0.0f;
|
||||||
}
|
}
|
||||||
|
|
@ -188,11 +188,8 @@ int main(int argc, char* argv[])
|
||||||
Rasterizer::Rasterizer rasterizer(frameBuffer);
|
Rasterizer::Rasterizer rasterizer(frameBuffer);
|
||||||
|
|
||||||
Scene::Camera camera;
|
Scene::Camera camera;
|
||||||
camera.set_position(Math::Vector3(0.0f, 0.0f, 3.0f));
|
camera.transform.position = Math::Vector3(0.0f, 0.0f, 3.0f);
|
||||||
camera.set_target(Math::Vector3(0.0f, 0.0f, 0.0f));
|
camera.transform.rotation = Math::Vector3(0.0f, 3.1415926535f, 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<Math::Vector3, 8> cubeVertices = {
|
const std::array<Math::Vector3, 8> cubeVertices = {
|
||||||
Math::Vector3(-0.5f, -0.5f, -0.5f),
|
Math::Vector3(-0.5f, -0.5f, -0.5f),
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,433 @@
|
||||||
|
# CPU Software Renderer TODO
|
||||||
|
|
||||||
|
## 目标
|
||||||
|
|
||||||
|
把当前项目从“能显示线框立方体”的状态,逐步推进到“具备基本 3D 渲染管线能力的 CPU Software Renderer”。
|
||||||
|
|
||||||
|
当前已经具备的基础:
|
||||||
|
|
||||||
|
- SDL 窗口、纹理和显示循环
|
||||||
|
- `FrameBuffer`
|
||||||
|
- 像素写入
|
||||||
|
- 直线光栅化
|
||||||
|
- 三角形包围盒遍历和点内测试雏形
|
||||||
|
- `Model -> View -> Projection -> NDC -> Viewport`
|
||||||
|
- 简单的线框绘制与面可见性判断
|
||||||
|
|
||||||
|
下面的 TODO 按建议实现顺序展开。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 1. 稳定 2D 光栅基础
|
||||||
|
|
||||||
|
目标:把当前 2D 线段 / 三角形基础打稳,为后续 3D 面渲染做准备。
|
||||||
|
|
||||||
|
### 1.1 线段光栅化收尾
|
||||||
|
|
||||||
|
- 确认 Bresenham 在所有斜率下都正确
|
||||||
|
- 验证水平线、垂直线、45 度线、反向输入点
|
||||||
|
- 统一线段接口的输入输出约定
|
||||||
|
- 为调试准备简单测试图形
|
||||||
|
|
||||||
|
完成标准:
|
||||||
|
|
||||||
|
- 任意两个屏幕点都能稳定画线
|
||||||
|
- 没有明显断裂、漏点、方向依赖问题
|
||||||
|
|
||||||
|
### 1.2 三角形 2D 填充稳定化
|
||||||
|
|
||||||
|
- 校验 `BoundingBox` 的边界是否正确
|
||||||
|
- 校验点在三角形内判定的顶点顺序依赖
|
||||||
|
- 明确像素采样点采用像素中心还是整数坐标
|
||||||
|
- 明确边界像素规则,避免共享边重复或漏画
|
||||||
|
|
||||||
|
完成标准:
|
||||||
|
|
||||||
|
- 任意顺序输入的三角形都能被正确填充
|
||||||
|
- 边界行为稳定,没有明显裂缝
|
||||||
|
|
||||||
|
### 1.3 TriangleRasterizer 接入主流程前的准备
|
||||||
|
|
||||||
|
- 让 `TriangleRasterizer` 支持直接接收屏幕空间三角形
|
||||||
|
- 明确光栅阶段输入结构
|
||||||
|
- 为之后接 `DepthBuffer` 预留每像素深度写入点
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 2. MVP 与视口变换
|
||||||
|
|
||||||
|
目标:把 3D 顶点稳定变换到屏幕空间。
|
||||||
|
|
||||||
|
### 2.1 Model / View / Projection 约定固定
|
||||||
|
|
||||||
|
- 统一矩阵乘法约定
|
||||||
|
- 明确当前使用列向量还是行向量
|
||||||
|
- 明确当前 view space 是右手系还是左手系
|
||||||
|
- 明确相机前方是 `-Z`
|
||||||
|
- 明确 `nearPlane` 和 `farPlane` 使用正距离
|
||||||
|
|
||||||
|
完成标准:
|
||||||
|
|
||||||
|
- `M * V * P` 的方向清晰
|
||||||
|
- 相机朝向、投影矩阵、裁剪空间定义互相一致
|
||||||
|
|
||||||
|
### 2.2 NDC 与 Viewport 约定固定
|
||||||
|
|
||||||
|
- 明确 NDC 使用 `[-1, 1]`
|
||||||
|
- 明确屏幕原点为左上角
|
||||||
|
- 明确 `x` 向右、`y` 向下
|
||||||
|
- 明确 viewport 使用 `width - 1` / `height - 1`
|
||||||
|
|
||||||
|
完成标准:
|
||||||
|
|
||||||
|
- `ProjectToScreen()` 或等价流程不再分散写逻辑
|
||||||
|
- `Camera::get_viewport_matrix()` 成为唯一屏幕映射入口
|
||||||
|
|
||||||
|
### 2.3 从 main 中抽离变换逻辑
|
||||||
|
|
||||||
|
- 把 `clip -> ndc -> viewport` 从 `main.cpp` 抽到专门模块
|
||||||
|
- 明确输入是 object vertex 还是 assembled vertex
|
||||||
|
- 为后续 mesh / triangle pipeline 准备统一入口
|
||||||
|
|
||||||
|
建议新增模块:
|
||||||
|
|
||||||
|
- `Renderer` 或 `Pipeline`
|
||||||
|
- `ProjectVertex`
|
||||||
|
- `AssembleTriangle`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 3. 三角形装配与实心面渲染
|
||||||
|
|
||||||
|
目标:从“线框”进入“实心三角形渲染”。
|
||||||
|
|
||||||
|
### 3.1 三角形装配
|
||||||
|
|
||||||
|
- 从 mesh 顶点和索引装配三角形
|
||||||
|
- 不再手写 cube edge 作为主流程
|
||||||
|
- 统一输入为 triangle list
|
||||||
|
|
||||||
|
完成标准:
|
||||||
|
|
||||||
|
- 主流程以“三角形”为核心,而不是“边”
|
||||||
|
|
||||||
|
### 3.2 背面剔除
|
||||||
|
|
||||||
|
- 统一顶点绕序约定
|
||||||
|
- 明确在 view space 还是 screen space 做剔除
|
||||||
|
- 验证法线方向与绕序一致
|
||||||
|
|
||||||
|
完成标准:
|
||||||
|
|
||||||
|
- 可稳定剔除背面
|
||||||
|
- 不会因为模型顶点顺序混乱而随机闪面
|
||||||
|
|
||||||
|
### 3.3 实心立方体跑通
|
||||||
|
|
||||||
|
- 用三角形填充代替线框
|
||||||
|
- 先可不加光照,只显示纯色面
|
||||||
|
- 验证旋转时每个面都正确显示
|
||||||
|
|
||||||
|
完成标准:
|
||||||
|
|
||||||
|
- 可以正确绘制一个实心旋转立方体
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 4. 深度测试
|
||||||
|
|
||||||
|
目标:解决遮挡关系。
|
||||||
|
|
||||||
|
### 4.1 DepthBuffer 基础
|
||||||
|
|
||||||
|
- 初始化深度缓冲
|
||||||
|
- 明确默认值是最远深度
|
||||||
|
- 每帧清空深度缓冲
|
||||||
|
- 和 `FrameBuffer` 保持相同分辨率
|
||||||
|
|
||||||
|
### 4.2 深度写入与比较
|
||||||
|
|
||||||
|
- 明确深度值使用哪个空间
|
||||||
|
- 推荐先使用 NDC z 或映射后的深度值
|
||||||
|
- 明确比较方向
|
||||||
|
- 在通过测试时更新颜色和深度
|
||||||
|
|
||||||
|
完成标准:
|
||||||
|
|
||||||
|
- 前面的三角形能正确遮挡后面的三角形
|
||||||
|
|
||||||
|
### 4.3 深度约定补全
|
||||||
|
|
||||||
|
- 明确 `near -> 0 / far -> 1` 还是其他映射
|
||||||
|
- 明确 viewport 是否处理 z
|
||||||
|
- 明确是否保留 `w` 供后续透视校正使用
|
||||||
|
|
||||||
|
这是当前 TODO 中原本写得比较粗的部分,建议补成文档。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 5. 重心坐标与属性插值
|
||||||
|
|
||||||
|
目标:从“只会画面”走到“会在片元阶段传递数据”。
|
||||||
|
|
||||||
|
### 5.1 重心坐标
|
||||||
|
|
||||||
|
- 在三角形内部计算每个像素的重心坐标
|
||||||
|
- 输出 `alpha / beta / gamma`
|
||||||
|
- 先用于深度插值
|
||||||
|
|
||||||
|
### 5.2 线性属性插值
|
||||||
|
|
||||||
|
- 插值颜色
|
||||||
|
- 插值深度
|
||||||
|
- 插值法线
|
||||||
|
- 插值世界坐标
|
||||||
|
- 插值 UV
|
||||||
|
|
||||||
|
### 5.3 透视校正插值
|
||||||
|
|
||||||
|
这一项建议显式加入 TODO,原文没有写清楚。
|
||||||
|
|
||||||
|
需要补上的内容:
|
||||||
|
|
||||||
|
- 保存每个顶点的 `1 / w`
|
||||||
|
- 对属性做 `attr / w` 插值
|
||||||
|
- 最后再除以插值后的 `1 / w`
|
||||||
|
|
||||||
|
完成标准:
|
||||||
|
|
||||||
|
- 纹理坐标和法线在透视投影下不发生明显拉伸错误
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 6. 裁剪
|
||||||
|
|
||||||
|
目标:正确处理部分进入视锥的几何体。
|
||||||
|
|
||||||
|
这一项当前代码和原 TODO 都不够明确,但它是完整渲染器必须补的。
|
||||||
|
|
||||||
|
### 6.1 近平面裁剪
|
||||||
|
|
||||||
|
- 不要简单按顶点是否在 NDC 内直接丢弃
|
||||||
|
- 支持一个三角形部分穿过近平面
|
||||||
|
- 处理裁剪后生成 1 个或 2 个新三角形
|
||||||
|
|
||||||
|
### 6.2 视锥裁剪
|
||||||
|
|
||||||
|
- 支持左右上下近平远平面
|
||||||
|
- 最开始至少要处理 near plane
|
||||||
|
- 后续再扩展到完整 frustum clipping
|
||||||
|
|
||||||
|
完成标准:
|
||||||
|
|
||||||
|
- 靠近相机的大三角形不会突然破碎或整块消失
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 7. Shader 数据流
|
||||||
|
|
||||||
|
目标:让渲染流程从“硬编码逻辑”过渡到“清晰的着色阶段”。
|
||||||
|
|
||||||
|
这一项 README 提过方向,但 TODO 里不够明确,建议单独成阶段。
|
||||||
|
|
||||||
|
### 7.1 顶点输出结构
|
||||||
|
|
||||||
|
- 定义 vertex shader 输出
|
||||||
|
- 至少包含:
|
||||||
|
- clip position
|
||||||
|
- world position
|
||||||
|
- normal
|
||||||
|
- color / uv
|
||||||
|
|
||||||
|
### 7.2 片元输入结构
|
||||||
|
|
||||||
|
- 定义 rasterizer 传给 fragment 的输入
|
||||||
|
- 区分线性插值字段与透视校正字段
|
||||||
|
|
||||||
|
### 7.3 Shader 接口整理
|
||||||
|
|
||||||
|
- 不要把光照直接写死在 `main`
|
||||||
|
- 把着色逻辑集中到 `Shading/`
|
||||||
|
- 明确 shader 输入输出边界
|
||||||
|
|
||||||
|
完成标准:
|
||||||
|
|
||||||
|
- 后续增加 Lambert / Blinn-Phong / 纹理采样时不需要重写主流程
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 8. Blinn-Phong 光照
|
||||||
|
|
||||||
|
目标:实现基本光照模型。
|
||||||
|
|
||||||
|
### 8.1 光照输入准备
|
||||||
|
|
||||||
|
- 法线插值
|
||||||
|
- 世界空间或观察空间位置
|
||||||
|
- 相机方向
|
||||||
|
- 光源方向
|
||||||
|
|
||||||
|
### 8.2 Blinn-Phong 模型
|
||||||
|
|
||||||
|
- ambient
|
||||||
|
- diffuse
|
||||||
|
- specular
|
||||||
|
- half vector `H = normalize(L + V)`
|
||||||
|
|
||||||
|
### 8.3 法线空间与归一化
|
||||||
|
|
||||||
|
- 明确法线在 world 还是 view space 计算
|
||||||
|
- 每像素重新归一化法线
|
||||||
|
|
||||||
|
完成标准:
|
||||||
|
|
||||||
|
- 模型出现稳定高光和明暗面变化
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 9. OBJ 加载与通用模型渲染
|
||||||
|
|
||||||
|
目标:从内置立方体过渡到真实模型。
|
||||||
|
|
||||||
|
### 9.1 OBJ 基础加载
|
||||||
|
|
||||||
|
- 读取 `v`
|
||||||
|
- 读取 `vn`
|
||||||
|
- 读取 `vt`
|
||||||
|
- 读取 `f`
|
||||||
|
|
||||||
|
### 9.2 Mesh 数据整理
|
||||||
|
|
||||||
|
- 统一索引格式
|
||||||
|
- 处理 OBJ 中 position / uv / normal 分离索引
|
||||||
|
- 生成项目自己的 mesh 数据结构
|
||||||
|
|
||||||
|
### 9.3 模型接入渲染管线
|
||||||
|
|
||||||
|
- 用真实 mesh 走完整 triangle pipeline
|
||||||
|
- 不再依赖 demo 专用几何体
|
||||||
|
|
||||||
|
完成标准:
|
||||||
|
|
||||||
|
- 能渲染 teapot、bunny 或其他基础模型
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 10. 相机控制
|
||||||
|
|
||||||
|
目标:让 renderer 具备交互能力。
|
||||||
|
|
||||||
|
### 10.1 平移控制
|
||||||
|
|
||||||
|
- `WASD`
|
||||||
|
- 上下移动
|
||||||
|
|
||||||
|
### 10.2 旋转控制
|
||||||
|
|
||||||
|
- 鼠标控制 yaw / pitch
|
||||||
|
- 限制 pitch 防止翻转
|
||||||
|
|
||||||
|
### 10.3 运行时参数调整
|
||||||
|
|
||||||
|
- FOV
|
||||||
|
- near / far plane
|
||||||
|
- 光照参数
|
||||||
|
|
||||||
|
完成标准:
|
||||||
|
|
||||||
|
- 能在场景中自由观察模型
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Step 11. 结构收口
|
||||||
|
|
||||||
|
目标:让代码不再长期堆在 `main.cpp`。
|
||||||
|
|
||||||
|
### 11.1 Renderer 主流程落地
|
||||||
|
|
||||||
|
- 把渲染调度逻辑移入 `Renderer`
|
||||||
|
- `main.cpp` 只保留:
|
||||||
|
- 初始化
|
||||||
|
- 场景创建
|
||||||
|
- 输入处理
|
||||||
|
- 每帧调用 renderer
|
||||||
|
|
||||||
|
### 11.2 数据结构分层
|
||||||
|
|
||||||
|
- `Scene`: 相机、模型、变换
|
||||||
|
- `RenderData`: 光栅输入输出数据
|
||||||
|
- `Rasterizer`: 三角形覆盖与插值
|
||||||
|
- `Shading`: 顶点 / 片元着色
|
||||||
|
- `Core`: framebuffer / depthbuffer / renderer
|
||||||
|
|
||||||
|
### 11.3 调试与测试工具
|
||||||
|
|
||||||
|
- 增加简单数学单元测试
|
||||||
|
- 增加投影和视口断言
|
||||||
|
- 增加线框 / 深度 / 法线可视化模式
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 可选增强
|
||||||
|
|
||||||
|
这些不影响基础 renderer 完成,但能显著提升项目质量。
|
||||||
|
|
||||||
|
### A. Texture Mapping
|
||||||
|
|
||||||
|
- 纹理采样
|
||||||
|
- UV 插值
|
||||||
|
- 可先做 nearest,再做 bilinear
|
||||||
|
|
||||||
|
### B. MSAA
|
||||||
|
|
||||||
|
- 子像素采样
|
||||||
|
- Resolve 到最终 framebuffer
|
||||||
|
|
||||||
|
### C. 阴影或简单后处理
|
||||||
|
|
||||||
|
- Shadow map
|
||||||
|
- Gamma 校正
|
||||||
|
- Tone mapping
|
||||||
|
|
||||||
|
### D. 性能优化
|
||||||
|
|
||||||
|
- 降低无效像素遍历
|
||||||
|
- 更高效的 depth / color 写入
|
||||||
|
- SIMD 或分块光栅化
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 里程碑
|
||||||
|
|
||||||
|
### Milestone 1
|
||||||
|
|
||||||
|
- 线框立方体稳定显示
|
||||||
|
- MVP 与 viewport 正确
|
||||||
|
|
||||||
|
### Milestone 2
|
||||||
|
|
||||||
|
- 实心立方体
|
||||||
|
- 背面剔除
|
||||||
|
- Z-buffer
|
||||||
|
|
||||||
|
### Milestone 3
|
||||||
|
|
||||||
|
- 属性插值
|
||||||
|
- Blinn-Phong
|
||||||
|
- OBJ 模型
|
||||||
|
|
||||||
|
### Milestone 4
|
||||||
|
|
||||||
|
- 相机交互
|
||||||
|
- 结构整理
|
||||||
|
- 可选增强项
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 当前最建议优先做的 5 件事
|
||||||
|
|
||||||
|
1. 把主流程从线框切到实心三角形
|
||||||
|
2. 接入 `DepthBuffer`
|
||||||
|
3. 实现重心坐标和深度插值
|
||||||
|
4. 明确并实现透视校正插值
|
||||||
|
5. 把 `main.cpp` 的渲染逻辑收口到 `Renderer`
|
||||||
Loading…
Reference in New Issue