WebGL & Three.js

Three.jsでMaterialの色を設定する方法|Color・RGB・HSL・色空間を解説

Three.jsでMaterialの色を設定・変更する方法を解説します。16進数、CSS色、RGB、HSL、Color.set・copy・lerp、emissive、vertex color、textureとの乗算、sRGB・Linear-sRGB、色が暗い・違う原因まで実装例で確認できます。

この記事の目次
  1. Materialのcolorを設定する基本
  2. 色を指定できる形式一覧
  3. 16進数をnumberで指定する
  4. CSS色文字列を指定する
  5. RGB componentを0〜1で指定する
  6. 生成後にmaterial.colorを変更する
  7. set・setHex・setStyleを使い分ける
  8. setRGB・setHSLで数値指定する
  9. Colorをcopy・cloneする
  10. Colorを文字列・hexへ戻す
  11. HTML color pickerで色を変更する
  12. 色を補間してanimationする
  13. Three.jsの色空間を理解する
  14. setRGBとCSS rgbが同じ数値でも違う
  15. ColorManagementを無効化しない
  16. textureとmaterial.colorは乗算される
  17. emissiveで発光色を設定する
  18. vertex colorで頂点ごとに色を付ける
  19. vertex colorをruntime更新する
  20. opacityはColorとは別に設定する
  21. 色を変えてもneedsUpdateが不要な場合
  22. 同じMaterialを共有する場合の注意
  23. 指定色と画面の色が違う原因
  24. lightの影響を受けている
  25. textureのcolorSpaceが未設定
  26. Material colorでtextureをtintしている
  27. setRGBへsRGB値をそのまま渡している
  28. tone mapping・post-processingが影響している
  29. Colorではなく別Materialを変更している
  30. Color設定チェックリスト
  31. まとめ

Three.jsのMaterial色は、constructorのcolorへ16進数やCSS色文字列を渡すか、生成後にmaterial.color.set()などで変更します。

const material = new THREE.MeshStandardMaterial({
  color: 0x38bdf8,
  roughness: 0.6,
  metalness: 0.1,
});

// 生成後に変更
material.color.set("#f97316");

現在のThree.jsではMaterialのcolorTHREE.Color instanceです。hex・CSS色は通常sRGB inputとしてLinear-sRGBのworking color spaceへ変換され、lighting計算後にdisplay用sRGBへ出力されます。

この記事では、色の指定形式、Color API、動的変更、emissive、vertex color、textureとの組み合わせ、色空間、想定より暗い原因まで解説します。

Materialのcolorを設定する基本

Material作成時にcolorを渡します。

const geometry = new THREE.TorusGeometry(1, 0.35, 24, 64);
const material = new THREE.MeshStandardMaterial({
  color: 0x22c55e,
});
const mesh = new THREE.Mesh(geometry, material);

scene.add(mesh);

MeshStandardMaterialはlightの影響を受けます。色設定だけを切り分けたいときは、light不要のMeshBasicMaterialで確認できます。

const material = new THREE.MeshBasicMaterial({
  color: 0x22c55e,
});

Scene、Camera、Rendererを含む最小構成から確認したい場合は、先にThree.jsの始め方を参照してください。

Material種類・PBR・透明度・side・wireframeの違いはThree.js Materialの種類と使い方で扱い、この記事は色だけに集中します。

スポンサーリンク

色を指定できる形式一覧

Color constructorやColor.set()は複数形式を受け取れます。

形式 注意点
hex number 0xff0000 Three.js docsで標準的な形式
CSS hex string "#ff0000" color pickerと組み合わせやすい
CSS rgb "rgb(255, 0, 0)" 0〜255またはpercentage
CSS hsl "hsl(0, 100%, 50%)" hueはdegree表現
CSS color name "skyblue" lowercaseのX11 color name
RGB component new Color(1, 0, 0) 各値は基本0〜1
Color instance new Color(existingColor) 既存色をcopy

16進数をnumberで指定する

JavaScriptの0xから始まるhex numberです。

const red = new THREE.Color(0xff0000);
const material = new THREE.MeshBasicMaterial({ color: 0xff0000 });

"0xff0000"のようにquoteした文字列はCSS色ではありません。文字列なら"#ff0000"を使います。

CSS色文字列を指定する

CSS hex、rgb、hsl、color nameを使えます。

const color1 = new THREE.Color("#ff0000");
const color2 = new THREE.Color("rgb(255, 0, 0)");
const color3 = new THREE.Color("rgb(100%, 0%, 0%)");
const color4 = new THREE.Color("hsl(0, 100%, 50%)");
const color5 = new THREE.Color("skyblue");

CSS color nameはThree.js公式docsで案内されている140種類のX11 nameに対応します。alphaはColorではなくMaterialのopacityとして管理します。

RGB componentを0〜1で指定する

3つのnumberを渡す場合、red・green・blueを0〜1で指定します。

const red = new THREE.Color(1, 0, 0);
const gray = new THREE.Color(0.5, 0.5, 0.5);

ただし、数値componentはThree.jsのworking color spaceを前提にします。Webのcolor pickerから得たsRGB componentを渡す場合の違いは後半の色空間sectionで解説します。

生成後にmaterial.colorを変更する

material.colorはColor instanceなので、Colorのmethodを使います。

material.color.set(0xef4444);
material.color.set("#ef4444");
material.color.set("hsl(0, 82%, 60%)");

次のようにnumberをpropertyへ直接代入しません。

// Color instanceをnumberで壊すため避ける
material.color = 0xef4444;

通常のcolor値変更だけならmaterial.needsUpdate = trueは不要です。Color uniformは次のrenderへ反映されます。shader構成を変えるpropertyと、単なるuniform値の変更を分けます。

set・setHex・setStyleを使い分ける

入力形式が変わる場合はset()が便利です。形式を明示したい場合は専用methodを使います。

const color = new THREE.Color();

color.set(0x3b82f6);
color.setHex(0x3b82f6);
color.setStyle("#3b82f6");
color.setStyle("rgb(59, 130, 246)");
  • set(): hex number、CSS string、Colorを受ける汎用method
  • setHex(): hex numberとsource color spaceを指定可能
  • setStyle(): CSS style stringとsource color spaceを指定可能

setRGB・setHSLで数値指定する

RGB componentを直接設定します。

material.color.setRGB(1, 0.25, 0.05);

HSLはhue・saturation・lightnessをすべて0〜1で指定します。CSSのhsl(270, 100%, 50%)とはhueの単位が異なります。

// hue 270deg相当 = 270 / 360 = 0.75
material.color.setHSL(0.75, 1, 0.5);

現在色を基準に少し変化させるoffsetHSL()もあります。

material.color.offsetHSL(0.05, 0, -0.1);

繰り返し呼ぶと差分が累積します。元色から毎回計算したい場合はbase Colorを保持します。

Colorをcopy・cloneする

paletteをColor instanceで管理できます。

const palette = {
  primary: new THREE.Color("#2563eb"),
  accent: new THREE.Color("#f97316"),
};

material.color.copy(palette.primary);

const independentColor = palette.primary.clone();
independentColor.offsetHSL(0, 0, 0.15);

copy()は既存Colorの値を変更します。clone()は独立した新しいColorを返します。

同じColor objectを複数箇所で直接共有して後から変更する設計より、Material自身のColorへcopyするほうが意図しない連動を避けやすくなります。

Colorを文字列・hexへ戻す

debug表示やUI同期にはgetterを使います。

const color = new THREE.Color("#38bdf8");

console.log(color.getHex());       // number
console.log(color.getHexString()); // "38bdf8"
console.log(color.getStyle());     // "rgb(...)"

HTML color inputへ戻す場合は#を付けます。

colorInput.value = `#${material.color.getHexString()}`;

ColorManagementが有効な通常設定では、getterはdisplay・CSSで扱いやすいsRGB出力へ変換できます。

HTML color pickerで色を変更する

<input type="color">#rrggbb文字列を返します。そのままset()へ渡せます。

<label>
  Material color
  <input id="material-color" type="color" value="#38bdf8">
</label>
const colorInput = document.querySelector("#material-color");

colorInput.addEventListener("input", (event) => {
  material.color.set(event.currentTarget.value);
});

CSS hex inputはsRGBとして扱われ、Three.jsのworking color spaceへ自動変換されます。

色を補間してanimationする

lerpColors()は2色間を0〜1で補間します。

const startColor = new THREE.Color("#2563eb");
const endColor = new THREE.Color("#f97316");

function updateColor(progress) {
  material.color.lerpColors(startColor, endColor, progress);
}

updateColor(0.5);

lerp()は現在色からtargetへ近づけます。毎frame一定alphaで呼ぶと時間ではなくframe数に依存しやすいため、durationに対するprogressを計算してlerpColorsへ渡すと制御しやすくなります。

色相をたどる補間が必要ならlerpHSL()を検討します。RGB直線補間とは途中色が異なります。

Three.jsの色空間を理解する

Three.jsのlighting・補間・renderingはLinear-sRGBのworking color spaceで行われます。displayやCSS colorは通常sRGBです。

