0%

之前写过一些前后端的项目,但这是头一次把网站部署到公网给别人用,这里做一些记录当作经验积累

技术

为了加快开发速度,选型还是熟悉的Vue+Vuetify,这个网站目前只需要前端功能,因此后端还没有选型。这一套写的很熟,所以第一版一天就给写出来了。后来遇到一个问题是正常的Vue单页应用对搜索引擎不友好,有一种说法是Google的爬虫对SPA有支持但是别的没有,所以后来用prerender-spa-plugin给打包成了服务端渲染,这个的配置参考了这篇文章,做了SSR还有一个好处就是接入统计的时候可以直接在index.html里插入代码,插入的统计代码会被复制到所有打包出来页面的<head>里,不用再动路由了。为了SEO还带了一个插件vue-meta-info,可以根据每页的配置自动生成<meta>,Bing的爬虫似乎很需要这个东西,但是Google还是不需要。

统计方面接入了百度统计和Google Analytics,虽然百度是一坨屎,但是百度统计令人感到意外的很好用,插入代码之后五分钟之内就有数据了,还有实时的数据,就是这个网站存不住登录信息,过几个小时都要重新登陆和邮箱验证。不知道为啥Google Analytics这几天一直都没跑起来,可能是配置出了问题。在做了SSR的打包之后应该直接按照他们提供的方法在index.html<head>里插入代码就好了。否则还需要在main.js里头加router.beforeEach来处理。

服务器选了UCloud的香港服务器,单核2G内存1M带宽,买了两个月花了二百五十左右,主要是为了免去备案的麻烦,但是买完会有客服打电话加微信还是挺烦的。域名直接在阿里买了一年的,接近一百块。服务器就是普通的Nginx,没做特别的配置。运行了几天都挺稳定的,就是加载速度略慢,这个的原因还有待排查。

后来发现确实是服务器的问题,传输一个500K的网页居然要6秒,看来需要搭CDN了,国内直接买也还要备案。客服似乎发现我服务器一直有流量进来还给我换了个高级客服。这几天发现有奇怪的域名访问进来,才想起来忘记在服务器配置域名了。现在Google Analytics也跑起来了,确实是时间的问题,但是感觉还是没有百度的好用。

2022.5.30 这几天新加了个英文站,部署在/en/的路径下,然后用spa-plugin打包的时候遇到了路径的坑,打包出来因为路径对不上效果出错,参考了这个链接https://github.com/chrisvfritz/prerender-spa-plugin/issues/215#issuecomment-415942268解决了问题。

运营

1.21浏览量大概在50,主要是当天在B站专栏发了广告
1.22浏览量也大概在60,广告继续发挥作用,还有零星从Bing来的,说明搜索引擎开始发挥作用了
1.26浏览量头一次破百,主要还是广告,Bing来的不超过十,有零星从百度移动端来的
5.30 最近浏览量一直在60左右,来自搜索引擎的占三分之一,还是打算在英文上多做一些,中文有点不合适了

SEO

Google

Google的爬虫是三个里面最灵的,用Search Console可以检查域名有没有被收录,之后就可以向Google提交域名,这个是三个搜索引擎里最灵的,上午十一点左右提交,当天下午五点左右就给收录了,而且不需要其他信息。但是Google给的初始排名不怎么高,除了精确搜索标题排到前二之外剩下的都不在第一页里,可能是需要做SEO的地方。

Bing

Bing可以用Webmaster Tool提交域名并检查是否被收录,但是已发现但未爬网的状态让人感觉很迷,事后发现等一阵子就好了。Bing要求很多,需要提供description,标题还要超过15字之类的,还好这些都可以用他的网站扫描暴露出来,这些修好以后可以重新提交。Bing在上线四天左右给收录了,而且排名给到了第一页,收录之后就有了从Bing来的流量(虽然只有个位数)。

百度

百度的链接在搜索资源平台提交,百度是最烂的,只能检查可不可以正常爬下来,没法主动检查有没有被收录,而且到现在(上线四天以后)也没给收录,不知道发生了啥,难道是备案的问题?

