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のcolorはTHREE.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を受ける汎用methodsetHex(): 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などにはemissiveとemissiveIntensityがあります。
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まで順番に切り分けることが重要です。