我们正在全力以赴地将机器从目前的阿尔法状态带入测试版,我们将在不久的将来写更多关于进入测试版的内容以及这意味着什么。今天,我将转而分享一系列迷你博客文章的第一部分,这些博客文章涵盖了几个编辑器功能,这些功能位于呈现和编辑器代码之间的某个地方,或者更准确地说,是:
如果我最终在这个领域做了更多让人感兴趣的东西,我可能会把它加到这个清单上。
以上三个话题的共同点是,它们在我脑后渴望关注已经有很长一段时间了。虽然我们在机械编辑器中为他们提供了某种半途而废的解决方案,但我痛苦地意识到,他们肯定需要一些爱。但我一直在努力寻找动力和时间,用更好的东西取代现有的解决方案,但随着我们准备发布测试版,我们真的希望开始与编辑器合作时的第一印象能让人感觉良好,所以拖延的时间结束了。
具有讽刺意味的是,当我最终抽出一些时间来做这件事时,这三件事都比我预期的要容易得多,并得到了显著的改善。
在第一部分中,我们将讨论栅格的渲染。老实说,在我们开始我们的机器之前,我真的没有想过要渲染一个像样的网格。相反,我有些傲慢地认为这是工具程序员要解决的问题。虽然他们通常很好地实现了各种类型的捕捉工具和栅格,但他们从来没有引起任何注意,也没有帮助很好地将它们可视化。通常,这意味着他们决定使用一些基本的调试线条绘制API。这不仅从性能的角度来看是糟糕的,而且通常网格线缺乏任何形式的抗锯齿,因为谁会关心调试线的视觉质量呢?
由于我们的Mechanical的主要目标之一是找出如何以一种使工具开发更快的方式来构建EngineTechnology,并且希望还能弥合工具程序员和“运行时”程序员之间的知识鸿沟,至少在某种程度上,我认为分享我如何实现当前的网格渲染器是有意义的,我们将在即将发布的Mechanical测试版中推出该渲染器。
我们希望从编辑器代码中使用一个简单的API,允许在一个或多个视口中渲染一个或多个网格。
我们希望能够自由指定网格大小、变换、单元格大小以及颜色,而不必太在意任何潜在的性能影响。
我们希望能够高亮显示每十行,以增加网格的可读性。
我们希望网格从任何角度看起来都很美观(即我们想要尽可能少的云纹)。
tyecif struct tm_visual_grid_t{//世界变换tm_mat44_t tm;//网格在X,Z平面上的范围浮动grid_size;//一个单元格浮动的最小尺寸cell_size;//sRGB颜色和细线的Alpha tm_color_sRGB_t Thin_Lines_color;//sRGB颜色和粗线的Alpha(每隔十行)tm_color_sRGB_t Thick_Lines_color;}tm_visual_color;}tm_visual_。struct tm_grid_renender_api{void(*ender)(const tm_visual_grid_t*grid,const struct tm_shader_system_context_o*context,struct tm_shader_o*grid_shader,struct tm_renender_resource_command_buffer_o*res_buf,struct tm_renender_command_buffer_o*cmd_buf);};
从编辑器的角度来看,我们只需要关心tm_visual_grid_t,这是一个POD结构。目前,我们只支持指定二次网格范围和单元格大小,没有什么可以阻止我们处理非正方形范围(或单元格大小),但是我看不到任何您想要的情况,所以我选择了极简主义。当我们在后处理和色调映射之后渲染网格时,在8位sRGB颜色空间中指定颜色是最有意义的,使颜色空间与UI代码的其余部分保持一致。
当需要渲染视口时,会为编辑器想要绘制的每个网格调用tm_grid_renender_api->;ender()。在内部,此API除了将tm_visual_grid_t的内容写入到grid_shader拥有的常量缓冲区并发出具有6个顶点的绘制调用之外,并不执行任何其他操作。其余部分在着色器中进行。
在顶点着色器中,6个顶点被解释为在X、Z平面上横跨栅格范围的两个三角形的角点(我们在机械中使用右手Y+UP坐标系)。输入到像素着色器的是从{-Extent,-Extent}到{Extent,Extent}的插值2D栅格坐标。
//uv为像素的网格空间坐标。float2uv=input.grid_position;//查找网格空间的屏幕空间导数。[1]float2 dudv=float2(length(float2(ddx(uv.x),ddy(uv.x),length(float2(ddx(uv.y),ddy(uv.y);//定义LOD切换前单元线之间的最小像素数。const Float Min_Pixels_Better_Cells=1.f;//加载单元大小来自tm_visual_grid_t,以世界单位表示的网格单元的最小大小//要可视化的网格单元的最小大小。FLOAT cs=load_cell_size();//Calc Lod-level[2].Float Lod_Level=max(0,log10((length(Dudv)*min_Pixels_Better_Cells)/cs)+1);Float Lod_Fade=frac(LOD_LEVEL);//lost0、lod1和low2的Calc单元格大小。Float low0_cs=cs*power(10,Floor(LOD_LEVEL));Float lost1_cs=low0_cs*10.f;Float low2_cs=low1_cs*10.f;//允许每条抗锯齿线最多覆盖2个像素。dudv*=2;//为每个LOD计算到单元格线中心的无符号距离[3]float2 low0_cross_a=1.f-abs(saturate(fmod(uv,low0_cs)/dudv)*2-1.f);//选择max of x,y以获得lolololo0_a=max(lod0_cross_a.x,low0_Cross_a.y)的覆盖α值;float2 load1_Cross_a=1.。FLOAT LOD1_a=max(LOD1_CROSS_A.X,LOD1_CROSS_A.Y);FLOAT2 LOD2_CROSS_a=1.f-abs(saturate(fmod(uv,low2_cs)/dudv)*2-1.f);FLOAT LOD2_a=max(LOD2_CROSS_A.X,LOD2_CROSS_A.Y);//从tm_visual_grid_t(转换为0-1范围)加载sRGB颜色。float4 thick_color=load_thick_lines_color();//混合衰减颜色以处理LOD过渡[4]float4 c=low2_a>;0?THICK_COLOR:住宿1_a>;0?LERP(THICH_COLOR,THIN_COLOR,LOD_FADE):TINH_COLOR;//根据到栅格范围的距离和渲染角度计算不透明度衰减。[5]Float3view_dir=规格化(input.view_dir);Float op_gracing=1.f-power(1.f-abs(dot(view_dir,load_tm()._m10_m11_m12)),16);Float op_Distance=(1.f-饱和(length(UV)/load_grid_size();Float op=op_gracing*op_Distance;//LOD级别ALL之间混合。[6]C.A*=(lost2_a>;0?住宿2_a:住宿1_a>;0?lod1_a:(lod0_a*(1-lod_fade)*op;output.color=c;
对于有经验的着色器程序员来说,这段代码可能是不言而喻的(我相信可能有更好或更有效的方法来实现这一点,请随时启发我)。对于你们中的其他人,我将做一次演练:
我们首先找出当前网格坐标的导数(ddx/ddy)的长度。这基本上是我们在网格空间中从一个像素移动到下一个像素的程度。
由于我们要求能够指定任何单元格大小(世界单位,在机器中是米),而不必关心查看者的位置,因此我们需要某种自动细节级别(LOD),以避免最终陷入莫尔地狱。我们将LOD转换度量定义为两个单元行之间允许的最小距离(MIN_PIXES_BETWEEN_CELES),直到该行完全淡出并完全过渡到下一个LOD-步骤。由于我们突出显示了每10个单元格之间的网格线,因此使用该网格线来定义LOD步骤是有意义的。这就是log10()和power(10,LOD_LEVEL)的来源,对于每个LOD级别,我们将覆盖10^LOD_LEVEL x多个最小尺寸的单元格(tm_visual_grid_t中的cell_size)。
由于我们希望在LOD步骤之间混合,因此我们将计算在(2)中计算的三种不同像元大小(low0_cs、low1_cs、low2_cs)的网格线覆盖率。单元格大小实质上是新线应该可见的频率。在计算覆盖值时,用图形准确地显示正在发生的事情会更容易,因此,我创建了一个小演示图。
在该图中,w是行宽(上面代码中的dudv),c是行频(low0_cs、low1_cs、low2_cs)。演示图形在1D中工作,但我们在2D中的栅格平面上工作,栅格线相互交叉,所以我们只取X,Y的最大覆盖率,并使用它作为每个LOD-Step(lod0_a,lode1_a,low2_a)的out alpha遮罩值。
这相当简单,但本质上,这里我们只是根据LOD_FADE计算细线颜色和粗线颜色之间的淡入度,并使用(3)中计算的覆盖Alpha值计算当前像素属于某个LOD-Step的程度。
根据观察角度和到图形中心的距离计算所有网格线的不透明度淡入度。这是暂时的,可能会进一步调整,一些旋钮可能需要在tm_visual_grid_t中暴露出来。
最后,我们用三个LOD步骤的线条覆盖蒙版的另一个手波混合来调制像素的最终Alpha值,并将整个Shebang与在(5)中计算的不透明度值相乘。
就这样。看起来不错的格子。您可以自己实现一个,或者在我们即将发布的测试版中检查一下。
注意:在未来的帖子中,我可能会在使用此技术时注意一些浮点精度方面的注意事项,也许还有一些关于在TAA抖动的深度缓冲区上很好地进行深度测试的注意事项,但是时间已经到了,我需要把这个贴出来,然后在测试版上重新做一些工作。🙂