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 100Pass{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 100Pass{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.0struct 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 Factorfloat 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;}#endiffloat4 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 100Pass{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.0float _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 Factorfloat 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;}#endiffloat4 frag (VertexOutput i) : SV_Target{return float4(1.0,1.0,1.0,1.0);}ENDCG}}Fallback "Diffuse"
}