You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Unity中无需后处理实现运动模糊及着色器获取前一位置问询

在Unity着色器中获取顶点上一帧位置(Previous Position)的实现方法

要获取顶点的上一帧位置,核心逻辑是手动缓存当前帧的顶点位置到纹理中,下一帧读取该纹理得到上一帧数据——Unity不会自动保留上一帧的顶点状态,必须通过离线存储实现。

具体实现步骤

1. 初始化位置缓存纹理

在C#脚本中创建一个浮点格式的RenderTexture,用于存储每一帧的顶点位置(浮点格式能避免精度丢失):

public RenderTexture prevPositionRT;

void Start() {
    prevPositionRT = new RenderTexture(Screen.width, Screen.height, 0, RenderTextureFormat.ARGBFloat);
    prevPositionRT.Create();
}

2. 渲染当前帧位置到缓存纹理

写一个专用着色器,将当前帧顶点的裁剪空间位置(已包含相机视图投影变换,无需额外处理相机运动)归一化后输出到缓存纹理:

Shader "Custom/StorePosition" {
    SubShader {
        Tags { "RenderType"="Opaque" }
        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata {
                float4 vertex : POSITION;
            };

            struct v2f {
                float4 pos : SV_POSITION;
                float4 clipPos : TEXCOORD0;
            };

            v2f vert (appdata v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                // 把裁剪空间位置归一化到[0,1]范围,适配纹理存储
                o.clipPos = o.pos * 0.5 + 0.5;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target {
                return fixed4(i.clipPos.xyz, 1);
            }
            ENDCG
        }
    }
}

在C#脚本的每一帧,先使用这个着色器渲染场景,将结果写入prevPositionRT。

3. 在目标着色器中读取上一帧位置

在需要计算运动模糊的着色器中,采样缓存纹理获取上一帧位置,再转换回原始空间:

Shader "Custom/MotionBlur" {
    Properties {
        _MainTex ("Texture", 2D) = "white" {}
        _PrevPositionTex ("Previous Position Texture", 2D) = "white" {}
    }
    SubShader {
        Tags { "RenderType"="Opaque" }
        Pass {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"

            struct appdata {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f {
                float2 uv : TEXCOORD0;
                float4 clipPos : TEXCOORD1;
                float4 pos : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            sampler2D _PrevPositionTex;

            v2f vert (appdata v) {
                v2f o;
                o.pos = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                // 当前帧裁剪位置归一化,用于采样上一帧纹理
                o.clipPos = o.pos * 0.5 + 0.5;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target {
                // 采样上一帧的归一化裁剪位置
                float4 prevClipPos = tex2D(_PrevPositionTex, i.clipPos.xy);
                // 转换回原始裁剪空间
                prevClipPos = prevClipPos * 2 - 1;

                // 计算运动向量(当前帧与上一帧的位置差)
                float2 motionVec = i.clipPos.xy - prevClipPos.xy;

                // 后续运动模糊逻辑(基于运动向量采样多帧颜色等)
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }
    }
}

关键注意事项

  • 空间一致性:存储和读取的位置必须使用同一空间(比如都用裁剪空间),否则运动向量计算会出错。
  • 纹理精度:必须使用浮点格式RenderTexture,避免位置数据被压缩导致精度丢失。
  • 相机同步:裁剪空间位置已包含相机的视图投影变换,无需额外处理相机的移动、旋转,能直接得到正确的运动向量。

内容的提问来源于stack exchange,提问作者Strawberry

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 16:51:16