【Unity】RGBをHSVに変換して明るさとかを変えるシェーダー
【Unity】RGBをHSVに変換して明るさとかを変えるシェーダー:
この記事はカヤックUnityアドベントカレンダー2018の15日目の記事です。
今回はRGBをHSVに変換して明るさとかを変えるシェーダーの話です。
Unityスクリプト上でHSVを変更するにはColor.RGBToHSV,Color.HSVToRGBを使う必要があります。
しかし今回はマテリアルのRGBとHSVを相互変換する仕組みを入れたシェーダーを作って、Inspectorやスクリプト上で直接マテリアルのHSVを変更できるようにしてみます。
これを改修してHSVを弄れるようにします。

次に

マテリアルのInspector内にあるShaderから
以下が改修した後のソースコードです。
改修した部分をピックアップして説明します。
次に
HSVは環状や円錐の視覚モデルで表現することができ、H(色相)は色環の角度から決定されます。

(著作権者:Wapcaplet、ライセンス:CC by-sa 3.0、HSV色空間 - Wikipedia)
RGB<->HSVの変換式は下記を参考にしています。
https://answers.unity.com/questions/600199/hsv-shader-with-alpha-1.html
https://beesbuzz.biz/code/16-hsv-color-transforms
https://t-pot.com/program/112_HSV/index.html
また、RGB<->HSVの変換関数は下記のように短縮できます。
http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
これでシェーダーの準備は終わりです。
今回は適当に作ったSphereの

Inspectorのマテリアルが切り替わりHSVのプロパティが追加されています。
テクスチャをセットする場合は白黒のものを避けるとHSVの変化が分かりやすいです。

インスペクターから直接マテリアルの操作も可能ですが、
今回はスクリプト上でマテリアルを操作したいので簡単なコントローラーのスクリプトを作り、Sphereにアタッチします。


スマ◯ラのような色変えしたり、

うぉっまぶしっ。
以上「RGBをHSVに変換して明るさとかを変えるシェーダー」の話でした。
今回の話が何かのお役に立てれば幸いです。
明日は魏さんによる『Unityエディター拡張のカスタムプレビュー』です。
© UTJ/UCL
はじめに
こんにちは。ソーシャルゲーム事業部の小笠原です。この記事はカヤックUnityアドベントカレンダー2018の15日目の記事です。
今回はRGBをHSVに変換して明るさとかを変えるシェーダーの話です。
概要
オブジェクトを暗くしたり、彩度を低くしたい時、HSV(色相、彩度、明度)を直接変更できるのは非常に便利です。Unityスクリプト上でHSVを変更するにはColor.RGBToHSV,Color.HSVToRGBを使う必要があります。
しかし今回はマテリアルのRGBとHSVを相互変換する仕組みを入れたシェーダーを作って、Inspectorやスクリプト上で直接マテリアルのHSVを変更できるようにしてみます。
1.シェーダー作成
まずAssets->Create->Shader->Standard Surface Shaderからサーフェイスシェーダーを作成します。 これを改修してHSVを弄れるようにします。

