前某篇:LearnGL - 15 - Skybox - 天空盒 - 优化天空盒的渲染队列 - 有说一丢丢关于渲染顺序的优化,这个是直接使用 渲染队列值 来排序的
这一篇:我们将分别对 不透明几何体、半透明几何体、其他几何体 的渲染顺序进行排序
本人才疏学浅,如有什么错误,望不吝指出。
渲染队列
之前有提到过,现在再复制一篇:
enum class RenderQueueType {
Skybox = 0500, // 天空盒子
Background = 1000, // 背景
Geometry = 2000, // 不透明几何体
AlphaTest = 2500, // FS 带有 discard 的
Transprent = 3000, // 透明的几何体
Gizmos = 3500, // 开发调式用的 Gizmos 绘制
Overlay = 4000, // UI
};
渲染队列值越小,绘制就越早
核心排序函数
目前比较简单,因为提供的功能有限,后续如果不断增加各种维度来排序的话,可能就来越复杂了
bool Camera::rendererCmp(MeshRenderer* a, MeshRenderer* b) {
if (a->queue == b->queue) {
// 注意我们的 view 是右手坐标
// 意味着:从屏幕内指向我们观察(相机)的位置是 z 值的增量方向
// 所以 view z 值越大,里我们相机近
// 几何体不透明的话,先绘制近的,再绘制远的,这样可以类似 early-z 的效果,剔除后续不必要的 fragment shader
// 如果是半透明的话,先绘制远的,再绘制近的,因为半透一般不写深度,需要与后面先绘制的背景内容融混处理
// 几何体 或是 AlphaTest 的几何体
if (a->queue >= (int)RenderQueueType::Geometry && a->queue <= (int)RenderQueueType::AlphaTest) {
return
a->getOwner()->getTrans()->get_view_pos().z >
b->getOwner()->getTrans()->get_view_pos().z; // 降序,view z越大越早绘制,先绘制比较近于镜头的,再绘制背后原理镜头的,Draw Front To Back
}
// 半透明
else if (a->queue >= (int)RenderQueueType::Transprent && a->queue < (int)RenderQueueType::Gizmos) {
return
a->getOwner()->getTrans()->get_view_pos().z <
b->getOwner()->getTrans()->get_view_pos().z; // 升序,view z越大越早绘制,先绘制背后远离镜头的,再绘制比较近于镜头的,Draw Back To Front
}
}
else {
// 其他几何体 目前是直接使用渲染队列值来排序
return a->queue < b->queue;
}
}
具体看注释的说明
不透明几何体
虽然你可以直接看我的注释说明,但是我觉得很有必要强调说明
// 几何体 或是 AlphaTest 的几何体
if (a->queue >= (int)RenderQueueType::Geometry && a->queue <= (int)RenderQueueType::AlphaTest) {
return
a->getOwner()->getTrans()->get_view_pos().z >
b->getOwner()->getTrans()->get_view_pos().z; // 降序,view z越大越早绘制,先绘制比较近于镜头的,再绘制背后原理镜头的,Draw Front To Back
}
可以看到,我是将 Geometry 到 AlphaTest 渲染队列之间的都算是 不透明 的几何体
这类 不透明几何体 我们都可以先绘制离镜头 近 一些的,再绘制离镜头 远 一些的,这样就能尽可能的剔除了后续被 depth test 测试失败剔除了片段,从而避免 fragment shader 的执行,渲染效率也就提升了
总结为:不透明几何体,渲染顺序 先近后远
半透明几何体
虽然你可以直接看我的注释说明,但是我觉得很有必要强调说明
// 半透明
else if (a->queue >= (int)RenderQueueType::Transprent && a->queue < (int)RenderQueueType::Gizmos) {
return
a->getOwner()->getTrans()->get_view_pos().z <
b->getOwner()->getTrans()->get_view_pos().z; // 升序,view z越大越早绘制,先绘制背后远离镜头的,再绘制比较近于镜头的,Draw Back To Front
}
可以看到,我是将 Transprent 到 Gizmos 渲染队列之间的都算是 半透明 的几何体
这类 半透明的几何体 我们都可以先绘制离镜头 远 一些的,再绘制离镜头 近 一些的,这样才能从渲染效果上看上去比较好一些
总结为:半透明几何体,渲染顺序 先远后近
其它几何体
其他几何体的渲染排序,目前我是直接使用 渲染队列值 来排序的
没啥可说,但要知道目前是有区别的
else {
// 其他几何体 目前是直接使用渲染队列值来排序
return a->queue < b->queue;
}
查看半透明效果
因为在渲染不透明几何体通常会开启深度剔除,所以渲染顺序的不同,在视觉上看不到区别的
因此我们只查看半透明在不同渲染顺序策略下的区别
先看正确的,都是在:Transparent 队列下的
正常效果
渲染队列为 Transparent 是正确的,因为半透明先渲染远的,再渲染近的
不正常的效果
下面将 Transparent 渲染队列修改为 Geometry,因为Geometry 是从近到远渲染的,效果应用在半透明上就不对了
深度写入的问题
因为我们半透明通常是不写入深度的,所以我们在这些几何体渲染是,会将渲染状态配置为不可写入深度
到如果该次 Draw Call 最后一个绘制关闭了深度写入,这时到了每一帧的 glClear
清理(重置)深度值时,就会有问题,因为上次还是保持着不写入深度的配置状态,因此 glClear
也是不会写入深度的,即:不能成功的清理深度
所以我们很有必要在 glClear
清理之前,先去开启一些深度写入即可 glDepthMask(GL_TRUE);
,代码如下:
// 这里要强制开启一下 深度写入,因为有些绘制关闭深度写入
// 如果是最后一个渲染的话就会影响这里的下次清楚深度写入的问题
glDepthMask(GL_TRUE);
glClearColor(clearColor.x, clearColor.y, clearColor.z, clearColor.w); // 设置清理颜色缓存时,填充的颜色值
glClearDepthf(clearDepth); // 设置清理深度缓存时,填充的深度值
glClear(clearFlag); // 清理颜色缓存 与 深度缓存 与 模板缓存