WebGL & Three.js

Three.js Materialの種類と使い方|選び方・変更・透明度を解説

Three.jsのMaterialを目的別に比較し、MeshBasic・Standard・Physical・Toon・Matcapなどの選び方、生成後の変更、透明度、side、wireframe、needsUpdate、disposeまで初心者向けに解説します。

この記事の目次
  1. Three.jsのMaterialとは
  2. Materialの種類を比較する
  3. MeshBasicMaterialはLightなしで表示する
  4. wireframe表示にも使える
  5. MeshLambertMaterialとMeshPhongMaterial
  6. MeshLambertMaterialは光沢のない表面向け
  7. MeshPhongMaterialは簡易な光沢向け
  8. MeshStandardMaterialを基本のPBRにする
  9. environment mapと組み合わせる
  10. MeshPhysicalMaterialは高度な材質に使う
  11. MeshToonMaterialでcartoon調にする
  12. MeshMatcapMaterialはLightなしで陰影を作る
  13. debug・特殊用途のMesh Material
  14. MeshNormalMaterial
  15. MeshDepthMaterial
  16. ShadowMaterial
  17. Line・Points・Spriteには専用Materialを使う
  18. Materialを比較できる最小scene
  19. 作成後にMaterialの値を変更する
  20. Materialそのものを交換する
  21. needsUpdateが必要な変更を理解する
  22. 透明度はopacityだけでは変わらない
  23. 切り抜きにはalphaTestを使う
  24. alphaHashも選択肢にする
  25. depthWriteを安易に切らない
  26. side・flatShading・wireframe・vertexColors
  27. sideは通常FrontSideのままにする
  28. flatShadingで面を強調する
  29. vertexColorsはbooleanで指定する
  30. textureとMaterialの関係
  31. Materialを共有・clone・複数割り当てする
  32. Geometry groupへ複数Materialを使う
  33. Materialのperformanceを改善する
  34. 不要なMaterialをdisposeする
  35. Materialが反映されないときの確認
  36. objectが真っ黒になる
  37. Lightを追加しても陰影が出ない
  38. opacityを下げても透明にならない
  39. textureの色が違う
  40. Planeが片側から消える
  41. wireframeの線が太くならない
  42. property変更が反映されない
  43. Material交換後にmemory使用量が増え続ける
  44. Material選択チェックリスト
  45. まとめ

Three.jsのMaterial(マテリアル)は、3D objectの表面をどう描画するか決める要素です。色だけでなく、光への反応、金属感、粗さ、透明度、texture、表裏、wireframeなどを制御します。

最初の選択で迷ったら、次を基準にしてください。

  • Light不要の一定色・UI的な表現: MeshBasicMaterial
  • 一般的な3D・PBR表現: MeshStandardMaterial
  • glass・car paint・clothなど高度なPBR: MeshPhysicalMaterial
  • cartoon調: MeshToonMaterial
  • Lightなしで陰影付きmodelを見せる: MeshMatcapMaterial
  • normalのdebug: MeshNormalMaterial

この記事では、Materialの種類を目的別に比較し、作成、変更、透明度、sidewireframeneedsUpdate、共有、破棄まで解説します。色の入力形式と色空間はThree.jsでMaterialの色を設定する方法へ分離し、ここでは「どれを選び、どう扱うか」に集中します。

Three.jsのMaterialとは

Three.jsでは、Geometryが頂点・面などの形状データを持ち、Materialが表面の描画方法を持ちます。両方をMeshに渡してsceneに追加します。

const geometry = new THREE.BoxGeometry(1, 1, 1);

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

const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

MaterialはGeometryそのものを変形しません。同じGeometryでもMaterialを交換すれば、表面の見え方を変えられます。反対に同じMaterialを複数Meshで共有することもできます。

THREE.MeshStandardMaterialを使うべきか」と迷ったときの答えは単純です。Lightを置いて現実的な材質を見せたいならMeshStandardMaterialを選びます。Lightなしで一定の色だけ出したいならMeshBasicMaterial、光沢のないmatteな面を軽く描きたいならMeshLambertMaterialが候補です。いずれもMeshの第2引数に渡す点は同じで、違いはLightへの反応と調整できるpropertyの数です。

