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效果

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/653160.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【UE5】如何给人物骨骼绑定Control Rig用来制作动画(控制)

本篇文章暂时只教绑定人物手部的Control Rig&#xff0c;脚的Control Rig举一反三即可 1&#xff0c;右键-创建-控制绑定 2在控制绑定中-右键创建基本IK 3&#xff0c;填入上臂-下臂-手 4【手和下臂】右键-新建-Add Controls For Selected&#xff0c;&#xff08;或者新建-…

第十四章 RabbitMQ应用

文章目录 前言1、RabbitMQ概念1.1、生产者和消费者1.2、队列1.3、交换机、路由键、绑定1.3.1、交换机类型 2、RabbitMQ运转流程2.1、生产者发送消息流程2.2、消费者接收消息的过程2.3、AMQP协议 3、RabbitMQ windows安装3.1、下载3.2、安装 4、Spring Boot 整合RabbitMQ4.1、在…

【语录】岁月

中年 写中年&#xff0c;应该是年少励志三千里 踌躇百步无寸功&#xff0c;转眼高堂已白发 儿女蹒跚学堂中&#xff0c;不如意事常八九&#xff0c;可与人言无二三 可是诸位&#xff0c;不用悲伤&#xff0c;稻盛和夫说&#xff0c; 人生并不是一场物质的盛宴&#xff0c;而是…

单片机学习笔记---LCD1602调试工具

LCD1602调试工具 这一节开始之前先说明一下&#xff0c;模块化编程相关的知识&#xff08;就是将代码分成多个文件来写&#xff0c;比如函数的定义放在.c文件中&#xff0c;函数的声明写在.h文件中&#xff09;属于是C语言的内容&#xff0c;学过C语言的伙伴应该都知道。由于这…

Vue2.0+Element实现日历组件

(壹)博主介绍 &#x1f320;个人博客&#xff1a; 尔滨三皮⌛程序寄语&#xff1a;木秀于林&#xff0c;风必摧之&#xff1b;行高于人&#xff0c;众必非之。 (贰)文章内容 1、安装依赖 npm install moment2.29.4 --savenpm install lunar0.0.3 --savenpm install lunar-java…

配置华为交换机生成树VBST案例

知识改变命运&#xff0c;技术就是要分享&#xff0c;有问题随时联系&#xff0c;免费答疑&#xff0c;欢迎联系 厦门微思网络​​​​​​https://www.xmws.cn 华为认证\华为HCIA-Datacom\华为HCIP-Datacom\华为HCIE-Datacom 思科认证CCNA\CCNP\CCIE 红帽认证Linux\RHCE\RHC…

代理IP使用指南:风险与注意事项

在当今的数字化时代&#xff0c;使用在线代理IP已经成为一种常见的网络行为。然而&#xff0c;在使用这些代理IP时&#xff0c;我们需要注意一些风险和问题&#xff0c;以确保我们的网络安全和隐本私文。将探讨使用代理IP时需要注意的几个关键问题。 1、代理IP的安全性 使用代理…

设计模式:工厂方法模式

工厂模式属于创建型模式&#xff0c;也被称为多态工厂模式&#xff0c;它在创建对象时提供了一种封装机制&#xff0c;将实际创建对象的代码与使用代码分离&#xff0c;有子类决定要实例化的产品是哪一个&#xff0c;把产品的实例化推迟到子类。 使用场景 重复代码 : 创建对象…

OpenAI ChatGPT-4开发笔记2024-07:Embedding之Text Similarity文本相似度

语义相似性semantic similarity 背景结果 背景 OpenAI has made waves online with its innovative embedding and transcription models, leading to breakthroughs in NLP and speech recognition. These models enhance accuracy, efficiency, and flexibility while speed…

算法每日一题: 边权重均等查询 | 公共子祖先

大家好&#xff0c;我是星恒&#xff0c;今天给大家带来的是一道图里面有关公共子祖先的题目&#xff0c;理解起来简单&#xff0c;大家 题目&#xff1a;leetcode 2846 现有一棵由 n 个节点组成的无向树&#xff0c;节点按从 0 到 n - 1 编号。给你一个整数 n 和一个长度为 n …

