Unity3D Tessellation曲面细分

简述

曲面细分是渲染管线的一个可配置的阶段,在opengl和direct3d里都有实现,Unity的表面着色器对曲面细分有一定的支持,不过必须运行在Shader Model 4.6以上。
曲面细分说白了就是可以实时生成更多的顶点,我们知道在顶点着色器里是不能生成新的顶点的,如果没有曲面细分阶段的话想实时生成新的顶点只能在cpu里生成然后再传输到gpu里,因此为了提升实时生成顶点的速度就多了曲面细分这样一个渲染阶段。
曲面细分阶段是配置式的,不能像顶点着色器和片元着色器一样自己写shader来完全的控制,但是可以配置生成新顶点的方式,数量等等。
曲面细分阶段主要是为了提升渲染的质量和性能,可以对重要的模型生成更多的顶点,对远处或者不重要的模型生成更少的顶点,而更多的顶点就代表更好的渲染质量。虽然有很多类似法线贴图视差贴图之类优化在低模上的渲染效果的方法,但是这些方法的效果都比不过直接提升顶点数量来的直接,这也是曲面细分的强大之处。
我们以曲面细分配合位移贴图(displacement map)来描述一下unity中曲面细分的配置方法,曲面细分配合位移贴图也是一种常见的用法。

位移贴图

位移贴图是一种和法线贴图,视差贴图同类型的技术,都是为了提升低模的显示效果。
如果说法线贴图是将高模的法线覆盖到低模上,视差贴图是为了解决法线贴图部分顶点不会互相遮挡的问题。
那么位移贴图就是更近一步,直接用一张贴图来对顶点进行位移,彻底解决了法线贴图和视差贴图解决不了的问题。
说起来还是挺简单的,位移贴图就是一张灰度图,然后用顶点对图片进行采样,根据采样得到的灰度将该顶点朝法线方向进行移动相应的距离,这个灰度图一般由高模生成。
有的人可能会觉得,那直接用高模不就行了吗?先用低模然后又用一张贴图生成更多的顶点然后又进行位移,最后不是又和高模一样了吗?
其实是不一样的,这主要是因为曲面细分是可以配置的,虽然我们理论上可以生成超过高模的顶点数,但是一般是根据性能指标来进行配置的,想要效果更好就生成更多的顶点,依次类推。而且还可以根据模型的重要性来对曲面细分进行不同的配置,或者是根据距离来确定曲面细分的配置,因此使用曲面细分从低模生成更多的顶点比起直接使用高模还是要更加灵活的。

先写一个简单的表面着色器来使用位移贴图,这个着色器就是简单采样了主纹理,然后再用顶点采样位移贴图来位移顶点。

