使用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
相关产品推荐
相关产品推荐

