UnityShader 曲面细分着色器 生成地形 高度贴图

什么是曲面细分着色器

如下图 请添加图片描述

曲面细分着色器比较官方定义:可以将一个几何体细化为一个球体也能将一根直线无限向曲线逼近
曲面细分着色器将复杂的曲面转换为简单的点,线,三角形等。它分为三部分:曲面细分控制着色器(Tessellation Control Shaders),曲面细分引擎(The Tessellation Engine),曲面细分求值着色器(Tessellation Evaluation Shaders)。

废话不多说,先看效果。

根据高度图 逐渐细分形成山峰

原理

和光照贴图相似的是,我们需要的是一个高度贴图。 然后我们根据高度贴图的高度值去细分顶点,最终效果就是精细化模型,原理很简单,主要的是细分着色器的使用和项目中我们如何根据摄像机远近去显示高低模型的细分。 这个往往需要是个很头疼的问题。

第一步 是开启曲面细分着色器 跟顶点着色器一样 使用前需要声明

#pragma hull hs
#pragma domain ds
#pragma target 4.6

要使用曲面细分,最小的shader target必须是4.6,如果我们没有手动地设置target,Unity将发出警告并自动使用这个等级。我们将为forward base和additive passes, 以及deferred pass添加曲面细分,保证inlude MyTessellation(放在include MyFlatWireframe之后)在这些pass中。
这里要用到两个细分着色器 一个是 hull 细分着色器 和domain 着色器, 两个功能不一样,负责处理的 几何效果也不同。

第二步 开启 HS 这一步注意点有很多 ,包括我们要细分原片的规则, 细分原片的数量,位置等等。

[UNITY_domain("tri")]
[UNITY_partitioning("fractional_odd")]
[UNITY_outputtopology("triangle_cw")]
[UNITY_patchconstantfunc("hsconst")]
[UNITY_outputcontrolpoints(3)]
InternalTessInterp_appdata hs (InputPatch<InternalTessInterp_appdata,3> v, uint id : SV_OutputControlPointID) {
  return v[id];
}

UnityTessellationFactors hsconst (InputPatch<InternalTessInterp_appdata,3> v) {
  UnityTessellationFactors o;
  float4 tf;
  tf = float4(_Xifen,_Xifen,_Xifen,_Xifen);
  o.edge[0] = tf.x; 
  o.edge[1] = tf.y; 
  o.edge[2] = tf.z; 
  o.inside = tf.w;
  return o;
}

完成上面的工作后,会产生几个编译错误,抱怨着我们没有正确地指明hull shader。因此,跟几何着色器一样,它需要一个属性来指明。首先,我们通过UNITY_domain的tri参数来显示地声明它处理的是三角形面片。
这还不够,我们还需要显示地指明函数将要为每个面片输出三个控制点,即三角形的三个角对应的点。使用[UNITY_outputcontrolpoints(3)]来指明。
GPU还需要知道如何来切割面片,通过 UNITY_partitioning属性来指明。这个属性定义了几种不同的切割方式,我们稍后再来研究它。在这里,我们先将它设置为整数模式:integer mode
GPU创建新的三角形时,它需要知道我们想要的是顺时针还是逆时针定义三角形。如同unity中的其他任何三角形一样,我们要的是顺时针。通过将UNITY_outputtopology属性设置为triangle_cw来指明。

其中,_XIfen是我们具体要细分的顶点数量。

第三步 也是最后一步,
完成上面工作后,shader编译器将提示我们不能只有曲面细分控制部分(tessellation control shader)而没有曲面细分评估部分(tessellation evaluation shader)。我们在上面所定义的hull shader只是让曲面细分着色器工作起来的一个部分而已。一旦曲面细分着色器决定了如何去分割面片,它将根据几何着色器去评估计算结果并最终生成细分后的三角形的顶点数据。因此,让我们先来创建一个domain shader,仍然从一个函数根来作为起点
之后则,我们需要把细分的顶点逐一采样 运算。

[UNITY_domain("tri")]
		v2f ds (UnityTessellationFactors tessFactors, const OutputPatch<InternalTessInterp_appdata,3> vi, float3 bary : SV_DomainLocation) {
		  appdata v;

		  v.vertex = vi[0].vertex*bary.x + vi[1].vertex*bary.y + vi[2].vertex*bary.z;
		  v.tangent = vi[0].tangent*bary.x + vi[1].tangent*bary.y + vi[2].tangent*bary.z;
		  v.normal = vi[0].normal*bary.x + vi[1].normal*bary.y + vi[2].normal*bary.z;
		  v.texcoord = vi[0].texcoord*bary.x + vi[1].texcoord*bary.y + vi[2].texcoord*bary.z;

		  v2f o = vert (v);
		  return o;
		}

		v2f vert (appdata v)
		{
			v2f o;
			o.texcoord = TRANSFORM_TEX(v.texcoord, _MainTex);
			v.vertex +=  tex2(_MainTex,float4(o.texcoord,0,0) );
			o.vertex = UnityObjectToClipPos(v.vertex);
			return o;
		}

当hull阶段指明了面片如何被细分之后,实际上还未生成任何新的顶点,而是给出了顶点的重心坐标。domain阶段将利用这些重心坐标来计算出最终的顶点。为达到这个目的,domain函数需要每个顶点执行一次,还需要为它提供所需要的重心坐标,重心坐标使用SV_DomainLocation语义来指明。
为确定如何去划分一个三角形,GPU将使用四个曲面细分因子(tessellation factors),三角形每个边各有一个因子,三角形内部也有一个因子。三个边因子将作为一个用SV_TessFactor语义修饰的float数组来进行传递,内部因子单独用SV_InsideTessFactor语义来指明。我们将创建一个结构体来存储这四个因子。
简单的概括是:vi 三角形的三个顶点 ,bary则是权重值,tessFactors是细分常量因子,需要仔细研究的同学可以找官网看看。
https://learn.microsoft.com/en-us/windows/uwp/graphics-concepts/domain-shader-stage–ds-

如果按照上面三步走对 ,呢么我们就得到 下面的效果。

根据高度图 逐渐细分形成山峰


这里我们可以手动调节细分网格的数量,我们可以看到 网格的复杂程度随着我们调节_Xifen 而有显著的变化。呢么我们可以举一反三,在项目中 我们可以根据摄像机的距离主动调节_Xifen的 数量,那么我们就可以解决更加轻量 高效 的低模使用问题。

我们需要模型在远距离和近距离的时候有不同精度和细节程度的表现,当然可以使用LOD技术,但LOD技术的缺点是灵活性较低,对内存的占用比较大,而且需要美术做三个不同LOD的模型,工作量也相对较大。使用曲面细分不仅解决了细分内存问题,同时也能解决 法线贴图 离近看的虚假问题。

细分程度逐渐提升的效果

总结

虽然是减少了内存,但也同时增加了 额外的GPU计算力,会发现很多没用的 面片 也被增加到细分里面去,这无疑也是对GPU的算力消耗,总而言之 ,也是一种 时间和 空间的 选择问题。但是特点是 在减少 内存的情况下,更加向现实世界的渲染方向偏近了。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值