Unity Shader unity文档学习笔记(十九):粘土效果,任意网格转化成一个球(顶点动画,曲面着色器)

任意网格转换球原理:

由某一点向网格的所有顶点发射一条射线,到圆上,最终就会形成一个圆

关键的代码

复制代码
                float4 worldPos =  mul(unity_ObjectToWorld, v.vertex);

                // 计算原始顶点到圆心的向量
                float3 toCenter = worldPos - _WorldSourcePos;
                float3 dirObj = normalize(toCenter); // 世界空间方向(圆心→顶点的单位向量)
                // 计算世界空间目标位置(圆周上的点)
                float3 targetWorldPos = _WorldTargetPos + dirObj * _Radius; // 世界空间目标位置
                // 将目标位置转换回模型空间(用于顶点混合)
                float3 targetModelPos = mul(unity_WorldToObject, float4(targetWorldPos, 1)).xyz;

                float3 baseLocalPos = lerp(v.vertex, targetModelPos, _Transition);

                o.vertex = UnityObjectToClipPos(baseLocalPos);

整个shader

复制代码
Shader "Custom/WorldCenterCircleShader"
{
    Properties
    {
        _MainTex("MainTex",2D) ="white"{}

        _Radius ("目标圆半径", Float) = 1.0       // 最终圆的半径(世界空间)
        _WorldSourcePos ("原世界坐标", Vector) = (0,0,0) // 原世界坐标
        _WorldTargetPos ("目标世界坐标", Vector) = (0,0,0) // 目标世界坐标
        _Color ("基础颜色", Color) = (1,0,0,1)   // 圆的颜色
        _Transition ("过渡系数", Range(0,1)) = 0 // 0=原模型,1=完整圆(世界空间变形)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;  // 模型空间顶点位置
                float3 normal : NORMAL;    // 模型空间原始法线
                float2 uv:TEXCOORD0;//纹理
            };

            struct v2f
            {
                float4 vertex : SV_POSITION; // 裁剪空间位置
                float3 worldPos : TEXCOORD0; // 世界空间位置(用于光照)
                float3 normal : NORMAL;      // 过渡后的法线(模型空间,用于光照)
                float2 uv:TEXCOORD1; 
            };

            sampler2D _MainTex;
            float _Radius;
            float3 _WorldSourcePos;
            float3 _WorldTargetPos;
            fixed4 _Color;
            float _Transition;
            float _MinX;
            float _MaxX;

            v2f vert (appdata v)
            {
                v2f o;
                float4 worldPos =  mul(unity_ObjectToWorld, v.vertex);

                // 计算原始顶点到圆心的向量
                float3 toCenter = worldPos - _WorldSourcePos;
                float3 dirObj = normalize(toCenter); // 世界空间方向(圆心→顶点的单位向量)
                // 计算世界空间目标位置(圆周上的点)
                float3 targetWorldPos = _WorldTargetPos + dirObj * _Radius; // 世界空间目标位置

                // 将目标位置转换回模型空间(用于顶点混合)
                float3 targetModelPos = mul(unity_WorldToObject, float4(targetWorldPos, 1)).xyz;

               float3 baseLocalPos = lerp(v.vertex, targetModelPos, _Transition);

                // 转换为裁剪空间
                o.vertex = UnityObjectToClipPos(baseLocalPos);

                float3 originalNormal = normalize(UnityObjectToWorldNormal(v.normal));
                float3 circleNormal = normalize(toCenter); // 圆周法线(指向外侧)
                float3 normal = lerp(originalNormal, circleNormal, _Transition); 
                o.normal = mul(normal, (float3x3)unity_WorldToObject);

                o.uv = v.uv;

                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                //float4 texColor = tex2D(_MainTex, i.uv); //tex2D(smapler, x) 二维纹理查找
                // 简单漫反射光照(使用过渡后的模型空间法线)
                float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                // 法线从模型空间转换到世界空间(光照计算需要世界空间法线)
                float3 normalDir = normalize(i.normal);
                float diff = saturate(dot(lightDir, normalDir));
                //fixed4 col = texColor + _Color * (diff * 0.5 + 0.5); // 环境光+漫反射
                fixed4 col = _Color * diff; // 环境光+漫反射

                return col;
            }
            ENDCG
        }
    }
    FallBack "Diffuse"
}

