Skip to content

Commit 23ca1e0

Browse files
author
skywind3000
committed
局部优化
1 parent 9717683 commit 23ca1e0

5 files changed

+149
-206
lines changed

RenderHelp.h

+57-105
Original file line numberDiff line numberDiff line change
@@ -1036,36 +1036,24 @@ class Bitmap
10361036
// 着色器定义
10371037
//---------------------------------------------------------------------
10381038

1039-
// VertexShader 输入
1040-
// 不同属性使用整数 key 进行访问
1041-
struct VS_Input {
1042-
Vec4f pos; // 顶点坐标
1043-
std::map<int, float> attrib_float; // 浮点数属性列表
1044-
std::map<int, Vec2f> attrib_vec2f; // 二维矢量属性列表
1045-
std::map<int, Vec3f> attrib_vec3f; // 三维矢量属性列表
1046-
std::map<int, Vec4f> attrib_vec4f; // 四维矢量属性列表
1047-
};
1048-
1049-
1050-
// PixelShader 输入,同时也是 VertexShader 的输出
1051-
// 前面 VS 设置好 pos 和 varying 后,每次调用 PS 前会根据像素坐标进行插值
1052-
struct PS_Input {
1053-
Vec4f pos; // VS 输出的新坐标
1039+
// 着色器上下文,由 VS 设置,再由渲染器按像素逐点插值后,供 PS 读取
1040+
struct ShaderContext {
10541041
std::map<int, float> varying_float; // 浮点数 varying 列表
10551042
std::map<int, Vec2f> varying_vec2f; // 二维矢量 varying 列表
10561043
std::map<int, Vec3f> varying_vec3f; // 三维矢量 varying 列表
10571044
std::map<int, Vec4f> varying_vec4f; // 四维矢量 varying 列表
10581045
};
10591046

10601047

1061-
// 顶点着色器:输入 VS_Input,需要填充 PS_Input
1062-
// 顶点着色器需要根据 VS_Input 的内容,设置好 PS_Input 的 pos
1063-
typedef std::function<void(VS_Input &, PS_Input &)> VertexShader;
1048+
// 顶点着色器:因为是 C++ 编写,无需传递 attribute,传个 0-2 的顶点序号
1049+
// 着色器函数直接在外层根据序号读取响应数据即可,最后需要返回一个坐标 pos
1050+
// 各项 varying 设置到 output 里,由渲染器插值后传递给 PS
1051+
typedef std::function<Vec4f(int index, ShaderContext &output)> VertexShader;
10641052

10651053

1066-
// 像素着色器:输入 PS_Input,需要返回 Vec4f 类型的颜色
1067-
// 三角形内每个点的 PS_Input 具体值会根据前面三个顶点的 PS_Input 插值得到
1068-
typedef std::function<Vec4f(PS_Input &)> PixelShader;
1054+
// 像素着色器:输入 ShaderContext,需要返回 Vec4f 类型的颜色
1055+
// 三角形内每个点的 input 具体值会根据前面三个顶点的 output 插值得到
1056+
typedef std::function<Vec4f(ShaderContext &input)> PixelShader;
10691057

10701058

10711059
//---------------------------------------------------------------------
@@ -1108,7 +1096,6 @@ class RenderHelp
11081096
delete []_depth_buffer;
11091097
_depth_buffer= NULL;
11101098
}
1111-
ClearVertex();
11121099
_color_fg = 0xffffffff;
11131100
_color_bg = 0xff191970;
11141101
}
@@ -1139,35 +1126,10 @@ class RenderHelp
11391126
}
11401127
}
11411128

1142-
// 清空顶点的 attrib/varying
1143-
inline void ClearVertex() {
1144-
for (int i = 0; i < 3; i++) {
1145-
Vertex &vertex = _vertex[i];
1146-
vertex.vs_input.attrib_float.clear();
1147-
vertex.vs_input.attrib_vec4f.clear();
1148-
vertex.vs_input.attrib_vec3f.clear();
1149-
vertex.vs_input.attrib_vec2f.clear();
1150-
vertex.ps_input.varying_float.clear();
1151-
vertex.ps_input.varying_vec4f.clear();
1152-
vertex.ps_input.varying_vec3f.clear();
1153-
vertex.ps_input.varying_vec2f.clear();
1154-
}
1155-
}
1156-
11571129
// 设置 VS/PS 着色器函数
11581130
inline void SetVertexShader(VertexShader vs) { _vertex_shader = vs; }
11591131
inline void SetPixelShader(PixelShader ps) { _pixel_shader = ps; }
11601132

