任意网格转换球原理:
由某一点向网格的所有顶点发射一条射线,到圆上,最终就会形成一个圆
关键的代码
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"
}