現在はTHREE.ColorManagement.enabledがdefaultでtrueです。一般的なinputは次のように扱われます。

  • hex numberとCSS color string: sRGB inputとしてLinear-sRGBへ自動変換
  • Color内部のr・g・b: Linear-sRGB working value
  • setRGB() / setHSL(): defaultではworking color spaceの値
  • WebGLRendererのdisplay output: defaultはsRGB

setRGBとCSS rgbが同じ数値でも違う

次の2つは、入力color spaceの前提が異なります。

const workingGray = new THREE.Color().setRGB(0.5, 0.5, 0.5);
const cssGray = new THREE.Color().setStyle("rgb(50%, 50%, 50%)");

setRGB(0.5, ...)はdefaultでLinear-sRGB component、CSS stringはsRGB inputです。sRGBのnormalized RGB値をnumberで受け取ったならsource color spaceを明示できます。

const color = new THREE.Color().setRGB(
  0.5,
  0.5,
  0.5,
  THREE.SRGBColorSpace,
);

Web UIと色を合わせるだけなら、color pickerの#rrggbbをset()またはsetStyle()へ渡す方法が簡単です。

ColorManagementを無効化しない

古いsampleにはTHREE.ColorManagement.enabled = falseや旧encoding APIが出ることがあります。現在のlinear workflowを理解せず無効化すると、Material・texture・outputの変換が一致しなくなります。

既存projectをupgradeする場合は、Three.jsのversion、texture annotation、renderer output、post-processingを一緒に確認します。

textureとmaterial.colorは乗算される

Materialにcolor mapを設定すると、textureの色とMaterialのcolorが乗算されます。texture本来の色を保ちたい場合はwhiteを使います。

const texture = new THREE.TextureLoader().load("/textures/albedo.jpg");
texture.colorSpace = THREE.SRGBColorSpace;

const material = new THREE.MeshStandardMaterial({
  color: 0xffffff,
  map: texture,
});

color: 0xff0000へ変えるとtextureへred tintがかかります。黒を指定すると乗算結果も黒くなります。

PNG・JPEGなど見た目の色を含むmapは通常SRGBColorSpaceを指定します。normalMap・roughnessMap・metalnessMapなど非color dataはdefaultのNoColorSpaceを使います。

TextureLoader、UV、wrap、filterの詳細はThree.jsでtextureを設定する方法で解説しています。

emissiveで発光色を設定する

MeshStandardMaterialなどにはemissiveemissiveIntensityがあります。

const material = new THREE.MeshStandardMaterial({
  color: 0x111827,
  emissive: 0xff6600,
  emissiveIntensity: 2,
});

emissiveはMaterial自身が明るく見える成分です。他objectを照らすLightになるわけではありません。周囲を照らすにはPointLightなどを別に追加します。

emissiveMapを使う場合、emissive colorとintensityでmapが調整されます。default emissiveはblackなので、mapを設定しても必要なemissive colorがblackのままでは見えない点に注意します。

Lightの種類・intensity・配置はThree.jsの基本Lightで分離して解説しています。

vertex colorで頂点ごとに色を付ける

BufferGeometryへcolor attributeを追加し、MaterialのvertexColorsをtrueにします。

const geometry = new THREE.BufferGeometry();

geometry.setAttribute(
  "position",
  new THREE.Float32BufferAttribute([
    -1, -1, 0,
     1, -1, 0,
     0,  1, 0,
  ], 3),
);

const colors = [];
new THREE.Color("#ef4444").toArray(colors, 0);
new THREE.Color("#22c55e").toArray(colors, 3);
new THREE.Color("#3b82f6").toArray(colors, 6);

geometry.setAttribute(
  "color",
  new THREE.Float32BufferAttribute(colors, 3),
);

const material = new THREE.MeshBasicMaterial({
  color: 0xffffff,
  vertexColors: true,
  side: THREE.DoubleSide,
});

Color.toArray()はColor内部のLinear-sRGB componentを配列へ書き出します。vertex color attributeもworking color spaceのdataとして扱います。

Materialのbase colorはvertex colorと乗算されるため、元のvertex colorを保つならwhiteにします。

vertex colorをruntime更新する

color attributeの値を変更したらneedsUpdateを設定します。

const colorAttribute = geometry.getAttribute("color");
const nextColor = new THREE.Color("#a855f7");

colorAttribute.setXYZ(
  0,
  nextColor.r,
  nextColor.g,
  nextColor.b,
);
colorAttribute.needsUpdate = true;

MaterialのColor uniform変更と、BufferAttribute更新では通知方法が異なります。

opacityはColorとは別に設定する

THREE.ColorはRGB colorを表し、Materialのalphaを保持しません。半透明にする場合はMaterial側を設定します。

