質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.31%
C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

Q&A

解決済

2回答

3285閲覧

【Unity】EmissionのIntensityについて

Y0241-N

総合スコア1066

C#

C#はマルチパラダイムプログラミング言語の1つで、命令形・宣言型・関数型・ジェネリック型・コンポーネント指向・オブジェクティブ指向のプログラミング開発すべてに対応しています。

Unity

Unityは、Unity Technologiesが開発・販売している、IDEを内蔵するゲームエンジンです。主にC#を用いたプログラミングでコンテンツの開発が可能です。

0グッド

0クリップ

投稿2022/02/21 08:43

使用バージョン Unity2019.4.13f1

EmissionのIntensityをスクリプトから調整したいのですが、調べると出てくるSet("_EmissionColor", ColorCode * intensityValue)を試していますが、intenstiyValueに渡した数値通りになりません。

例えば4を渡してマテリアルのintensityの実数値を確認すると4ではなく2.416924となっていました。
8を渡せば4になるのかと思えば、3.416925となっていました。
調べた中では渡すintensityをMathf.Pow(2,intensity)とすると書いてある場合もありましたが、これも試してみましたが値が近付きはするものの同じ値にはなりませんでした。

何か方法が間違っているのでしょうか?どのようにすればEmissionのIntensityを指定した値と同じ値に変更できるでしょうか。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

Intensityのセットについては、Mathf.Pow(2,intensity)の方を使うのが適切だろうと思います。
カラーピッカーで見てみたときの値の狂いについては、おそらくカラーピッカーがHDRカラーをベースカラーとIntensityに分解するときの計算方法のせいじゃないでしょうかね?

たとえば下図では、EmissionとしてRGBがすべて255の白色、Intensityが2の色をセットしているのですが、カラーピッカーをいったん閉じて開き直すとRGBが191に暗くなり、代わりにIntensityは2.416924に上昇してしまいました。
ご質問者さんの場合も、もしかしてIntensityだけではなくベースカラーの方も本来のColorCodeとは違う値になってはいませんでしょうか?

図

以前別の方の「Unity マテリアルのEmission intensityについて」とのご質問に回答しました際に調べたところによると、どうやらこのHDRカラーの分解はColorMutatorが行っているようでしたが、下記引用のように...

C#

