您的位置:首页 > 移动开发 > Unity3D

Unity Shader之Tessellation

2017-06-29 17:29 856 查看
这是一个很牛逼的东东,得益于显卡技术的发展,可以将一些粗糙的低精模型,搞出高精的效果,首先,来说说本质是啥



如图所示,Tessellation就是可以在原来的三角形上细分出很多更小的三角形,增加很多顶点。

但是在具体运用中,我们应该怎么使用,达到我们想要的效果呢,这里就不得不先说一个东西置换贴图,也叫高度图,大家都知道法线贴图,可以使一个平面产生视觉上凹凸的感觉,但是这个平面越垂直于我们的视线,看起来效果就越假,因为始终这还是一个平面,没有产生物理上的形变,高度图就是帮我们解决这个问题,用一张图来位移这个平面上的顶点,使之真正获得物理上的形变,可以看下每种不同的效果,如图



第一张只有漫反射,第二个加入法线贴图,第三个贴上置换贴图获得形变。

为啥刚还在说Tessellation,怎么突然就说到置换贴图了,这是因为,在Tessellation这个技术出来之前,置换贴图的效果其实是不怎么好的,这是因为用到置换贴图的时候,需要用到足够多的顶点去支持这个形变,打个比方,一张平面上只有8个顶点,你贴一张精美的浮雕动物像上去,却只有这8个点获得形变,你可以想象一下最后出来的效果,肯定成四不像了,在tessellation技术出来之前,要用到置换贴图,需要模型本身的顶点数非常多,是高精模型,这就限制了置换贴图的使用。

回头你再看看Tessellation,就知道我为什么要说置换贴图了吧

对!我们可以把置换贴图和Tessellation结合起来,就可以在低精模型上实现我们想要的高精效果!

先看看unity shader里面使用Tessellation的语法吧,unity官方文档里面是用表面着色器来举例子,这里也只讨论用表面着色器用Tessellation的方法,在vertex/frag shader里面使用的话,要涉及到hull和domain shader ,下面是实现的代码,有兴趣的同学可以看看,这里就不多阐述了

#ifdef UNITY_CAN_COMPILE_TESSELLATION
struct TessVertex {
float4 vertex : INTERNALTESSPOS;
float3 normal : NORMAL;
float4 tangent : TANGENT;
};
struct OutputPatchConstant {
float edge[3]         : SV_TessFactor;
float inside          : SV_InsideTessFactor;
float3 vTangent[4]    : TANGENT;
float2 vUV[4]         : TEXCOORD;
float3 vTanUCorner[4] : TANUCORNER;
float3 vTanVCorner[4] : TANVCORNER;
float4 vCWts          : TANWEIGHTS;
};
TessVertex tessvert (VertexInput v) {
TessVertex o;
o.vertex = v.vertex;
o.normal = v.normal;
o.tangent = v.tangent;
return o;
}
float Tessellation(TessVertex v){
return _node_5663;
}
float4 Tessellation(TessVertex v, TessVertex v1, TessVertex v2){
float tv = Tessellation(v);
float tv1 = Tessellation(v1);
float tv2 = Tessellation(v2);
return float4( tv1+tv2, tv2+tv, tv+tv1, tv+tv1+tv2 ) / float4(2,2,2,3);
}
OutputPatchConstant hullconst (InputPatch<TessVertex,3> v) {
OutputPatchConstant o = (OutputPatchConstant)0;
float4 ts = Tessellation( v[0], v[1], v[2] );
o.edge[0] = ts.x;
o.edge[1] = ts.y;
o.edge[2] = ts.z;
o.inside = ts.w;
return o;
}
[domain("tri")]
[partitioning("fractional_odd")]
[outputtopology("triangle_cw")]
[patchconstantfunc("hullconst")]
[outputcontrolpoints(3)]
TessVertex hull (InputPatch<TessVertex,3> v, uint id : SV_OutputControlPointID) {
return v[id];
}
[domain("tri")]
VertexOutput domain (OutputPatchConstant tessFactors, const OutputPatch<TessVertex,3> vi, float3 bary : SV_DomainLocation) {
VertexInput v = (VertexInput)0;
v.vertex = vi[0].vertex*bary.x + vi[1].vertex*bary.y + vi[2].vertex*bary.z;
v.normal = vi[0].normal*bary.x + vi[1].normal*bary.y + vi[2].normal*bary.z;
v.tangent = vi[0].tangent*bary.x + vi[1].tangent*bary.y + vi[2].tangent*bary.z;
VertexOutput o = vert(v);
return o;
}
#endif


这里我们重点讲下表面着色器中的使用方法,很简单适合初学者,首先在加上相关Tessellation的声明

#pragma surface surf BlinnPhong addshadow fullforwardshadows vertex:disp tessellate:tessFixed nolightmap


看到没,只需要在声明中加上tessellate:tessFixed

tessFixed是一个函数,返回一个float4

float4 tessFixed()
{
return _Tess;
}
这里tess是一个参数,可以用户自定义,也可以根据不同的条件在不同的情况使用不同的数值,大家可以看下Tessellation.cginc这个脚本,可以看到unity为我们提供了常见的几种函数可以处理这个问题

UnityDistanceBasedTess //根据摄像机远近来生成顶点,越近的地方生成顶点越多

UnityEdgeLengthBaseTess //根据三角形大小来生成顶点,三角形越大的顶点生成越多

还有一些其他的函数,大家可以自行查看。也可以根据自己的需求自行定义

除此之外,我们还可以使用一些算法,在不使用displacement贴图的时候,让一些低精的模型边缘看起来更圆滑一遍,如图所示



很简单,unity为我们封装了一个叫Phone tessellation的方法,只需要在刚声明tessellation的地方加上tessphone:_Phone _Phone是一个浮点数,可以让用户自行定义,除了这个算法,还有其他的算法,这个感兴趣的可以自行研究实现了
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: