当前位置: 首页 > news >正文

Unity - gamma space下还原linear space效果

文章目录

  • 环境
  • 目的
  • 环境
  • 问题
  • 实践结果
  • 处理要点
    • 处理细节
      • 【OnPostProcessTexture 实现 sRGB 2 Linear 编码】 - 预处理
      • 【封装个简单的 *.cginc】 - shader runtime
      • 【shader需要gamma space下还原记得 #define _RECOVERY_LINEAR_IN_GAMMA】
      • 【颜色参数应用前 和 颜色贴图采样后】
      • 【灯光颜色】
      • 【F0应用(绝缘体正对视角下的反射率)】
      • 【BRDF BRDF1_Unity_PBS 不适用gamma调整】
      • 【自发光颜色处理】
      • 【雾效颜色】
      • 【FBO的color处理Linear 2 sRGB的后处理】
      • 【预处理阶段处理所有材质里面的所有 color 遍历处理(工具化,注意:可能不便于维护)】
    • Cubemap texture to linear
  • Project
  • References


环境

unity : 2023.3.37f1
pipeline : BRP


之前写过一篇: Gamma Correction/Gamma校正/灰度校正/亮度校正 - 部分 DCC 中的线性工作流配置,此文,自己修修改改不下于 50次,因为以前很多概念模糊

如果发现还有错误,请指出来,谢谢


目的

处理H5使用 WEB GL 1.0 的问题
因为项目要移植到 H5 WEB GL 1.0 的 graphics api
而因为我们之前的项目配置,使用的是 color space : linear
然后在H5平台中,如果使用 linear 的话,unity 会提示,只支持 WEB GL 2.0
而WEB GL 2.0 无论是 unity,微信,还是 浏览器,都是处于 BETA 阶段
甚至iOS或是 mac 下,直接不支持 (因为苹果要推他的 web metal,但是以前的 web gl 1.0 他是支持的)

因此为了设备兼容率,我们只能将 linear 转为 gamma

但是颜色空间不同的话,其实最大差异就是 sRGB 贴图颜色 和 最后后处理的 gamma校正 的处理
还有比较隐蔽的一些颜色相关的常量值 (比如PBR中的 绝缘体的 F0 常量值,等等)
还有灯光颜色,材质颜色,等


环境

unity : 2020.3.37f1
pipeline : BRP


问题

正常 Linear space 和 gamma space 下渲染差异如下:
在这里插入图片描述
在这里插入图片描述


实践结果

如下图,我目前对比了 linear 和 gamma 下的渲染区别
并且在 gamma space 下,尽可能的还原了 linear space 的效果
其中人物的衣服渲染算是还原了
这里头还有很多需要还原的:

  • skybox (cubemap ,这类 cube 还不能通过 SetPixels 设置值,会有报错)
  • 皮肤
  • 后处理的所有颜色

请添加图片描述

下面是又是后续处理了皮肤
还有头发之后的 (头发没有处理完整,因为使用 ASE 连连看练出来的,使用 surface shader,虽然可以生成一下 vert, frag 的方式在修改,但是我懒得去修改了,这样就是 PBR 的 BRDF 里面的部分曲线是不一样的,所以可以看到头发有一些差异)

(剩下一些: cubemap 的贴图部分没有没有还原,这部分后续再想想方案)
请添加图片描述


处理要点

  1. 所有颜色贴图 (注意不是数据贴图)的 RGB 通道需要处理 预处理的 sRGB 2 Linear - 性能高一些,毕竟是预处理
  2. 或者是不在预处理阶段,而是改在: shader sample 后的 pow(tex_color, 2.2) - 会比较浪费性能,但是如果为了快速出效果,或是验证,这是不二之选
  3. 所有shading时,材质 (shahder program)传入的颜色相关参数都需要在 shading 前做 pow(color, 2.2)
  4. 也可以在预处理阶段处理所有材质里面的所有 color 遍历处理(工具化)
  5. 所有shading 结束后,增加一个 postprocess 后处理,将屏幕的所有颜色处理 Linear 2 sRGB

处理细节

【OnPostProcessTexture 实现 sRGB 2 Linear 编码】 - 预处理

在 AssetPostProcessor 中的 OnPostProcessTexture 回调用处理 Texture2D 的资源
其中 Texture2D 只包含, Texture2D, Sprite 的回调处理

注意:如果是 Cubemap 的纹理,unity是不会回调进这个函数的
而且 cubemap 的问题,我一直没想好怎么处理

还要注意,如果实现了预处理贴图,就不要在 shader runtime 对 sample 后的颜色贴图做 sRGB 2 Linear 了

    private static void GammaSpace_Non_HDR_TexPP_Handler(Texture2D texture){for (int mipmapIDX = 0; mipmapIDX < texture.mipmapCount; mipmapIDX++){Color[] c = texture.GetPixels(mipmapIDX);for (int i = 0; i < c.Length; i++){c[i] = c[i].linear;}texture.SetPixels(c, mipmapIDX);}}private static bool NeedToRemoveGammaCorrect(string assetPath){TextureImporter ti = AssetImporter.GetAtPath(assetPath) as TextureImporter;return NeedToRemoveGammaCorrect(ti);}// jave.lin : 是否需要删除 gamma correctprivate static bool NeedToRemoveGammaCorrect(TextureImporter ti){if (ti == null) return false;// jave.lin : 没开启if (PlayerPrefs.GetInt("Enabled_GammaSpaceTexPP", 0) == 0) return false;// jave.lin : linear color space 下不处理,gamma color space 下才处理if (QualitySettings.activeColorSpace == ColorSpace.Linear) return false;// jave.lin : 原来 linear 下,不是 sRGB 不用处理if (ti.sRGBTexture == false) return false;return true;}private void OnPostprocessTexture(Texture2D texture){Debug.Log($"OnPostprocessTexture.assetPath:{assetPath}");if (NeedToRemoveGammaCorrect(assetPath)){GammaSpace_Non_HDR_TexPP_Handler(texture);}}

