Unity中URP下的顶点偏移

本文介绍了如何在Unity的UniversalRenderPipeline(URP)下,通过顶点着色器利用正弦函数实现模型的动态顶点偏移效果,包括调整振幅、周期以及透明度变化。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


前言

在上篇文章中,我们实现了URP下的半透明效果。

在这篇文章中,我们实现一下像鬼魂一样的顶点偏移效果。


一、实现思路

在顶点着色器中,对模型本地空间坐标在转化成齐次裁剪坐标前,进行赋值修改

  • y = A sin(ωx + φ) + B

二、实现URP下的顶点偏移

1、在顶点着色器中使用正弦函数,实现左右摇摆的效果

v.vertexOS.z += sin(_Time.y);

请添加图片描述

2、在正弦函数的传入参数中,加入一个扰度值,实现不规则的顶点偏移

这里用模型顶点本地空间下的 y 值 作为扰度值

v.vertexOS.z += sin(_Time.y + v.vertexOS.y);

请添加图片描述

3、修改正弦函数的振幅 A,让我们的偏移程度合适

  • 在图形计算器中,看一下效果
    请添加图片描述

v.vertexOS.z += 0.3 * sin(_Time.y + v.vertexOS.y);

请添加图片描述

4、修改正弦函数的 ω 来调节周期,调节偏移频率

  • 在图形计算器中,看一下效果
    请添加图片描述

v.vertexOS.z += 0.3 * sin((_Time.y + v.vertexOS.y)*6);

请添加图片描述

5、对其 x 也做同样的偏移(该效果根据个人喜好添加)

v.vertexOS.x += 0.3 * sin((_Time.y + v.vertexOS.y)*6);

请添加图片描述

6、在属性面板定义一个四维变量 用来控制 正弦的振幅 和 周期

_Animation(“Repeat(XY) Intensity(ZW)”,Vector) = (0,0,0,0)

  • 在顶点着色器中

v.vertexOS.z += _Animation.z * sin((_Time.y + v.vertexOS.y)_Animation.x);
v.vertexOS.x += _Animation.w * sin((_Time.y + v.vertexOS.y)
_Animation.y);

请添加图片描述


三、测试代码

为了同时支持 Universal Render Pipeline 和 BuildIn Render Pipeline。
需要写一个同样逻辑 BRP 下的SubShader

//URP下的菲涅尔效果
//URP下的透明效果
//URP下的顶点偏移
Shader "MyShader/URP/P3_2_5"
{
    Properties
    {
        _FresnelColor("FresnelColor",Color) = (0,0,0,0)
        _Fresnel("Fade(X) Intensity(Y) Top(Z) Offset(W)",Vector) = (4,1,1,0)
        _Animation("Repeat(XY) Intensity(ZW)",Vector) = (0,0,0,0)
    }
    SubShader
    {
        Tags
        {
            //告诉引擎,该Shader只用于 URP 渲染管线
            "RenderPipeline"="UniversalPipeline"
            //渲染类型
            "RenderType"="Transparent"
            //渲染队列
            "Queue"="Transparent"
        }
        Blend One One ZWrite On
        Pass
        {


            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.core/ShaderLibrary/Color.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"

            struct Attributes
            {
                float3 vertexOS : POSITION;
                float3 normalOS : NORMAL;
            };

            struct Varyings
            {
                float3 vertexOS : TEXCOORD0;
                float4 vertexCS : SV_POSITION;
                float3 vertexWS : TEXCOORD1;
                float3 normalWS : TEXCOORD2;
            };

            CBUFFER_START(UnityPerMaterial)
                half4 _FresnelColor;
                half4 _Fresnel;
                float _Offset;
                float4 _Animation;
            CBUFFER_END

            Varyings vert(Attributes v)
            {
                Varyings o;
                o.vertexOS = v.vertexOS;
                v.vertexOS.z += _Animation.z * sin((_Time.y + v.vertexOS.y) * _Animation.x);
                v.vertexOS.x += _Animation.w * sin((_Time.y + v.vertexOS.y) * _Animation.y);
                o.vertexWS = TransformObjectToWorld(v.vertexOS);
                o.vertexCS = TransformWorldToHClip(o.vertexWS);
                o.normalWS = TransformObjectToWorldNormal(v.normalOS);

                return o;
            }

            half4 frag(Varyings i) : SV_Target
            {
                //dot(N,L)
                half3 N = normalize(i.normalWS);
                half3 L = normalize(_WorldSpaceCameraPos - i.vertexWS);
                half NdotL = dot(N, L);
                //菲涅尔效果 1 - dot(N,L)
                half fresnel = 1 - saturate(NdotL);
                //菲涅尔自定义
                half4 fresnel1 = pow(fresnel, _Fresnel.x) * _Fresnel.y * _FresnelColor;

                //透明渐变效果
                float alphaMask = saturate(i.vertexOS.y * -1 + i.vertexOS.x * -1 + _Fresnel.w);
                fresnel1 = alphaMask * fresnel1;

                //头部菲涅尔效果和下部菲涅尔效果做出区别
                fresnel1 = lerp(fresnel1, _FresnelColor * alphaMask * fresnel1, alphaMask * _Fresnel.z);

                return fresnel1;
            }
            ENDHLSL
        }
    }
    SubShader
    {
        Tags
        {
            //渲染类型
            "RenderType"="Transparent"
            //渲染队列
            "Queue"="Transparent"
        }
        Blend One One ZWrite On
        Pass
        {


            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata
            {
                float3 vertexOS : POSITION;
                float3 normalOS : NORMAL;
            };

            struct v2f
            {
                float3 vertexOS : TEXCOORD0;
                float4 vertexCS : SV_POSITION;
                float3 vertexWS : TEXCOORD1;
                float3 normalWS : TEXCOORD2;
            };

           
            half4 _FresnelColor;
            half4 _Fresnel;
            float _Offset;
            float4 _Animation;
        
            v2f vert(appdata v)
            {
                v2f o;
                o.vertexOS = v.vertexOS;
                v.vertexOS.z += _Animation.z * sin((_Time.y + v.vertexOS.y) * _Animation.x);
                v.vertexOS.x += _Animation.w * sin((_Time.y + v.vertexOS.y) * _Animation.y);
                o.vertexWS = mul(unity_ObjectToWorld,v.vertexOS);
                o.vertexCS = UnityWorldToClipPos(o.vertexWS);
                o.normalWS = UnityObjectToWorldNormal(v.normalOS);

                return o;
            }

            half4 frag(v2f i) : SV_Target
            {
                //dot(N,L)
                half3 N = normalize(i.normalWS);
                half3 L = normalize(_WorldSpaceCameraPos - i.vertexWS);
                half NdotL = dot(N, L);
                //菲涅尔效果 1 - dot(N,L)
                half fresnel = 1 - saturate(NdotL);
                //菲涅尔自定义
                half4 fresnel1 = pow(fresnel, _Fresnel.x) * _Fresnel.y * _FresnelColor;

                //透明渐变效果
                float alphaMask = saturate(i.vertexOS.y * -1 + i.vertexOS.x * -1 + _Fresnel.w);
                fresnel1 = alphaMask * fresnel1;

                //头部菲涅尔效果和下部菲涅尔效果做出区别
                fresnel1 = lerp(fresnel1, _FresnelColor * alphaMask * fresnel1, alphaMask * _Fresnel.z);

                return fresnel1;
            }
            ENDCG
        }
    }
}

