Unity vertex shader nanny level explanation

What is a vertex:

1: In computer graphics, a vertex (Vertex) refers to a point in space, which is usually used to represent a vertex of a 3D model or a corner of a 2D graphic. A vertex usually includes attribute information such as its position in space, normal vector, color, and texture coordinates.

2: In a 3D model, vertices are one of the basic elements of the model. They are connected together to form polygons such as triangles and quadrilaterals, and finally constitute the surface of the model. In a triangle, vertices are the three corners of the triangle, which determine the shape and orientation of the triangle, and contain attribute information such as the normal vector, color, and texture coordinates of the triangle.

3: In two-dimensional graphics, vertices are usually used to represent the corner points of the graphics and the key points on the curve. For example, in a rectangle, the vertices are the four corners of the rectangle; on a curve, the vertices are the inflection points of the curve or key points on the curve, which are used to determine the shape and direction of the curve.

4: In the Unity shader, the vertex shader is also a very important part, which is used to process the geometric information and lighting information of the rendered object.

The vertex shader in Unity is usually used to achieve the following functions:

1: Vertex transformation: The vertex shader can perform operations such as translation, rotation, and scaling through matrix transformation. These transformations can be used to control the position, orientation and size of objects.
2: Normal vector transformation: When calculating lighting, the normal vector of the object needs to be used to determine the angle and orientation of the object surface. The vertex shader can transform the normal vector of the object for calculation in the subsequent fragment shader.
3: Texture mapping: The vertex shader can transform the texture coordinates of the object, so as to achieve the effects of texture tiling, rotation and scaling.
4: Skeletal animation: Vertex shaders can be used with the skeletal animation system to achieve dynamic deformation and transformation of objects.

The vertex shader in Unity usually needs to receive some input parameters, such as the object’s vertex position, normal vector, texture coordinates and other information. At the same time, the vertex shader also needs to output some parameters, such as the transformed vertex position, normal vector, and texture coordinates, so that the subsequent fragment shader can calculate and render.

In the rendering pipeline

1: The vertex shader is the first stage of the rendering pipeline, which is used to process and transform the input vertex data for subsequent geometry processing and rasterization stages.

2: The main function of the vertex shader is to transform the vertex data from the model space to the clip space, and at the same time calculate the attributes of each vertex (such as color, normal vector, texture coordinates, etc.) and output to the next stage in the rendering pipeline.

3: Specifically, the input data received by the vertex shader usually includes attribute information such as vertex coordinates, normal vectors, texture coordinates, and colors. When executing a vertex shader, it first transforms the input vertex data from model space to world space or view space, and then applies a projective transformation to transform the vertex coordinates from 3D space to 2D space (clipping space). At the same time, the vertex shader can also perform other transformation operations, such as translation, rotation and scaling.

4: After the transformation, the vertex shader calculates the attributes of each vertex (such as color, normal vector, texture coordinates, etc.) and outputs these attributes to the next stage of the rendering pipeline. These attributes can be used in subsequent geometry processing and rasterization stages, such as lighting calculations, multi-texture blending, and other operations.

5: It should be noted that the number of executions of the vertex shader is proportional to the number of vertices in the model. Therefore, in real-time rendering, in order to improve rendering efficiency, some optimization techniques are usually used, such as vertex buffering, vertex culling, etc., to reduce unnecessary vertex shader calculations and rendering operations.

rendering pipeline

The following is an example of a simple vertex shader that transforms the input vertex coordinates and texture coordinates into clip space and outputs it to the next stage of the rendering pipeline.

