在游戏制作后期,项目如果需要风格化校色会设置一个全局优先级最高的volume组件用于应用校色
勤快的项目组一般会准备多种预调色配置,调整各种校色组件及参数
懒一些的则会从网上直接下载成套校色图,替换color lookup组件中的素材
那么继续说懒的方式,如果需要有一两个特殊定制的风格效果时,下载的素材就体现出了不可定制性
我们可以通过unity导出lookup table,导入到各种校色工具中校色,再输出回unity的做法来实现
C#
public class ExportTEST : EditorWindow
{
public enum TextureType
{
png = 0,
exr = 1
}
private string _filePath = "...";
private Material _mat;
private TextureType _textureType = TextureType.png;
[MenuItem("*TEST*/导出场景HDR格式图片")]
private static void Init()
{
ExportHDR window = GetWindowWithRect<ExportHDR>(new Rect(0,0,300,150));
window.titleContent = new GUIContent("导出HDR格式场景截图");
window.Show();
}
private void OnGUI()
{
GUILayout.Label("保存到:" + _filePath);
if (GUILayout.Button("保存位置"))
{
_filePath = EditorUtility.OpenFolderPanel("", "", "");
}
_textureType = (TextureType)EditorGUILayout.EnumPopup("选择LUT保存格式",_textureType);
if (GUILayout.Button("生成原始LUT"))
{
try
{
CreateLUTBase();
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
if (GUILayout.Button("生成原始场景截图(tga)"))
{
try
{
CreateSceneBase();
}
catch (Exception e)
{
Console.WriteLine(e);
throw;
}
}
}
//我们在打开其他窗口时,焦点不在game视口,获得的screen宽高是激活窗口的
public static Vector2 GetMainGameViewSize()
{
//通过反射获取game宽高
System.Type T = System.Type.GetType("UnityEditor.GameView,UnityEditor");
System.Reflection.MethodInfo GetSizeOfMainGameView = T.GetMethod("GetSizeOfMainGameView",System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Static);
System.Object Res = GetSizeOfMainGameView.Invoke(null,null);
return (Vector2)Res;
}
void CreateSceneBase(out string state)
{
var path = _filePath + "/SceneColor_Base.tga";
if(File.Exists(path)) File.Delete(path);
if (_mat == null)
{
_mat = CoreUtils.CreateEngineMaterial("Hidden/ExportRT");
}
Vector2 size = GetMainGameViewSize();
//这里只是设置图的宽高,实际图像尺寸需要在pipeline设置opaque图压缩质量
RenderTexture rt = new RenderTexture((int)size.x, (int)size.y, 0);
rt.format = RenderTextureFormat.ARGBFloat;
rt.enableRandomWrite = true;
rt.wrapMode = TextureWrapMode.Clamp;
rt.Create();
Graphics.Blit(null, rt, _mat);
var originalRT = RenderTexture.active;
RenderTexture.active = rt;
var tex = new Texture2D(rt.width, rt.height, TextureFormat.ARGB32, false);
tex.ReadPixels(new Rect(0, 0, rt.width, rt.height), 0, 0);
tex.Apply();
var bytes = tex.EncodeToTGA(EncodeToTGAExtension.Compression.None);
if(File.Exists(path)) File.Delete(path);
File.WriteAllBytes(path, bytes);
DestroyImmediate(tex);
rt.Release();
RenderTexture.active = originalRT;
}
void CreateLUTBase(out string state)
{
var path = _filePath;
if(File.Exists(path)) File.Delete(path);
//这里根据pipeline设置给参数,ldr是1024x32
var tex = new Texture2D(1024,32, TextureFormat.RGBAFloat, false);
tex.wrapMode = TextureWrapMode.Clamp;
tex.filterMode = FilterMode.Point;
var colors = new Color[1024,32];
for (var b = 0; b < 32; b++)
{
for (var g = 0; g < 32; g++)
{
for (var r = 0; r < 32; r++)
{
colors[r + b * 32, g] = new Color(r/32f,g/32f,b/32f);
}
}
}
for (var h = 0; h < 1024; h++)
{
for (var v = 0; v < 32; v++)
{
tex.SetPixel(h, v, colors[h, v]) ;
}
}
tex.Apply();
byte[] bytes;
switch (_textureType)
{
case TextureType.png:
bytes=tex.EncodeToPNG();
path = path +"/UnityLUT_Base.png";
if(File.Exists(path)) File.Delete(path);
File.WriteAllBytes(path,bytes);
break;
case TextureType.exr:
bytes=tex.EncodeToEXR(Texture2D.EXRFlags.None);
path = path +"/UnityLUT_Base.exr";
if(File.Exists(path)) File.Delete(path);
File.WriteAllBytes(path,bytes);
break;
}
DestroyImmediate(tex);
}
}
Shader
Shader "Hidden/ExportRT"
{
Properties
{
}
SubShader
{
Tags { "Queue" = "Transparent" }
LOD 100
Pass
{
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;
};
sampler2D_float _CameraOpaqueTexture;
//sampler2D_float _MainTex;
float3 LinearToSRGB(float3 c)
{
float3 sRGBLo = c * 12.92;
float3 sRGBHi = (pow(abs(c), float3(1.0/2.4, 1.0/2.4, 1.0/2.4)) * 1.055) - 0.055;
float3 sRGB = (c <= 0.0031308) ? sRGBLo : sRGBHi;
return sRGB;
}
half3 SRGBToLinear(half3 c)
{
half3 linearRGBLo = c / 12.92;
half3 linearRGBHi = pow(abs((c + 0.055) / 1.055), 2.4);
half3 linearRGB = (c <= 0.04045) ? linearRGBLo : linearRGBHi;
return linearRGB;
}
v2f vert (appdata v)
{
v2f o;
o.vertex = UnityObjectToClipPos(v.vertex);
o.uv = v.uv;
return o;
}
float4 frag (v2f i) : SV_Target
{
// sample the texture
float4 col = tex2D(_CameraOpaqueTexture, i.uv);
//opaque图为linear状态,输出到图需要转sRGB,本身color lookup回读的也是sRGB图
return float4(LinearToSRGB(col.rgb), col.a);
}
ENDCG
}
}
}