本应是Mesh系列的第一篇~为了赶动画原理拖了几天。
在上一篇中,我们用半边结构读取了网格,但实际上还没真正用到半边。
今天就是半边结构大显身手(?)的时候了!它可以快速找到每个顶点的所有邻居节点,以计算顶点的法线。
(入坑两年了,怎么还有人在写写了800次的基础卡渲.......)
B类作业④ 实验报告
实验原理
Shading
使用半边结构读入obj格式的三维网格模型,通过叉乘计算顶点法线,使用Blinn-Phone模型进行简单的渲染。
Interaction
模型交互使用了A类作业①魔方程序中构建的TrackBall类,将鼠标在窗口内的位置移动映射到球面,计算球面角对模型进行旋转。
实验步骤
1.使用半边结构读入网格
详见B类作业③实验报告。
2.计算顶点法线(见Vertex::computeNormal()
)
通过叉乘三角面的两个半边,我们可以获得三角面的法线。但顶点不止存在于一个三角面上,因此需要遍历所有其所在的三角面,对所有法线求平均值。
顶点不存储面相关的信息,因此遍历所有定点所在三角面是通过获取 所有顶点的出半边 实现的。通过outEdge->twin->next
即可访问下一条出半边。
碎碎念:半边结构其实改了特别特别久。最开始没有成对创建半边,也没想起用哈希表,就分类讨论判断是否加新边之类的....改到头秃,最后索引关系还不对,算出来的法线: 像我的精神状态一样错乱......
3.渲染
将灯光位置与相机位置传入着色器,通过与Position做差可得到视线向量viewDir
与光线向量lightDir
,取平均得到半程向量H = normalize(viewDir+lightDir)
.点乘法线即可进行简单的光照计算。
4.鼠标交互-旋转
引入了TrackBall
类。
-
当鼠标在窗口按下时,通过回调函数
click_callback
将鼠标位置传入TrackBall
,并用布尔值leftkey
记录按键状态。 - 当鼠标在窗口移动时,若
leftkey
为真,通过回调函数mouse_callback
将鼠标位置传入TrackBall
,计算旋转角,并生成旋转矩阵,传递给vertex shader
。
实现细节:
- 通过
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL);
开启鼠标监听 - 使用
glfwGetCursorPos()
获得鼠标的屏幕坐标,并重映射至[-1,1] TrackBall
中用mat4 lasttrans
记录先前的历史旋转。球面角计算得到的旋转轴是世界空间的,需要通过lasttrans
的逆矩阵将旋转轴转换至物件空间。
5.鼠标交互-缩放
缩放的实现主要通过更改透视投影矩阵的视锥角实现。
- 当滚轮在窗口改变时,通过回调函数
scroll_callback
将滚轮增减作用于视锥角。
6.渲染补充
补充了简单的程序化背景和简单的卡通渲染效果。
-
在Blinn-Phone的光照基础上,对漫反射、高光的点乘计算值进行
step()/smoothstep()
即可获取清晰的光照边界。也可以添加ramp map
以映射不同阶数的光照表现。 -
背景由一个的四边形面片构成,在片元着色器中计算颜色。
- 渐变:用
TexCoords.y
插值两种颜色。 - 晶格化渐变:以
TexCoords.y
为半径的sdf画圆。
- 渐变:用
- 将顶点朝法线方向偏移,即可获得描边效果。为刻画圈圈的网格结构,这里描边只绘制网格的背面,且绘制后不清理深度缓冲,这样在网格转折明显的部位仍可以出现描边效果。对比如下图:
(Yeah 我知道还有让描边宽窄不变的trick🤪🤪但我懒得写了。这个case里摄像机根本就不能动。)
- 渲染时发现,附件提供的mesh模型缺少了一个三角面: (还以为法线又算错了 生气🤪
为了视觉效果,在obj文件中添加了这个面。因此实际提交的`eight.uniform.obj`比附件提供的文件要多一行~
实验效果
- 通过鼠标左键旋转模型。
- 通过鼠标滚轮缩放模型。
- 按键盘1/2/3切换显示效果。
实验环境
Window 10. Visual Studio 2022.
依赖库:GLFW,GLAD, GLM