Unity Shader - Surface Shaders with DX11 / OpenGL Core Tessellation 表面着色器使用DX11/OpenGL Core曲面细分

目录:Unity Shader - 知识点目录(先占位,后续持续更新)
原文:Surface Shaders with DX11 / OpenGL Core Tessellation
版本:2019.1

Surface Shaders with DX11 / OpenGL Core Tessellation

表面着色器使用DX11/OpenGL Core曲面细分

Surface Shaders 有一些支持DirectX 11 / OpenGL GPU的曲面细分。

  • 指定使用曲面细分功能使用 tessellate:FunctionName 修改器。该函数计算三角形边与面积作为曲面细分的因子。
  • 当使用曲面细分是,“vertex modifier”(vertex:FunctionName 将会再曲面细分后执行,每个顶点在shader执行域内生成。在这你需要处理displacement mapping(位移映射)。
  • 表面着色器可以可选的使用phong tessellation(冯氏曲面细分)的平滑计算模型表面,甚至不需要displacement mapping(位移映射)的配合使用了。

当前支持曲面细分的一些限制:

  • 仅支持三角形细分,不支持四边形,不支持等值线的细分。
  • 当你使用曲面细分,着色器将自动编译为4.6版本的shader model,这样就可以阻止比较旧的硬件中运行。

No GPU tessellation, displacement in the vertex modifier

没有曲面细分,但有顶点位移映射修改器处理

下面的例子展示位移映射,但没有曲面细分的处理。位移映射处理中仅仅是沿着法线方向移动,移动量为:从位移贴图从采样的值。

    Shader "Tessellation Sample" {
        Properties {
            _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 nolightmap
            #pragma target 4.6

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

            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"
    }

上面的是比较标准的着色器写法:

  • 顶点修改器disp采样了位移贴图(国内有些人叫:置换贴图,反正都是前人翻译留下来的坑,一代代的坑下去了,-_-!)位移贴图并沿着法线方向位移顶点。
  • 使用了自定义的"vertex data input"(顶点输入数据)的数据结构(appdata)而不是使用默认的appdata_full。因为不一定之内用appdata_full,很多不需要的功能、变量,你自定义后,曲面细分处理时将效率更高,尽量使用更小的数据结构。
  • 因为我们不需要第二个UV坐标,所以我们添加了nolightmap指令来禁止使用lightmaps

下图显示了该着色的应用效果。
在这里插入图片描述


Fixed amout of tesselation

固定的细分量

如果你的模型渲染出来的面的大小在屏幕上显示都大概差不多,这时给网格(整个网格都将应用一样的细分)使用固定的细分量的处理是OK的。

下面是应用了固定细分量的例子。

    Shader "Tessellation Sample" {
        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:tessFixed nolightmap
            #pragma target 4.6

            struct appdata {
                float4 vertex : POSITION;
                float4 tangent : TANGENT;
                float3 normal : NORMAL;
                float2 texcoord : TEXCOORD0;
            };

            float _Tess;

            float4 tessFixed()
            {
                return _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"
    }

上面例子中,tessFixed 全面细分函数返回4因子在一个float4的值里:3个因子是给三角形三边的,1个因子是给三角形面积的。

该例子中tessellate函数是直接返回一个Material Inspector中设置的属性_Tess的值。

在这里插入图片描述

Distance-based tessellation

基于距离的曲面细分

你可以基于与相机的距离来改变曲面细分的程度。例如,你可以定义两个距离值:

  • 曲面细分的最大距离(如:10米)。
  • 曲面细分程度的逐渐减少的距离(如:20米)
    Shader "Tessellation Sample" {
        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 4.6
            #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"
    }

曲面细分函数以三角形的三个顶点坐标值前三个参数。

Unity需要这些顶点数据来计算曲面细分程度。

例子include了内置的文件,Tessellation.cginc,并从此文件调用了UnityDistanceBasedTess函数来完成所有处理。此函数计算了每个顶点与相机的距离,然后推算出最终的曲面细分因子。

在这里插入图片描述

Edge length based tessellation

基于边长的曲面细分

基于纯距离的曲面细分仅仅作用于三角形都差不多的大小(因为距离差不多)。在上图中,很小的三角形的曲面细分是相当多的,导致大三角形的曲面细分不够。

有一种方法可以改善这些曲面细分的程度,基于三角形对象屏幕上的边长。这样大块的三角形也会曲面细分了。

    Shader "Tessellation Sample" {
        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 4.6
            #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"
    }

在此例中,调用了Tessellation.cginc中UnityEdgeLengthBasedTess函数来处理。
在这里插入图片描述
为么性能考虑,调用的UnityEdgeLengthBasedTessCull增加了对相机视椎的剔除。这么做虽然看起来shader的处理内容变多了而需要更多性能似的,其实它可以节省了相机视图外的网格的处理那部分消耗处理。


Phong Tessellation

冯氏曲面细分

Phong Tessellation修改了细分面的顶点位置,对应修改面的法线也调整了。这对应平滑低模来说是个很有效的方法。

Unity Surface Shader可以计算Phong tessellation使用tessphong:VeriableName编译指令。

    Shader "Phong Tessellation" {
        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"
    }

比较正常shader(上面那排)和使用了Phong tessellation(下面那配)。可以看到,没有及时位移映射,曲面也一样变得平滑了。
在这里插入图片描述

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值