代码太多,我只罗列出关键要修改的 PBR 着色的地方要修改的地方


【封装个简单的 *.cginc】 - shader runtime

注意:如果使用了 OnPostProcessTexture 实现 sRGB 2 Linear 编码 的预处理,就不要处理 shader runtime 里面的 sample 后的 COLOR_TRANS 或是 CHANGED_COLOR 处理

#ifndef __CUSTOM_COLOR_SPACE_VARS_H__
#define __CUSTOM_COLOR_SPACE_VARS_H__// jave.lin 2024/01/17
// custom the color space const & vars#define unity_ColorSpaceGrey1 fixed4(0.214041144, 0.214041144, 0.214041144, 0.5)
#define unity_ColorSpaceDouble1 fixed4(4.59479380, 4.59479380, 4.59479380, 2.0)
#define unity_ColorSpaceDielectricSpec1 half4(0.04, 0.04, 0.04, 1.0 - 0.04) // standard dielectric reflectivity coef at incident angle (= 4%)
#define unity_ColorSpaceLuminance1 half4(0.0396819152, 0.458021790, 0.00609653955, 1.0) // Legacy: alpha is set to 1.0 to specify linear mode#if defined(UNITY_COLORSPACE_GAMMA) && defined(_RECOVERY_LINEAR_IN_GAMMA)
// jave.lin : force using linear effect
#define __FORCE_LINEAR_EFFECT__
#endif#ifdef __FORCE_LINEAR_EFFECT__// sRGB to Linear    #define COLOR_TRANS(col) pow(col, 2.2)#define CHANGED_COLOR(col) (col = pow(col, 2.2));// const defines#define GREY_COLOR (unity_ColorSpaceGrey1)#define DOUBLE_COLOR (unity_ColorSpaceDouble1)#define DIELECTRIC_SPEC_COLOR (unity_ColorSpaceDielectricSpec1)#define LUMINANCE_COLOR (unity_ColorSpaceLuminance1)
#else// sRGB to Linear  #define COLOR_TRANS(col) (col)#define CHANGED_COLOR(col) // const defines - gamma space#define GREY_COLOR (unity_ColorSpaceGrey)#define DOUBLE_COLOR (unity_ColorSpaceDouble)#define DIELECTRIC_SPEC_COLOR (unity_ColorSpaceDielectricSpec)#define LUMINANCE_COLOR (unity_ColorSpaceLuminance)
#endif#endif

【shader需要gamma space下还原记得 #define _RECOVERY_LINEAR_IN_GAMMA】

`#define _RECOVERY_LINEAR_IN_GAMMA`

【颜色参数应用前 和 颜色贴图采样后】

half4 Albedo1(float4 texcoords)
{//return _Color * tex2D(_MainTex, texcoords.xy);//return _Color * tex2Dbias(_MainTex, float4(texcoords.xy, 0.0, UNITY_ACCESS_INSTANCED_PROP(Props, _MainTex_mipmapBias)));half4 __color = _Color; // jave.lin : if this color is HDR color, unnesscessory to do sRGB to Linearhalf4 __tex_color = tex2D(_MainTex, texcoords.xy);//CHANGED_COLOR(__color.rgb)CHANGED_COLOR(__tex_color.rgb)return __color * __tex_color;
}

【灯光颜色】

    UnityLight mainLight = MainLight();CHANGED_COLOR(mainLight.color.rgb) // jave.lin : gamma correct light color

【F0应用(绝缘体正对视角下的反射率)】

使用我们自己定义的 DIELECTRIC_SPEC_COLOR

inline half OneMinusReflectivityFromMetallic1(half metallic)
{// We'll need oneMinusReflectivity, so//   1-reflectivity = 1-lerp(dielectricSpec, 1, metallic) = lerp(1-dielectricSpec, 0, metallic)// store (1-dielectricSpec) in DIELECTRIC_SPEC_COLOR.a, then//   1-reflectivity = lerp(alpha, 0, metallic) = alpha + metallic*(0 - alpha) =//                  = alpha - metallic * alphahalf oneMinusDielectricSpec = DIELECTRIC_SPEC_COLOR.a;return oneMinusDielectricSpec - metallic * oneMinusDielectricSpec;
}inline half3 DiffuseAndSpecularFromMetallic1(half3 albedo, half metallic, out half3 specColor, out half oneMinusReflectivity)
{specColor = lerp(DIELECTRIC_SPEC_COLOR.rgb, albedo, metallic);oneMinusReflectivity = OneMinusReflectivityFromMetallic1(metallic);return albedo * oneMinusReflectivity;
}FragmentCommonData1 MetallicSetup1(half3 albedo, fixed2 metallicGloss)
{half metallic = metallicGloss.x;half smoothness = metallicGloss.y; // this is 1 minus the square root of real roughness m.half oneMinusReflectivity;half3 specColor;// half3 diffColor = DiffuseAndSpecularFromMetallic(Albedo(i_tex), metallic, /*out*/ specColor, /*out*/ oneMinusReflectivity);half3 diffColor = DiffuseAndSpecularFromMetallic1(albedo, metallic, /*out*/specColor, /*out*/oneMinusReflectivity);FragmentCommonData1 o = (FragmentCommonData1) 0;o.diffColor = diffColor;o.specColor = specColor;o.oneMinusReflectivity = oneMinusReflectivity;o.smoothness = smoothness;return o;
}