Shader "Custom/VertexShaderExample" {<!-- -->
    // This shader has only one Pass, which is used to render a set of Mesh
    SubShader {<!-- -->
        Pass {<!-- -->
            CGPROGRAM
            #pragma vertex vert // use a custom vertex shader
            #pragma fragment frag // use the default fragment shader

            // Define the input structure, including vertex coordinates and texture coordinates
            struct appdata {<!-- -->
                float4 vertex : POSITION; // vertex coordinates
                float2 uv : TEXCOORD0; // texture coordinates
            };

            // Define the output structure, including the transformed vertex coordinates and texture coordinates
            struct v2f {<!-- -->
                float4 vertex : SV_POSITION; // vertex coordinates in clip space
                float2 uv : TEXCOORD0; // texture coordinates
            };

            // vertex shader function
            v2f vert (appdata v) {<!-- -->
                v2f o; // define the output structure
                o.vertex = UnityObjectToClipPos(v.vertex); // Transform vertex coordinates to clipping space
                o.uv = v.uv; // pass texture coordinates
                return o; // return the output structure
            }

            ENDCG
        }
    }
    FallBack "Diffuse" // If the shader is not supported by the hardware, use the Diffuse material instead
}

The vertex shader is a very important rendering pipeline stage, which is responsible for transforming the input vertex data into the clip space, and passing the transformed data to the next stage of the rendering pipeline. Developers can achieve more flexible and efficient rendering effects by writing custom vertex shaders.

Pros and cons of vertex shaders:

As an important stage in the rendering pipeline, the vertex shader has the following advantages and disadvantages:

advantage:

1. High flexibility: Developers can customize vertex shader functions to achieve their desired vertex transformation and calculation operations, thereby achieving more flexible and efficient rendering effects.

2 Strong scalability: The vertex shader supports multiple input and output channels, and the data format of input and output can be expanded as needed.

3 Strong processing ability: Vertex shader can process multiple vertices in parallel, so it can greatly improve rendering efficiency and performance.

shortcoming:

1 Code difficulty is relatively high: writing a custom vertex shader requires certain knowledge of mathematics and computer graphics, and the code difficulty is relatively high.

2. Poor stability: Vertex shaders may cause damage to vertex data, causing rendering errors and crashes. Therefore, developers need to conduct strict testing and debugging.

3: Poor compatibility: Different graphics hardware and platforms may have differences in shader languages and instruction sets, so developers need to perform compatibility testing and optimization.

The vertex shader is a very important stage of the rendering pipeline, which can achieve more flexible and efficient rendering effects, but requires developers to have certain knowledge of mathematics and computer graphics, and conduct strict testing and debugging to ensure stability and compatibility sex.

What a vertex shader can do:

The vertex shader is an important stage in the rendering pipeline. Its function is to transform and calculate the input vertex data and output the transformed vertex data. Specifically, a vertex shader can implement the following functions:

1 Vertex transformation: Perform transformation operations such as translation, rotation, and scaling on the input vertex to realize the movement and deformation of the object in the scene.

2 Normal vector calculation: Calculate the normal vector of the vertex, which is used to realize the lighting effect and shadow calculation.

3 Skeletal animation: According to the bone information in the animation controller, the bone weight calculation is performed on the vertices to realize the bone animation effect.

4. Vertex coloring: Coloring operations on vertices to achieve various rendering effects, such as texture maps, color transformations, lighting models, etc.

5: Vertex clipping: According to the position and direction of the camera frustum, clip the vertices to remove invisible parts and improve rendering efficiency.

6: Particle system: Calculate and transform the position, size, color, etc. of particles to achieve particle effects.

7: Others: The vertex shader can also implement other advanced calculations and transformation operations, such as geometric deformation, ray tracing, etc.

The vertex shader can implement a variety of complex transformation and calculation operations, so as to achieve rich rendering effects. Developers can write corresponding vertex shader functions according to their own needs to achieve custom rendering effects.

We implement the Lambert lighting model with vertex shaders to demonstrate the use of vertex shaders

