Unity 顶点着色器保姆级讲解

什么是顶点:

1: 在计算机图形学中,顶点(Vertex)是指空间中的一个点,它通常被用于表示三维模型的一个顶点或者二维图形的一个角点。顶点通常包括其在空间中的位置、法向量、颜色和纹理坐标等属性信息。

2: 在三维模型中,顶点是模型的基本元素之一,它们连接在一起形成了三角形、四边形等多边形,最终构成了模型的表面。在一个三角形中,顶点是该三角形的三个角点,它们确定了三角形的形状和朝向,并且包含了该三角形的法向量、颜色和纹理坐标等属性信息。

3: 在二维图形中,顶点通常被用于表示图形的角点和曲线上的关键点。例如,在一个矩形中,顶点就是矩形的四个角点;在一条曲线上,顶点就是该曲线的拐点或者曲线上的关键点,用于确定曲线的形状和方向。

4: 在Unity着色器中,顶点着色器也是非常重要的一部分,它被用于处理渲染对象的几何信息和光照信息。

Unity中的顶点着色器通常被用于实现以下功能:

1: 顶点变换:顶点着色器可以通过矩阵变换来实现平移、旋转和缩放等操作。这些变换可以用于控制物体的位置、朝向和大小。
2: 法向量变换:在计算光照时,需要使用物体的法向量来确定物体表面的角度和朝向。顶点着色器可以将物体的法向量进行变换,以便在后续的片段着色器中进行计算。
3: 纹理映射:顶点着色器可以对物体的纹理坐标进行变换,从而实现纹理的平铺、旋转和缩放等效果。
4: 骨骼动画:顶点着色器可以与骨骼动画系统一起使用,以实现物体的动态变形和变换。

Unity中的顶点着色器通常需要接收一些输入参数,例如物体的顶点位置、法向量、纹理坐标等信息。同时,顶点着色器也需要输出一些参数,例如变换后的顶点位置、法向量和纹理坐标等信息,以便后续的片段着色器进行计算和渲染。

在渲染流水线中

1: 顶点着色器是渲染管线的第一个阶段,它用于对输入的顶点数据进行处理和变换,以便后续的几何处理和光栅化阶段使用。

2: 顶点着色器的主要作用是将顶点数据从模型空间变换到裁剪空间,同时计算每个顶点的属性(例如颜色、法向量、纹理坐标等)并输出到渲染管线中的下一阶段。

3: 具体来说,顶点着色器接收的输入数据通常包括顶点坐标、法向量、纹理坐标、颜色等属性信息。在执行顶点着色器时,它首先将输入的顶点数据从模型空间变换到世界空间或观察空间,然后应用投影变换,将顶点坐标从3D空间变换到2D空间(裁剪空间)。同时,顶点着色器还可以执行其它的变换操作,例如平移、旋转和缩放等。

4: 在变换后,顶点着色器会计算每个顶点的属性(例如颜色、法向量、纹理坐标等),并将这些属性输出到下一个阶段的渲染管线中。这些属性可以在后续的几何处理和光栅化阶段中被使用,例如进行光照计算、进行多重纹理混合等操作。

5: 需要注意的是,顶点着色器的执行次数与模型的顶点数量成正比。因此,在实时渲染中,为了提高渲染效率,通常会使用一些优化技术,例如顶点缓存、顶点剔除等,以减少不必要的顶点着色器计算和渲染操作。

渲染流水线

下面是一个简单的顶点着色器示例,它将输入的顶点坐标和纹理坐标变换到裁剪空间,并输出到下一个阶段的渲染管线中。

Shader "Custom/VertexShaderExample" {
    // 该着色器只有一个 Pass,用于渲染一组 Mesh
    SubShader {
        Pass {
            CGPROGRAM
            #pragma vertex vert // 使用自定义的顶点着色器
            #pragma fragment frag // 使用默认的片元着色器

            // 定义输入结构体,包含顶点坐标和纹理坐标
            struct appdata {
                float4 vertex : POSITION; // 顶点坐标
                float2 uv : TEXCOORD0; // 纹理坐标
            };

            // 定义输出结构体,包含变换后的顶点坐标和纹理坐标
            struct v2f {
                float4 vertex : SV_POSITION; // 裁剪空间中的顶点坐标
                float2 uv : TEXCOORD0; // 纹理坐标
            };

            // 顶点着色器函数
            v2f vert (appdata v) {
                v2f o; // 定义输出结构体
                o.vertex = UnityObjectToClipPos(v.vertex); // 将顶点坐标变换到裁剪空间
                o.uv = v.uv; // 传递纹理坐标
                return o; // 返回输出结构体
            }

            ENDCG
        }
    }
    FallBack "Diffuse" // 如果硬件不支持该着色器,使用 Diffuse 材质替代
}

顶点着色器是一个非常重要的渲染管线阶段,它负责将输入的顶点数据变换到裁剪空间,并将变换后的数据传递给下一个阶段的渲染管线。开发者可以通过编写自定义的顶点着色器来实现更加灵活和高效的渲染效果。

顶点着色器的优缺点:

顶点着色器作为渲染管线中的一个重要阶段,具有以下优缺点:

优点:

1 灵活性高:开发者可以自定义顶点着色器函数,实现自己想要的顶点变换和计算操作,从而实现更加灵活和高效的渲染效果。

2 可扩展性强:顶点着色器支持多个输入和输出通道,可以根据需要扩展输入和输出的数据格式。

3 处理能力强:顶点着色器可以并行处理多个顶点,因此可以大大提高渲染效率和性能。