【BRDF BRDF1_Unity_PBS 不适用gamma调整】

注释掉下面代码

//#ifdef UNITY_COLORSPACE_GAMMA
//        specularTerm = sqrt(max(1e-4h, specularTerm)); // jave.lin : if you want to recovery linear result in gamma space, don't do this one
//#endif

【自发光颜色处理】

    // jave.lin : emissionhalf3 emission_col = Emission(i.tex.xy);CHANGED_COLOR(emission_col.rgb)c.rgb += emission_col.rgb;

【雾效颜色】

    CHANGED_COLOR(unity_FogColor.rgb)UNITY_EXTRACT_FOG_FROM_EYE_VEC(i);UNITY_APPLY_FOG(_unity_fogCoord, c.rgb);return OutputForward(c, s.alpha);

【FBO的color处理Linear 2 sRGB的后处理】

csharp monobehaviour 如下

// jave.lin : 2024/01/08
// testing linear to gamma (linear to srgb)using UnityEngine;[ExecuteInEditMode]
public class LinearToGammaPP : MonoBehaviour
{public Color backgroundColor;public Shader shader;private Material material;private Camera cam;private bool InLinearColorSpace(){return QualitySettings.activeColorSpace == ColorSpace.Linear;}private void OnRenderImage(RenderTexture source, RenderTexture destination){if(cam == null) cam = GetComponent<Camera>();cam.backgroundColor = InLinearColorSpace() ? backgroundColor : backgroundColor.linear;if (InLinearColorSpace()){Graphics.Blit(source, destination);return;}if (material == null){material = new Material(shader);}Graphics.Blit(source, destination, material);}private void OnDestroy(){if(material != null){if (Application.isPlaying)Object.Destroy(material);elseObject.DestroyImmediate(material);}}
}

shader 如下