Scene・Camera・Rendererを含む最小構成から確認したい場合は、先にThree.js入門チュートリアルを参照してください。

スポンサーリンク

Materialの種類を比較する

Meshに使う主なMaterialを比較すると次のとおりです。

Material Light 特徴 主な用途
MeshBasicMaterial 不要・反応しない 単純で軽量 一定色、背景、小物、debug
MeshLambertMaterial 必要 matteな拡散反射 光沢不要の軽量表現
MeshPhongMaterial 必要 specular highlight 簡易な光沢表現
MeshStandardMaterial 必要 PBR、roughness・metalness 一般的な3D model
MeshPhysicalMaterial 必要 Standardを拡張したPBR glass、car paint、cloth、薄膜
MeshToonMaterial 必要 段階的な陰影 cartoon・cell shading
MeshMatcapMaterial 不要・反応しない MatCap textureで陰影 preview、造形重視の表現
MeshNormalMaterial 不要 normal方向をRGB表示 Geometryのdebug
MeshDepthMaterial 不要 cameraからのdepthを表示 depth effect・debug

高機能なMaterialほど常に優れているわけではありません。必要な見た目を満たす中で、最も単純なものを選ぶとshader計算と調整項目を抑えられます。

MeshBasicMaterialはLightなしで表示する

MeshBasicMaterialはLightの影響を受けません。sceneにLightがなくても表示され、surfaceの向きによる明暗もつきません。

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

Lightの調整なしで一定の色を見せたいobject、背景的なobject、debug表示に向きます。ただし「Lightを追加したのに立体に陰影が出ない」ときは、MeshBasicMaterialを使っていないか確認してください。

wireframe表示にも使える

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

wireframeはtriangle edgeを表示する機能です。輪郭線を自由に描く機能ではなく、Geometryの分割数によって線の密度が変わります。

MeshLambertMaterialとMeshPhongMaterial

どちらもLightに反応する従来型のMaterialです。PBRほど現実の材質に沿ったparameterではありませんが、単純な見た目や軽量化を優先するときに使えます。

MeshLambertMaterialは光沢のない表面向け

const material = new THREE.MeshLambertMaterial({
  color: 0xf97316,
});

matteな拡散反射を表現します。現在の公式docsでは、MeshLambertMaterialはfragment単位でshadingを計算すると説明されています。古い資料にある「vertex単位(Gouraud)で計算するため粗い」という説明は、現行versionには当てはまりません。specular highlightを持たない分、PhongやStandardより計算は軽く、光沢のいらない壁・床・小物に向きます。

MeshPhongMaterialは簡易な光沢向け

const material = new THREE.MeshPhongMaterial({
  color: 0x3b82f6,
  specular: 0xffffff,
  shininess: 80,
});

shininessを上げるとspecular highlightが小さく鋭くなります。既存projectでPhong表現を維持する場合には有効ですが、新規の一般的な3D modelではroughness・metalnessを使うMeshStandardMaterialから検討すると材質を整理しやすくなります。

MeshStandardMaterialを基本のPBRにする

MeshStandardMaterialはmetallic-roughness workflowを使うPBR Materialです。一般的な3D modelの第一候補にできます。調整するpropertyは主にcolorroughnessmetalnessの3つで、Lightとenvironment mapがあれば金属もplasticも同じMaterialで表現できます。

const material = new THREE.MeshStandardMaterial({
  color: 0x94a3b8,
  roughness: 0.35,
  metalness: 0.8,
});
  • roughness = 0: 滑らかでreflectionが鋭い
  • roughness = 1: 粗く、reflectionがぼける
  • metalness = 0: wood、plastic、stoneなど非金属の基準
  • metalness = 1: metalの基準

rustの境界などtextureで連続値を使うことはありますが、均一材質ではmetalnessを0か1から考えると迷いにくくなります。