Shader "Custom/VertexLit" {<!-- -->
    Properties {<!-- -->
        _MainTex ("Base (RGB)", 2D) = "white" {<!-- -->}
        _Color ("Color", Color) = (1,1,1,1)
        _SpecColor ("Specular", Color) = (0.5, 0.5, 0.5, 1)
        _Shininess ("Shininess", Range (0.01, 1)) = 0.078125
        _Ambient ("Ambient", Range (0, 1)) = 0.5
        _LightColor0 ("Light Color", Color) = (1,1,1,1)
        _Attenuation0 ("Attenuation", Range (0, 1.5)) = 0.5
        _LightPosition0 ("Light Position", Vector) = (0,0,0,1)
    }

    SubShader {<!-- -->
        Tags {<!-- --> "RenderType"="Opaque" }

        CGPROGRAM
        #pragma surface surf Lambert vertex:vert

        struct Input {<!-- -->
            float2 uv_MainTex;
        };

        // define the output structure
        struct Output {<!-- -->
            float3 pos : SV_POSITION; // Transformed vertex position
            float3 worldPos : TEXCOORD0; // Vertex position in the world coordinate system
            float3 worldNormal : TEXCOORD1; // normal vector in the world coordinate system
            float4 color : COLOR0; // output color
        };

        // matrix transformation function
        float4x4 _Object2World;
        float4x4_World2Object;

        // Define the light source structure
        struct Lighting {<!-- -->
            float4 ambient; // ambient light intensity
            float4 lightPos; // Light position
            float4 lightColor; // light source color
            float4 atten; // attenuation coefficient
        };

        // main function
        Output vert (appdata_full v) {<!-- -->
            Output o;

            // transform vertex position
            o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
            o.worldPos = mul(_Object2World, v.vertex).xyz;
            o.worldNormal = normalize(mul(_World2Object, float4(v.normal, 0.0)).xyz);

            // calculate vertex color
            o.color = _Color;

            return o;
        }

        // lighting function
        void lighting (Input IN, SurfaceOutputStandard o, inout Lighting lighting) {<!-- -->
            float3 worldPos = o.worldPos;
            float3 worldNormal = o.worldNormal;

            // calculate ambient light
            lighting.ambient = _Ambient * o.albedo;

            // calculate diffuse and specular reflection
            float3 lightDir = normalize(lighting. lightPos. xyz - worldPos);
            float NdotL = max(dot(worldNormal, lightDir), 0.0);
            float3 diffuse = NdotL * lighting.lightColor.rgb * o.albedo.rgb;
            float3 halfDir = normalize(lighting. lightPos. xyz + worldPos);
            float3 specular = pow(max(dot(worldNormal, halfDir), 0.0), _Shininess) * _SpecColor.rgb * lighting.lightColor.rgb;

        // Calculate distance attenuation
        float dist = distance(lighting. lightPos. xyz, worldPos);
        float attenuation = 1.0 / (1.0 + _Attenuation0 * dist * dist);

        // Aggregate lighting contributions
        lighting.atten = attenuation;
        lighting.lightColor *= (diffuse + specular) * attenuation;
    }

    // define the surface shader function
    void surf (Input IN, inout SurfaceOutputStandard o) {<!-- -->
        // Initialize the color
        o.albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
        o.alpha = 1;

        // Initialize diffuse and specular reflections
        float3 diffuse = o.albedo.rgb;
        float3 specular = _SpecColor.rgb;

        // calculate lighting contribution
        Lighting lighting;
        lighting.lightPos = _LightPosition0;
        lighting.lightColor = _LightColor0;
        lighting.atten = 1;

        // perform lighting calculations
        lighting(IN, o, lighting);

        // Aggregate lighting contributions
        o.emissive = lighting.ambient.rgb + lighting.lightColor.rgb;
        o.diffuse = diffuse * lighting.ambient.rgb + diffuse * lighting.lightColor.rgb;
        o. specular = specular * lighting. lightColor. rgb;
    }

    ENDCG
}

FallBack "Diffuse"
// When we introduce the lighting model later, we will discuss it in detail

The vertex shader is the shader with the highest performance. In actual development, we can implement functions that consume more performance here. Of course, the price is that the rendering is not delicate enough.
Next, we will start the wonderful journey of shader through several examples, and try to update tomorrow