天天看點

Shader+UGUI仿制Unity拾色器Shader+UGUI仿制Unity拾色器

Shader+UGUI仿制Unity拾色器

先上視訊

使用Shader+UGUI仿制Unity環形拾色器

由于視訊有點小,下面放個大圖

Shader+UGUI仿制Unity拾色器Shader+UGUI仿制Unity拾色器

原理介紹

  • Shader 着色

    元件共使用了3個自定義Shader,對應了拾色器的不同組成部分,分别是:

  1. 選色調的色環Shader

    其實很簡單,首先根據UV圖生成色調圖,然後用一個環來lerp一下,然後在目标位置生成一個小圈圈。

    Shader+UGUI仿制Unity拾色器Shader+UGUI仿制Unity拾色器
    如上圖所示,暴露很多參數供C#調整,有背景顔色、大圈圈大小、小圈圈的偏移、大小粗細等等,其中最重要的是Hue,這是就是色調了。。當我們選取了環上的顔色時,就要動态去設定該值,這樣shader就能在正确的位置生成小圈圈。關鍵的代碼如下:
float3 frag(v2f i) : SV_Target
{
	float2 uv = i.uv.xy;
	float2 toCenter = float2(0.5, 0.5) - uv;
	float angle = atan2(toCenter.y, toCenter.x);
	float radius = length(toCenter) * 2;
	float3 col = hsb2rgb(float3((angle / UNITY_TWO_PI) + 0.5, radius, 1.0));

	float ring = step(_RingInner, radius);
	ring = ring * ( 1 - step(_RingOuter, radius));
	col = lerp(_BackgroundColor,col, ring);

	float ang = -_Hue * UNITY_TWO_PI - UNITY_PI * 0.5;
	float2 pos = _PickerRingOffet * (float2(sin(ang), cos(ang)));

	float huering = _PickerRingThink / abs(length(toCenter - pos) - _PickerRingSize);
	col = lerp(col, float3(0, 0, 0), step(0.5, huering) * ring );

	return col;
}
           
  1. 選具體顔色的矩形Shader

    矩形shader更簡單了,直接用uv來生成矩形的色圖。

    Shader+UGUI仿制Unity拾色器Shader+UGUI仿制Unity拾色器

    最重要的參數是TargetPos,xy分量用來表示小圈圈的位置,z分量用來表示色調(意義同環形shader)。

    矩形的shader非常簡單,5行代碼搞定(不算return。。):

float3 frag(v2f i) : SV_Target
{
	float3 col = hsb2rgb(float3(_TargetPos.z, i.uv.x, i.uv.y));

	float2 st = i.uv.xy * 2.0 - 1.0;
	float rmpring = _PickerRingThink / abs(length(st - _TargetPos.xy) - _PickerRingSize);
	float3 rmprc = lerp(float3(0,0,0), float3(0.5,0.5,0.5), step( 1, rmpring));
	col += rmprc;

	return col;
}
           
  1. 選RGBA分量的Slider的Shader

    當我們在環裡選了色調,或者在矩形裡選了具體的顔色,那麼RGB每個分量的顔色都會有變化,表示在目前顔色下,如果改變各自的分量,将得到什麼顔色。

    Shader+UGUI仿制Unity拾色器Shader+UGUI仿制Unity拾色器

    這裡保留兩個參數,一個是目前的顔色,另一個表示所屬的分量。比如(1,0,0)表示紅色,(0,1,0)表示綠色,(0,0,1)表示藍色。w分量沒有用到。

    代碼就更簡單了(4行):

float3 frag(v2f i) : SV_Target
{
	float3 col;
	col.r = lerp( _CurrColor.r, i.uv.x, _TargetColor.x );
	col.g = lerp( _CurrColor.g, i.uv.x, _TargetColor.y );
	col.b = lerp( _CurrColor.b, i.uv.x, _TargetColor.z );
	return col;
}
           
  • C#代碼

    先用UGUI擺好拾色器,做成預制體:

    Shader+UGUI仿制Unity拾色器Shader+UGUI仿制Unity拾色器
    然後寫封裝一下代碼:
    Shader+UGUI仿制Unity拾色器Shader+UGUI仿制Unity拾色器

    基本上就是各種元件的拖動而已。

    封裝好之後,可以通過

    OnColorChanged

    事件來獲得當顔色被改變時的通知。也可以使用

    ColorValue

    變量來直接擷取顔色,當然,也可以通過對這個變量的指派,來讓拾色器各個控件進行相應的更新。比如對這個變量指派紅色,那麼環形拾色器、矩形拾色器還有各個分量的slider都會自動進行計算更新到紅色的訓示。

    實際上C#代碼沒什麼高深複雜的邏輯,隻不過就是對這種控件的OnValueChanged事件進行監聽,然後将各自的變化更新元件。

最核心的代碼也許隻有這些,感覺其餘的都沒啥必要貼。。太簡單了。。

private void UpdateValueToUI( UpdateMode mode )
{
	prevImage.color = m_theColor;

	if ((mode & UpdateMode.UPDATE_PICKER) != 0)
	{
		rectPicker.SetColor(m_theColor, false);
		ringPicker.SetHue(rectPicker.Hue, false);
	}

	if ((mode & UpdateMode.UPDATE_SLIDER) != 0)
	{
		redSlider.SetValueWithoutNotify(m_theColor.r);
		greenSlider.SetValueWithoutNotify(m_theColor.g);
		blueSlider.SetValueWithoutNotify(m_theColor.b);
		alphaSlider.SetValueWithoutNotify(m_theColor.a);
	}

	redLineImage.material.SetColor(currColorId, m_theColor);
	greenLineImage.material.SetColor(currColorId, m_theColor);
	blueLineImage.material.SetColor(currColorId, m_theColor);
	alphaLineImage.color = m_theColor;

	if ((mode & UpdateMode.UPDATE_TEXT) != 0)
	{
		redInput.SetTextWithoutNotify(Mathf.RoundToInt(m_theColor.r * 255f).ToString("0"));
		greenInput.SetTextWithoutNotify(Mathf.RoundToInt(m_theColor.g * 255f).ToString("0"));
		blueInput.SetTextWithoutNotify(Mathf.RoundToInt(m_theColor.b * 255f).ToString("0"));
		alphaInput.SetTextWithoutNotify(Mathf.RoundToInt(m_theColor.a * 255f).ToString("0"));
	}

	if(( mode & UpdateMode.UPDATE_VALTXT ) != 0 )
		colorText.SetTextWithoutNotify(ColorUtility.ToHtmlStringRGB(m_theColor));
}
           

工程下載下傳連結

https://download.csdn.net/download/sdhexu/20463995

或猛擊此處下載下傳