environment mapと組み合わせる

PBR MaterialはLightだけでなく周囲のenvironmentを反射します。金属を自然に見せるには、処理済みのenvironment mapをscene.environmentに設定することが重要です。背景画像をscene.backgroundに設定しただけでは、Materialを照らすenvironmentにはなりません。

MeshPhysicalMaterialは高度な材質に使う

MeshPhysicalMaterialはMeshStandardMaterialを拡張し、より高度なPBR propertyを追加します。

property 表現例
clearcoat car paint、varnish、wet surface
transmission・thickness・ior glassや透過材
sheen cloth、velvet
iridescence soap bubble、油膜、昆虫の翅
anisotropy brushed metal
const glassMaterial = new THREE.MeshPhysicalMaterial({
  color: 0xffffff,
  roughness: 0.08,
  metalness: 0,
  transmission: 1,
  thickness: 0.4,
  ior: 1.5,
});

MeshPhysicalMaterialはfeatureを有効にするほどpixelあたりのcostが増えます。Standardで足りるobjectまでPhysicalに統一せず、glassなど必要なobjectに限定します。良い結果を得るにはenvironment mapも用意します。

MeshToonMaterialでcartoon調にする

MeshToonMaterialはLightに反応し、段階的な陰影を作ります。

const material = new THREE.MeshToonMaterial({
  color: 0xfacc15,
});

独自の段階数や境界を使う場合は、横1列のgradientMapを指定します。gradient textureはcolor textureとして扱い、filter設定によって階調の境界がぼけないようにします。

cartoon表現はMaterialだけで完成するとは限りません。outline、Light方向、shadow、modelの形状、色設計も合わせて調整します。

MeshMatcapMaterialはLightなしで陰影を作る

MeshMatcapMaterialはMatCap textureから色と陰影を取得し、sceneのLightには反応しません。

const matcapTexture = textureLoader.load("/textures/matcap.png");
matcapTexture.colorSpace = THREE.SRGBColorSpace;

const material = new THREE.MeshMatcapMaterial({
  matcap: matcapTexture,
});

少ない設定で造形を見せやすいため、model previewやstylized表現に向きます。一方、scene内Lightと整合した現実的な照明を作る用途にはStandard・Physicalなどを使います。

debug・特殊用途のMesh Material

一般的な完成表現とは別に、確認や特殊なpassに使うMaterialがあります。

MeshNormalMaterial

view spaceのnormal方向をRGBとして表示します。Lightは不要です。面の向きやnormalが壊れていないかを確認できます。

mesh.material = new THREE.MeshNormalMaterial();

MeshDepthMaterial

cameraのnear・farに基づくdepthを描画します。depth effectやdebugに使います。単純な「cameraから遠いほど任意色に変えるMaterial」ではありません。

ShadowMaterial

shadowを受け取る部分以外を透明に見せたい床などで使います。影を投げるobject用ではなく、影を受け取って合成するsurface向けです。

const shadowMaterial = new THREE.ShadowMaterial({
  color: 0x000000,
  opacity: 0.25,
});

shadow mapの有効化やLight側の設定はThree.jsで影を落とす方法で解説しています。

Line・Points・Spriteには専用Materialを使う

MaterialはMesh用だけではありません。render objectに合う種類を組み合わせます。

object Material 用途
Line・LineSegments LineBasicMaterial 実線
Line LineDashedMaterial 破線
Points PointsMaterial particle・point cloud
Sprite SpriteMaterial camera-facing 2D表示

LineDashedMaterialを使うだけでは破線に必要な距離データがありません。Line作成後にcomputeLineDistances()を呼びます。

const line = new THREE.Line(
  geometry,
  new THREE.LineDashedMaterial({
    color: 0xffffff,
    dashSize: 0.25,
    gapSize: 0.12,
  })
);

line.computeLineDistances();
scene.add(line);

WebGLRendererではlinewidthを1より大きくしても、多くのplatformで太くなりません。太い線が必要ならthree/addonsのLine2系など、幅を持つ別手法を検討します。