其他推广

1.21写了一篇广告(写的像论文一样)用小号发到B站专栏上了,两天大概170的浏览量10的收藏,给网站贡献了80左右的浏览量和60左右的UV,转化率还是很高。别的平台以后慢慢打广告罢,还要注册小号之类的。简书发了一篇被撤回了,好像因为外链之类的原因。目前网站主要的流量还是这些推广,希望以后能往搜索引擎方向倾斜。主要还是想多发一些带外链的广告,这样也有利于SEO。

B站专栏在后面几天访问量居然一直线性增长,到27号已经破1200了,还有140左右的收藏,看来方向是正确的,这些天又深入研究了下这个方向发现同类的东西做的确实不行,应该可以多搞一搞。

SimpleEngine(SE)是自己造的一个游戏引擎轮子,这篇文章用来记录SE的开发思路

渲染

数据结构

渲染器对多个渲染层进行渲染,每个层根据功能包括三个渲染队列:不透明队列、阴影队列、透明队列。三个队列各自使用一棵场景树对物体进行管理,各个队列具体情况如下:

  • 不透明队列:用于渲染不透明物体。
  • 阴影队列:用于渲染阴影贴图,在这一队列中的物体需要加入不透明队列才能正常渲染,这一队列不需要进行光照剔除,但需要根据光源进行视锥体剔除。
  • 透明队列:用于渲染半透明物体,这一队列中的物体需要在视锥体剔除后按由远到近的顺序进行渲染。

场景管理

引擎使用稀疏八叉树管理一个渲染队列中的物体和光源(目前只有在阴影队列中的点光源和聚光灯有意义)。这一八叉树有一个最大高度,物体和光源根据其在世界坐标中的作用范围(物体使用AABB包围盒,光源可能使用球体)插入八叉树的某一结点。物体需要插入能包含它的最细粒度的节点,而光源需要插入到能与其唯一相交的最细粒度节点(即这一节点的兄弟与光源都是相离的关系)物体位置移动时需要更新其在八叉树中的位置,若物体仍被包含在原节点,则需要在该节点及其子节点中找到一个新位置插入物体,否则就将物体删除后重新插入树中。对光源位置的更新也是删除重新插入即可。在将物体删除后,需要合并空节点,若被合并节点有灯光,需要将灯光提高至更高层节点,并在此后节点分裂时重新下推。

为了加速对象的删除和更新,需要维护对光源和物体在树中位置的索引。对于物体,只需简单记录物体所在节点及其在节点中的物体链表对应的指针即可。而对于光源,由于在合并空节点时需要将光源提高,因此需要支持查找某一子树中的所有光源的操作。这一操作一种可能的实现方法:首先对八叉树节点进行编号(可能利用中序遍历的模式编一个三维的向量),光源记录其所在节点的号,所有光源根据号的大小串成一个链表,这一过程可以用一棵线索二叉树实现,一个新光源插入时查找其前驱和后继确定其在链表中的位置,并将其加入链表和二叉树。一棵子树对应的编号是连续的,因此操作子树时只需要在二叉树中定位到链表的头尾即可。

对于开放世界游戏,对象在树中的深度会随对象远离原点而增加,另一方面,场景中出现的对象只占据整个世界的一小部分连续区域,也就是说八叉树中只有一部分子树中存在对象,而这一子树到树根的路径都是空的,因而不需要实际维护这一路径,这种空闲现象伴随物体删除产生,因此在物体删除导致出现一条唯一路径指向一棵子树时,可以删除这一路径只保留根;而在插入物体时,如果发现根节点不能包含物体,需要重新生成根节点的父亲并将其作为新的根节点直到其包含物体。

对场景进行渲染时,从树根处出发对八叉树节点进行视锥体剔除。对光源阴影渲染时,从光源索引处出发对子树中节点进行光源视锥体剔除。