1 internal static void DecomposeHdrColor(Color linearColorHdr, out Color32 baseLinearColor, out float exposure) 2 { 3 baseLinearColor = linearColorHdr; 4 var maxColorComponent = linearColorHdr.maxColorComponent; 5 // replicate Photoshops's decomposition behaviour 6 if (maxColorComponent == 0f || maxColorComponent <= 1f && maxColorComponent >= 1 / 255f) 7 { 8 exposure = 0f; 9 10 baseLinearColor.r = (byte)Mathf.RoundToInt(linearColorHdr.r * 255f); 11 baseLinearColor.g = (byte)Mathf.RoundToInt(linearColorHdr.g * 255f); 12 baseLinearColor.b = (byte)Mathf.RoundToInt(linearColorHdr.b * 255f); 13 } 14 else 15 { 16 // calibrate exposure to the max float color component 17 var scaleFactor = k_MaxByteForOverexposedColor / maxColorComponent; 18 exposure = Mathf.Log(255f / scaleFactor) / Mathf.Log(2f); 19 20 // maintain maximal integrity of byte values to prevent off-by-one errors when scaling up a color one component at a time 21 baseLinearColor.r = Math.Min(k_MaxByteForOverexposedColor, (byte)Mathf.CeilToInt(scaleFactor * linearColorHdr.r)); 22 baseLinearColor.g = Math.Min(k_MaxByteForOverexposedColor, (byte)Mathf.CeilToInt(scaleFactor * linearColorHdr.g)); 23 baseLinearColor.b = Math.Min(k_MaxByteForOverexposedColor, (byte)Mathf.CeilToInt(scaleFactor * linearColorHdr.b)); 24 } 25 }

入力されたHDRカラーがLDRカラーで表現できない(明度が1/255を下回っているか、1を超えている)場合、元のHDRカラーの明度が191/255に(k_MaxByteForOverexposedColorとして定義されている値に)なるようスケーリングしたものをベースカラーとして決めて、それに合わせたIntensityを計算しているらしいですね。

カラーピッカー上の値とスクリプト上の値の不整合を解消するとなると、おそらくスクリプト側の値の方をカラーピッカー側の値に合わせる...つまりColorMutatorと同じ計算式で色を矯正して、ColorCodeintensityValueの方をカラーピッカーと一致するよう書き換えることになるんじゃないかと思います。

スクリプト側の値を矯正する例

下記のようなスクリプトをオブジェクトにアタッチして...

C#

1using UnityEngine; 2 3[RequireComponent(typeof(Renderer))] 4public class EmissionController : MonoBehaviour 5{ 6 private static readonly int EmissionColorProperty = Shader.PropertyToID("_EmissionColor"); 7 8 [ColorUsage(false)] public Color ColorCode = Color.black; 9 public float intensityValue; 10 11 private Color previousColorCode; 12 private float previousIntensityValue = float.NaN; 13 private new Renderer renderer; 14 15 private void Update() 16 { 17 if (this.renderer == null) 18 { 19 this.renderer = this.GetComponent<Renderer>(); 20 } 21 22 // ColorCodeもintensityValueも前フレームから変わっていなければ 23 // Emission更新の必要はないと見なして何もしない 24 if ((this.ColorCode == this.previousColorCode) && (this.intensityValue == this.previousIntensityValue)) 25 { 26 return; 27 } 28 29 // まずColorCodeをintensityValueに応じて増幅し、Emissionにセットするべき色を決める 30 var hdrColor = this.ColorCode * Mathf.Pow(2.0f, this.intensityValue); 31 hdrColor.a = this.ColorCode.a; 32 33 // EmissionにhdrColorをセットする 34 var material = this.renderer.material; 35 if (material != null) 36 { 37 material.EnableKeyword("_EMISSION"); 38 material.SetColor(EmissionColorProperty, hdrColor); 39 } 40 41 // そしてhdrColorをベースカラーとIntensityに分解する 42 // まずhdrColorのRGBのうち最も大きい成分の値を取得する 43 var maxComponent = hdrColor.maxColorComponent; 44 45 // intensityValueはひとまず0にする 46 this.intensityValue = 0.0f; 47 48 // その最大成分が0なら完全な黒なのでLDRで表現でき、分解の必要はない 49 // また、1/255以上1以下ならLDRで表現でき、分解の必要はない 50 if ((maxComponent != 0.0f) && ((maxComponent < (1.0f / 255.0f)) || (maxComponent > 1.0f))) 51 { 52 // そうでないならば、まずmaxComponentに対する191/255の倍率を求める 53 var scale = 191.0f / (maxComponent * 255.0f); 54 55 // そしてそのスケールを使ってColorCodeのRGBを矯正する 56 this.ColorCode = hdrColor * scale; 57 this.ColorCode.a = hdrColor.a; 58 59 // また、scaleの逆数の2を底とする対数を求め、intensityValueとする 60 this.intensityValue = Mathf.Log(1.0f / scale, 2.0f); 61 } 62 63 // 最後に、次のフレームに備えて現在のColorCodeとintensityValueを保存しておく 64 this.previousColorCode = this.ColorCode; 65 this.previousIntensityValue = this.intensityValue; 66 } 67}

ゲーム実行時にスクリプト側のColorCodeintensityValueを設定したところ、ColorCodeは少し暗く、intensityValueは少し大きく変化しました。
そして、マテリアル側のEmissionもスクリプト側と同じ設定値になりました。

図

カラーピッカー側の値を矯正しようとするとどうなるか

たとえばスクリプトを下記のようなものに変更した場合(なんだかマルウェア的な変なことをしていて気持ち悪いかもしれませんが、あくまでも実験ということでご容赦ください)...

C#

1using System; 2using System.Reflection; 3using System.Runtime.CompilerServices; 4using System.Runtime.InteropServices; 5using UnityEngine; 6#if UNITY_EDITOR 7using UnityEditor; 8#endif 9 10[RequireComponent(typeof(Renderer))] 11public class EmissionController2 : MonoBehaviour 12{ 13 private static readonly int EmissionColorProperty = Shader.PropertyToID("_EmissionColor"); 14 15 [ColorUsage(false)] public Color ColorCode = Color.black; 16 public float intensityValue; 17 18 private readonly byte[] decomposeHdrColorBackup = new byte[5]; 19 private IntPtr decomposeHdrColorPointer; 20 private Color previousColorCode; 21 private float previousIntensityValue = float.NaN; 22 private new Renderer renderer; 23 24 private void Update() 25 { 26 if (this.renderer == null) 27 { 28 this.renderer = this.GetComponent<Renderer>(); 29 } 30 31 // ColorCodeもintensityValueも前フレームから変わっていなければ 32 // Emission更新の必要はないと見なして何もしない 33 if ((this.ColorCode == this.previousColorCode) && (this.intensityValue == this.previousIntensityValue)) 34 { 35 return; 36 } 37 38 // まずColorCodeをintensityValueに応じて増幅し、Emissionにセットするべき色を決める 39 var hdrColor = this.ColorCode * Mathf.Pow(2.0f, this.intensityValue); 40 hdrColor.a = this.ColorCode.a; 41 42 // EmissionにhdrColorをセットする 43 var material = this.renderer.material; 44 if (material != null) 45 { 46 material.EnableKeyword("_EMISSION"); 47 material.SetColor(EmissionColorProperty, hdrColor); 48 } 49 50 // ColorMutator.DecomposeHdrColorを乗っ取り、代わりにDecomposeHdrColor255を実行させるようにする 51 this.HijackDecomposeHdrColor(); 52 53 // 最後に、次のフレームに備えて現在のColorCodeとintensityValueを保存しておく 54 this.previousColorCode = this.ColorCode; 55 this.previousIntensityValue = this.intensityValue; 56 } 57 58 // コンポーネント破壊時にColorMutator.DecomposeHdrColorを元の状態に戻す 59 private void OnDestroy() 60 { 61 if (this.decomposeHdrColorPointer == IntPtr.Zero) 62 { 63 return; 64 } 65 Marshal.Copy(this.decomposeHdrColorBackup, 0, this.decomposeHdrColorPointer, this.decomposeHdrColorBackup.Length); 66 this.decomposeHdrColorPointer = IntPtr.Zero; 67 } 68 69 // ColorMutator.DecomposeHdrColorを乗っ取り、DecomposeHdrColor255へ制御を移すよう改変を加える 70 private void HijackDecomposeHdrColor() 71 { 72 #if UNITY_EDITOR 73 if (this.decomposeHdrColorPointer != IntPtr.Zero) 74 { 75 return; 76 } 77 78 // ColorMutator.DecomposeHdrColorのメモリ上の位置を取得する 79 const BindingFlags bindingFlags = BindingFlags.DeclaredOnly | BindingFlags.NonPublic | BindingFlags.Static; 80 var decomposeHdrColorMethodHandle = typeof(EditorWindow).Assembly.GetType("UnityEditor.ColorMutator").GetMethod("DecomposeHdrColor", bindingFlags).MethodHandle; 81 RuntimeHelpers.PrepareMethod(decomposeHdrColorMethodHandle); 82 this.decomposeHdrColorPointer = decomposeHdrColorMethodHandle.GetFunctionPointer(); 83 84 // DecomposeHdrColor255のメモリ上の位置を取得する 85 var decomposeHdrColor255MethodHandle = typeof(EmissionController2).GetMethod(nameof(DecomposeHdrColor255), bindingFlags).MethodHandle; 86 RuntimeHelpers.PrepareMethod(decomposeHdrColor255MethodHandle); 87 var decomposeHdrColor255Pointer = decomposeHdrColor255MethodHandle.GetFunctionPointer(); 88 89 // コンポーネント破壊時にColorMutator.DecomposeHdrColorを復元するため、先頭5バイトをバックアップしておく 90 Marshal.Copy(this.decomposeHdrColorPointer, this.decomposeHdrColorBackup, 0, this.decomposeHdrColorBackup.Length); 91 92 // ColorMutator.DecomposeHdrColorの先頭5バイトにDecomposeHdrColor255へジャンプするコードを埋め込む 93 Marshal.WriteByte(this.decomposeHdrColorPointer, 0xE9); 94 Marshal.WriteInt32(this.decomposeHdrColorPointer, 1, (int)decomposeHdrColor255Pointer - (int)this.decomposeHdrColorPointer - this.decomposeHdrColorBackup.Length); 95 #endif 96 } 97 98 // オリジナルのColorMutator.DecomposeHdrColorはベースカラーの基準値が191だが、 99 // こちらは255を基準にベースカラーを決める 100 private static void DecomposeHdrColor255(Color linearColorHdr, out Color32 baseLinearColor, out float exposure) 101 { 102 baseLinearColor = linearColorHdr; 103 var maxColorComponent = linearColorHdr.maxColorComponent; 104 if (maxColorComponent is 0.0f or <= 1.0f and >= 1.0f / 255.0f) 105 { 106 exposure = 0.0f; 107 baseLinearColor.r = (byte)Mathf.RoundToInt(linearColorHdr.r * 255.0f); 108 baseLinearColor.g = (byte)Mathf.RoundToInt(linearColorHdr.g * 255.0f); 109 baseLinearColor.b = (byte)Mathf.RoundToInt(linearColorHdr.b * 255.0f); 110 } 111 else 112 { 113 var scaleFactor = 255.0f / maxColorComponent; 114 exposure = Mathf.Log(maxColorComponent, 2.0f); 115 baseLinearColor.r = (byte)Math.Min(255, Mathf.CeilToInt(scaleFactor * linearColorHdr.r)); 116 baseLinearColor.g = (byte)Math.Min(255, Mathf.CeilToInt(scaleFactor * linearColorHdr.g)); 117 baseLinearColor.b = (byte)Math.Min(255, Mathf.CeilToInt(scaleFactor * linearColorHdr.b)); 118 } 119 } 120}

マテリアル上でもIntensityが2になりました。

図

一見うまくいったように見えるかもしれませんが、逆にスクリプト側のベースカラー輝度が191でも、マテリアル側のベースカラー輝度は255になってしまいます。

図

(255, 128, 64)、Intensity 2の色と(191, 96, 48)、Intensity 2.416924の色は計算誤差を無視すれば同等で区別できず、カラーピッカー側で本来のベースカラー・Intensityを復元するのは無理っぽいです...

投稿2022/02/21 10:11

編集2022/03/02 12:36
Bongo

総合スコア10816

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Y0241-N

2022/02/22 01:44

丁寧な回答ありがとうございます。 入力値をColorMutatorと同じ計算式で矯正する、ということでしたので、 baseLinearColor = ColorCode; var maxColorComponent = intensityValue; としてDecomposeHdrColorを通し、outされたexposureを exposure = Mathf.pow(2,exposure)した値を SetColor("_EmissionColor", baseLinearColor * exposure )してみたのですが、値が合いませんでした。 こういうことではないのでしょうか?
Bongo

2022/02/22 13:46

矯正と申し上げたのは、そのようなことを意図しておりました。うまくいかなかったとなると、なにか計算式に問題があるんでしょうかね...? ご参考までに、私もちょっとEmissionを設定するスクリプトを作成してみました。追記しましたように、とりあえず私の試した限りではマテリアル側に合わせた値にスクリプト側が矯正されたようだったのですが、あれと比べてどこか原因と思われる部分は見当たりますでしょうか?
Y0241-N

2022/03/01 07:19

返答が遅くなってしまいましたが、頂いた追記内容を試してみましたが思っていたようにはいきませんでした。 試していただいた内容で言うと、矯正後のHDRColorにセットされているIntensityの値を入力値と同じ値にしたかったのですが、そういう訳ではなかったです。 またColorCodeに渡すカラーが初めからHDRColorを渡すか、RGBColorを渡すかによっても矯正後のIntensityの値が変わってくるため、おとなしく[ColorUsage(false, true)]を使用したHDRColorをInspectorからIntensityを調整してSetColorしようと思います。
Bongo

2022/03/02 12:36

すみませんでした、ご質問者さんの意図と私の認識に齟齬があったようで、ご希望の挙動ではなかったようですね... 何かコメントせねばと思いまして追記いたしましたが、結局意図通りの挙動は実現できずすみません。この問題はいわば「○×□=12です。○と□はそれぞれいくつでしょう?」と問われても○と□を一意に決定できないように、原理的に解決できない問題かもしれませんね。不本意かとお察ししますが、コメントいただいたようにそういったものとして諦めていただくことになりそうです...
Y0241-N

2022/03/03 02:43

いえいえ、ご丁寧に回答していただいてありがとうございました。 また機会がありましたらその際もご助力いただけますと幸いです。 この質問は自己回答にて「現状スクリプトからIntensityを数値指定して変更することはできない」としてクローズ致します。お時間を割いていただいてありがとうございました。
guest

0

自己解決

結論として現状スクリプトからIntensityを数値指定して変更することはできないと思われます。

投稿2022/03/03 02:44

Y0241-N

総合スコア1066

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.31%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問