解决Lightmap在PC上与ios和Android上表现不同的问题

转自:unity圣典


1、  问题描述 

相信很多人碰到过 Lightmap 的一些问题:  
烘培好 Lightmap 之后,在 PC 上看起来相当给力,而打包成 ios android 之后,就傻眼了, Lightmap 往往就出现了改变,例如灯光曝光度不够、光照颜色偏冷色调、有时候甚至黄色光也能变成绿色光等等。  
2、 造成LightmapPCiosandroid上表现不同的原因  
u3d 里, Lightmap 的格式是 .exr(openEXR) exr 格式的储存方式是使用4*16Bit RGBA来储存数据的,即是说,它使用四个通道分别为RGB和alpha,每个通道占16位储存空间,每个像素占48位储存空间来储存数据。所以EXR格式的图片颜色值域范围就达到了[-65504,65504],远大于8bit(颜色值域[0,255],用浮点数表示就是[0,1])格式所能储存的数值范围。  
但是EXR格式的Lightmap打包成 android或ios之后,就变成LDR格式(可能是单通道8bit)的了,这就丢失的很多光照信息。例如在PC上烘培出的灯光亮度值是2000,转变成LDR格式后,亮度值就变成了255,这就是为什么打包成android或ios后 灯光曝光度不够、光照颜色偏冷色调的原因。  
3、 解决方法。  
HDR 有几种比较常见的编码格式,这里用的编码格式是LogLuv,就是将RGBA_FP32bit(每个通道32bit或16bit)的数据编码到RGBA32(每个通道8bit)的图片中。LogLuv算法几乎能100%还原unity切换平台后导致Lightmap丢掉的精度信息,算法如下:
    //FP32(RGB) to LogLUV
   fixed4 EncodeLogLuv(fixed3 vRGB)
   {
       fixed3x3 M = fixed3x3(
           0.2209, 0.3390, 0.4184,
           0.1138, 0.6780, 0.7319,
           0.0102, 0.1130, 0.2969 );
       fixed4 vResult;
       fixed3 Xp_Y_XYZp = mul(vRGB, 
M);
       Xp_Y_XYZp = max(Xp_Y_XYZp, 
fixed3(1e-6, 1e-6, 1e-6));
       vResult.xy = Xp_Y_XYZp.xy / 
Xp_Y_XYZp.z;
       fixed Le = 2 * 
log2(Xp_Y_XYZp.y) + 127;
       vResult.w = frac(Le);
       vResult.z = (Le - 
(floor(vResult.w*255.0f)) / 255.0f) / 255.0f;
        return  vResult;

}
//LogLuv to FP32(RGB)
   fixed3 DecodeLogLuv( in  fixed4 vLogLuv)
   {
       fixed3x3 InverseM = fixed3x3(
          6.0014, -2.7008, -1.7996,
          -1.3320, 3.1029, -5.7721,
          0.3008, -1.0882, 5.6268 );
       fixed Le = vLogLuv.z * 255 + 
vLogLuv.w;
       fixed3 Xp_Y_XYZp;
       Xp_Y_XYZp.y = exp2((Le - 127) 
/ 2);
       Xp_Y_XYZp.z = Xp_Y_XYZp.y / 
vLogLuv.y;
       Xp_Y_XYZp.x = vLogLuv.x * 
Xp_Y_XYZp.z;
       fixed3 vRGB = mul(Xp_Y_XYZp, 
InverseM);

return  max(vRGB, 0);  
}  
4、 实现步骤  
1、  PC 平台烘培好光照贴图 Lightmap_PC(.exr 格式 )  
2、  通过 Lightmap_PC 生成新的光照贴图 Lightmap_LogLuv(.png 格式 ,RGBA32bit ,单通道是 8bit) 。生成过程就用上面提到的 LogLuv 算法进行编码。这里用 GPU 来进行生成,有类似 GPGPU 的思想。如下:  
 
// LogLuv 算法生成的 Lightmap 输出到 temp 上,然后就将 temp 保存到一张 .png 格式的图片。
        public   bool  SaveRenderTextureToPNG( Texture  inputTex, Shader  outputShader,  string  contents,  string  pngName)
{
              RenderTexture  temp =  RenderTexture .GetTemporary(inputTex.width, inputTex.height, 0,  RenderTextureFormat .ARGB32);
              Material  mat =  new   Material (outputShader);
              Graphics .Blit(inputTex, temp, mat);
              bool  ret = SaveRenderTextureToPNG(temp, contents,pngName);
              RenderTexture .ReleaseTemporary(temp);
              return  ret;

       }  
        
//
RenderTexture 保存成一张 png 图片
          public   bool  SaveRenderTextureToPNG( RenderTexture  rt, string  contents,  string  pngName)
         {
               RenderTexture  prev =  RenderTexture .active;
               RenderTexture .active = rt;
               Texture2D  png =  new   Texture2D (rt.width, rt.height,  TextureFormat .ARGB32,  false );
              png.ReadPixels( new   Rect (0, 0, rt.width, rt.height), 0, 0);
               byte [] bytes = png.EncodeToPNG();
               if  (! Directory .Exists(contents))
                    Directory .CreateDirectory(contents);
               FileStream  file =  File .Open(contents +  "/"  + pngName +  ".png" FileMode .Create);
               BinaryWriter  writer =  new   BinaryWriter (file);
              writer.Write(bytes);
              file.Close();
               Texture2D .DestroyImmediate(png);
              png =  null ;
               RenderTexture .active = prev;
               return   true ;

         }  
3、  用新生成的光照贴图 Lightmap_LogLuv 替换原来的光照贴图 Lightmap_PC 。因为 Lightmap_PC 在切换 android ios 会自动将 Lightmap_PC 压缩为 LDR 格式,这就丢失了精度。  

5、编写生成工具

我已经写好了一个简单的工具,可以将当前的场景下所有用 Diffuse Shader 的模型自动替换成新的 Shader ,改新的 Shader 将不再使用 Unity 原来 .EXR 格式的光照贴图,而是使用新生成的光照贴图。  
 

将会在   Assets/Data/Lightmap/ 当前场景名字 目录下生成对应的光照贴图,如下:  
 

 

将当前的场景下所有用Diffuse Shader的模型自动替换成新的Shader,如下:



代码请参考原文

转自:unity圣典 

------------------------ 分割线 ---------------------------

由于平台位数的差异,即便转换了格式,还是会出现差别,我们最后的方案是,降低单个光源的强度。

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值