Materialを比較できる最小scene

MeshStandardMaterialを使う場合はLightが必要です。次は回転するtorus knotを表示する最小例です。

import * as THREE from "three";

const scene = new THREE.Scene();
scene.background = new THREE.Color(0x111827);

const camera = new THREE.PerspectiveCamera(45, 1, 0.1, 100);
camera.position.set(4, 3, 6);

const renderer = new THREE.WebGLRenderer({ antialias: true });
renderer.setPixelRatio(Math.min(window.devicePixelRatio, 2));
renderer.setSize(800, 600);
document.body.appendChild(renderer.domElement);

const geometry = new THREE.TorusKnotGeometry(1, 0.35, 128, 24);
const material = new THREE.MeshStandardMaterial({
  color: 0x38bdf8,
  roughness: 0.35,
  metalness: 0.15,
});

const mesh = new THREE.Mesh(geometry, material);
scene.add(mesh);

const ambientLight = new THREE.AmbientLight(0xffffff, 0.25);
const directionalLight = new THREE.DirectionalLight(0xffffff, 3);
directionalLight.position.set(4, 5, 3);
scene.add(ambientLight, directionalLight);

renderer.setAnimationLoop(() => {
  mesh.rotation.y += 0.005;
  renderer.render(scene, camera);
});

Materialを比較するときは、Geometry、camera、Light、backgroundを固定し、Materialだけを交換します。条件を同じにしないと、Material差と照明差を混同します。比較中にcameraを回して裏側や反射を確認したいときはOrbitControlsで視点を操作する方法、roughnessやmetalnessをsliderで動かしながら見比べたいときはlil-guiとstats.jsの使い方が役立ちます。Lightの種類と物理光量はThree.js Lightの使い方を参照してください。

作成後にMaterialの値を変更する

多くのpropertyはMaterial生成後にも変更できます。

material.color.set(0xf43f5e);
material.roughness = 0.8;
material.metalness = 0;
material.wireframe = true;

material.colorはnumberではなくTHREE.Color instanceです。既存instanceを保ったまま.set()などで変更します。色変更だけなら通常needsUpdateは不要です。

Materialそのものを交換する

const oldMaterial = mesh.material;

mesh.material = new THREE.MeshNormalMaterial();
oldMaterial.dispose();

元のMaterialをほかのMeshも共有している場合、ここでdisposeしてはいけません。どのobjectが所有しているかを確認してから破棄します。Meshをclone()したときにMaterialが共有されるのか複製されるのかは、Three.jsでオブジェクトを複製する方法で整理しています。

needsUpdateが必要な変更を理解する

Materialの数値を変えるたびにneedsUpdate = trueを設定する必要はありません。color、roughness、metalness、opacityなどuniformとして更新される値は通常そのまま反映されます。

一方、shader programの構成が変わる変更では再compileが必要です。代表例は次のとおりです。

  • flatShadingを切り替える
  • textureがない状態からmapを追加する
  • mapを使う状態から完全に外す
material.map = colorTexture;
material.needsUpdate = true;

毎frame無条件にneedsUpdateを立てると不要なshader compileを招きます。propertyの種類に応じて使い分けます。

透明度はopacityだけでは変わらない

通常のalpha blendingで半透明にするには、transparent: trueopacityを組み合わせます。

const material = new THREE.MeshStandardMaterial({
  color: 0x60a5fa,
  transparent: true,
  opacity: 0.45,
});

transparentがfalseのままでは、opacityを0.45に変えてもMaterialは不透明として扱われます。transparent objectは不透明objectの後に描画され、重なり順のsortingが必要になるため、交差する面や複雑なmodelでは表示破綻が起こる場合があります。

切り抜きにはalphaTestを使う

葉や金網のように「半透明」ではなく「表示するか捨てるか」でよい部分はalphaTestを検討します。

const leafMaterial = new THREE.MeshStandardMaterial({
  map: leafTexture,
  alphaTest: 0.5,
  side: THREE.DoubleSide,
});

