Unity3D Tessellation surface subdivision

brief description

Surface subdivision is a configurable stage of the rendering pipeline. It is implemented in both opengl and direct3d. Unity's surface shader has certain support for surface subdivision, but it must run on Shader Model 4.6 or above.
To put it bluntly, tessellation can generate more vertices in real time. We know that new vertices cannot be generated in the vertex shader. If there is no tessellation stage, if you want to generate new vertices in real time, you can only generate them in the CPU and then It is transmitted to the GPU, so in order to improve the speed of real-time vertex generation, there is an additional rendering stage such as surface subdivision.
The tessellation stage is configurable. It cannot be completely controlled by writing shaders like vertex shaders and fragment shaders, but it can be configured to generate new vertices, the number, and so on.
The tessellation stage is mainly to improve the quality and performance of rendering. More vertices can be generated for important models, and fewer vertices can be generated for distant or unimportant models, and more vertices represent better rendering. quality. Although there are many ways to optimize the rendering effect on low-poly, such as normal map parallax map, the effect of these methods is not as direct as directly increasing the number of vertices, which is also the power of surface subdivision.
We use surface subdivision with displacement map to describe the configuration method of surface subdivision in unity. Surface subdivision with displacement map is also a common usage.

displacement map

Displacement mapping is a technology similar to normal mapping and parallax mapping, all of which are used to improve the display effect of low-mode.
If the normal map is to cover the normal of the high model on the low model, the parallax map is to solve the problem that the vertices of the normal map will not block each other.
Then the displacement map is a step closer, directly using a map to displace the vertices, which completely solves the problem that the normal map and the parallax map cannot solve.
It is quite simple to say, the displacement map is a grayscale image, and then use the vertex to sample the image, and move the vertex to the normal direction according to the grayscale obtained by the sampling. The grayscale image is generally composed of high model generation.
Some people may think, why not just use the high model directly? First use the low model and then use a texture to generate more vertices and then perform displacement. Isn't it the same as the high model in the end?
In fact, it is different. This is mainly because surface subdivision is configurable. Although we can theoretically generate more vertices than high modulus, it is generally configured according to performance indicators. If you want better results, you can generate more more vertices, and so on. And you can also configure the tessellation differently according to the importance of the model, or determine the configuration of the tessellation according to the distance, so using tessellation to generate more vertices from the low poly than directly using the high poly or Be more flexible.

First write a simple surface shader to use the displacement map. This shader simply samples the main texture, and then uses the vertex sampling displacement map to displace the vertices.

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

As for the displacement map, we simply use a texture with a gradient white circle protruding from the middle for demonstration. Ideally, this texture can protrude a relatively perfect hemisphere from the middle of the plane.
Please add a picture description

Create a quad to use this shader, and found that there is no response at all, because the above code has not added the tessellation configuration, and the quad has only four vertices. Imagine that there is no sampling at all, which is true for sampling displacement maps. too little. Displacement maps need enough vertices to see the effect. The more vertices, the smoother the effect.
Please add a picture description
Because tessellation is a stage of direct configuration, we don't need to write too much code. Add tessellate:tessFixed after the first line of configuration of the surface shader to use fixed value tessellation

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

Then add the function corresponding to the name in CGPROGRAM, and then return a surface subdivision value, the greater the value, the greater the degree of surface subdivision.

            float _Tess;

            float4 tessFixed()
            {
    
    
                return _Tess;
            }

After setting the parameters, you can see
Please add a picture description

After switching to the rendering mode of the scene to wireframe, you can see that the tessellation generates new vertices
tess=1 (original state, only four vertices)
Please add a picture description
tess=20, a lot of new vertices are generated, and we can also see , Unlike the normal map that expresses the 'false' bulge effect by controlling the degree of brightness, the tessellation actually generates new vertices.
Please add a picture description

This is how the fixed-value tessellation is configured. If you want to use distance-based tessellation, unity also has a built-in function.
Also configure the name of the tessellation function above.

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

Then use the built-in UnityDistanceBasedTess function of unity to realize distance-based surface subdivision. The three parameters of tessDistance are the three vertices of the triangle, which are directly passed to the UnityDistanceBasedTess function in sequence, and then minDist and maxDist are the starting value of the distance and The end value, _Tess is the degree of tessellation.
This function needs to include the Tessellation.cginc file

            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 also supports tessellation based on the length of the line segment, which can prevent the tessellation of some small triangles from being too high

            float _EdgeLength;

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

There is also a tessphong configuration that can be used together with the tessellate configuration. tessphong can make the generated vertex position smoother. _Phong is a parameter, the larger the smoother.

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

If you are interested, you can also study the internal implementation of these functions. In fact, in the end, a tessellation value is returned according to conditions such as distance.

final parameter panel

Please add a picture description

full code

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

In addition, the code has also been passed to the github warehouse, you can also pay attention to it~
my github

Guess you like

Origin blog.csdn.net/o83290102o5/article/details/120275590