聊聊大模型 RAG 探索之路的血泪史,一周出Demo,半年用不好

大家好&#xff0c;今天我们来继续看看 RAG 落地的一些有趣的事儿&#xff0c;从技术社群早上的讨论开始&#xff0c;喜欢技术交流的可以文末加入我们 一、从一周出Demo、半年用不好说起 最近读了读2024-傅盛开年AI大课&#xff0c;其中有讲到RAG环节&#xff0c;三张片子比较…

2023.1.23 关于 Redis 哨兵模式详解

目录 引言 人工恢复主节点故障 ​编辑 主从 哨兵模式 Docker 模拟部署哨兵模式 关于端口映射 展现哨兵机制 哨兵重新选取主节点的流程 哨兵模式注意事项 引言 人工恢复主节点故障 1、正常情况 2、主节点宕机 3、程序员主动恢复 先看看该主节点还能不能抢救如果不好定…

统一异常处理

统一异常处理 统一异常处理创建一个类定义方法ControllerAdvice和ExceptionHandler注意事项 统一异常处理 创建一个类 首先,我们来创建一个类,名字随意,这里我们取名ERHandler 定义方法 在ERHandler中,我们可以定义几个类,参数用来接收各种异常,这里的异常可以是任意的,返回…

面试官:你可以说一说你对Jmeter元素的理解吗?下

面试官&#xff1a;你可以说一说你对Jmeter元素的理解吗&#xff1f;下 监听器配置元素CSV数据集配置HTTPCookie管理器HTTP请求默认值登录配置元素 监听器 Listeners&#xff1a;显示测试执行的结果。它们可以以不同的格式显示结果&#xff0c;例如树、表、图形或日志文件 图…

LLM大语言模型(五):用streamlit开发LLM应用

目录 背景准备工作切记streamlit开发LLM demo开一个新页面初始化session先渲染历史消息接收用户输入模拟调用LLM 参考 背景 Streamlit是一个开源Python库&#xff0c;可以轻松创建和共享用于机器学习和数据科学的漂亮的自定义web应用程序&#xff0c;用户可以在几分钟内构建一…

DNS欺骗

DNS(域名系统)作为当前全球最大 、最复杂的分布式层次数据库系统&#xff0c;具有着开放、庞大、复杂的特性。它为全球用户提供域名解析服务&#xff0c;是互联网的重要基础设施。但由于其在设计之初未考虑安全性、人为破坏等因素 &#xff0c;DNS系统在互联网高度发达的今天面…

Ubuntu下APT下载工具(Ubuntu 下最常用的下载和安装软件方法)

前言 本篇文章记录我学习Ubuntu 下用的最多的下载工具&#xff1a; APT 下载工具&#xff0c; APT 下载工具可以实现软件自动下载、配置、安装二进制或者源码的功能。 APT 下载工具和我们前面一篇文章讲解的“install”命令结合在一起构成了 Ubuntu 下最常用的下载和安装软件方…

【代码随想录】LC 242. 有效的字母异位词

文章目录 前言一、题目1、原题链接2、题目描述 二、解题报告1、思路分析2、时间复杂度3、代码详解 前言 本专栏文章为《代码随想录》书籍的刷题题解以及读书笔记&#xff0c;如有侵权&#xff0c;立即删除。 一、题目 1、原题链接 242. 有效的字母异位词 2、题目描述 二、解题…

刘知远团队大模型技术与交叉应用L5-BMSystem

为什么需要BMTrain&#xff1f; PLM越来越大。为了使训练更高效和廉价。我们有必要 1.分析GPU的显存去哪了&#xff1f; 2.理解GPU间的合作模式是如何的&#xff1f; 显存都去了哪里&#xff1f; CPU vs GPU CPU适合复杂逻辑运算。GPU适合大量重复的数值运算。 显存成分 1.前…

csp----寻宝!大冒险!

题目描述&#xff1a; AC代码如下&#xff1a; /*思路&#xff1a; 把A变成小块 因为B是A里的一部分 通过把A变成小块 去寻找B这样速度更快 如果AB,BA&#xff0c;说明找到了。 */#include <iostream> #include <cstring> #include <algorithm> #include …