查看: 5313|回复: 2
打印 上一主题 下一主题

[经验分享] Unity3d 使用DX11的曲面细分(二)

[复制链接]

100

主题

3

听众

7683

积分

高级设计师

Rank: 6Rank: 6

纳金币
2378
精华
0

最佳新人 活跃会员 热心会员 灌水之王 突出贡献

跳转到指定楼层
楼主
发表于 2015-4-30 08:51:42 |只看该作者 |倒序浏览

曲面细分函数tessFixed返回一个float4的值:xyz是三角形的三个顶点的细分程度,w是比例。在本shader里只是一个float常量来作为模型的细分程度。

以距离为基础的曲面细分(Distance-based tessellation)

We can also change tessellation level based on distance from the camera. For example, we could define two distance values; distance at which tessellation is at maximum, and distance towards which tessellation level gradually decreases.


我们也能通过模型与相机的距离改变细分程度。举个例子,我们需要定义两个距离值;一个是距离相机最近的距离值,一个是最远的距离值。

Shader 'Custom/testShader' { Properties {            _Tess ('Tessellation', Range(1,32)) = 4            _MainTex ('Base (RGB)', 2D) = 'white' {}            _DispTex ('Disp Texture', 2D) = 'gray' {}            _NormalMap ('Normalmap', 2D) = 'bump' {}            _Displacement ('Displacement', Range(0, 1.0)) = 0.3            _Color ('Color', color) = (1,1,1,0)            _SpecColor ('Spec color', color) = (0.5,0.5,0.5,0.5)        }        SubShader {            Tags { 'RenderType'='Opaque' }            LOD 300                        CGPROGRAM            #pragma surface surf BlinnPhong addshadow fullforwardshadows vertex:disp tessellate:tessDistance nolightmap            #pragma target 5.0            #include 'Tessellation.cginc'            struct appdata {                float4 vertex : POSITION;                float4 tangent : TANGENT;                float3 normal : NORMAL;                float2 texcoord : TEXCOORD0;            };            float _Tess;            float4 tessDistance (appdata v0, appdata v1, appdata v2) {                float minDist = 10.0;                float maxDist = 25.0;                return UnityDistanceBasedTess(v0.vertex, v1.vertex, v2.vertex, minDist, maxDist, _Tess);            }            sampler2D _DispTex;            float _Displacement;            void disp (inout appdata v)            {                float d = tex2Dlod(_DispTex, float4(v.texcoord.xy,0,0)).r * _Displacement;                v.vertex.xyz += v.normal * d;            }            struct Input {                float2 uv_MainTex;            };            sampler2D _MainTex;            sampler2D _NormalMap;            fixed4 _Color;            void surf (Input IN, inout SurfaceOutput o) {                half4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;                o.Albedo = c.rgb;                o.Specular = 0.2;                o.Gloss = 1.0;                o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_MainTex));            }            ENDCG        }        FallBack 'Diffuse'    }

We found the UnityDistanceBasedTess function In 'Tessellation.cginc'

“Tessellation.cginc”中我们找到了UnityDistanceBasedTess函数

float4 UnityDistanceBasedTess (float4 v0, float4 v1, float4 v2, float minDist, float maxDist, float tess){        float3 f;        f.x = UnityCalcDistanceTessFactor (v0,minDist,maxDist,tess);        f.y = UnityCalcDistanceTessFactor (v1,minDist,maxDist,tess);        f.z = UnityCalcDistanceTessFactor (v2,minDist,maxDist,tess);        return UnityCalcTriEdgeTessFactors (f);}

We can see the UnityDistanceBasedTess function is call  theUnityCalcDistanceTessFactor function, we also found it in the 'Tessellation.cginc';

我们能看到UnityDistanceBasedTess函数主要还是调用UnityCalcDistanceTessFactor这个函数,于是我们又在'Tessellation.cginc'中找到了它;

float UnityCalcDistanceTessFactor (float4 vertex, float minDist, float maxDist, float tess){        float3 wpos = mul(_Object2World,vertex).xyz;        float dist = distance (wpos, _WorldSpaceCameraPos);        float f = clamp(1.0 - (dist - minDist) / (maxDist - minDist), 0.01, 1.0) * tess;        return f;}

In the UnityCalcDistanceTessFactor function ,take the vertex chang to world space, then compute the distance between the vertex and the camera,finally compute the tess.
UnityCalcDistanceTessFactor函数中把点转换为世界坐标wpos,在求出点与同是世界坐标的相机的距离dist,再求出细分程度tess。


Here the tessellation function takes three parameters; the vertex data of three triangle corners before tessellation. This is needed to compute tessellation levels, which depend on vertex positions now. We include a built-in helper file “Tessellation.cginc” (in EditorDataCGIncludes) and call UnityDistanceBasedTess function from it to do all the work. That function computes distance of each vertex to the camera and derives final tessellation factors.

这里的细分函数有三个参数;三角形的三个角在曲面细分之前顶点数据。这通过顶点位置来计算细分程度。我们声明包含帮助文件“Tessellation.cginc”(EditorDataCGIncludes中)并且调用其中的UnityDistanceBasedTess函数来做所有工作。那个函数计算每个顶点与相机的距离,并返回细分函数的float4值。

远(far):

近(close):

这个gif也能体现相机距离与细分程度的变化

基于边缘长度的曲面细分(Edge length based tessellation)

Purely distance based tessellation is good only when triangle sizes are quite similar.
Tessellation levels could be computed based on triangle edge length on the screen - the longer the edge, the larger tessellation factor should be applied.


单纯的基于距离的曲面细分只是在当在三角形大小差不多时效果很好。
将要基于屏幕上的三角形边缘长度计算曲面细分,可以使用更大的细分程度

Shader 'Custom/testShader' {Properties {            _EdgeLength ('Edge length', Range(2,50)) = 15            _MainTex ('Base (RGB)', 2D) = 'white' {}            _DispTex ('Disp Texture', 2D) = 'gray' {}            _NormalMap ('Normalmap', 2D) = 'bump' {}            _Displacement ('Displacement', Range(0, 1.0)) = 0.3            _Color ('Color', color) = (1,1,1,0)            _SpecColor ('Spec color', color) = (0.5,0.5,0.5,0.5)        }        SubShader {            Tags { 'RenderType'='Opaque' }            LOD 300                        CGPROGRAM            #pragma surface surf BlinnPhong addshadow fullforwardshadows vertex:disp tessellate:tessEdge nolightmap            #pragma target 5.0            #include 'Tessellation.cginc'            struct appdata {                float4 vertex : POSITION;                float4 tangent : TANGENT;                float3 normal : NORMAL;                float2 texcoord : TEXCOORD0;            };            float _EdgeLength;            float4 tessEdge (appdata v0, appdata v1, appdata v2)            {                return UnityEdgeLengthBasedTess (v0.vertex, v1.vertex, v2.vertex, _EdgeLength);            }            sampler2D _DispTex;            float _Displacement;            void disp (inout appdata v)            {                float d = tex2Dlod(_DispTex, float4(v.texcoord.xy,0,0)).r * _Displacement;                v.vertex.xyz += v.normal * d;            }            struct Input {                float2 uv_MainTex;            };            sampler2D _MainTex;            sampler2D _NormalMap;            fixed4 _Color;            void surf (Input IN, inout SurfaceOutput o) {                half4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;                o.Albedo = c.rgb;                o.Specular = 0.2;                o.Gloss = 1.0;                o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_MainTex));            }            ENDCG        }        FallBack 'Diffuse'    }
float4 UnityEdgeLengthBasedTess (float4 v0, float4 v1, float4 v2, float edgeLength){        float3 pos0 = mul(_Object2World,v0).xyz;        float3 pos1 = mul(_Object2World,v1).xyz;        float3 pos2 = mul(_Object2World,v2).xyz;        float4 tess;        tess.x = UnityCalcEdgeTessFactor (pos1, pos2, edgeLength);        tess.y = UnityCalcEdgeTessFactor (pos2, pos0, edgeLength);        tess.z = UnityCalcEdgeTessFactor (pos0, pos1, edgeLength);        tess.w = (tess.x + tess.y + tess.z) / 3.0f;        return tess;}

UnityEdgeLengthBasedTess function inbound three parameters v0, v1, v2 is the three vertices of the triangle.

UnityEdgeLengthBasedTess传入的v0,v1,v2是三角形的三个顶点

float UnityCalcEdgeTessFactor (float3 wpos0, float3 wpos1, float edgeLen){        // distance to edge center        float dist = distance (0.5 * (wpos0+wpos1), _WorldSpaceCameraPos);        // length of the edge        float len = distance(wpos0, wpos1);        // edgeLen is approximate desired size in pixels        float f = max(len * _ScreenParams.y / (edgeLen * dist), 1.0);        return f;}


In UnityEdgeLengthBasedTess function compute the distance of the middle of two vertices and the camera’s postation(both two are in world space) , then compute the distance of two vertices, finally compute the tess;

UnityCalcEdgeTessFactor中先求出两个点的中点与相机的距离(全是世界坐标),再求出两个点的距离,再求出细分程度



For performance reasons, it’s advisable to call UnityEdgeLengthBasedTessCull function instead, which will do patch frustum culling. This makes the shader a bit more expensive, but saves a lot of GPU work for parts of meshes that are outside of camera’s view.
由于性能的原因,可以适当的调用UnityEdgeLengthBasedTessCull函数代替(通过判断相机平截头体,对不在范围内的点不进行细分),
调用这个代替函数虽然也浪费一些性能,但不必细分不必要的点

float4 UnityEdgeLengthBasedTessCull (float4 v0, float4 v1, float4 v2, float edgeLength, float maxDisplacement){        float3 pos0 = mul(_Object2World,v0).xyz;        float3 pos1 = mul(_Object2World,v1).xyz;        float3 pos2 = mul(_Object2World,v2).xyz;        float4 tess;        if (UnityWorldViewFrustumCull(pos0, pos1, pos2, maxDisplacement))// UnityWorldViewFrustumCull平截头体的剔除如果被剔除(返回0)则不进行细分(Tess = 0)        {                tess = 0.0f;        }        else        {                tess.x = UnityCalcEdgeTessFactor (pos1, pos2, edgeLength);                tess.y = UnityCalcEdgeTessFactor (pos2, pos0, edgeLength);                tess.z = UnityCalcEdgeTessFactor (pos0, pos1, edgeLength);                tess.w = (tess.x + tess.y + tess.z) / 3.0f;        }        return tess;}

Phong细分(Phong Tessellation)

Before see the Phong tessellation shader, we need to know that theory.
再看Phong曲面细分的shader之前先
深入了解一下Phong曲面细分


This is the Phong tessellation by Tamy Boubekeu and Marc Alexa
Tamy Boubekeur和Marc Alexa 做出的phong曲面细分,



They explain the theory in a video.
他们用一个视频来简要讲解做法

输入一个带有法线的三角形,在重心插入点来做线性细分

正交投影在与此点的“点法式”(法线相垂直的)平面上

在投射的位置线性的插入

1.     计算出线性细分

2.     在三角形三个点上的正切平面上做正交投射

3.     计算重心插入这三个投影

这是他们做出的结果:




Phong Tessellation modifies positions of the subdivided faces so that the resulting surface follows the mesh normals a bit. It’s quite an effective way of making low-poly meshes become more smooth.

Unity’s surface shaders can compute Phong tessellation automatically using tessphong:VariableName compilation directive.


Phong细分修改细分面的位置,使细分面沿着法线突出一点。这是一个让低模变光滑的非常有效的方式。

Unity得surface shaders中能使用tessphong:VariableName编译指令自动计算Phong曲面细分。

Shader 'Custom/testShader' { Properties {            _EdgeLength ('Edge length', Range(2,50)) = 5            _Phong ('Phong Strengh', Range(0,1)) = 0.5            _MainTex ('Base (RGB)', 2D) = 'white' {}            _Color ('Color', color) = (1,1,1,0)        }        SubShader {            Tags { 'RenderType'='Opaque' }            LOD 300                        CGPROGRAM            #pragma surface surf Lambert vertex:dispNone tessellate:tessEdge tessphong:_Phong nolightmap            #include 'Tessellation.cginc'            struct appdata {                float4 vertex : POSITION;                float3 normal : NORMAL;                float2 texcoord : TEXCOORD0;            };            void dispNone (inout appdata v) { }            float _Phong;            float _EdgeLength;            float4 tessEdge (appdata v0, appdata v1, appdata v2)            {                return UnityEdgeLengthBasedTess (v0.vertex, v1.vertex, v2.vertex, _EdgeLength);            }            struct Input {                float2 uv_MainTex;            };            fixed4 _Color;            sampler2D _MainTex;            void surf (Input IN, inout SurfaceOutput o) {                half4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;                o.Albedo = c.rgb;                o.Alpha = c.a;            }            ENDCG        }        FallBack 'Diffuse'    }



Let’s see a comparison between regular shader and one that uses Phong tessellation. Even without any displacement mapping, the surface becomes more round.
看看普通shader和Phong细分之后的比较。如果不用之前几个shader的贴图置换,你也能看到模型的表面比以前更圆更光滑了


Phong细分与贴图置换结合(Phong tessellation&Displacement mapping)


Base on Phong tessellation we add Displacement mapping, the result is perfect
曲面细分再加上贴图置换(Displacement mapping)就是完美


Shader 'Custom/sufaceshaderTessellation' {        Properties {                _EdgeLength ('Edge length', Range(2,50)) = 5                        _Phong ('Phong Strengh', Range(0,1)) = 0.5                        _MainTex ('Base (RGB)', 2D) = 'white' {}                _Color ('Color', color) = (1,1,1,0)                        _DispTex ('Disp Texture', 2D) = 'gray' {}                _NormalMap ('Normalmap', 2D) = 'bump' {}                _Displacement ('Displacement', Range(0, 1.0)) = 0.3                        _SpecColor ('Spec color', color) = (0.5,0.5,0.5,0.5)        }        SubShader {                Tags { 'RenderType'='Opaque' }                LOD 300                        CGPROGRAM#pragma surface surf Lambert vertex:disp tessellate:tessEdge tessphong:_Phong nolightmap#include 'Tessellation.cginc'                struct appdata {                        float4 vertex : POSITION;                        float4 tangent : TANGENT;                        float3 normal : NORMAL;                        float2 texcoord : TEXCOORD0;                };                sampler2D _DispTex;                float _Displacement;                void disp (inout appdata v)                {                        float d = tex2Dlod(_DispTex, float4(v.texcoord.xy,0,0)).r * _Displacement;                        v.vertex.xyz += v.normal * d;                }                float _Phong;                float _EdgeLength;                float4 tessEdge (appdata v0, appdata v1, appdata v2)                {                        return UnityEdgeLengthBasedTess (v0.vertex, v1.vertex, v2.vertex, _EdgeLength);                }                struct Input {                        float2 uv_MainTex;                };                fixed4 _Color;                sampler2D _MainTex;                sampler2D _NormalMap;                void surf (Input IN, inout SurfaceOutput o) {                        half4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;                        o.Albedo = c.rgb;                        o.Specular = 0.2;                        o.Gloss = 1.0;                        o.Normal = UnpackNormal(tex2D(_NormalMap, IN.uv_MainTex));                        o.Alpha = c.a;                }                ENDCG        }        FallBack 'Diffuse'}






分享到: QQ好友和群QQ好友和群 腾讯微博腾讯微博 腾讯朋友腾讯朋友 微信微信
转播转播0 分享淘帖0 收藏收藏0 支持支持0 反对反对0
回复

使用道具 举报

rvan    

0

主题

2

听众

262

积分

设计实习生

Rank: 2

纳金币
1
精华
0

最佳新人

沙发
发表于 2015-5-27 14:57:42 |只看该作者
感谢分享
回复

使用道具 举报

0

主题

1

听众

141

积分

设计实习生

Rank: 2

纳金币
8
精华
0

最佳新人

板凳
发表于 2015-5-27 16:58:49 |只看该作者
学习了!
回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

关闭

站长推荐上一条 /1 下一条

手机版|纳金网 ( 闽ICP备08008928号

GMT+8, 2024-5-4 05:18 , Processed in 0.096351 second(s), 31 queries .

Powered by Discuz!-创意设计 X2.5

© 2008-2019 Narkii Inc.

回顶部