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

使用Graphics.Blit复制材质到纹理结果不符的技术问题

解决方案:HDRP下自定义Shader Blit保存纹理与显示不一致

以下是针对性的排查和修复方向:


1. 修正RenderTexture的格式与颜色空间设置

HDRP默认使用线性颜色空间,默认创建的RenderTexture格式(ARGB32)可能无法匹配Shader的输出,尤其是当Shader输出HDR颜色时。修改SaveMaterialToTexture方法,指定符合HDRP的RenderTexture参数:

public Texture2D SaveMaterialToTexture(Material mat, int width, int height)
{
    // 创建支持HDR的RenderTexture,匹配HDRP输出
    RenderTexture rt = new RenderTexture(width, height, 0, RenderTextureFormat.DefaultHDR);
    // 根据当前颜色空间自动设置sRGB标记
    rt.sRGB = QualitySettings.activeColorSpace == ColorSpace.Linear;
    
    RenderTexture.active = rt;
    // 清空RenderTexture避免残留数据干扰
    GL.Clear(true, true, Color.clear);
    Graphics.Blit(null, rt, mat, 0);
    
    // 创建对应格式的Texture2D,确保和RenderTexture匹配
    Texture2D tex = new Texture2D(width, height, TextureFormat.RGBAHalf, false);
    tex.ReadPixels(new Rect(0, 0, width, height), 0, 0);
    tex.Apply();
    
    // 清理资源
    RenderTexture.active = null;
    DestroyImmediate(rt);
    
    return tex;
}

2. 确保自定义Shader兼容HDRP渲染管线

测试Shader正常但自定义Shader异常,大概率是缺少HDRP的必要标记或渲染状态配置。在自定义Shader的SubShader中添加管线标签,并修正Pass的渲染状态:

Shader "Custom/YourUnlitShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        // 其他自定义属性...
    }
    SubShader
    {
        // 必须添加HDRP管线标签,确保渲染逻辑适配
        Tags { "RenderType"="Opaque" "RenderPipeline"="HDRenderPipeline" }
        LOD 100

        Pass
        {
            // 关闭深度测试、写入,避免Blit时受场景深度影响
            ZTest Always ZWrite Off Cull Off
            
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            // 引入HDRP通用工具函数,确保顶点转换正确
            #include "Packages/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/RenderPass/CustomPass/CustomPassCommon.hlsl"

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

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
                // 使用HDRP的顶点转换函数,保证全屏UV正确映射
                o.vertex = TransformWorldToHClip(v.vertex.xyz);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }

            float4 frag (v2f i) : SV_Target
            {
                // 你的纹理处理逻辑
                float4 col = tex2D(_MainTex, i.uv);
                // 线性空间下需转换为sRGB输出(如果最终要保存为PNG)
                col = SRP_LINEAR_TO_SRGB(col);
                return col;
            }
            ENDHLSL
        }
    }
}

3. 使用AsyncGPUReadback避免GPU渲染延迟

ReadPixels可能在GPU还未完成渲染时读取数据,导致结果错误。改用Unity异步GPU读取API:

public void SaveTexToDisk()
{
    RenderTexture rt = new RenderTexture(width, height, 0, RenderTextureFormat.DefaultHDR);
    rt.sRGB = QualitySettings.activeColorSpace == ColorSpace.Linear;
    RenderTexture.active = rt;
    GL.Clear(true, true, Color.clear);
    Graphics.Blit(null, rt, mat, 0);
    
    // 异步读取GPU纹理数据,确保渲染完成后再处理
    AsyncGPUReadback.Request(rt, 0, TextureFormat.RGBAHalf, (request) =>
    {
        if (request.hasError)
        {
            Debug.LogError("GPU readback error");
            return;
        }
        Texture2D tex = new Texture2D(width, height, TextureFormat.RGBAHalf, false);
        tex.LoadRawTextureData(request.GetData<byte>());
        tex.Apply();
        
        // 转换为sRGB格式后保存为PNG(如果需要)
        Texture2D sRGBTex = tex.ConvertToSRGB();
        byte[] bytes = sRGBTex.EncodeToPNG();
        System.IO.File.WriteAllBytes(Application.dataPath + "/Tex.png", bytes);
        
        // 释放临时资源
        DestroyImmediate(rt);
        DestroyImmediate(tex);
        DestroyImmediate(sRGBTex);
    });
}

4. 检查纹理采样与UV映射问题

如果自定义Shader使用了复杂的UV计算(如偏移、缩放或模型UV),要确保Blit时的UV和材质显示时一致。Graphics.Blit传入的是全屏四边形,UV范围为[0,1],若Shader依赖模型UV而非全屏UV,会导致采样错误——此时需确保顶点着色器输出全屏UV。


5. 避免PNG格式的颜色丢失

若Shader输出HDR颜色(亮度超过1.0),直接用EncodeToPNG会截断颜色值(PNG仅支持0-1的8位sRGB),导致与屏幕HDR效果不一致。建议保存为EXR格式:

byte[] bytes = tex.EncodeToEXR(Texture2D.EXRFlags.CompressZIP);
System.IO.File.WriteAllBytes(Application.dataPath + "/Tex.exr", bytes);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 11:57:03