### 实现带有顶点偏移URP水面扭曲效果 为了实现带有顶点偏移URP水面扭曲效果,可以通过Unity Shader Graph工具来创建自定义Shader。以下是详细的说明: #### 创建基础Shader Graph 1. **新建Shader Graph** 在Unity中右键点击Assets窗口 -> Create -> Shader -> Universal Render Pipeline/Lit,命名为`WaterDistortionEffect`。 2. **配置Material属性** 在Shader Graph编辑器中,添加两个Vector类型的Property节点用于控制顶点偏移的方向和强度: - `VertexOffsetDirection`: Vector3 类型,默认值为 `(0, 0, 1)`。 - `VertexOffsetIntensity`: Float 类型,默认值为 `0.1`。 3. **计算顶点偏移** 使用Time节点生成动态变化的时间变量,并将其作为输入参数传递给Sine函数或Cosine函数,从而生成周期性的波动效果。将此波形乘以`VertexOffsetIntensity`并沿指定方向应用到顶点位置上[^1]。 ```csharp // 计算顶点偏移逻辑伪代码示例 float time = _Time.y; float waveValue = sin(time * frequency + uv.x); vertexPosition.xyz += VertexOffsetDirection * waveValue * VertexOffsetIntensity; ``` 4. **调整纹理映射** 利用UV坐标变换技术改变水面上反射/折射图案的表现形式。通过修改Main Texture的Tiling与Offset参数模拟水流运动状态[^2]。 ```csharp // 修改纹理平铺比例和平移距离 uv.xy *= mainTextureScale; uv.xy += mainTextureOffset; ``` 5. **集成至渲染管线** 配置URP中的Renderer Features支持该特殊效果显示。具体操作参见如何向Forward Renderer添加额外Render Object功能描述[^4]。 6. **优化性能表现** 考虑实际运行效率问题,在必要时降低细节层次LOD(Level Of Detail),或者采用更简单的数学模型近似复杂物理过程[^3]。 #### 注意事项 - 确保所使用的资源包版本兼容当前项目的URP设置。 - 测试不同平台上的视觉差异以便做出相应适配方案。 ```python # 示例Python脚本片段展示可能涉及的数据处理部分 import math def calculate_vertex_offset(uv, intensity, direction): """基于UV坐标的简单正弦波形位移""" freq = 2.0 phase_shift = uv[0] amplitude = intensity displacement = math.sin(freq * uv[1] + phase_shift) * amplitude return tuple(d * displacement for d in direction) example_uv = (0.5, 0.75) intensity_value = 0.1 direction_vector = (0, 0, 1) offset_result = calculate_vertex_offset(example_uv, intensity_value, direction_vector) print(f"Calculated Offset Result: {offset_result}") ```
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

楠溪泽岸

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值