次に
Assets->Create->Materialから今作ったシェーダーを入れるマテリアルも作っておきます。
マテリアルのInspector内にあるShaderから
Custom/SufaceShaderを選ぶことでマテリアルにシェーダーがセットされます。 Custom/SufaceShader部分はシェーダーのコード内一番上にあるShader "Custom/SurfaceShader"を書き換えることで名称を変更出来ます。
以下が改修した後のソースコードです。
改修した部分をピックアップして説明します。
Shader "Custom/SurfaceShader-HSV" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
_Hue ("Hue", Float) = 0
_Sat ("Saturation", Float) = 1
_Val ("Value", Float) = 1
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
CGPROGRAM
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0
sampler2D _MainTex;
struct Input {
float2 uv_MainTex;
};
half _Glossiness;
half _Metallic;
fixed4 _Color;
half _Hue, _Sat, _Val;
// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
// #pragma instancing_options assumeuniformscaling
UNITY_INSTANCING_BUFFER_START(Props)
// put more per-instance properties here
UNITY_INSTANCING_BUFFER_END(Props)
// RGB->HSV変換
float3 rgb2hsv(float3 rgb)
{
float3 hsv;
// RGBの三つの値で最大のもの
float maxValue = max(rgb.r, max(rgb.g, rgb.b));
// RGBの三つの値で最小のもの
float minValue = min(rgb.r, min(rgb.g, rgb.b));
// 最大値と最小値の差
float delta = maxValue - minValue;
// V(明度)
// 一番強い色をV値にする
hsv.z = maxValue;
// S(彩度)
// 最大値と最小値の差を正規化して求める
if (maxValue != 0.0){
hsv.y = delta / maxValue;
} else {
hsv.y = 0.0;
}
// H(色相)
// RGBのうち最大値と最小値の差から求める
if (hsv.y > 0.0){
if (rgb.r == maxValue) {
hsv.x = (rgb.g - rgb.b) / delta;
} else if (rgb.g == maxValue) {
hsv.x = 2 + (rgb.b - rgb.r) / delta;
} else {
hsv.x = 4 + (rgb.r - rgb.g) / delta;
}
hsv.x /= 6.0;
if (hsv.x < 0)
{
hsv.x += 1.0;
}
}
return hsv;
}
// HSV->RGB変換
float3 hsv2rgb(float3 hsv)
{
float3 rgb;
if (hsv.y == 0){
// S(彩度)が0と等しいならば無色もしくは灰色
rgb.r = rgb.g = rgb.b = hsv.z;
} else {
// 色環のH(色相)の位置とS(彩度)、V(明度)からRGB値を算出する
hsv.x *= 6.0;
float i = floor (hsv.x);
float f = hsv.x - i;
float aa = hsv.z * (1 - hsv.y);
float bb = hsv.z * (1 - (hsv.y * f));
float cc = hsv.z * (1 - (hsv.y * (1 - f)));
if( i < 1 ) {
rgb.r = hsv.z;
rgb.g = cc;
rgb.b = aa;
} else if( i < 2 ) {
rgb.r = bb;
rgb.g = hsv.z;
rgb.b = aa;
} else if( i < 3 ) {
rgb.r = aa;
rgb.g = hsv.z;
rgb.b = cc;
} else if( i < 4 ) {
rgb.r = aa;
rgb.g = bb;
rgb.b = hsv.z;
} else if( i < 5 ) {
rgb.r = cc;
rgb.g = aa;
rgb.b = hsv.z;
} else {
rgb.r = hsv.z;
rgb.g = aa;
rgb.b = bb;
}
}
return rgb;
}
float3 shift_col(float3 rgb, half3 shift)
{
// RGB->HSV変換
float3 hsv = rgb2hsv(rgb);
// HSV操作
hsv.x += shift.x;
if (1.0 <= hsv.x)
{
hsv.x -= 1.0;
}
hsv.y *= shift.y;
hsv.z *= shift.z;
// HSV->RGB変換
return hsv2rgb(hsv);
}
void surf (Input IN, inout SurfaceOutputStandard o) {
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
half3 shift = half3(_Hue, _Sat, _Val);
fixed4 shiftColor = fixed4(shift_col(c.rgb, shift), c.a);
o.Albedo = shiftColor.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = shiftColor.a;
}
ENDCG
}
FallBack "Diffuse"
}
まずPropertiesです。Properties {
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
_Hue ("Hue", Float) = 0
_Sat ("Saturation", Float) = 1
_Val ("Value", Float) = 1
}ここにはHSVにあたる変数を書くことで、Inspecterやスクリプトで値にアクセスできるようになります。次に
SubShaderです。half _Hue, _Sat, _Val;Propertiesに追記したHSVの変数と同名のものをこちらでも定義します。
// RGB->HSV変換
float3 rgb2hsv(float3 rgb)
{
float3 hsv;
// RGBの三つの値で最大のもの
float maxValue = max(rgb.r, max(rgb.g, rgb.b));
// RGBの三つの値で最小のもの
float minValue = min(rgb.r, min(rgb.g, rgb.b));
// 最大値と最小値の差
float delta = maxValue - minValue;
// V(明度)
// 一番強い色をV値にする
hsv.z = maxValue;
// S(彩度)
// 最大値と最小値の差を正規化して求める
if (maxValue != 0.0){
hsv.y = delta / maxValue;
} else {
hsv.y = 0.0;
}
// H(色相)
// RGBのうち最大値と最小値の差から求める
if (hsv.y > 0.0){
if (rgb.r == maxValue) {
hsv.x = (rgb.g - rgb.b) / delta;
} else if (rgb.g == maxValue) {
hsv.x = 2 + (rgb.b - rgb.r) / delta;
} else {
hsv.x = 4 + (rgb.r - rgb.g) / delta;
}
hsv.x /= 6.0;
if (hsv.x < 0)
{
hsv.x += 1.0;
}
}
return hsv;
}
// HSV->RGB変換
float3 hsv2rgb(float3 hsv)
{
float3 rgb;
if (hsv.y == 0){
// S(彩度)が0と等しいならば無色もしくは灰色
rgb.r = rgb.g = rgb.b = hsv.z;
} else {
// 色環のH(色相)の位置とS(彩度)、V(明度)からRGB値を算出する
hsv.x *= 6.0;
float i = floor (hsv.x);
float f = hsv.x - i;
float aa = hsv.z * (1 - hsv.y);
float bb = hsv.z * (1 - (hsv.y * f));
float cc = hsv.z * (1 - (hsv.y * (1 - f)));
if( i < 1 ) {
rgb.r = hsv.z;
rgb.g = cc;
rgb.b = aa;
} else if( i < 2 ) {
rgb.r = bb;
rgb.g = hsv.z;
rgb.b = aa;
} else if( i < 3 ) {
rgb.r = aa;
rgb.g = hsv.z;
rgb.b = cc;
} else if( i < 4 ) {
rgb.r = aa;
rgb.g = bb;
rgb.b = hsv.z;
} else if( i < 5 ) {
rgb.r = cc;
rgb.g = aa;
rgb.b = hsv.z;
} else {
rgb.r = hsv.z;
rgb.g = aa;
rgb.b = bb;
}
}
return rgb;
}
float3 shift_col(float3 rgb, half3 shift)
{
// RGB->HSV変換
float3 hsv = rgb2hsv(rgb);
// HSV操作
hsv.x += shift.x;
if (1.0 <= hsv.x)
{
hsv.x -= 1.0;
}
hsv.y *= shift.y;
hsv.z *= shift.z;
// HSV->RGB変換
return hsv2rgb(hsv);
}RGB<->HSVの変換用関数です。HSVは環状や円錐の視覚モデルで表現することができ、H(色相)は色環の角度から決定されます。

