Unity Shader Color、HSV、HDRColor以及HDRColor面板转换源码分析

RGB颜色

这个就是祖传手艺了,RGB三原色,不多说。他的应该是这样的,没错一个正方体在这里插入图片描述

HSV颜色简介

作为一个程序员,对于 颜色的理解就是RGBA,但是当我们跟美术大大进行沟通时,他们老是给整什么饱和度,色相之类的外星话。我们给他说你通过调节RGB的值做效果,大眼瞪小眼了。
其实对于美术而言他们更熟悉的HSV(也叫HSB)模型:
H 为 色相(hue):取值范围0-360, 在0~360°的标准色轮上,色相是按位置度量的。在通常的使用中,色相是由颜色名称标识的,比如红、绿或橙色。黑色和白色无色相。
S为饱和度(saturation):取值范围0-1(或者0-100百分制),表示色彩的纯度,为0时为灰色。白、黑和其他灰色色彩都没有饱和度的。在最大饱和度时,每一色相具有最纯的色光。取值范围0~100%。
V 为 亮度(value):取值范围0-1(或者0-100百分制),是色彩的明亮度。为0时即为黑色。最大亮度是色彩最鲜明的状态。取值范围0~100%。
在Unity中他长这个样子:在这里插入图片描述
如果用数学模型去表示HSV的话,他大概是这个样子:
在这里插入图片描述
啊?按照上面说的HSV的取值范围不应该是一个圆柱体么?怎么是个倒三角?我只能强行理解一波就是亮度越大我们能识别的颜色范围越大,亮度为0可识别的也就只有黑色。(也不知道这么想对不对。。)

RGB与HSV的转换

这个转换的版本有点多,这里就直接拿Unity的转换了,UnityEngine.Color类

RGB TO HSV

// UnityEngine.Color
public static void RGBToHSV(Color rgbColor, out float H, out float S, out float V)
{
	if (rgbColor.b > rgbColor.g && rgbColor.b > rgbColor.r)
	{
		Color.RGBToHSVHelper(4f, rgbColor.b, rgbColor.r, rgbColor.g, out H, out S, out V);
	}
	else if (rgbColor.g > rgbColor.r)
	{
		Color.RGBToHSVHelper(2f, rgbColor.g, rgbColor.b, rgbColor.r, out H, out S, out V);
	}
	else
	{
		Color.RGBToHSVHelper(0f, rgbColor.r, rgbColor.g, rgbColor.b, out H, out S, out V);
	}
}
private static void RGBToHSVHelper(float offset, float dominantcolor, float colorone, float colortwo, out float H, out float S, out float V)
{
	V = dominantcolor;
	if (V != 0f)
	{
		float num;
		if (colorone > colortwo)
		{
			num = colortwo;
		}
		else
		{
			num = colorone;
		}
		float num2 = V - num;
		if (num2 != 0f)
		{
			S = num2 / V;
			H = offset + (colorone - colortwo) / num2;
		}
		else
		{
			S = 0f;
			H = offset + (colorone - colortwo);
		}
		H /= 6f;
		if (H < 0f)
		{
			H += 1f;
		}
	}
	else
	{
		S = 0f;
		H = 0f;
	}
}

HSV TO RGB

public static Color HSVToRGB(float H, float S, float V, bool hdr)
{
	Color white = Color.white;
	if (S == 0f)
	{
		white.r = V;
		white.g = V;
		white.b = V;
	}
	else if (V == 0f)
	{
		white.r = 0f;
		white.g = 0f;
		white.b = 0f;
	}
	else
	{
		white.r = 0f;
		white.g = 0f;
		white.b = 0f;
		float num = H * 6f;
		int num2 = (int)Mathf.Floor(num);
		float num3 = num - (float)num2;
		float num4 = V * (1f - S);
		float num5 = V * (1f - S * num3);
		float num6 = V * (1f - S * (1f - num3));
		switch (num2 + 1)
		{
		case 0:
			white.r = V;
			white.g = num4;
			white.b = num5;
			break;
		case 1:
			white.r = V;
			white.g = num6;
			white.b = num4;
			break;
		case 2:
			white.r = num5;
			white.g = V;
			white.b = num4;
			break;
		case 3:
			white.r = num4;
			white.g = V;
			white.b = num6;
			break;
		case 4:
			white.r = num4;
			white.g = num5;
			white.b = V;
			break;
		case 5:
			white.r = num6;
			white.g = num4;
			white.b = V;
			break;
		case 6:
			white.r = V;
			white.g = num4;
			white.b = num5;
			break;
		case 7:
			white.r = V;
			white.g = num6;
			white.b = num4;
			break;
		}
		if (!hdr)
		{
			white.r = Mathf.Clamp(white.r, 0f, 1f);
			white.g = Mathf.Clamp(white.g, 0f, 1f);
			white.b = Mathf.Clamp(white.b, 0f, 1f);
		}
	}
	return white;
}