效果

但是这种方式对网格顶点是有要求的,如果顶点不够多就会这样

这样

项目的网格不可能每个顶点都这么多,有没有办法给网格新增一些顶点,当然是有:曲面着色器:

unity的正方体

参数调到5

调到10

转换后:

曲面细分shader:

复制代码
Shader "Unlit/TessShader"
{
    Properties
    {
        _TessellationUniform("TessellationUniform",Range(1,64)) = 1
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
        Pass
        {
            CGPROGRAM
            //定义2个函数 hull domain
            #pragma hull hullProgram
            #pragma domain ds
           
            #pragma vertex tessvert
            #pragma fragment frag

            #include "UnityCG.cginc"
            //引入曲面细分的头文件
            #include "Tessellation.cginc" 

            #pragma target 5.0
            
            struct VertexInput
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
                float4 tangent : TANGENT;
            };

            struct VertexOutput
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float3 normal : NORMAL;
                float4 tangent : TANGENT;
            };

            VertexOutput vert (VertexInput v)
            //这个函数应用在domain函数中,用来空间转换的函数
            {
                VertexOutput o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;
                o.tangent = v.tangent;
                o.normal = v.normal;
                return o;
            }

            //有些硬件不支持曲面细分着色器,定义了该宏就能够在不支持的硬件上不会变粉,也不会报错
            #ifdef UNITY_CAN_COMPILE_TESSELLATION
                //顶点着色器结构的定义
                struct TessVertex{
                    float4 vertex : INTERNALTESSPOS;
                    float3 normal : NORMAL;
                    float4 tangent : TANGENT;
                    float2 uv : TEXCOORD0;
                };

                struct OutputPatchConstant { 
                    //不同的图元,该结构会有所不同
                    //该部分用于Hull Shader里面
                    //定义了patch的属性
                    //Tessellation Factor和Inner Tessellation Factor
                    float edge[3] : SV_TESSFACTOR;
                    float inside  : SV_INSIDETESSFACTOR;
                };

                TessVertex tessvert (VertexInput v){
                    //顶点着色器函数
                    TessVertex o;
                    o.vertex  = v.vertex;
                    o.normal  = v.normal;
                    o.tangent = v.tangent;
                    o.uv      = v.uv;
                    return o;
                }

                float _TessellationUniform;
                OutputPatchConstant hsconst (InputPatch<TessVertex,3> patch){
                    //定义曲面细分的参数
                    OutputPatchConstant o;
                    o.edge[0] = _TessellationUniform;
                    o.edge[1] = _TessellationUniform;
                    o.edge[2] = _TessellationUniform;
                    o.inside  = _TessellationUniform;
                    return o;
                }

                [UNITY_domain("tri")]//确定图元,quad,triangle等
                [UNITY_partitioning("fractional_odd")]//拆分edge的规则,equal_spacing,fractional_odd,fractional_even
                [UNITY_outputtopology("triangle_cw")]
                [UNITY_patchconstantfunc("hsconst")]//一个patch一共有三个点,但是这三个点都共用这个函数
                [UNITY_outputcontrolpoints(3)]      //不同的图元会对应不同的控制点
              
                TessVertex hullProgram (InputPatch<TessVertex,3> patch,uint id : SV_OutputControlPointID){
                    //定义hullshaderV函数
                    return patch[id];
                }

                [UNITY_domain("tri")]//同样需要定义图元
                VertexOutput ds (OutputPatchConstant tessFactors, const OutputPatch<TessVertex,3>patch,float3 bary :SV_DOMAINLOCATION)
                //bary:重心坐标
                {
                    VertexInput v;
                    v.vertex = patch[0].vertex*bary.x + patch[1].vertex*bary.y + patch[2].vertex*bary.z;
			        v.tangent = patch[0].tangent*bary.x + patch[1].tangent*bary.y + patch[2].tangent*bary.z;
			        v.normal = patch[0].normal*bary.x + patch[1].normal*bary.y + patch[2].normal*bary.z;
			        v.uv = patch[0].uv*bary.x + patch[1].uv*bary.y + patch[2].uv*bary.z;

                    VertexOutput o = vert (v);
                    return o;
                }
            #endif

            float4 frag (VertexOutput i) : SV_Target
            {

                return float4(1.0,1.0,1.0,1.0);
            }
            ENDCG
        }
    }
    Fallback "Diffuse"
}