Shader "LX/tessellation"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
        _DispTex ("DispTex", 2D) = "white" {}
        _Displacement ("Displacement", float) = 1
        _Tess ("Tess", float) =1
        _Power ("Power", range(1,8)) =1
        _Phong ("Phong Strengh", Range(0,1)) = 0.5
        _EdgeLength ("Edge length", Range(2,50)) = 5
    }
    SubShader
    {
        Tags
        {
            "RenderType"="Opaque"
        }
        LOD 200

        CGPROGRAM
        #pragma surface surf Standard fullforwardshadows vertex:disp
        #pragma target 3.0
        #include "Tessellation.cginc"

        sampler2D _MainTex;
        sampler2D _DispTex;

        float _Phong;

        struct Input
        {
            float2 uv_MainTex;
            float3 worldNormal;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;
        float _Displacement;


        float _Tess;
        float _Power;
        float _EdgeLength;


        void disp(inout appdata_base v)
        {
            float d = pow(Luminance(tex2Dlod(_DispTex, float4(v.texcoord.xy, 0, 0))), _Power) * _Displacement;
            v.vertex.xyz += v.normal * d;
        }

        void surf(Input IN, inout SurfaceOutputStandard o)
        {
            fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

至于位移贴图我们这边简单使用了一个中间凸出一块渐变白色圆形的贴图来做示范,理想情况下这个贴图可以把该平面中间凸出一个比较完美的半球。
请添加图片描述

创建一个quad来使用这个着色器,发现根本没有任何反应,因为我们上面的代码还没有加入曲面细分配置,而quad只有四个顶点,想象一下就知道根本无从采样,对于采样位移贴图来说实在太少了。位移贴图是比较要有足够多的顶点才可以看出效果的,顶点越多效果就越平滑。
请添加图片描述
因为曲面细分是直接配置的一个阶段,所以我们并不用写太多代码, 在表面着色器的第一行配置后面追加tessellate:tessFixed 来使用固定值的曲面细分

#pragma surface surf Standard fullforwardshadows vertex:disp tessellate:tessFixed

再在CGPROGRAM里加入对应名字的函数,然后返回一个曲面细分值,这个值越大曲面细分的程度越大。

            float _Tess;

            float4 tessFixed()
            {
                return _Tess;
            }

设置一下参数之后可以看到
请添加图片描述

切换到scene 的渲染模式到wireframe之后可以看到曲面细分生成了新的顶点
tess=1(原始状态,只有四个顶点)
请添加图片描述
tess=20,生成了非常多的新顶点,而且我们也可以看到,和法线贴图那种通过控制明暗程度来表现’虚假’的凸出效果不同,曲面细分是实实在在的生成了新的顶点。
请添加图片描述

固定值的曲面细分就是这么配置,那么如果想要使用基于距离的曲面细分,unity也有写好的内置函数
同样先在上面配置曲面细分函数的名称

#pragma surface surf Standard fullforwardshadows vertex:disp tessellate:tessFixed

然后使用unity内置的UnityDistanceBasedTess函数来实现基于距离的曲面细分,tessDistance 的三个参数分别是三角形的三个顶点,这个不用管直接依次传入UnityDistanceBasedTess函数,然后minDist和maxDist就是距离的起始值和结束值,_Tess就是曲面细分程度。
这个函数的话需要包含Tessellation.cginc文件

            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);
            }

unity还支持基于线段长度的曲面细分,可以防止一些很小的三角形的曲面细分程度过高

            float _EdgeLength;

            float4 tessEdge (appdata v0, appdata v1, appdata v2)
            {
                return UnityEdgeLengthBasedTess (v0.vertex, v1.vertex, v2.vertex, _EdgeLength);
            }

还有一个tessphong配置可以和tessellate配置一起用,tessphong可以使生成出来的顶点位置更加平滑,_Phong的话就是一个参数,越大越平滑。

 #pragma surface surf Standard fullforwardshadows vertex:disp tessellate:tessEdge tessphong:_Phong

有兴趣的话也可以研究一下这几个函数的内部实现,其实最后还是根据距离之类的条件返回一个曲面细分值。

最后的参数面板

请添加图片描述

完整代码

Shader "LX/tessellation"
{
    Properties
    {
        _Color ("Color", Color) = (1,1,1,1)
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Glossiness ("Smoothness", Range(0,1)) = 0.5
        _Metallic ("Metallic", Range(0,1)) = 0.0
        _DispTex ("DispTex", 2D) = "white" {}
        _Displacement ("Displacement", float) = 1
        _Tess ("Tess", float) =1
        _Power ("Power", range(1,8)) =1
        _Phong ("Phong Strengh", Range(0,1)) = 0.5
        _EdgeLength ("Edge length", Range(2,50)) = 5
    }
    SubShader
    {
        Tags
        {
            "RenderType"="Opaque"
        }
        LOD 200

        CGPROGRAM
        #pragma surface surf Standard fullforwardshadows vertex:disp tessellate:tessEdge tessphong:_Phong
        #pragma target 3.0
        #include "Tessellation.cginc"

        sampler2D _MainTex;
        sampler2D _DispTex;

        float _Phong;

        struct Input
        {
            float2 uv_MainTex;
            float3 worldNormal;
        };

        half _Glossiness;
        half _Metallic;
        fixed4 _Color;
        float _Displacement;


        float _Tess;
        float _Power;
        float _EdgeLength;

        // float4 tessFixed()
        // {
        //     return _Tess;
        // }

        float4 tessEdge(appdata_base v0, appdata_base v1, appdata_base v2)
        {
            return UnityEdgeLengthBasedTess(v0.vertex, v1.vertex, v2.vertex, _EdgeLength);
        }

        void disp(inout appdata_base v)
        {
            float d = pow(Luminance(tex2Dlod(_DispTex, float4(v.texcoord.xy, 0, 0))), _Power) * _Displacement;
            v.vertex.xyz += v.normal * d;
        }

        void surf(Input IN, inout SurfaceOutputStandard o)
        {
            fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
            o.Albedo = c.rgb;
            o.Metallic = _Metallic;
            o.Smoothness = _Glossiness;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

另外代码也传到github仓库里了,大家也可以关注一下哦~
我的github

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
细分shader和几何shader是计算机图形学中的两种重要的着色器类型。 细分shaderTessellation Shader)是一种用于进行细分操作的着色器。它可以将输入的三角形网格进行细分,并生成更高分辨率的三角形网格。细分shader通常用于提高曲面细节的展示效果。通过在输入的三角形上插入新的顶点,并对这些顶点进行位置调整,细分着色器可以产生更加平滑的曲面细节,并增加模型的细节级别。细分shader常用于模型细分细节的增强,如地形细分、角色细分曲面细分等。 几何shader(Geometry Shader)是在细分shader之后执行的着色器。细分shader生成的细分网格会进一步传递给几何shader。几何shader可以对输入的三角形网格进行操作,包括增加、删除和改变顶点的属性。通过几何shader,我们可以对模型进行各种形状的变换和处理,如通过给三角形添加顶点产生更多的几何图元,或者通过删除顶点合并多个三角形。几何shader在进一步处理模型形状、拆分或合并模型几何体、调整三角形顶点属性等方面具有极大的灵活性。它可以在多个输入图元之间进行交互,并能够生成不同类型的图元作为输出。 综上所述,细分shader和几何shader是在计算机图形学中常用的两种着色器类型。细分shader用于增加模型的细节级别,而几何shader用于对模型的形状进行操作和变换。它们在计算机图形学中扮演着重要的角色,为实时渲染和图形处理提供了更大的灵活性和细节控制。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值