先弄几个方便使用的结构
帧的概念
什么是一帧? 其实我们说的帧就是一幅静止的画面,无数个静止的画面连起来播放,通过我们的脑补帧来让我们看到连贯的动画,fps就是我们常用的衡量多少帧每秒的单位,一般情况下29fps是电视台用的帧率,60fps是一般的游戏的帧率(在我们玩游戏的时候神经高度集中,29会让我们觉得晕),144fps就是一些竞技类的游戏想要达到的帧率.
帧又是怎么诞生的呢? 一般情况下就是在电脑里渲染出3D场景然后利用投射投到2D的屏幕上,也就是fps就是一秒钟我们能渲染几次这个图像,gfx-hal
使用的都是常见的光栅化的渲染(虽然正在准备加入Nvidia Ray Tracing
支持),所谓的光栅化其实也是非常的简单,我们在电脑里获取了每一个物体的顶点的位置和顶点的连接之后,利用着色器(shader)来给顶点上色,然后在给顶点围起来的区域上色.
这里在补充一下,光线追踪指的是利用每一束计算机模拟的光打在物体上模拟出来的光路来上色的,上色的细节会比光栅化好的很多(因为一般涉及到那种带有反射啊,金属材质什么的光栅化需要很多的骚技巧来模拟)
看到这里你们大概有个概念了吧 下面是伪码
Vec<Picture> //一堆帧
//渲染主程序
loop{
//...
render(hardware,data);
}
这里再补充一下,很多时候你们会听到帧缓冲这个概念,什么是帧缓冲的? 其实说白了就是提前渲染好的一堆帧用来当GPU忙的喘不过气来的时候来顶替一下,混淆一下你的大脑,因为毕竟每一帧的东西都不太一样,难免有些地方会慢一两秒.所以帧缓冲就是来改善体验,让用户觉得更流畅的.
如何控制上面的循环呢
在第一课里,你已经掌握了事件循环和窗口的创建,但是实际上这距离真正的图形学还有点远,我们还需要处理一些非常底层的,硬件的,和GPU沟通的硬件代码,虽然gfx-hal
已经给我们提供了一个很好的核心,但在核心之上还需要写一些硬件代码,为了符合Rust的美学,我们需要用什么结构来包装起来
吐槽啊,hkt什么时候出来,现在写的这一堆monad类似的东西用一个hkt能简化多少工作流程 所以这里要引入
HardwareState
用来控制GPU
的状态 和 LocalState
控制用户的输入输出之类的状态,来更好的用高级抽象控制GPU的执行,控制非纯操作的作用范围
上面的那个render
函数就是用来渲染的 函数定义伪码
pub fn render(hal:&mut HardwareState,data:&LocalState){
hal.draw(data.color)
}
这段是写给真正从事过这方面开发的人看的,有些时候render会出错,出错了要debug,那么这个gfx
是怎么debug的呢?其实,gfx
是一个胶水库,粘合了所有的底层API,所以如果你使用的是DX12
后端那么你可以用你最喜欢的Visual Studio
来调试可执行程序,如果你使用的是Vulkan
后端那么你可以使用LunarG Vulkan SDK
提供给你的一系列程序来调试 同时 gfx
会自动的报出debug layer
的数据,Metal
我没使用过,不清楚,如果你想让我写关于这方面的可以考虑资助我一台Macbook
,你喜欢OpenGL
??!!??!! 对不起,我们不认识,你可选择右上角⌧
再见
设置后端
#[cfg(feature = "dx12")]
use gfx_backend_dx12 as back;
#[cfg(feature = "vulkan")]
use gfx_backend_vulkan as back;
#[cfg(feature = "metal")]
use gfx_backend_metal as back;
#[cfg(not(any(feature = "vulkan", feature = "dx12",feature = "metal",)))]
use gfx_backend_vulkan as back;
这几行代码会先查看你的cargo.toml
来确定你使用的是哪一个后端,然后再使用,和C语言
里面的#ifdef
差不多
然后在编译的时候需要指定 cargo run --features vulkan
之类的来确定到底用了那个后端
开始设计HardwareState
pub struct HardwareState {
current_frame: usize, // 当前帧
frames_in_flight: usize, // 当前传输的帧
in_flight_fences: Vec<<back::Backend as Backend>::Fence>, // 正在运行中的内存屏障
render_finished_semaphores: Vec<<back::Backend as Backend>::Semaphore>, // 渲染结束时发送的信号
image_available_semaphores: Vec<<back::Backend as Backend>::Semaphore>, // 可用信号
command_buffers: Vec<CommandBuffer<back::Backend, Graphics, MultiShot, Primary>>, // 命令缓冲区域
command_pool: ManuallyDrop<CommandPool<back::Backend, Graphics>>, // 命令池
framebuffers: Vec<<back::Backend as Backend>::Framebuffer>, // 帧缓冲
image_views: Vec<(<back::Backend as Backend>::ImageView)>, // 图像视图
render_pass: ManuallyDrop<<back::Backend as Backend>::RenderPass>, // 渲染过程
render_area: Rect, // 渲染区域
queue_group: QueueGroup<back::Backend, Graphics>, // 队列簇
swapchain: ManuallyDrop<<back::Backend as Backend>::Swapchain>, // 交换链
device: ManuallyDrop<back::Device>, // 设备
_adapter: Adapter<back::Backend>, //用来抽象设备的转接器
_surface: <back::Backend as Backend>::Surface, //你的屏幕
_instance: ManuallyDrop<back::Instance>, //示例
}
这个结构是我们用来管理硬件状态用的最基本的结构,我们会拆出几个章节来分别讲解怎么把这个结构创建出来