加上顶点动画后的shader

主要改动

复制代码
Shader "Unlit/TessShader"
{
    Properties
    {
        _TessellationUniform("TessellationUniform",Range(1,64)) = 1

        _Radius ("目标圆半径", Float) = 1.0       // 最终圆的半径(世界空间)
        _WorldSourcePos ("原世界坐标", Vector) = (0,0,0) // 原世界坐标
        _WorldTargetPos ("目标世界坐标", Vector) = (0,0,0) // 目标世界坐标
        _Transition ("过渡系数", Range(0,1)) = 0 // 0=原模型,1=完整圆(世界空间变形)
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100
        Pass
        {
            CGPROGRAM
            //定义2个函数 hull domain
            #pragma hull hullProgram
            #pragma domain ds
           
            #pragma vertex tessvert
            #pragma fragment frag

            #include "UnityCG.cginc"
            //引入曲面细分的头文件
            #include "Tessellation.cginc" 

            #pragma target 5.0

            float _Radius;
            float3 _WorldSourcePos;
            float3 _WorldTargetPos;
            fixed4 _Color;
            float _Transition;
            float _TessellationUniform;
            
            struct VertexInput
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
                float4 tangent : TANGENT;
            };

            struct VertexOutput
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float3 normal : NORMAL;
                float4 tangent : TANGENT;
            };

            VertexOutput vert (VertexInput v)
            //这个函数应用在domain函数中,用来空间转换的函数
            {
                VertexOutput o;
                float4 worldPos =  mul(unity_ObjectToWorld, v.vertex);

                // 计算原始顶点到圆心的向量
                float3 toCenter = worldPos - _WorldSourcePos;
                float3 dirObj = normalize(toCenter); // 世界空间方向(圆心→顶点的单位向量)
                // 计算世界空间目标位置(圆周上的点)
                float3 targetWorldPos = _WorldTargetPos + dirObj * _Radius; // 世界空间目标位置
                // 将目标位置转换回模型空间(用于顶点混合)
                float3 targetModelPos = mul(unity_WorldToObject, float4(targetWorldPos, 1)).xyz;

                float3 baseLocalPos = lerp(v.vertex, targetModelPos, _Transition);

                o.vertex = UnityObjectToClipPos(baseLocalPos);
                o.uv = v.uv;
                o.tangent = v.tangent;

                float3 originalNormal = normalize(UnityObjectToWorldNormal(v.normal));
                float3 circleNormal = normalize(toCenter); // 圆周法线(指向外侧)
                float3 normal = lerp(originalNormal, circleNormal, _Transition); 
                //o.normal = normal;
                o.normal = mul(normal, (float3x3)unity_WorldToObject);
                return o;
            }

            //有些硬件不支持曲面细分着色器,定义了该宏就能够在不支持的硬件上不会变粉,也不会报错
            #ifdef UNITY_CAN_COMPILE_TESSELLATION
                //顶点着色器结构的定义
                struct TessVertex{
                    float4 vertex : INTERNALTESSPOS;
                    float3 normal : NORMAL;
                    float4 tangent : TANGENT;
                    float2 uv : TEXCOORD0;
                };

                struct OutputPatchConstant { 
                    //不同的图元,该结构会有所不同
                    //该部分用于Hull Shader里面
                    //定义了patch的属性
                    //Tessellation Factor和Inner Tessellation Factor
                    float edge[3] : SV_TESSFACTOR;
                    float inside  : SV_INSIDETESSFACTOR;
                };

                TessVertex tessvert (VertexInput v){
                    //顶点着色器函数
                    TessVertex o;
                    o.vertex  = v.vertex;
                    o.normal  = v.normal;
                    o.tangent = v.tangent;
                    o.uv      = v.uv;
                    return o;
                }

                OutputPatchConstant hsconst (InputPatch<TessVertex,3> patch){
                    //定义曲面细分的参数
                    OutputPatchConstant o;
                    o.edge[0] = _TessellationUniform;
                    o.edge[1] = _TessellationUniform;
                    o.edge[2] = _TessellationUniform;
                    o.inside  = _TessellationUniform;
                    return o;
                }

                [UNITY_domain("tri")]//确定图元,quad,triangle等
                [UNITY_partitioning("fractional_odd")]//拆分edge的规则,equal_spacing,fractional_odd,fractional_even
                [UNITY_outputtopology("triangle_cw")]
                [UNITY_patchconstantfunc("hsconst")]//一个patch一共有三个点,但是这三个点都共用这个函数
                [UNITY_outputcontrolpoints(3)]      //不同的图元会对应不同的控制点
              
                TessVertex hullProgram (InputPatch<TessVertex,3> patch,uint id : SV_OutputControlPointID){
                    //定义hullshaderV函数
                    return patch[id];
                }

                [UNITY_domain("tri")]//同样需要定义图元
                VertexOutput ds (OutputPatchConstant tessFactors, const OutputPatch<TessVertex,3>patch,float3 bary :SV_DOMAINLOCATION)
                //bary:重心坐标
                {
                    VertexInput v;
                    v.vertex = patch[0].vertex*bary.x + patch[1].vertex*bary.y + patch[2].vertex*bary.z;
			        v.tangent = patch[0].tangent*bary.x + patch[1].tangent*bary.y + patch[2].tangent*bary.z;
			        v.normal = patch[0].normal*bary.x + patch[1].normal*bary.y + patch[2].normal*bary.z;
			        v.uv = patch[0].uv*bary.x + patch[1].uv*bary.y + patch[2].uv*bary.z;

                    VertexOutput o = vert (v);
                    return o;
                }
            #endif

            float4 frag (VertexOutput i) : SV_Target
            {

                return float4(1.0,1.0,1.0,1.0);
            }
            ENDCG
        }
    }
    Fallback "Diffuse"
}
相关推荐
sswithyou12 分钟前
Socket 套接字的学习--UDP
网络协议·学习·udp
cwn_1 小时前
计算机视觉CS231n学习(9)
人工智能·深度学习·神经网络·学习·计算机视觉
freed_Day1 小时前
Java进阶学习之Stream流的基本概念以及使用技巧
java·开发语言·学习
谷宇.1 小时前
【Unity3D实例-功能-移动】角色行走和奔跑的相互切换
游戏·unity·c#·unity3d·游戏开发·游戏编程
Warren981 小时前
Java后端面试题(含Dubbo、MQ、分布式、并发、算法)
java·开发语言·分布式·学习·算法·mybatis·dubbo
饕餮争锋2 小时前
设计模式笔记_结构型_门面模式
笔记·设计模式
鸢栀w2 小时前
前端css学习笔记5:列表&表格&背景样式设置
前端·css·笔记·学习
阿群今天学习了吗2 小时前
label studio 服务器端打开+xshell端口转发设置
linux·运维·服务器·笔记·python
饮浊酒3 小时前
Python学习-----3.基础语法(2)
python·学习