material.color.set("#38bdf8");
material.transparent = true;
material.opacity = 0.5;

transparent objectはopaque objectの後に描画され、depth・sortingを含む別の注意があります。完全に透明・不透明を切り替えるだけならvisibilityやalphaTestが適する場合もあります。

色を変えてもneedsUpdateが不要な場合

次のような数値・Color uniformの変更は、通常material.needsUpdateなしで反映されます。

material.color.set("#f43f5e");
material.emissive.set("#7c2d12");
material.emissiveIntensity = 1.5;
material.opacity = 0.8;

一方、mapがnullだったMaterialへtextureを初めて追加する、vertexColorsの有無などshader program構成へ影響するpropertyを変える場合はmaterial.needsUpdate = trueが必要になることがあります。

毎frame無条件にneedsUpdateを設定するとshader再compileにつながり、performanceを悪化させます。

同じMaterialを共有する場合の注意

複数Meshへ同じMaterial instanceを渡すと、色変更はすべてへ反映されます。

const sharedMaterial = new THREE.MeshStandardMaterial({
  color: 0x38bdf8,
});

const meshA = new THREE.Mesh(geometryA, sharedMaterial);
const meshB = new THREE.Mesh(geometryB, sharedMaterial);

sharedMaterial.color.set("#f97316"); // AとBの両方が変わる

片方だけ変えるならMaterialをcloneします。

meshB.material = sharedMaterial.clone();
meshB.material.color.set("#a855f7");

Material arrayを使うMeshでは、mesh.materialが配列の場合もあります。対象indexを確認してColorを変更します。

指定色と画面の色が違う原因

lightの影響を受けている

MeshStandardMaterial、MeshPhysicalMaterial、MeshLambertMaterial、MeshPhongMaterialなどはlightの方向・色・intensity、shadow、environment mapの影響を受けます。

純粋なbase colorを確認するときは一時的にMeshBasicMaterialへ切り替え、color inputとlightingを分離します。

textureのcolorSpaceが未設定

color mapへSRGBColorSpaceを付けていないと、暗い・色味が違う結果になります。normalMapなど非color textureへ誤ってsRGBを付けることも避けます。

Material colorでtextureをtintしている

mapとcolorは乗算されます。texture本来の色を確認するならMaterial colorをwhiteへ戻します。

material.color.set(0xffffff);

setRGBへsRGB値をそのまま渡している

UIや画像編集toolのnormalized RGBはsRGBであることが一般的です。setRGBのdefault working spaceと取り違えると、特に中間toneで差が出ます。CSS stringを使うか、sourceにSRGBColorSpaceを指定します。

tone mapping・post-processingが影響している

tone mapping、exposure、render target、post-processingのoutput conversionにより最終色は変わります。Three.js標準post-processingでは最後にOutputPassを置く構成を確認します。

input colorだけを明るくして補正する前に、input texture、working space、output conversionがそれぞれ1回ずつ正しく設定されているか確認します。

Colorではなく別Materialを変更している

MeshがMaterialをcloneしている、array Materialを使っている、loaderが複数Materialを生成している場合があります。

console.log(mesh.material);
console.log(mesh.material.color);
console.log(mesh.material.uuid);

実際にrenderされるMaterial instanceを確認します。

Color設定チェックリスト

  • Material作成時はcolor: 0xrrggbbまたはCSS stringを使う
  • 生成後はmaterial.color.set()で変更する
  • material.colorへnumberを直接代入しない
  • setRGB / setHSLの0〜1値とsource color spaceを確認する
  • HTML color pickerの#rrggbbはset()へ渡す
  • Color変更だけでmaterial.needsUpdateを乱用しない
  • color mapにはSRGBColorSpace、非color mapにはNoColorSpaceを使う
  • map本来の色を保つならbase colorをwhiteにする
  • emissiveは周囲を照らすLightではない
  • vertex color attributeはLinear-sRGB dataとして用意する
  • shared Materialの変更範囲を確認する
  • 色が違うときはlight・texture・tone mapping・outputを分離して調べる

まとめ

Three.jsのMaterial色は、constructorのcolorへhex numberやCSS stringを渡し、生成後はColor instanceのset・setHex・setStyle・setRGB・setHSLなどで変更します。

現在のThree.jsはLinear-sRGBをworking color spaceとして使い、hex・CSS色をsRGBから自動変換します。direct component、texture、vertex colorではcolor spaceの前提を確認してください。

mapとcolor、emissiveMapとemissive、vertex colorとbase colorは組み合わせて最終色を作ります。指定色と画面が違うときは、Color inputだけでなくlight、texture.colorSpace、tone mapping、output conversionまで順番に切り分けることが重要です。

スポンサーリンク