alphaTestはthreshold未満のfragmentを描画しないため、通常の半透明sorting問題を避けやすくなります。

alphaHashも選択肢にする

alphaHashはrandom thresholdでfragmentを間引き、alpha blendingのsorting問題を避けながら半透明を近似します。grainが出るため、TAAなどとの組み合わせや見た目のtrade-offを確認します。

depthWriteを安易に切らない

透明objectの重なり問題を避けるためdepthWrite = falseを使う例がありますが、別のocclusion問題を生む可能性があります。alphaTest、object分割、描画順、alphaHashなどを含め、sceneに合う方法を比較してください。

side・flatShading・wireframe・vertexColors

共通propertyは見た目だけでなく描画costや不具合にも影響します。

sideは通常FrontSideのままにする

  • THREE.FrontSide: front faceだけ。default
  • THREE.BackSide: back faceだけ
  • THREE.DoubleSide: 両面

Planeなど両側から見えるsurfaceにはDoubleSideが便利ですが、閉じた立体に無条件に使うと不要な描画が増えます。裏面から消える場合は、まずGeometryのnormalと面の向きを確認します。

flatShadingで面を強調する

material.flatShading = true;
material.needsUpdate = true;

smoothな補間をやめてpolygon面を強調します。切り替え後はshader再compileが必要です。

vertexColorsはbooleanで指定する

現行Three.jsではvertexColors: trueを使います。古いsampleにあるTHREE.VertexColorsTHREE.FaceColorsは使いません。Geometry側にcolor属性が必要です。

const material = new THREE.MeshStandardMaterial({
  vertexColors: true,
});

Geometryの構造やBufferAttributeの更新はThree.js Geometryの種類と変更方法で解説しています。

textureとMaterialの関係

多くのMesh MaterialはmapnormalMaproughnessMapなどを持ちます。ただしtextureのchannelとcolor spaceは用途ごとに異なります。

  • mapemissiveMap: 色データ。一般的な画像はSRGBColorSpace
  • normalMaproughnessMapmetalnessMap: 非色データ。defaultのNoColorSpace
  • Materialのcolormap: 基本的に乗算される
const colorTexture = textureLoader.load("/textures/base-color.jpg");
colorTexture.colorSpace = THREE.SRGBColorSpace;

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

画像をそのまま見せたい場合、colorを白にして不要なtintを避けます。TextureLoader、UV、wrap、filterはThree.jsでtextureを設定する方法へ分離しています。

Materialを共有・clone・複数割り当てする

同じMaterial instanceを複数Meshで共有すると、shaderや設定を再利用しやすくなります。

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

meshA.material = sharedMaterial;
meshB.material = sharedMaterial;

この状態でsharedMaterial.colorを変えると両方が変わります。片方だけ変更したい場合はcloneします。

meshB.material = sharedMaterial.clone();
meshB.material.color.set(0xef4444);

Geometry groupへ複数Materialを使う

Meshの第2引数にMaterial配列を渡し、Geometry groupのmaterialIndexで使い分けられます。

const mesh = new THREE.Mesh(geometry, [materialA, materialB]);

Materialが増えるとgroupごとにdraw callが増えます。単純な色違いのためだけに細かく分割する前に、vertex color、texture atlas、instance属性なども検討します。

Materialのperformanceを改善する

Material選択はGPU負荷とdraw callに影響します。

  • Lightが不要ならMeshBasicMaterialを使う
  • Standardで足りるobjectにPhysicalの高度なfeatureを有効にしない
  • 同一設定のMaterial instanceを共有する
  • Material配列・Geometry groupの増加によるdraw callを確認する
  • 透明objectとDoubleSideを必要な範囲に絞る
  • 毎frameのneedsUpdateを避ける
  • mobile実機でGPU時間と見た目を確認する

公式manualでは、一般にBasic、Lambert、Phong、Standard、Physicalの順でshaderが高機能になり、計算costも増えると説明されています。ただし最終的な負荷はLight、texture、shadow、pixel数、enabled featureなどを含むため、実際のsceneで計測します。

