模板规则推导
模板推导规则
1 | template<typename T> |
SFINAE
SFINAE(Substitution Failure Is Not An Error)是 C++ 中的一个重要概念,主要用于模板编程。它的意思是,当模板参数替换导致错误时,编译器不会将其视为错误,而是会继续查找其他可能的匹配。这使得我们可以在模板中进行条件编译,选择合适的重载或特化
SFINAE 的优势
条件编译:可以根据类型特征选择不同的实现,增强代码的灵活性和可重用性。
避免编译错误:当模板参数替换导致错误时,编译器不会将其视为错误,而是继续查找其他匹配的模板。
类型安全:通过类型特征,可以确保只有符合条件的类型才能使用特定的模板实现。
SFINAE 是 C++ 模板编程中的一个强大工具,允许开发者根据类型特征选择合适的模板特化或重载。通过结合 std::enable_if 和类型特征,开发者可以编写更灵活和安全的代码。
电子书目录
渲染整理
开放世界的场景管理
1. 切成tile
2. 数据分级LOD
3. 根据天顶角,调整远平面,裁剪数据
游戏引擎分层架构
1. EditorLayer
2. FuntionLayer
1. Rendering
2. Animation
3. Camera
4. Physics
5. Script
3. ResourceLayer
4. CoreLayer
1. threadPoolManagement
2. memoryPool
3. mathLibrary
5. PlatformLayer
1. RHI
Renderable
1. shader
1. vs,fs
2. macro
2. RenderState
3. MVP
4. Texture
5. DrawImp
渲染效果
1. 前向渲染
1. PBR
2. 布林冯模型
2. 阴影shadowMap:
1. 根据相机的位置,对整个场景,绘制出深度图,表示光的可见性
2. 渲染时,将相机位置变化到光源位置,计算深度值与shadowmap进行比较,从而决定绘制的亮度
3. 楼的倒影
1. 矩阵楼块插入地面
2. 模板测试,只有地面和水才绘制
4. AO
5. 聚光灯效果
6. UV动画
技术点提炼
1. 楼块
1. 切成小块
2. 柔化圆角
3. 贴UV
2. 3d瓦片绘制
1. 瓦片的加载,与cache
2. 非实施例渲染,按材质进行分类渲染
3. 实例化渲染,动态计算lod进行渲染
3. 模型渲染
1. PBR渲染
2. 非PBR渲染
c++ 可变参数模板
可变模版参数(variadic templates)
可以对参数进行高度泛化,标识0到任意个数参数
两种展开形式
- 使用特化的终止函数结合递归
1 | #include <iostream> |
- 使用逗号表达式
初始化列表,通过初始化列表来初始化一个变长数组, {(printarg(args), 0)…}将会展开成((printarg(arg1),0), (printarg(arg2),0), (printarg(arg3),0), etc… ),最终会创建一个元素值都为0的数组int arr[sizeof…(Args)]
1 | template<class F, class... Args>void expand(const F& f, Args&&...args) |
initializer_list
为了编写处理不同数量实参的函数,如果参数类型相同,可以使用initializer_list, 如果实参类型不同,使用可变参数模板
1 | struct myclass { |
c++继承权限
继承有三种权限,public,proteced,private,默认不写是private
权限的最低是public,其次是protected,最高private
继承方式代表是父类属性在当前类中的最低呈现
父类中的privated属性在子类中不可访问
OpenGL模板测试流程
模板测试流程
不考虑earlyZ的情况下,fragment执行后,进行模板测试,通过后,进入深度测试
模板测试一般使用流程:
1. 启用模板缓冲写入
2. 渲染物体,更新模板缓冲
3. 禁用模板缓冲写入
4. 渲染其他物体,根据模板缓冲内容决定是否丢弃片段
使用模板测试绘制物体轮廓的例子
1
2
3
glStencilMask();
glStencilFunc(GLenum func, GLint ref, GLuint mask);
glStencilOp(GLenum sfail, GLenum dpfail, GLenum dppass);
1. 开启模板测试和深度测试
2. 第一次render pass,主要是绘制,并写入模板
1. 开启模板测试和深度测试
2. glStencilMask(0xFF);
3. glStencilOp(keep, keep, replace);
4. glStencilFunc(always, 1, 0xFF);
5. 绘制物体
3. 第二次render pass, 放大物体,通过模板测试剔除非边缘像素
1. 将物体缩放变大
2. 关闭深度测试 //因为这里的边缘不需要有拓扑关系
3. 关闭模板写入glStencilMask(0x00);
4. glStencilFunc(not_equal, 1, 0xFF);
5. 绘制物体
关于OpenGL里面的Mask
1. 写入颜色是,r,g,b,a 分别与对应的mask,进行&运算后写入
2. depth也是同样道理,如果设置成true,就是允许写入,设置成false,不允许写入
3. stencil的Mask,是0xFF~0x00,之间的256个数,一般设置是0xFF,允许任意值写入,0x00是不允许写入
实现完美转发
什么是完美转发?
在理解什么是完美转发之前,需要知道什么是万能引用?
在模板推导过程中,使用T&& a,这时候,并不是类型T的右值引用,而是万能引用,如果a是左值,这时候,就是一个左值引用,如果a是右值,这时候就是一个右值引用,具体原理是发生引用折叠。
1 | template <typename T> |
根据参数的具体类型,来实例化模板,准确的生成左值引用和右值引用的实例,这就是万能引用
万能引用遇到的问题?
上面的例子中,Add函数参数虽然是类型是右值引用,但是值确实左值,导致函数内继续使用调用其他函数时,参数类型由右值变成左值,也就是无法将右值引用这个类型继续转发.
1 | template <typename T> |
解决方案: std::forward
1 | template <typename T> |
std::forward的具体实现
1 | template <class _Tp> |
具体分析一下,也是通过引用折叠来实现
- 如果_Tp的类型是int&, 通过引用折叠 int& && 折叠后是左值引用int&
- 如果_Tp的类型是int&&, 通过引用折叠 int&& && 折叠后是int&&
add Image
###测试插入一张图片
test end
c++ 11 智能指针
智能指针
share_ptr使用
sharet_ptr构造函数和std::make_share 的区别
- 两个堆内存和一个堆内存,std::make_share效率更高
weak_ptr使用
- expired(),返回指向对堆对象是否释放
- use_count,share_ptr的强引用计数
- lock,返回share_ptr,如果释放,返回空
share_ptr线程安全话题
- share_ptr引用计数本身是线程安全的
- 一个share_ptr对象,在多个线程操作,不能保证线程安全
- share_ptr指向的对象本身,进行操作时,也无法保证线程安全,完全取决于指向对象是否线程安全
stl容器多线程安全时的性能考虑
code使用
1 | int main() |