// jave.lin 2024/01/08 postprocess for linear 2 sRGBShader "Hidden/LinearToGamma"
{Properties{_MainTex ("Texture", 2D) = "white" {}}SubShader{// No culling or depthCull Off ZWrite Off ZTest AlwaysPass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include "UnityCG.cginc"struct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;};v2f vert (appdata v){v2f o;o.vertex = UnityObjectToClipPos(v.vertex);o.uv = v.uv;return o;}sampler2D _MainTex;fixed4 frag (v2f i) : SV_Target{fixed4 col = tex2D(_MainTex, i.uv);#if defined(UNITY_COLORSPACE_GAMMA)col.rgb = pow(col.rgb, 1.0/2.2);//col.rgb = pow(col.rgb, 2.2);#endifreturn col;}ENDCG}}
}

【预处理阶段处理所有材质里面的所有 color 遍历处理(工具化,注意:可能不便于维护)】

处理要点里面,我提过:“也可以在预处理阶段处理所有材质里面的所有 color 遍历处理(工具化)”

但是要注意:如果shadering 里面对上线材质传递的颜色参数,二次修改为 pow(color, 2.2) 的值,这种方式,虽然渲染能成功,但是对于要记住 shader 中,哪些颜色参数是预处理过的,是需要维护成本的

因此不建议使用,但是如果你想要优化极致性能,那么可以考虑使用这种方式,代码如下:

// jave.lin 2024/01/08
// 將所有的SRGB color 转到 linear 下using System.Collections.Generic;
using UnityEditor;
using UnityEngine;public class MaterialSrgbToLinearTool
{//[MenuItem("Tools/TestBuildAB")]//public static void TestBuildAB()//{//    var bundles = new AssetBundleBuild[1];//    bundles[0] = new AssetBundleBuild//    {//        assetBundleName = "sg_noise_ccs_124.jpg",//        assetNames = new string[] { "Assets/Art/Effects/Textures/Textures/sg_noise_ccs_124.jpg" }//    };//    BuildPipeline.BuildAssetBundles("Bundle", bundles, BuildAssetBundleOptions.ChunkBasedCompression | BuildAssetBundleOptions.DeterministicAssetBundle, BuildTarget.Android);//}//public static void SelectedSrgbImageToLinearImage()//{//    foreach (string guid in Selection.assetGUIDs)//    {//        string assetPath = AssetDatabase.GUIDToAssetPath(guid);//        TextureImporter ti = AssetImporter.GetAtPath(assetPath) as TextureImporter;//        if (ti == null)//            continue;//        if (ti.sRGBTexture == false)//            continue;//    }//}public const string TransformedLabel = "AllColorProp_Has_sRGB2Linear";public static List<string> label_list_helper = new List<string>();private static bool HandleSingleMatRes_sRGB2Linear(Material mat){try{// method 1 : iterating all color props values//SerializedObject serializedObject = new SerializedObject(mat);//SerializedProperty prop = serializedObject.GetIterator();//while (prop.NextVisible(true))//{//    if (prop.propertyType == SerializedPropertyType.Color)//    {//        Color colorValue = prop.colorValue;//        Debug.Log("Color property " + prop.name + " value: " + colorValue);//    }//}// method 2 : iterating m_SavedProperties/m_Colors props valuesvar so = new SerializedObject(mat);var sp = so.FindProperty("m_SavedProperties");for (int j = 0; j < sp.FindPropertyRelative("m_Colors").arraySize; j++){var elementProp = sp.FindPropertyRelative("m_Colors").GetArrayElementAtIndex(j);var fistElement = elementProp.FindPropertyRelative("first");var secondElement = elementProp.FindPropertyRelative("second");//Debug.Log($"{fistElement.stringValue}, r:{secondElement.colorValue.r},g:{secondElement.colorValue.g},b:{secondElement.colorValue.b},a:{secondElement.colorValue.a}");var col = secondElement.colorValue;float maxComponent = Mathf.Max(col.r, col.g, col.b);if (maxComponent > 1.0f){// hdr//Debug.Log($"maxComponent: {maxComponent}");Debug.Log($"{fistElement.stringValue} is HDR color.");float npot = Mathf.Max(Mathf.NextPowerOfTwo((int)maxComponent), 1.0f);//Debug.Log($"npot: {npot}");Color linearColor = new Color(col.r / npot, col.g / npot, col.b / npot, col.a).linear;//Debug.Log($"linearColor: {linearColor}");secondElement.colorValue = linearColor * new Color(npot, npot, npot, 1.0f);//Debug.Log($"finalColor: {secondElement.colorValue}");}else{// ldrsecondElement.colorValue = secondElement.colorValue.linear;}}so.ApplyModifiedPropertiesWithoutUndo();return true;}catch (System.Exception er){Debug.LogError(er);return false;}}private static bool HandleSingleMatRes_Recovery_sRGB2Linear(Material mat){try{// method 1 : iterating all color props values//SerializedObject serializedObject = new SerializedObject(mat);//SerializedProperty prop = serializedObject.GetIterator();//while (prop.NextVisible(true))//{//    if (prop.propertyType == SerializedPropertyType.Color)//    {//        Color colorValue = prop.colorValue;//        Debug.Log("Color property " + prop.name + " value: " + colorValue);//    }//}// method 2 : iterating m_SavedProperties/m_Colors props valuesvar so = new SerializedObject(mat);var sp = so.FindProperty("m_SavedProperties");for (int j = 0; j < sp.FindPropertyRelative("m_Colors").arraySize; j++){var elementProp = sp.FindPropertyRelative("m_Colors").GetArrayElementAtIndex(j);var fistElement = elementProp.FindPropertyRelative("first");var secondElement = elementProp.FindPropertyRelative("second");//Debug.Log($"{fistElement.stringValue}, r:{secondElement.colorValue.r},g:{secondElement.colorValue.g},b:{secondElement.colorValue.b},a:{secondElement.colorValue.a}");var col = secondElement.colorValue;float maxComponent = Mathf.Max(col.r, col.g, col.b);if (maxComponent > 1.0f){// hdr//Debug.Log($"maxComponent: {maxComponent}");Debug.Log($"{fistElement.stringValue} is HDR color.");float npot = Mathf.Max(Mathf.NextPowerOfTwo((int)maxComponent), 1.0f);//Debug.Log($"npot: {npot}");Color linearColor = new Color(col.r / npot, col.g / npot, col.b / npot, col.a).gamma;//Debug.Log($"linearColor: {linearColor}");secondElement.colorValue = linearColor * new Color(npot, npot, npot, 1.0f);//Debug.Log($"finalColor: {secondElement.colorValue}");}else{// ldrsecondElement.colorValue = secondElement.colorValue.gamma;}}so.ApplyModifiedPropertiesWithoutUndo();return true;}catch (System.Exception er){Debug.LogError(er);return false;}}public static bool AddLabel(AssetImporter ai, string adding_label){var assetPasth = ai.assetPath;GUID guid = new GUID(AssetDatabase.AssetPathToGUID(assetPasth));var labels = AssetDatabase.GetLabels(guid);label_list_helper.Clear();label_list_helper.AddRange(labels);if (!label_list_helper.Contains(adding_label)){label_list_helper.Add(adding_label);AssetDatabase.SetLabels(ai, label_list_helper.ToArray());return true;}return false;}public static bool RemoveLabel(AssetImporter ai, string removing_label){var assetPasth = ai.assetPath;GUID guid = new GUID(AssetDatabase.AssetPathToGUID(assetPasth));var labels = AssetDatabase.GetLabels(guid);label_list_helper.Clear();label_list_helper.AddRange(labels);if (label_list_helper.Remove(removing_label)){label_list_helper.Sort();AssetDatabase.SetLabels(ai, label_list_helper.ToArray());return true;}return false;}[MenuItem("Tools/Materials/sRGB2LinearAllMatColorProps")]public static void sRGB2LinearAllMatColorProps(){try{var guids = AssetDatabase.FindAssets("t:Material");for (int i = 0; i < guids.Length; i++){var guid = guids[i];var cancacle = EditorUtility.DisplayCancelableProgressBar("Transforming Material Color Props : sRGB to Linear",$"{i + 1}/{guids.Length}",(float)(i + 1) / guids.Length);if (cancacle){Debug.Log($"Transforming Material Color Props : sRGB to Linear is cancacled! Handled : {i}/{guids.Length}");break;}var assetPath = AssetDatabase.GUIDToAssetPath(guid);//if (assetPath != "Assets/Art/Effects/Materials/New/UI_sg_kapaizhujiemian_tianfui_02.mat")//    continue;AssetImporter ai = AssetImporter.GetAtPath(assetPath);var labels = AssetDatabase.GetLabels(ai);if (System.Array.IndexOf(labels, TransformedLabel) >= 0){continue;}var mat = AssetDatabase.LoadAssetAtPath<Material>(assetPath);if (mat == null) continue;Debug.Log($"Transforming Material Color Props, mat path : {assetPath}");HandleSingleMatRes_sRGB2Linear(mat);if (AddLabel(ai, TransformedLabel)){Debug.Log($"Tranforming Material Color Props, mat path : {assetPath}, added the Label : {TransformedLabel}");}else{Debug.LogWarning($"Tranforming Material Color Props, mat path : {assetPath}, alreading exsit the Label : {TransformedLabel}");}}Debug.Log($"Transforming Material Color Props : sRGB to Linear is completed!");}catch (System.Exception er){Debug.LogError(er);}finally{EditorUtility.ClearProgressBar();}}[MenuItem("Tools/Materials/Recovery_sRGB2LinearAllMatColorProps")]public static void Recovery_sRGB2LinearAllMatColorProps(){try{var guids = AssetDatabase.FindAssets("t:Material");for (int i = 0; i < guids.Length; i++){var guid = guids[i];var cancacle = EditorUtility.DisplayCancelableProgressBar("Transforming Material Color Props : sRGB to Linear",$"{i + 1}/{guids.Length}",(float)(i + 1) / guids.Length);if (cancacle){Debug.Log($"Transforming Material Color Props : sRGB to Linear is cancacled! Handled : {i}/{guids.Length}");break;}var assetPath = AssetDatabase.GUIDToAssetPath(guid);//if (assetPath != "Assets/Art/Effects/Materials/New/UI_sg_kapaizhujiemian_tianfui_02.mat")//    continue;AssetImporter ai = AssetImporter.GetAtPath(assetPath);var labels = AssetDatabase.GetLabels(ai);if (System.Array.IndexOf(labels, TransformedLabel) == -1){continue;}var mat = AssetDatabase.LoadAssetAtPath<Material>(assetPath);if (mat == null) continue;Debug.Log($"Recoverying Material Color Props, mat path : {assetPath}");HandleSingleMatRes_Recovery_sRGB2Linear(mat);if (RemoveLabel(ai, TransformedLabel)){Debug.Log($"Recoverying Material Color Props, mat path : {assetPath}, has remove Label : {TransformedLabel}");}else{Debug.LogWarning($"Recoverying Material Color Props, mat path : {assetPath}, not found the Label : {TransformedLabel}");}}Debug.Log($"Transforming Material Color Props : sRGB to Linear is completed!");}catch (System.Exception er){Debug.LogError(er);}finally{EditorUtility.ClearProgressBar();}}
}

Cubemap texture to linear

其实就是要对一些 HDR 贴图做 sRGB to Linear 的处理
HDR color 我们知道是: HDR_COLOR = color_normalized * pow(2, intensity)

因此我们只要算出 NextPowerOfTwo 就可以还原出 color_normalizedpow(2, intensity) ,就可以重新编码颜色

但是 HDR texture 的话,我们也尝试这种编码处理方式,但是会有 Unsupported GraphicsFormat(130) for SetPixel operations. 的错误,如下图:
在这里插入图片描述

CSHARP 代码中,我们看到代码没什么问题,但是 unity Cubemap 中不提供正确的 API 调用

    // jave.lin : 处理 HDR 的纹理// Cubemap.SetPixels 有异常: Unsupported GraphicsFormat(130) for SetPixel operations.// 通过 baidu, google 搜索得知,可以通过 un-compressed 格式 (比如:RGB(A)16,24,32,64)来避免这个问题// 但是会导致贴图内存增加很多(谨慎使用),因此只能代码中处理这部分的srgb to linearprivate static void GammaSpace_HDR_TexPP_Handler(Cubemap cubemap){var max_val = -1f;for (int faceIDX = 0; faceIDX < CubemapFaceIterateArray.Length; faceIDX++){var face = CubemapFaceIterateArray[faceIDX];// jave.lin : 获取第 0 层 mipmap 的 max valueColor[] colos_mipmap0 = cubemap.GetPixels(face, 0);for (int i = 0; i < colos_mipmap0.Length; i++){var c = colos_mipmap0[i];var temp_max_val = Mathf.Max(c.r, c.g, c.b);if (temp_max_val > max_val){max_val = temp_max_val;}}}Debug.Log($"max_val : {max_val}");if (max_val <= 1.0f){Debug.Log($"max_val <= 1.0f, non-HDR srgb to lienar, max_val : {max_val}");// jave.lin : 将 gamma space 下的 srgb to linearfor (int faceIDX = 0; faceIDX < CubemapFaceIterateArray.Length; faceIDX++){var face = CubemapFaceIterateArray[faceIDX];for (int mipmapIDX = 0; mipmapIDX < cubemap.mipmapCount; mipmapIDX++){Color[] colors_mipmap = cubemap.GetPixels(face, mipmapIDX);for (int i = 0; i < colors_mipmap.Length; i++){colors_mipmap[i] = colors_mipmap[i].linear;}// jave.lin : Unsupported GraphicsFormat(130) for SetPixel operations.cubemap.SetPixels(colors_mipmap, face, mipmapIDX);}}}else{//var assetPath = AssetDatabase.GetAssetPath(cubemap);//Debug.LogWarning($"不是HDR贴图不用处理, assetPath : {assetPath}");// jave.lin : 计算 next power of two (npot)var npot = (float)Mathf.Max(Mathf.NextPowerOfTwo((int)max_val), 1.0f);Debug.Log($"max_val > 1.0f, HDR srgb to lienar, max_val : {max_val}, npot : {npot}");// jave.lin : 将 gamma space 下的 srgb to linearfor (int faceIDX = 0; faceIDX < CubemapFaceIterateArray.Length; faceIDX++){var face = CubemapFaceIterateArray[faceIDX];for (int mipmapIDX = 0; mipmapIDX < cubemap.mipmapCount; mipmapIDX++){Color[] colors_mipmap = cubemap.GetPixels(face, mipmapIDX);for (int i = 0; i < colors_mipmap.Length; i++){var c = colors_mipmap[i];c = new Color(c.r / npot, c.g / npot, c.b / npot, c.a).linear;c *= new Color(npot, npot, npot, 1.0f);colors_mipmap[i] = c;}// jave.lin : Unsupported GraphicsFormat(130) for SetPixel operations.cubemap.SetPixels(colors_mipmap, face, mipmapIDX);}}}}private static void OnPostprocessCubemapEXT(string assetPath, Cubemap cubemap){Debug.Log($"OnPostprocessCubemapEXT.assetPath:{assetPath}");TextureImporter ti = AssetImporter.GetAtPath(assetPath) as TextureImporter;// jave.lin : 修改 readable (这一步风险有点大),会导致 主存、显存 都有一份 内存if (ti.isReadable == false){Debug.Log($"assetPath:{assetPath}, changing readable true");ti.isReadable = true;ti.SaveAndReimport();return;}GammaSpace_HDR_TexPP_Handler(cubemap);}private static void OnPostprocessAllAssets(string[] importedAssets, string[] deletedAssets, string[] movedAssets, string[] movedFromAssetPaths){foreach (var path in importedAssets){if (NeedToRemoveGammaCorrect(path)){Debug.Log($"OnPostprocessAllAssets.assetPath:{path}");//var tex2D = AssetDatabase.LoadAssetAtPath<Texture2D>(path);//var tex = AssetDatabase.LoadAssetAtPath<Texture>(path);var cubemap = AssetDatabase.LoadAssetAtPath<Cubemap>(path);// jave.lin : 下面输出:/*imported asset path: Assets/Scene/UiEffectScene/ReflectionProbe-0.exr, tex2D : , tex :ReflectionProbe-0 (UnityEngine.Cubemap), cubemap: ReflectionProbe-0 (UnityEngine.Cubemap)UnityEngine.Debug:Log(object)*///Debug.Log($"imported asset path: {path}, tex2D : {tex2D}, tex :{tex}, cubemap: {cubemap}");if (cubemap == null) continue;OnPostprocessCubemapEXT(path, cubemap);}}}

其实上面的代码判断 是否有分量 > 1.0f 的方式来判断是否 HDR 是不太合理的,因为不同的贴图格式的编码方式不同

有一些编码比如,RGBM,使用 A 通道来保存 255 被缩放的数值,作为: color_normalized * pow(2, A_channel_normalized * 255) 来解码

百度,谷歌上也没有搜索到对应的回答,唯一搜索到类似的:unity报错篇-Unsupported texture format - needs to be ARGB32。。。。

如果 使用了 带压缩格式的,然后再使用 Cubemap.SetPixels 都会报这个错误
在这里插入图片描述

注意压缩后大小非常小,才 288B 字节 (我这个是测试用的纹理)
在这里插入图片描述

然后我们将其格式修改成 未压缩 格式,就没有这个报错了
在这里插入图片描述

但是大小会比原来的大4倍
在这里插入图片描述

本身H5里面的内存就是很珍贵的设备资源,因此这种方式不可取
那么只能牺牲一些性能,在 shader 代码中采样处理了
比如: skybox对cubemap的处理,或是 reflection probe 等 IBL 反射效果 的 颜色的 pow(val, 2.2) 的处理


Project

  • Testing_Recovery_Linear_shading_in_UnityGammaSpace_2020.3.37f1_BRP.rar - 里面带有一些 逆向学习用的资源,不能公开
  • Testing_Recovery_Linear_shading_in_UnityGammaSpace_2020.3.37f1_BRP_V2.rar - 同上

References

  • gamma下还原linear效果

相关文章:

Unity - gamma space下还原linear space效果

文章目录 环境目的环境问题实践结果处理要点处理细节【OnPostProcessTexture 实现 sRGB 2 Linear 编码】 - 预处理【封装个简单的 *.cginc】 - shader runtime【shader需要gamma space下还原记得 #define _RECOVERY_LINEAR_IN_GAMMA】【颜色参数应用前 和 颜色贴图采样后】【灯…...

Rabbitmq调用FeignClient接口失败

文章目录 一、框架及逻辑介绍1.背景服务介绍2.问题逻辑介绍 二、代码1.A服务2.B服务3.C服务 三、解决思路1.确认B调用C服务接口是否能正常调通2.确认B服务是否能正常调用A服务3.确认消息能否正常消费4.总结 四、修改代码验证1.B服务异步调用C服务接口——失败2.将消费消息放到C…...

专业120+总分400+海南大学838信号与系统考研高分经验海大电子信息与通信

今年专业838信号与系统120&#xff0c;总分400&#xff0c;顺利上岸海南大学&#xff0c;这一年的复习起起伏伏&#xff0c;但是最后还是坚持下来的&#xff0c;吃过的苦都是值得&#xff0c;总结一下自己的复习经历&#xff0c;希望对大家复习有帮助。首先我想先强调一下专业课…...

如何区分 html 和 html5?

HTML&#xff08;超文本标记语言&#xff09;和HTML5在很多方面都存在显著的区别。HTML5是HTML的最新版本&#xff0c;引入了许多新的特性和元素&#xff0c;以支持更丰富的网页内容和更复杂的交互。以下是一些区分HTML和HTML5的关键点&#xff1a; 新特性与元素&#xff1a;H…...

Ps:将文件载入堆栈

Ps菜单&#xff1a;文件/脚本/将文件载入堆栈 Scripts/Load Files into Stack 将文件载入堆栈 Load Files into Stack脚本命令可用于将两个及以上的文件载入到同一个 Photoshop 新文档中。 载入的每个文件都将成为独立的图层&#xff0c;并使用其原始文件名作为图层名。 Photos…...

【格密码基础】:补充LWE问题

目录 一. LWE问题的鲁棒性 二. LWE其他分布选择 三. 推荐文献 四. 附密码学人心中的顶会 一. LWE问题的鲁棒性 robustness&#xff0c;翻译为鲁棒性 已有的论文表明&#xff0c;及时敌手获取到部分关于秘密和error的信息&#xff0c;LWE问题依旧是困难的&#xff0c;这能…...

【C++入门到精通】特殊类的设计 |只能在堆 ( 栈 ) 上创建对象的类 |禁止拷贝和继承的类 [ C++入门 ]

阅读导航 引言一、特殊类 --- 不能被拷贝的类1. C98方式&#xff1a;2. C11方式&#xff1a; 二、特殊类 --- 只能在堆上创建对象的类三、特殊类 --- 只能在栈上创建对象的类四、特殊类 --- 不能被继承的类1. C98方式2. C11方法 总结温馨提示 引言 在面向对象编程中&#xff0…...

VMware虚拟机部署Linux Ubuntu系统

本文介绍基于VMware Workstation Pro虚拟机软件&#xff0c;配置Linux Ubuntu操作系统环境的方法。 首先&#xff0c;我们需要进行VMware Workstation Pro虚拟机软件的下载与安装。需要注意的是&#xff0c;VMware Workstation Pro软件是一个收费软件&#xff0c;而互联网中有很…...

RFID标签:数字时代的智能身份

在数字时代&#xff0c;RFID标签&#xff08;Radio-Frequency Identification&#xff09;成为物联网&#xff08;IoT&#xff09;中不可或缺的一环。作为一种小巧却功能强大的设备&#xff0c;RFID标签在各个领域的应用不断扩展&#xff0c;为我们的生活和工作带来了新的可能性…...

《动手学深度学习(PyTorch版)》笔记3.2

注&#xff1a;书中对代码的讲解并不详细&#xff0c;本文对很多细节做了详细注释。另外&#xff0c;书上的源代码是在Jupyter Notebook上运行的&#xff0c;较为分散&#xff0c;本文将代码集中起来&#xff0c;并加以完善&#xff0c;全部用vscode在python 3.9.18下测试通过。…...

elasticsearch8.x版本docker部署说明

前提&#xff0c;当前部署没有涉及证书和https访问 1、环境说明,我采用三个节点&#xff0c;每个节点启动两个es&#xff0c;用端口区分 主机角色ip和端口服务器Amaster192.168.2.223:9200服务器Adata192.168.2.223:9201服务器Bdata,master192.168.2.224:9200服务器Bdata192.1…...

使用scyllaDb 或者cassandra存储聊天记录

一、使用scyllaDb的原因 目前开源的聊天软件主要还是使用mysql存储数据&#xff0c;数据量大的时候比较麻烦&#xff1b; 我打算使用scyllaDB存储用户的聊天记录&#xff0c;主要考虑的优点是&#xff1a; 1&#xff09;方便后期线性扩展服务器&#xff1b; 2&#xff09;p…...

Visual Studio如何修改成英文版

1、打开 Visual Studio Installer 2、点击修改 3、找到语言包&#xff0c;选择需要的语言包&#xff0c;而后点击修改 4、等待下载 5、 安装完成后启动Visual Studio 6、在工具-->选项-->环境-->区域设置-->English并确定 7、重启 Visual Studio&#xff0c;配置…...

gin中使用swagger生成接口文档

想要使用gin-swagger为你的代码自动生成接口文档&#xff0c;一般需要下面三个步骤&#xff1a; 按照swagger要求给接口代码添加声明式注释&#xff0c;具体参照声明式注释格式。使用swag工具扫描代码自动生成API接口文档数据使用gin-swagger渲染在线接口文档页面 第一步&…...

最新AI创作系统ChatGPT网站系统源码,Midjourney绘画V6 ALPHA绘画模型,ChatFile文档对话总结+DALL-E3文生图

一、前言 SparkAi创作系统是基于ChatGPT进行开发的Ai智能问答系统和Midjourney绘画系统&#xff0c;支持OpenAI-GPT全模型国内AI全模型。本期针对源码系统整体测试下来非常完美&#xff0c;那么如何搭建部署AI创作ChatGPT&#xff1f;小编这里写一个详细图文教程吧。已支持GPT…...

解析dapp:从底层区块链看DApp的脆弱性和挑战

每天五分钟讲解一个互联网只是&#xff0c;大家好我是啊浩说模式Zeropan_HH 在Web3时代&#xff0c;去中心化应用程序&#xff08;DApps&#xff09;已成为数字经济的重要组成部分。它们的同生性&#xff0c;即与底层区块链网络紧密相连、共存亡的特性&#xff0c;为DApps带来…...

机器学习整理

绪论 什么是机器学习&#xff1f; 机器学习研究能够从经验中自动提升自身性能的计算机算法。 机器学习经历了哪几个阶段&#xff1f; 推理期&#xff1a;赋予机器逻辑推理能力 知识期&#xff1a;使机器拥有知识 学习期&#xff1a;让机器自己学习 什么是有监督学习和无监…...

RISC-V常用汇编指令

RISC-V寄存器表&#xff1a; RISC-V和常用的x86汇编语言存在许多的不同之处&#xff0c;下面将列出其中部分指令作用&#xff1a; 指令语法描述addiaddi rd,rs1,imm将寄存器rs1的值与立即数imm相加并存入寄存器rdldld t0, 0(t1)将t1的值加上0,将这个值作为地址&#xff0c;取…...

第二篇:数据结构与算法-链表

概念 链表是线性表的链式存储方式&#xff0c;逻辑上相邻的数据在计算机内的存储位置不必须相邻&#xff0c; 可以给每个元素附加一个指针域&#xff0c;指向下一个元素的存储位 置。 每个结点包含两个域&#xff1a;数据域和指针域&#xff0c;指针域存储下一个结点的地址&…...

低代码配置-小程序配置

数据结构 {"data": {"layout": {"api":{"pageApi":{//api详情}},"config":{"title":"页面标题"&#xff0c;},"listLayout": {"fields": [{"componentCode": "grid…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级

在互联网的快速发展中&#xff0c;高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司&#xff0c;近期做出了一个重大技术决策&#xff1a;弃用长期使用的 Nginx&#xff0c;转而采用其内部开发…...

实现弹窗随键盘上移居中

实现弹窗随键盘上移的核心思路 在Android中&#xff0c;可以通过监听键盘的显示和隐藏事件&#xff0c;动态调整弹窗的位置。关键点在于获取键盘高度&#xff0c;并计算剩余屏幕空间以重新定位弹窗。 // 在Activity或Fragment中设置键盘监听 val rootView findViewById<V…...

selenium学习实战【Python爬虫】

selenium学习实战【Python爬虫】 文章目录 selenium学习实战【Python爬虫】一、声明二、学习目标三、安装依赖3.1 安装selenium库3.2 安装浏览器驱动3.2.1 查看Edge版本3.2.2 驱动安装 四、代码讲解4.1 配置浏览器4.2 加载更多4.3 寻找内容4.4 完整代码 五、报告文件爬取5.1 提…...

BLEU评分:机器翻译质量评估的黄金标准

BLEU评分&#xff1a;机器翻译质量评估的黄金标准 1. 引言 在自然语言处理(NLP)领域&#xff0c;衡量一个机器翻译模型的性能至关重要。BLEU (Bilingual Evaluation Understudy) 作为一种自动化评估指标&#xff0c;自2002年由IBM的Kishore Papineni等人提出以来&#xff0c;…...

加密通信 + 行为分析:运营商行业安全防御体系重构

在数字经济蓬勃发展的时代&#xff0c;运营商作为信息通信网络的核心枢纽&#xff0c;承载着海量用户数据与关键业务传输&#xff0c;其安全防御体系的可靠性直接关乎国家安全、社会稳定与企业发展。随着网络攻击手段的不断升级&#xff0c;传统安全防护体系逐渐暴露出局限性&a…...

Xcode 16.2 版本 pod init 报错

Xcode 版本升级到 16.2 后&#xff0c;项目执行 pod init 报错&#xff1b; ### Error RuntimeError - PBXGroup attempted to initialize an object with unknown ISA PBXFileSystemSynchronizedRootGroup from attributes: {"isa">"PBXFileSystemSynchron…...

FTPS、HTTPS、SMTPS以及WebSockets over TLS的概念及其应用场景

一、什么是FTPS&#xff1f; FTPS&#xff0c;英文全称File Transfer Protocol with support for Transport Layer Security (SSL/TLS)&#xff0c;安全文件传输协议&#xff0c;是一种对常用的文件传输协议(FTP)添加传输层安全(TLS)和安全套接层(SSL)加密协议支持的扩展协议。…...

Python网页自动化测试,DrissonPage库入门说明文档

&#x1f6f0;️ 基本逻辑​ 操作浏览器的基本逻辑如下&#xff1a; 创建浏览器对象&#xff0c;用于启动或接管浏览器获取一个 Tab 对象使用 Tab 对象访问网址使用 Tab 对象获取标签页内需要的元素对象使用元素对象进行交互 除此以外&#xff0c;还能执行更为复杂的操作&am…...

RMQ 算法详解(区间最值问题)

RMQ 算法详解&#xff08;区间最值问题&#xff09; 问题介绍解决方法暴力法ST表法基本思想算法步骤C实现 问题介绍 RMQ问题是OI中经常遇到的问题&#xff0c;主要是一下形式&#xff1a; 给你一堆数&#xff0c;不断的对里面的数进行操作&#xff0c;例如&#xff1a;让某个…...