1161-
// 设置顶点坐标,id 是顶点下标,范围是 [0, 2]
1162-
inline void SetVertex(int id, const Vec4f& pos) { _vertex[id].vs_input.pos = pos; }
1163-
inline void SetVertex(int id, const Vec3f& pos) { SetVertex(id, pos.xyz1()); }
1164-
1165-
// 设置顶点属性,id 是顶点下表,name 为属性列表的 key
1166-
inline void SetAttrib(int id, int name, float x) { _vertex[id].vs_input.attrib_float[name] = x; }
1167-
inline void SetAttrib(int id, int name, const Vec2f& v) { _vertex[id].vs_input.attrib_vec2f[name] = v; }
1168-
inline void SetAttrib(int id, int name, const Vec3f& v) { _vertex[id].vs_input.attrib_vec3f[name] = v; }
1169-
inline void SetAttrib(int id, int name, const Vec4f& v) { _vertex[id].vs_input.attrib_vec4f[name] = v; }
1170-
11711133
// 保存 FrameBuffer 到 BMP 文件
11721134
inline void SaveFile(const char *filename) { if (_frame_buffer) _frame_buffer->SaveFile(filename); }
11731135

@@ -1200,49 +1162,46 @@ class RenderHelp
12001162

12011163
// 绘制一个三角形,必须先 SetVertex/SetAttrib 设定好三个点坐标和属性
12021164
inline bool DrawPrimitive() {
1203-
if (_frame_buffer == NULL)
1165+
if (_frame_buffer == NULL || _vertex_shader == NULL)
12041166
return false;
12051167

12061168
// 顶点初始化
12071169
for (int k = 0; k < 3; k++) {
12081170
Vertex& vertex = _vertex[k];
12091171

1210-
// 先复制一遍坐标,vertex shader 不设置的话,就用这个默认值
1211-
vertex.ps_input.pos = vertex.vs_input.pos;
1172+
// 清空上下文 varying 列表
1173+
vertex.context.varying_float.clear();
1174+
vertex.context.varying_vec2f.clear();
1175+
vertex.context.varying_vec3f.clear();
1176+
vertex.context.varying_vec4f.clear();
12121177

1213-
// 运行顶点着色程序
1214-
if (_vertex_shader) {
1215-
_vertex_shader(vertex.vs_input, vertex.ps_input);
1216-
}
1178+
// 运行顶点着色程序,返回顶点坐标
1179+
vertex.pos = _vertex_shader(k, vertex.context);
12171180

12181181
// 简单裁剪,任何一个顶点超过 CVV 就剔除
1219-
Vec4f& pos = vertex.ps_input.pos;
1220-
float w = pos.w;
1182+
float w = vertex.pos.w;
12211183

12221184
// 这里图简单,当一个点越界,立马放弃整个三角形,更精细的做法是
12231185
// 如果越界了就在齐次空间内进行裁剪,拆分为 0-2 个三角形然后继续
12241186
if (w == 0.0f) return false;
1225-
if (pos.z < 0.0f || pos.z > w) return false;
1226-
if (pos.x < -w || pos.x > w) return false;
1227-
if (pos.y < -w || pos.y > w) return false;
1187+
if (vertex.pos.z < 0.0f || vertex.pos.z > w) return false;
1188+
if (vertex.pos.x < -w || vertex.pos.x > w) return false;
1189+
if (vertex.pos.y < -w || vertex.pos.y > w) return false;
1190+
1191+
// 计算 w 的倒数:Reciprocal of the Homogeneous W
1192+
vertex.rhw = 1.0f / w;
12281193

1229-
// 顶点所坐标除以 w
1230-
float reciprocal_w = 1.0f / w;
1231-
pos.w = reciprocal_w; // pos.w 保存 w 的倒数
1232-
pos.x *= reciprocal_w;
1233-
pos.y *= reciprocal_w;
1234-
pos.z *= reciprocal_w;
1194+
// 齐次坐标空间 /w 归一化到单位体积 cvv
1195+
vertex.pos *= vertex.rhw;
12351196

12361197
// 计算屏幕坐标
1237-
vertex.spf.x = (pos.x + 1.0f) * _fb_width * 0.5f;
1238-
vertex.spf.y = (1.0f - pos.y) * _fb_height * 0.5f;
1198+
vertex.spf.x = (vertex.pos.x + 1.0f) * _fb_width * 0.5f;
1199+
vertex.spf.y = (1.0f - vertex.pos.y) * _fb_height * 0.5f;
12391200

12401201
// 整数屏幕坐标:加 0.5 的偏移取屏幕像素方格中心对齐
12411202
vertex.spi.x = (int)(vertex.spf.x + 0.5f);
12421203
vertex.spi.y = (int)(vertex.spf.y + 0.5f);
12431204

1244-
// printf("%d: (%d, %d)\n", k, vertex.spi.x, vertex.spi.y);
1245-
12461205
// 更新外接矩形范围
12471206
if (k == 0) {
12481207
_min_x = _max_x = Between(0, _fb_width - 1, vertex.spi.x);
@@ -1254,16 +1213,6 @@ class RenderHelp
12541213
_min_y = Between(0, _fb_height - 1, Min(_min_y, vertex.spi.y));
12551214
_max_y = Between(0, _fb_height - 1, Max(_max_y, vertex.spi.y));
12561215
}
1257-
1258-
// 所有 varying 提前除以 w,为线性插值做准备
1259-
for (auto &it: vertex.ps_input.varying_float)
1260-
it.second = it.second * reciprocal_w;
1261-
for (auto &it: vertex.ps_input.varying_vec2f)
1262-
it.second = it.second * reciprocal_w;
1263-
for (auto &it: vertex.ps_input.varying_vec3f)
1264-
it.second = it.second * reciprocal_w;
1265-
for (auto &it: vertex.ps_input.varying_vec4f)
1266-
it.second = it.second * reciprocal_w;
12671216
}
12681217

12691218
// 绘制线框
@@ -1277,8 +1226,8 @@ class RenderHelp
12771226
if (_render_pixel == false) return false;
12781227

12791228
// 判断三角形朝向
1280-
Vec4f v01 = _vertex[1].ps_input.pos - _vertex[0].ps_input.pos;
1281-
Vec4f v02 = _vertex[2].ps_input.pos - _vertex[0].ps_input.pos;
1229+
Vec4f v01 = _vertex[1].pos - _vertex[0].pos;
1230+
Vec4f v02 = _vertex[2].pos - _vertex[0].pos;
12821231
Vec4f normal = vector_cross(v01, v02);
12831232

12841233
// 使用 vtx 访问三个顶点,而不直接用 _vertex 访问,因为可能会调整顺序
@@ -1342,58 +1291,60 @@ class RenderHelp
13421291
b = b * (1.0f / s);
13431292
c = c * (1.0f / s);
13441293

1345-
PS_Input& i0 = vtx[0]->ps_input;
1346-
PS_Input& i1 = vtx[1]->ps_input;
1347-
PS_Input& i2 = vtx[2]->ps_input;
1348-
1349-
// 插值得到当前点的 1/w,先前保存的 pos.w 已经是 w 的倒数了
1350-
float rw = a * i0.pos.w + b * i1.pos.w + c * i2.pos.w;
1294+
// 计算当前点的 1/w,因 1/w 和屏幕空间呈线性关系,故直接重心插值
1295+
float rhw = vtx[0]->rhw * a + vtx[1]->rhw * b + vtx[2]->rhw * c;
13511296

13521297
// 进行深度测试
1353-
if (rw < _depth_buffer[cy][cx]) continue;
1354-
_depth_buffer[cy][cx] = rw;
1298+
if (rhw < _depth_buffer[cy][cx]) continue;
1299+
_depth_buffer[cy][cx] = rhw;
1300+
1301+
// 还原当前像素的 w
1302+
float w = 1.0f / ((rhw != 0.0f)? rhw : 1.0f);
1303+
1304+
// 计算三个顶点插值 varying 的系数
1305+
// 先除以各自顶点的 w 然后进行屏幕空间插值然后再乘以当前 w
1306+
float k0 = vtx[0]->rhw * a * w;
1307+
float k1 = vtx[1]->rhw * b * w;
1308+
float k2 = vtx[2]->rhw * c * w;
13551309

1356-
// 更新 w
1357-
float w = 1.0f / rw;
1310+
// 准备为当前像素的各项 varying 进行插值
1311+
ShaderContext input;
13581312

1359-
// 初始化像素着色器输入
1360-
PS_Input input;
1361-
input.pos.w = w;
1362-
input.pos.x = (a * i0.pos.x + b * i1.pos.x + c * i2.pos.x) * w;
1363-
input.pos.y = (a * i0.pos.y + b * i1.pos.y + c * i2.pos.y) * w;
1364-
input.pos.z = (a * i0.pos.z + b * i1.pos.z + c * i2.pos.z) * w;
1313+
ShaderContext& i0 = vtx[0]->context;
1314+
ShaderContext& i1 = vtx[1]->context;
1315+
ShaderContext& i2 = vtx[2]->context;
13651316

13661317
// 插值各项 varying
13671318
for (auto const &it: i0.varying_float) {
13681319
int key = it.first;
13691320
float f0 = i0.varying_float[key];
13701321
float f1 = i1.varying_float[key];
13711322
float f2 = i2.varying_float[key];
1372-
input.varying_float[key] = (a * f0 + b * f1 + c * f2) * w;
1323+
input.varying_float[key] = k0 * f0 + k1 * f1 + k2 * f2;
13731324
}
13741325

13751326
for (auto const &it: i0.varying_vec2f) {
13761327
int key = it.first;
13771328
const Vec2f& f0 = i0.varying_vec2f[key];
13781329
const Vec2f& f1 = i1.varying_vec2f[key];
13791330
const Vec2f& f2 = i2.varying_vec2f[key];
1380-
input.varying_vec2f[key] = (a * f0 + b * f1 + c * f2) * w;
1331+
input.varying_vec2f[key] = k0 * f0 + k1 * f1 + k2 * f2;
13811332
}
13821333

13831334
for (auto const &it: i0.varying_vec3f) {
13841335
int key = it.first;
13851336
const Vec3f& f0 = i0.varying_vec3f[key];
13861337
const Vec3f& f1 = i1.varying_vec3f[key];
13871338
const Vec3f& f2 = i2.varying_vec3f[key];
1388-
input.varying_vec3f[key] = (a * f0 + b * f1 + c * f2) * w;
1339+
input.varying_vec3f[key] = k0 * f0 + k1 * f1 + k2 * f2;
13891340
}
13901341

13911342
for (auto const &it: i0.varying_vec4f) {
13921343
int key = it.first;
13931344
const Vec4f& f0 = i0.varying_vec4f[key];
13941345
const Vec4f& f1 = i1.varying_vec4f[key];
13951346
const Vec4f& f2 = i2.varying_vec4f[key];
1396-
input.varying_vec4f[key] = (a * f0 + b * f1 + c * f2) * w;
1347+
input.varying_vec4f[key] = k0 * f0 + k1 * f1 + k2 * f2;
13971348
}
13981349

13991350
// 执行像素着色器
@@ -1422,10 +1373,11 @@ class RenderHelp
14221373

14231374
// 顶点结构体
14241375
struct Vertex {
1425-
VS_Input vs_input; // VS 输入
1426-
PS_Input ps_input; // PS 输入,同时也是 VS 输出
1427-
Vec2f spf; // 浮点数屏幕坐标
1428-
Vec2i spi; // 整数屏幕坐标
1376+
ShaderContext context; // 上下文
1377+
float rhw; // w 的倒数
1378+
Vec4f pos; // 坐标
1379+
Vec2f spf; // 浮点数屏幕坐标
1380+
Vec2i spi; // 整数屏幕坐标
14291381
};
14301382

14311383
protected:

sample_01_triangle.cpp

+13-18
Original file line numberDiff line numberDiff line change
@@ -6,32 +6,27 @@ int main(void)
66
// 初始化渲染器和帧缓存大小
77
RenderHelp rh(800, 600);
88

9-
const int ATTRIB_COLOR = 0; // 定义一个属性的 key
109
const int VARYING_COLOR = 0; // 定义一个 varying 的 key
1110

12-
// 顶点着色器,输出坐标,初始化 varying
13-
rh.SetVertexShader([&] (VS_Input& input, PS_Input& output) {
14-
output.pos = input.pos; // 直接复制坐标
15-
output.varying_vec4f[VARYING_COLOR] = input.attrib_vec4f[ATTRIB_COLOR];
11+
// 顶点数据,由 VS 读取
12+
struct { Vec4f pos; Vec4f color; } vs_input[3] = {
13+
{ { 0.0, 0.7, 0.90, 1}, {1, 0, 0, 1} },
14+
{ { -0.6, -0.2, 0.01, 1}, {0, 1, 0, 1} },
15+
{ { +0.6, -0.2, 0.01, 1}, {0, 0, 1, 1} },
16+
};
17+
18+
// 顶点着色器,初始化 varying 并返回坐标,
19+
// 参数 index 是渲染器传入的顶点序号,范围 [0, 2] 用于读取顶点数据
20+
rh.SetVertexShader([&] (int index, ShaderContext& output) -> Vec4f {
21+
output.varying_vec4f[VARYING_COLOR] = vs_input[index].color;
22+
return vs_input[index].pos; // 直接返回坐标
1623
});
1724

1825
// 像素着色器,返回颜色
19-
rh.SetPixelShader([&] (PS_Input& input) -> Vec4f {
26+
rh.SetPixelShader([&] (ShaderContext& input) -> Vec4f {
2027
return input.varying_vec4f[VARYING_COLOR];
2128
});
2229

23-
// 设置三个顶点的坐标
24-
rh.SetVertex(0, { 0.0, 0.7, 0.90});
25-
rh.SetVertex(1, {-0.6, -0.2, 0.01});
26-
rh.SetVertex(2, {+0.6, -0.2, 0.01});
27-
28-
// 设置三个顶点的颜色属性,属性可以有四种类型:float, Vec2f/3f/4f
29-
// 这里设置的是 Vec4f 的属性,故上面 PS 中用 attrib_vec4f 取出来
30-
// 不同属性的 KEY 用整数来区别,这里就是 ATTRIB_COLOR 这个整数
31-
rh.SetAttrib(0, ATTRIB_COLOR, Vec4f(1, 0, 0, 1.0f));
32-
rh.SetAttrib(1, ATTRIB_COLOR, Vec4f(0, 1, 0, 1.0f));
33-
rh.SetAttrib(2, ATTRIB_COLOR, Vec4f(0, 0, 1, 1.0f));
34-
3530
// 渲染并保存
3631
rh.DrawPrimitive();
3732
rh.SaveFile("output.bmp");

sample_02_texture.cpp

+21-17
Original file line numberDiff line numberDiff line change
@@ -20,38 +20,42 @@ int main(void)
2020
Mat4x4f mat_proj = matrix_set_perspective(3.1415926f * 0.5f, 800 / 600.0, 1.0, 500.0f);
2121
Mat4x4f mat_mvp = mat_model * mat_view * mat_proj; // 综合变换矩阵
2222

23+
// 定义顶点输入
24+
struct VertexAttrib { Vec4f pos; Vec2f texuv; } vs_input[3];
25+
2326
// 定义属性和 varying 中的纹理坐标 key
24-
const int ATTR_TEXUV = 0;
2527
const int VARYING_TEXUV = 0;
2628

2729
// 顶点着色器
28-
rh.SetVertexShader([&] (VS_Input& input, PS_Input& output) {
29-
output.pos = input.pos * mat_mvp; // 输出变换后的坐标
30-
output.varying_vec2f[VARYING_TEXUV] = input.attrib_vec2f[ATTR_TEXUV];
30+
rh.SetVertexShader([&] (int index, ShaderContext& output) {
31+
Vec4f pos = vs_input[index].pos * mat_mvp; // 输出变换后的坐标
32+
output.varying_vec2f[VARYING_TEXUV] = vs_input[index].texuv;
33+
return pos;
3134
});
3235

3336
// 像素着色器
34-
rh.SetPixelShader([&] (PS_Input& input) -> Vec4f {
37+
rh.SetPixelShader([&] (ShaderContext& input) -> Vec4f {
3538
Vec2f coord = input.varying_vec2f[VARYING_TEXUV]; // 取得纹理坐标
3639
return texture.Sample2D(coord); // 纹理采样并返回像素颜色
3740
});
3841

3942
// 0 1
4043
// 3 2 绘制两个三角形,组成一个矩形
41-
rh.SetVertex(0, { 1, -1, -1});
42-
rh.SetVertex(1, { 1, 1, -1});
43-
rh.SetVertex(2, {-1, 1, -1});
44-
rh.SetAttrib(0, ATTR_TEXUV, {0, 0});
45-
rh.SetAttrib(1, ATTR_TEXUV, {1, 0});
46-
rh.SetAttrib(2, ATTR_TEXUV, {1, 1});
44+
VertexAttrib vertex[] = {
45+
{ { 1, -1, -1, 1}, {0, 0} },
46+
{ { 1, 1, -1, 1}, {1, 0} },
47+
{ {-1, 1, -1, 1}, {1, 1} },
48+
{ {-1, -1, -1, 1}, {0, 1} },
49+
};
50+
51+
vs_input[0] = vertex[0];
52+
vs_input[1] = vertex[1];
53+
vs_input[2] = vertex[2];
4754
rh.DrawPrimitive();
4855

49-
rh.SetVertex(0, {-1, 1, -1});
50-
rh.SetVertex(1, {-1, -1, -1});
51-
rh.SetVertex(2, { 1, -1, -1});
52-
rh.SetAttrib(0, ATTR_TEXUV, {1, 1});
53-
rh.SetAttrib(1, ATTR_TEXUV, {0, 1});
54-
rh.SetAttrib(2, ATTR_TEXUV, {0, 0});
56+
vs_input[0] = vertex[2];
57+
vs_input[1] = vertex[3];
58+
vs_input[2] = vertex[0];
5559
rh.DrawPrimitive();
5660

5761
// 保存文件

0 commit comments

Comments
 (0)