(著作権者:Wapcaplet、ライセンス:CC by-sa 3.0、HSV色空間 - Wikipedia)
void surf (Input IN, inout SurfaceOutputStandard o) {
// Albedo comes from a texture tinted by color
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
half3 shift = half3(_Hue, _Sat, _Val);
fixed4 shiftColor = fixed4(shift_col(c.rgb, shift), c.a);
o.Albedo = shiftColor.rgb;
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;
o.Smoothness = _Glossiness;
o.Alpha = shiftColor.a;
}Surf内を改修します。RGB<->HSVの変換式は下記を参考にしています。
https://answers.unity.com/questions/600199/hsv-shader-with-alpha-1.html
https://beesbuzz.biz/code/16-hsv-color-transforms
https://t-pot.com/program/112_HSV/index.html
また、RGB<->HSVの変換関数は下記のように短縮できます。
// RGB->HSV変換
float3 rgb2hsv(float3 rgb)
{
float4 K = float4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
float4 p = rgb.g < rgb.b ? float4(rgb.bg, K.wz) : float4(rgb.gb, K.xy);
float4 q = rgb.r < p.x ? float4(p.xyw, rgb.r) : float4(rgb.r, p.yzx);
float d = q.x - min(q.w, q.y);
float e = 1.0e-10;
return float3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
}
// HSV->RGB変換
float3 hsv2rgb(float3 hsv)
{
float4 K = float4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
float3 p = abs(frac(hsv.xxx + K.xyz) * 6.0 - K.www);
return hsv.z * lerp(K.xxx, saturate(p - K.xxx), hsv.y);
}参考:http://lolengine.net/blog/2013/01/13/fast-rgb-to-hsv
http://lolengine.net/blog/2013/07/27/rgb-to-hsv-in-glsl
これでシェーダーの準備は終わりです。
スクリプトでHSVを変えてみる
早速マテリアルをオブジェクトにアタッチしてみます。今回は適当に作ったSphereの
Mesh Rendererにマテリアルをアタッチします。
Inspectorのマテリアルが切り替わりHSVのプロパティが追加されています。
テクスチャをセットする場合は白黒のものを避けるとHSVの変化が分かりやすいです。

インスペクターから直接マテリアルの操作も可能ですが、
今回はスクリプト上でマテリアルを操作したいので簡単なコントローラーのスクリプトを作り、Sphereにアタッチします。
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HSVColorController : MonoBehaviour
{
private Material material = null;
[Range(0f, 1f)]
public float hue = 0f;
[Range(0f, 1f)]
public float sat = 1f;
[Range(0f, 1f)]
public float val = 1f;
// Use this for initialization
void Start ()
{
this.material = gameObject.GetComponent<Renderer>().material;
}
// Update is called once per frame
void Update ()
{
this.material.SetFloat("_Hue", hue);
this.material.SetFloat("_Sat", sat);
this.material.SetFloat("_Val", val);
}
}
これでスクリプトからマテリアルのHSVを弄れるようになりました。 
ついでに
少しソースコードを改修してユニティちゃんの服の色を変えてみます。
スマ◯ラのような色変えしたり、

うぉっまぶしっ。
おわりに
HSVはRGBと違い直感的に明度彩度を変更できるのが利点です。用途に合わせて使い分けてみてください。以上「RGBをHSVに変換して明るさとかを変えるシェーダー」の話でした。
今回の話が何かのお役に立てれば幸いです。
明日は魏さんによる『Unityエディター拡張のカスタムプレビュー』です。
© UTJ/UCL
| オリジナルのエンクロージャ: |
| 20181214181801.gif |
コメント
コメントを投稿