都是代码也没啥可以说的,还以其他的算法

HDR

HDRColor面板的疑惑

随着手机性能越来越强,现在HDR几乎已经成了游戏的标配了。扯到HDR就要有一点点Gamma和Linear的转换,具体什么是Gamma和Linear就不细说了。先来说一个当初一直让我苦恼的问题,先看情况1:在这里插入图片描述
一切看上去是那么的祥和,毫无违和感。不要眨眼,请看情况2:在这里插入图片描述
WTF?关闭再打开Intensity居然变了~当时我是怎么想都想不出是个啥机制,反正纠结挺久的,后来发现还是我想复杂了。其实就是HDR的Intensity和RGB的关系,直接改变Intensity会直接改变颜色值,所以情况2中改变Intensity,RGB直接发生变化,但是Intensity的真实取值并不是有上面的滑块决定,而是有颜色决定,所以当我们关闭再次打开(或者改变RGB的数值),都会用Intensity的数值刷新,显示其真正的数据,不得不说unity的这个设计真的有点而,就不能再加个变量控制啊!接下来我们看看,HDR的Intensity和RGB是怎么变化的,对于HDRColor的颜色值为(10,1,1,1)来说,究竟是个什么颜色?白色?红色?接下来,请欣赏代码:

HDRColor、Color、BloomColor、Intensity的转换代码

//颜色面板中的Indensity,可见对其进行赋值时,直接改变HDR的颜色值
public float exposureValue
{
	get
	{
		return this.m_ExposureValue;
	}
	set
	{
		if (this.m_ExposureValue != value)
		{
			this.m_ExposureValue = value;
			//--这里就是核心了,没错是pow(2,Indensity)
			Color color = this.color * Mathf.Pow(2f, this.m_ExposureValue);
			this.m_ColorHdr[0] = color.r;
			this.m_ColorHdr[1] = color.g;
			this.m_ColorHdr[2] = color.b;
		}
	}
}
//--通过HDRRGB计算Indensity(下面的exposure),以及将HDRColor转换成基础的颜色
//linearColorHdr:为HDR的Color的输入(接下来简称HDRColor)。
// baseLinearColor:为HDR转化为基础的颜色,Bloom的颜色(接下来简称BloomColor)。
//exposure:曝光度即Indensity
internal static void DecomposeHdrColor(Color linearColorHdr, out Color32 baseLinearColor, out float exposure)
{
	baseLinearColor = linearColorHdr;
	//-获取HDRColor的RGB中的最大值
	float maxColorComponent = linearColorHdr.maxColorComponent;
	//最大值小于1大于0.003921569(最小非0正数)或者等于0,则HDRColor就是Color
	if (maxColorComponent == 0f || (maxColorComponent <= 1f && maxColorComponent >= 0.003921569f))
	{
		exposure = 0f;
		baseLinearColor.r = (byte)Mathf.RoundToInt(linearColorHdr.r * 255f);
		baseLinearColor.g = (byte)Mathf.RoundToInt(linearColorHdr.g * 255f);
		baseLinearColor.b = (byte)Mathf.RoundToInt(linearColorHdr.b * 255f);
	}
	else
	{
	   //-下面两行就是计算Indensity的方法
		float num = 191f / maxColorComponent;
		exposure = Mathf.Log(255f / num) / Mathf.Log(2f);
		//-下面是HDRColor到BloomColor的转换,所以10,1,1,1应该显示色为白色,Bloom偏红色
		baseLinearColor.r = Math.Min(191, (byte)Mathf.CeilToInt(num * linearColorHdr.r));
		baseLinearColor.g = Math.Min(191, (byte)Mathf.CeilToInt(num * linearColorHdr.g));
		baseLinearColor.b = Math.Min(191, (byte)Mathf.CeilToInt(num * linearColorHdr.b));
	}
}

后效Bloom和HDR的关系

unity中有个PostProcessing的Package包,里面的Bloom的效果配合HDR可以达到天人合一的效果,总之就是很happy。在Shader中生命HDRColor的需要加上[HDR]的标签,通过上面的分析我们知道了HDR的数据计算原理,那么一个普通的颜色,我们可以把他做成HDR的效果么?
直接上结果了:
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

哇~~看来是可以啊,那我们就可以将美术的贴图通过其他方式给他做成HDR效果的了,是不是很兴奋。

  • 10
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 4
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘培玉--大王

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值