我是靠谱客的博主 顺利水池,这篇文章主要介绍光栅渲染器基础知识,现在分享给大家,希望可以做个参考。

从体绘制绕路过来的,三维重建,网格处理,鼠标交互,开始找不到路,边走边问,最后到了光栅渲染器,应该是这里吧?这个坑挖好久了,试试能填多少。这里没有公式和算法,网上和参考文献中都有详细的解释,不再重复内容。 --2020年4月9日
其实有点累了,有点想放弃,再坚持一下。 --2020年4月23日
这坑有点大,适当收敛一下。 --2020年4月24日
基本有个交代了,暂时就到这里。–2020年4月28日

第一步 将韦大的代码移植到gcc下,IDE用QtCreater。–2020年4月8日
复制代码
1
2
3
4
5
6
7
8
#ifdef _MSC_VER #pragma comment(lib, "gdi32.lib") #pragma comment(lib, "user32.lib") #endif //改成了 LIBS += libgdi32 libuser32

既然windows下的Qt也是win32扩展,那么win32那一套在Qt下也是可用的,so,win32 api 和 Qt api 混用也可以。目前将msc平台下的代码移植到gcc下运行没问题,可移植的最大原因是代码本来就跨平台。

第二步 代码抽离封装,并增加一些功能。–2020年4月9日
  • 添加了圆柱
  • 添加了读取点集后线
  • 添加了读取中心线后管状物
第三步 代码封装管线机制,并添加鼠标交互 --2020年4月11日

封装完成后扩展性和易读性好了很多。
只是将原来的键盘交互改成了鼠标响应,并没有实质性的变动,离真正的轨迹球模式还有一段路要走。
鼠标响应借用了Qt的mouse event。

第四步 添加了轨迹球,光照,相机 --2020年4月21日

重写了相机。
将原来的2个方向扩展N个方向的轨迹球。
加入了一个实现不太好的光照。

第五步 3D线框渲染流水结构梳理

回到最初的代码,去掉封装,看3D线框变化原理

1、摄像机是一个4X4的矩阵

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
// 设置摄像机:创建左手坐标系的观察矩阵 void struct_demo::matrix_set_lookat(matrix_t *m, //相机坐标 const vector_t *eye, //eye:相机所在的位置, const vector_t *at, //at:相机到目标的向量,默认{ 0, 0, 0, 1 } const vector_t *up) //up:向上的方向向量,这里用 { 0, 0, 1, 1 } { vector_t xaxis, yaxis, zaxis; vector_sub(&zaxis, at, eye);//zaxis = at - eye vector_normalize(&xaxis);//zaxis归一化 vector_crossproduct(&xaxis, up, &zaxis);// xaxis = up x zaxis vector_normalize(&xaxis);//xaxis归一化 vector_crossproduct(&yaxis, &zaxis, &xaxis);//yaxis = zaxis x xaxis m->m[0][0] = xaxis.x; m->m[1][0] = xaxis.y; m->m[2][0] = xaxis.z; m->m[3][0] = -vector_dotproduct(&xaxis, eye);//xaxis * eye m->m[0][1] = yaxis.x; m->m[1][1] = yaxis.y; m->m[2][1] = yaxis.z; m->m[3][1] = -vector_dotproduct(&yaxis, eye);//yaxis * eye m->m[0][2] = zaxis.x; m->m[1][2] = zaxis.y; m->m[2][2] = zaxis.z; m->m[3][2] = -vector_dotproduct(&zaxis, eye);//zaxis * eye m->m[0][3] = 0.0f; m->m[1][3] = 0.0f; m->m[2][3] = 0.0f; m->m[3][3] = 1.0f; }

左手坐标系的观察矩阵
eye:相机所在的位置
at:相机到目标的向量
up:向上的方向向量,书中介绍时为[0,1,0],而很多地方用[0,-1,0]
观察坐标系的z轴为:zaxis = normal(at - eye)
观察坐标系的x轴为:xaxis = normal(cross(up,zaxis))
观察坐标系的z轴为:yaxis = cross(zaxis,xaxis)
其中:normal为使单位向量化,cross为求两向量的法向量(单位向量)
dot为:轴 * eye.x + 轴 * eye.y + 轴 * eye.z
创建的矩阵为:

观察矩阵
xaxis.xyaxis.xzaxis.x0
xaxis.yyaxis.yzaxis.y0
xaxis.zyaxis.zzaxis.z0
-dot(xaxis,eye)-dot(yaxis,eye)-dot(zaxis,eye)1

在物体视角变换的过程中,全程只用了一个camera_at_zero函数,而这个函数也只调整了一个参数eye,即相机所在的位置:

复制代码
1
2
3
4
5
6
7
8
void struct_demo::camera_at_zero(device_t *device, float x, float y, float z) { point_t eye = { x, y, z, 1 }, at = { 0, 0, 0, 1 }, up = { 0, 0, 1, 1 }; matrix_set_lookat(&device->transform.view, &eye, &at, &up); //一旦调整相机位置,马上刷新观察矩阵。 transform_update(&device->transform); }

这里定义了坐标变换,最重要的是 transform = world * view * projection

复制代码
1
2
3
4
5
6
7
8
typedef struct { matrix_t world; // 世界坐标变换 matrix_t view; // 摄影机坐标变换 matrix_t projection; // 投影变换 matrix_t transform; // transform = world * view * projection float w, h; // 屏幕大小 } transform_t;

旋转是物体自传(如果是相机转呢?物体静止不动,细细想来,真是情况中两种情况都是存在的,应用场景不同),缩放是相机近大远小

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
int main(int argc, char *argv[]) { …… device_t device; int indicator = 0; int kbhit = 0; float alpha = 1; float pos = 3.5; …… if (screen_init(800, 600, title)) return -1; struct_demo *demo = new struct_demo; demo->device_init(&device, 800, 600, screen_fb);//给出一个初始化800*600大小的画布 demo->camera_at_zero(&device, 3, 0, 0);//给出一个初始化的相机,不然看不到。其实就是虚拟与真实世界的抽象和封装。 //demo->init_texture(&device);//给出一个初始化的纹理,如果不用纹理,可以注释 device.render_state = RENDER_STATE_WIREFRAME; while (screen_exit == 0 && screen_keys[VK_ESCAPE] == 0) { screen_dispatch(); demo->device_clear(&device, 1);//清空整个窗口画布 demo->camera_at_zero(&device, pos, 0, 0);//调整相机位置 if (screen_keys[VK_UP]) pos -= 0.01f;//这部分全是键盘输入参数 if (screen_keys[VK_DOWN]) pos += 0.01f; if (screen_keys[VK_LEFT]) alpha += 0.01f; if (screen_keys[VK_RIGHT]) alpha -= 0.01f; if (screen_keys[VK_SPACE]) { if (kbhit == 0) { kbhit = 1; if (++indicator >= 3) indicator = 0; device.render_state = RENDER_STATE_WIREFRAME; } } else { kbhit = 0; } demo->draw_box(&device, alpha);//刷新并绘制立方体 screen_update(); Sleep(1); } delete demo; return a.exec(); }

光栅渲染器关键词:

  1. 读取解析数据;
  2. 多边形网格:点->线->三角形->面。两点构成一线,三条线构成一个三角形,若干个三角形连接成三角带(面)。
  3. 纹理坐标,颜色
  4. Camera :平移,旋转,缩放:平移和自转是网格的矩阵变换,缩放是摄像机的矩阵变换
  5. KeyEvent,MouseEvent
  6. World Transform,View Transform ,Projection Transform
  7. 矢量运算,矩阵变换,顶点运算
  8. 齐次坐标,cvv,归一化,初始化
  9. 光栅化:光栅化是将几何数据经过一系列变换后最终转换为像素,从而呈现在显示设备上的过程。光栅化的本质是坐标变换、几何离散化。
  1. 剪裁,透视除法,背面剔除,视口转换,扫描转换
  2. 光照,阴影
  3. 左手坐标系:Z轴指向屏幕里

第六步 理论问题若干

基本结构

  1. 先定义一个4属性的向量vector,并组织向量的数学运算:叉乘,点乘,减法,归一化。
  2. 由向量派生空间点point。
  3. 由空间点构建顶点vertex。
  4. 将数据点集按照vertex的格式组织起来,构成点,线,三角形,面,体。
  5. 再定义一个4X4的矩阵matrix,组织该矩阵的数学运算:
  6. 由该矩阵产生3个对象:世界坐标变换world,摄影机坐标变换view,投影变换projection
  7. 由这3个对象产生一个变换矩阵:transform = world * view * projection
  8. 构建场景renderer,接管了绘制画布的内存,事实上所有的绘制都在这部分,其他的都是抽象和封装。
  9. 构建相机camera,相机的变化就是修改摄影机坐标变换view,然后刷新变换矩阵transform = world * view * projection
  10. 物体的旋转变化就是修改世界坐标变换world

几个问题

  1. 如何把物体放置在视图的正中央
  2. 在视野中,哪些点可见,哪些点不可见
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// 检查齐次坐标同 cvv 的边界用于视锥裁剪 int fishTransform::transform_check_cvv(const fishVector *v) { float w = v->w; int check = 0; if (v->z < 0.0f) check |= 1; if (v->z > w) check |= 2; if (v->x < -w) check |= 4; if (v->x > w) check |= 8; if (v->y < -w) check |= 16; if (v->y > w) check |= 32; return check; }
  1. 物体旋转矩阵
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
// 旋转矩阵 void fishMatrix::matrixSetRotate(matrix_t *m, float x, float y, float z, float theta) { float qsin = (float)sin(theta * 0.5f); float qcos = (float)cos(theta * 0.5f); fishVector vec; vec.x = x; vec.y = y; vec.z = z; vec.w = 1.0f; float w = qcos; // m_vector->vectorNormalize(&vec); vec.vectorNormalize(); x = vec.x * qsin; y = vec.y * qsin; z = vec.z * qsin; m->m[0][0] = 1 - 2 * y * y - 2 * z * z; m->m[1][0] = 2 * x * y - 2 * w * z; m->m[2][0] = 2 * x * z + 2 * w * y; m->m[0][1] = 2 * x * y + 2 * w * z; m->m[1][1] = 1 - 2 * x * x - 2 * z * z; m->m[2][1] = 2 * y * z - 2 * w * x; m->m[0][2] = 2 * x * z - 2 * w * y; m->m[1][2] = 2 * y * z + 2 * w * x; m->m[2][2] = 1 - 2 * x * x - 2 * y * y; m->m[0][3] = m->m[1][3] = m->m[2][3] = 0.0f; m->m[3][0] = m->m[3][1] = m->m[3][2] = 0.0f; m->m[3][3] = 1.0f; }
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//cube void fishBlocks::drawCube(float theta) { matrix_t m; //计算旋转矩阵 m_renderer->GetfishMatrix()->matrixSetRotate(&m, 1, 0, 0, theta);//Z //m_renderer->GetfishMatrix()->matrixSetRotate(&m, 0, 1, 0, theta);//X //m_renderer->GetfishMatrix()->matrixSetRotate(&m, 0, 0, 1, theta);//Y //m_renderer->GetfishMatrix()->matrixSetRotate(&m, -1, -0.5, 1, theta); //世界坐标变换 m_renderer->GetfishDevice()->GetFishTransform()->world = m; m_renderer->GetfishTransform()->transform_update(); drawPlane(0, 1, 2, 3); drawPlane(4, 5, 6, 7); drawPlane(0, 4, 5, 1); drawPlane(1, 5, 6, 2); drawPlane(2, 6, 7, 3); drawPlane(3, 7, 4, 0); // qDebug()<<"fishRenderer::draw_cube"; // std::cout<<"fishRenderer::draw_cube done"<<std::endl; }
  1. 相机旋转矩阵
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// 设置摄像机 void fishCamera::matrixSetLookat(matrix_t *m, const fishVector *eye, const fishVector *at, const fishVector *up) { fishVector xaxis, yaxis, zaxis; zaxis.vectorSub(at,eye); zaxis.vectorNormalize(); xaxis.vectorCrossproduct(up,&zaxis); xaxis.vectorNormalize(); yaxis.vectorCrossproduct(&zaxis,&xaxis); m->m[0][0] = xaxis.x; m->m[1][0] = xaxis.y; m->m[2][0] = xaxis.z; m->m[3][0] = -m_renderer->GetfishVector()->vectorDotproduct(&xaxis, eye); m->m[0][1] = yaxis.x; m->m[1][1] = yaxis.y; m->m[2][1] = yaxis.z; m->m[3][1] = -m_renderer->GetfishVector()->vectorDotproduct(&yaxis, eye); m->m[0][2] = zaxis.x; m->m[1][2] = zaxis.y; m->m[2][2] = zaxis.z; m->m[3][2] = -m_renderer->GetfishVector()->vectorDotproduct(&zaxis, eye); m->m[0][3] = m->m[1][3] = m->m[2][3] = 0.0f; m->m[3][3] = 1.0f; }

参考文献:

  1. 想用C++实现一个软件渲染器,类似DX和OpenGL,除了《3D游戏编程大师技巧》,或者什么网站推荐? - 知乎
    https://www.zhihu.com/question/33712299/answer/58495947
  2. 渲染器 1 —— 基本绘图 - 知乎
    https://zhuanlan.zhihu.com/p/20140034
  3. 《3D数学基础:图形与游戏开发》
  4. 《3D游戏编程大师技巧》
  5. 《计算机图形学与几何造型导论》

最后

以上就是顺利水池最近收集整理的关于光栅渲染器基础知识的全部内容,更多相关光栅渲染器基础知识内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(50)

评论列表共有 0 条评论

立即
投稿
返回
顶部