不要なMaterialをdisposeする

Meshをsceneから削除しただけでは、MaterialのGPU resourceは自動解放されません。再利用しないことを確認してdispose()を呼びます。

scene.remove(mesh);

mesh.geometry.dispose();
mesh.material.dispose();

Materialが参照するtextureはMaterial.dispose()だけでは破棄されません。textureも不要なら個別にdisposeします。

material.map?.dispose();
material.normalMap?.dispose();
material.dispose();

共有Material・共有textureを早くdisposeすると、まだ使っているMeshの表示に影響します。resource ownerと参照関係を決めてから解放してください。

Materialが反映されないときの確認

objectが真っ黒になる

MeshStandard・Physical・Phong・Lambert・ToonはLightに反応します。Lightをsceneへ追加し、intensity、objectとの位置、Materialのcolor・metalness、environmentを確認します。切り分け時は一度MeshNormalMaterialかMeshBasicMaterialへ交換すると、Geometryとcameraが正常か確認できます。

Lightを追加しても陰影が出ない

MeshBasicMaterialまたはMeshMatcapMaterialはsceneのLightに反応しません。Light対応Materialに変更します。

opacityを下げても透明にならない

通常の半透明ならtransparent = trueも設定します。切り抜き用途ならalphaTestを比較します。

textureの色が違う

color textureのcolorSpace、Material colorとの乗算、Light、tone mapping、environmentを確認します。normal・roughnessなど非色textureにSRGBColorSpaceを設定しないようにします。

Planeが片側から消える

MaterialのdefaultはFrontSideです。camera位置と面の向きが正しいか確認し、両面が本当に必要な場合だけDoubleSideへ変更します。

wireframeの線が太くならない

wireframeLinewidthwireframeLinecapwireframeLinejoinはWebGLRendererでは期待どおり機能せず、公式docsではSVGRendererのみとされています。太線は専用のline表現で作ります。

property変更が反映されない

colorやroughnessは通常即時反映されます。mapの追加・削除やflatShadingなどshader構成が変わる変更ではneedsUpdateを設定します。変更対象が共有Materialなのかcloneなのかも確認してください。

Material交換後にmemory使用量が増え続ける

古いMaterial、Geometry、Textureが不要になった時点でdisposeします。animation中に毎frame新しいMaterialを生成しないようにします。

Material選択チェックリスト

  • Light不要ならMeshBasicMaterialを選んだ
  • 一般的なPBRはMeshStandardMaterialから試した
  • Physical固有featureが必要なobjectだけMeshPhysicalMaterialにした
  • cartoonならToon、Light不要のpreviewならMatcapを比較した
  • Line・Points・Spriteに専用Materialを使った
  • MaterialとLightの対応を確認した
  • opacityとtransparentを用途に合わせて設定した
  • alphaTest・alphaHash・sortingのtrade-offを確認した
  • sideとDoubleSideを必要な範囲に限定した
  • vertexColorsはbooleanで指定した
  • textureの用途に合うcolorSpaceを設定した
  • needsUpdateを毎frame無条件に設定していない
  • 共有Materialの変更・dispose範囲を確認した
  • 不要なMaterial・Texture・Geometryをdisposeした

まとめ

Three.jsのMaterialは、表面の色だけでなく、Lightへの反応、材質、透明度、texture、表裏、描画costを決めます。迷ったら、Light不要ならMeshBasicMaterial、一般的な3DならMeshStandardMaterial、高度なglass・clearcoat・cloth表現が必要な部分だけMeshPhysicalMaterialから始めます。

Materialが暗い・透明にならない・変更が反映されない問題は、Light対応、transparent、texture color space、needsUpdate、共有instanceを順に確認すると切り分けやすくなります。完成後は実機でperformanceを測り、不要なresourceをdisposeしてください。Materialを決めたあとは、Meshのposition・rotation・scaleの操作に進むと、sceneを組み立てる流れがつながります。

スポンサーリンク