这一数据结构起到以下几个作用:

  • 利用八叉树加速视锥体剔除的过程
  • 利用八叉树快速筛选出渲染某一光源时需要提交Drawcall的物体

这一数据结构的设计思路和依据在于,物体和光源的变化具有一定的局部性,因此采用动态的八叉树结构可以将对象插入删除的开销分摊到每一帧。对于较少物体,八叉树可以收缩不必要的节点,因此遍历八叉树进行剔除的开销和遍历物体列表全部提交drawcall的(遍历)开销规模是近似的,而八叉树可以轻松剔除视锥外的物体减少drawcall使得实际性能得到提升。场景需要用到实时阴影的动态光源较少,因此即使暴力插入删除开销也是可以接受的,而其在渲染shadowmap时可以根据光源作用范围快速剔除大量无关物体,相较此前需要渲染所有物体性能大大提升。

场景管理的思路在过去几周一直在变,从最开始为每一个队列维护一棵树和所有光源,到所有物体和光源共享一棵树,再到现在的每个队列维护一棵树,只把投射阴影的光源加入阴影队列再用GPU中的数据结构管理光源。在各种细枝末节的事情上卡了好长时间才发现问题的根源,之前一直把做Shading的光源和渲染Shadowmap的光源混为一谈,为了优化巨量光源在树上进行各种插入删除煞费苦心,但实际上八叉树只需要对Shadowmap的渲染做剔除就可以了。

到这里就需要一种统一的管理大世界场景的框架,这一框架可以根据主摄像机位置动态按区块加载和移除各种LOD级别的资源,在加载灯光和物体后通知场景管理的数据结构进行更新,并且和未来的地形系统对接。其中的耦合是很严重的,尤其是这一框架需要根据地形系统的某些几何特性选择数据结构,这也是为什么这个项目酝酿了这么久。

渲染管线

目前实现了Cluster based Forward+管线,在实现的过程里遇到一些坑,一个是Shader里的Buffer布局有std140std430,140的数组要对齐成vec4,还有一个问题是所有单元的光源最大数量乘以单元数要小于等于全局光源索引表的大小,现在的Cluster是8*8*16,每个单元最多64盏灯光,一共65536个索引。在实现Depth prepass的过程中遇到两个问题,一个是在实际着色前关掉了深度写入,但是在下一帧清除前要开启深度写入,不然没法清除;还有一个是着色Shader和深度Shader用同一几何数据算出来的片元深度可能不一致,导致绘制出来存在闪烁现象,具体解决方法参见这里

管线的实现还存在以下几个问题:

  • Cluster对于z的线性划分不合理
  • 光源范围计算存在问题,目前是用衰减到0.1代入计算,但是画面会出现分块现象,但是从视觉效果上看光源影响范围要小

全局光照

全局光照系统的实现细节还没有确定,有几个大概思路:

  • 采用VXGI
  • 每个Voxel只保存Phong漫反射颜色的均值
  • Diffuse Cone实现参考IBL的Diffuse部分
  • 需要针对GGX BRDF确定Glossy Cone的实现细节

有一个可以优化的点,之前的VXGI实现中Voxel中存储的是材质与光源作用后的数据,如果能将二者分开可能会获得性能提升,尤其是在地形系统中,处理场景破坏时顺便就可以更新Voxel数据。一种暴力的实现方式是将一个Voxel看作一个面,存储面的平均颜色和法线以及可以对其造成影响的光源列表,在做ConeTracing的时候进行漫反射颜色计算,对于每个片元发出的每个Cone,造成的开销是能够对其产生影响的光源数量的点乘,但是问题在于如何高效维护Voxel的光源列表。

Welcome to Hexo! This is your very first post. Check documentation for more info. If you get any problems when using Hexo, you can find the answer in troubleshooting or you can ask me on GitHub.

Quick Start

Create a new post

1
$ hexo new "My New Post"

More info: Writing

Run server

1
$ hexo server

More info: Server

Generate static files

1
$ hexo generate

More info: Generating

Deploy to remote sites

1
$ hexo deploy

More info: Deployment