缺点:

1 代码难度较高:编写自定义的顶点着色器需要具备一定的数学和计算机图形学知识,代码难度相对较高。

2 稳定性差:顶点着色器可能会对顶点数据造成破坏,导致渲染错误和崩溃等问题,因此需要开发者进行严格的测试和调试。

3: 兼容性差:不同的图形硬件和平台可能存在着色器语言和指令集的差异,因此需要开发者进行兼容性测试和优化。

顶点着色器是一个非常重要的渲染管线阶段,可以实现更加灵活和高效的渲染效果,但需要开发者具备一定的数学和计算机图形学知识,并进行严格的测试和调试,以确保稳定性和兼容性。

顶点着色器可以做些什么:

顶点着色器是渲染管线中的一个重要阶段,其作用是对输入的顶点数据进行变换和计算操作,并输出变换后的顶点数据。具体来说,顶点着色器可以实现以下功能:

1 顶点变换:对输入的顶点进行平移、旋转、缩放等变换操作,实现物体在场景中的移动和变形。

2 法向量计算:计算顶点的法向量,用于实现光照效果和阴影计算。

3 骨骼动画:根据动画控制器中的骨骼信息,对顶点进行骨骼权重计算,实现骨骼动画效果。

4 顶点着色:对顶点进行着色操作,实现多种渲染效果,如纹理贴图、颜色变换、光照模型等。

5:顶点剪裁:根据相机视锥体的位置和方向,对顶点进行剪裁操作,去除不可见的部分,提高渲染效率。

6:粒子系统:对粒子的位置、大小、颜色等进行计算和变换,实现粒子效果。

7: 其他:顶点着色器还可以实现其他一些高级的计算和变换操作,如几何变形、光线追踪等。

顶点着色器可以实现多种复杂的变换和计算操作,从而实现丰富的渲染效果。开发者可以根据自己的需求,编写相应的顶点着色器函数,实现自定义的渲染效果。

我们用顶点着色器实现兰伯特光照模型,来展示顶点着色器的使用

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

        // 定义输出结构体
        struct Output {
            float3 pos : SV_POSITION;       // 变换后的顶点位置
            float3 worldPos : TEXCOORD0;    // 世界坐标系中的顶点位置
            float3 worldNormal : TEXCOORD1; // 世界坐标系中的法向量
            float4 color : COLOR0;          // 输出颜色
        };

        // 矩阵变换函数
        float4x4 _Object2World;
        float4x4 _World2Object;

        // 定义光源结构体
        struct Lighting {
            float4 ambient;     // 环境光强度
            float4 lightPos;    // 光源位置
            float4 lightColor;  // 光源颜色
            float4 atten;       // 衰减系数
        };

        // 主函数
        Output vert (appdata_full v) {
            Output o;

            // 变换顶点位置
            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);

            // 计算顶点颜色
            o.color = _Color;

            return o;
        }

        // 光照函数
        void lighting (Input IN, SurfaceOutputStandard o, inout Lighting lighting) {
            float3 worldPos = o.worldPos;
            float3 worldNormal = o.worldNormal;

            // 计算环境光
            lighting.ambient = _Ambient * o.albedo;

            // 计算漫反射和镜面反射
            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;

        // 计算距离衰减
        float dist = distance(lighting.lightPos.xyz, worldPos);
        float attenuation = 1.0 / (1.0 + _Attenuation0 * dist * dist);

        // 汇总光照贡献
        lighting.atten = attenuation;
        lighting.lightColor *= (diffuse + specular) * attenuation;
    }

    // 定义表面着色器函数
    void surf (Input IN, inout SurfaceOutputStandard o) {
        // 初始化颜色
        o.albedo = tex2D(_MainTex, IN.uv_MainTex).rgb;
        o.alpha = 1;

        // 初始化漫反射和镜面反射
        float3 diffuse = o.albedo.rgb;
        float3 specular = _SpecColor.rgb;

        // 计算光照贡献
        Lighting lighting;
        lighting.lightPos = _LightPosition0;
        lighting.lightColor = _LightColor0;
        lighting.atten = 1;

        // 执行光照计算
        lighting(IN, o, lighting);

        // 汇总光照贡献
        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"
// 在后面介绍光照模型的时候,我们在具体讨论

顶点着色器是新能最高的着色器,在实际开发中,我们可以把性能消耗比较多的功能,放在这里实现,当然,代价就是渲染不够细腻.
接下来,我们通过几个实例,开始shader的奇妙之旅,争取明天更新

  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
引用中提到了一种使用草地着色器的方法,该方法通过传递透明度贴图和顶点色来控制草的形状和颜色。这种方法可以有效减少DrawCall的数量,并且通过传递顶点的法线可以产生不同的受光效果。这样的草地着色器可以实现草地的交互效果。 另外,引用中提到了一些草地着色器的拓展性功能,例如延迟恢复、画笔工具、风压控制和引燃效果等。这些功能可以进一步增强草地的交互效果。 而引用中提到了草地着色器的优化方法。草地在风中会有规律性的摆动,因此需要生成具有规律的系数来控制草的宽度和高度。通过这种方法,可以实现个体上的随机性和整体上的一致性动画效果。 综上所述,Unity的草地着色器可以通过传递透明度贴图和顶点色来控制草的形状和颜色,并可以通过拓展性功能实现更多的交互效果。另外,通过优化草地着色器,可以实现草地在风中的规律性摆动效果。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [UnityShader[4]几何着色器与可交互草地](https://blog.csdn.net/Thanatos_Left/article/details/126141627)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值