p5.js WebGL入門 3 ライト、マテリアル

本稿は「18.3: Light and Material – WebGL and p5.js Tutorial」YouTube動画を元にしています。

概要

はじめに、3DCGに出て来るライトやマテリアルなどの重要な構成要素について、Blenderアプリを使って簡単に述べておきます。

Blenderを使って


上図全体の薄暗い部分はBlenderの画面で、3D世界のセンターに赤い立方体があります。この立方体を見ているのが左にあるカメラです。3Dアプリでは、カメラを通して3D世界を見て、それをレンダリング(描画)します。

上にはこの3D世界を照らすライトがあります。実際のライトと同様、ライトの種類や配置する位置、光の角度によって、レンダリング結果が変わります。

立方体を赤く見せているのは、マテリアルと呼ばれる、質感を決める属性です。Blenderでは下図のパネルで、立方体にマテリアルを割り当てます。

この設定でレンダリングしたのが下図です。立方体は赤いマテリアルを当てているので赤く見え、上からライトを当てているので、立方体の上面がほかの面より明るく見えます。また影の部分は薄暗くなっています。

p5.js WEBGLモードでは、こういった要素を全部コードで作成、設定していきます。

ライトとマテリアル

ライトは3D空間を照らす照明のことで、アンビエントライト(環境光)、ディレクショナルライト(方向性ライト)など、さまざまなものがあります。マテリアルは、ジオメトリの表面に当たったライトのカラーをどのように反射するかを決めるもので、ジオメトリに設定するマテリアルによって見え方が変わります。

たとえばライトとマテリアルを次のように設定すると、黄色いピンボール球のように見える下図の結果が得られます。p5.jsでは、ライトやマテリアルの関数を組み合わせて使用することで、さまざまな3D表現を実現することができます(とはいえ、p5.jsの3Dは本格的なものではないので、本格的にJavaScriptの3Dに取り組みたい方には、three.jsなど、3Dに特化したほかのライブラリをおすすめします)。

// 環境光の色を濃いグレーにする
ambientLight(30);
// 方向性ライトの色を黄色に、照らす方向を上手前からにする
const v = createVector(0, 1, -1);
directionalLight(255, 255, 0, v);
// 反射する色を黄色にする
ambientMaterial(255, 255, 0);
sphere(50);

基本マテリアル

3Dジオメトリには、2D図形と同様、fill()やstroke()関数で表面の色や線の太さ、色が設定できます。これは基本的なマテリアルと見なすことができます。ただしstroke()関連のコードを削除し代わりにnoStroke()を使うと、結果はべた塗りになり、3Dには見えなくなります。

// 基本マテリアル
fill(255, 255, 0);
strokeWeight(0.3);
stroke(200, 0, 0);

sphere(50);

ノーマル(法線)マテリアル

normalMaterial()関数を使用すると、とりあえずのマテリアルが設定できます。ライトの影響を受けず、X軸の正方向を向く面は赤、Y軸の正方向を向く面は緑、Z軸の正方向を向く面は青になります(各色はdebugMode()で表示される軸の矢印の色に対応)。

normalMaterial()

ライトの影響を受けないマテリアルで反射しない。多くの場合デバッグ用のプレースホルダ(暫定的な)マテリアルに用いられる。X軸の正方向を向く面は赤、Y軸の正方向を向く面は緑、Z軸の正方向を向く面は青になる。使用できるすべてのマテリアルはこのサンプルで見ることができる。
シンタックス
normalMaterial()

アンビエントライト

アンビエントライトは環境光と呼ばれるライトで、すべてのジオメトリの表面を等しく照らします。方向と光源の位置を持たず、ほかのライトで陰になった場所はこのライトで照らされます。

// 黄色のアンビエントライト
ambientLight(255, 255, 0);
box(100);

上記コードでは、ほかにライトはなく、マテリアルも設定されていないので、立方体の表面はアンビエントライトの色で描画されます。

ambientLight()

環境光をカラーで作成する。環境光は、キャンバス上のどこからでも来る光で、特定の光源を持たない。

シンタックス
ambientLight(v1, v2, v3, [alpha])
ambientLight(value)
ambientLight(gray, [alpha])
ambientLight(values)
ambientLight(color)

パラメータ
v1 数値: 現在のカラー範囲にもとづく赤か色相の値
v2 数値: 現在のカラー範囲にもとづく緑か彩度の値
v3 数値: 現在のカラー範囲にもとづく青か明度の値
alpha 数値: アルファ値(オプション)
value 文字列: カラー文字列
gray 数値: グレー値
values 数値[]: カラーの赤緑青とアルファ成分を含んだ配列
color p5.Color: 環境光のカラー

アンビエントマテリアル

アンビエントマテリアルは照らされるライトに反射するジオメトリの色を決めます。上記の黄色のアンビエントライトに対して、マテリアルを黄色にすると、立方体の表面は黄色に描画されます(下図の左)。

// 黄色のアンビエントライト
ambientLight(255, 255, 0);
// 黄色のマテリアル
ambientMaterial(255, 255, 0);

同じ黄色のアンビエントライトに対して、マテリアルを赤色にすると、立方体の表面は赤色に見えます(下図の右)。しかしこの色を描画ツールで抽出して調べると、255,26,0という色、つまりほんの少し黄色みがかった赤であることが分かります。

// 黄色のアンビエントライト
ambientLight(255, 255, 0);
// 赤色のマテリアル
ambientMaterial(255, 0, 0);

アンビエントライトは、ほかのライトからの照射で陰になる部分を照らすものであり、アンビエントマテリアルはその部分の質感を演出するものなので、次のようにほかのライトと組み合わせるときにその特徴が生きます。

// グレーのアンビエントライト
ambientLight(200);
// 左上から照らす黄色のポイントライト
pointLight(255, 255, 0, -120, -120, 0);

// グレーのアンビエントマテリアル
ambientMaterial(200);

ambientMaterial()

指定されたカラーを持つ環境光マテリアル。オプジェクトがすべてのライト下で反射するカラーを定義する。たとえば、環境光マテリアルが純粋な赤で、環境光が緑の場合、オブジェクトはどのライトも反射しない。使用できるすべてのマテリアルはこのサンプルで見ることができる。
シンタックス
ambientMaterial(v1, [v2], [v3])
ambientMaterial(color)
パラメータ
v1 数値: 赤か色相の値(その時点でのカラーモードによる)
v2 数値: 緑か彩度の値(オプション)
v3 数値: 青か明度の値(オプション)
color 数値[]|文字列|p5.Color: カラー配列、CSSカラー文字列、p5.Color値

ポイントライト

ポイントライトは点光源と呼ばれ、上記イラストのような電球と考えることができます。ライトの色と位置を持ち、ジオメトリはそこから照らされることになります。pointLight()関数には、ライトのカラーと位置を指定します。

pointLight()

カラーと光の位置を持つ点光源ライトを作成する。1度に最大5個の点光源ライトを有効にできる。

シンタックス
pointLight(v1, v2, v3, x, y, z)
pointLight(v1, v2, v3, position)
pointLight(color, x, y, z)
pointLight(color, position)

パラメータ
v1 数値: 赤か色相の値(その時点でのカラーモードによる)
v2 数値: 緑か彩度の値
v3 数値: 青か明度の値
x 数値: X位置
y 数値: Y位置
z 数値: Z位置
position p5.Vector: ライトの位置
color 数値[]|文字列|p5.Color: カラー配列、CSSカラー文字列、p5.Color値

ディレクショナルライト

ディレクショナルライトは方向性ライトと呼ばれます。光源の位置を持たず、方向を持ちます。このライトは遠くからやってくる太陽光と考えることができます。directionalLight()には、ライトのカラーと方向を指定します。

次のコードでは、球体を宇宙に浮かぶ地球に見立て、アンビエントライトを濃紺にしています。そしてディレクショナルライトを使って、左方向から照らしています。

// 濃紺のアンビエントライト
ambientLight(0, 50, 255);
// 左から照らす、白色の方向性ライト
directionalLight(255, 255, 255, 1, 0, 0);

// 白色のアンビエントマテリアル
ambientMaterial(255);

directionalLight()関数の最も分かりやすい書き方はdirectionalLight(v1, v2, v3, x, y, z)です。v1, v2, v3にはRGB値を指定し、x, y, zには各軸がどっちを向いているかを1か0,-1で指定します。このx,y,zは原点(0,0,0)からの単位ベクトルと見なすことができます。上記の(1,0,0)は(0,0,0)から(1,0,0)へのベクトルなので、光は左から来ます。

directionalLight()

カラーと方向を持つ指向性ライトを作成する。1度に最大5個の指向性ライトを有効にできる。

シンタックス
directionalLight(v1, v2, v3, position)
directionalLight(color, x, y, z)
directionalLight(color, position)
directionalLight(v1, v2, v3, x, y, z)

パラメータ
v1 数値: 赤か色相の値(その時点でのカラーモードによる)
v2 数値: 緑か彩度の値
v3 数値: 青か明度の値
position p5.Vector: ライトの方向
color 数値[]|文字列|p5.Color: カラー配列、CSSカラー文字列、p5.Color値
x 数値: X軸の向き
y 数値: Y軸の向き
z 数値: Z軸の向き

x,y,zパラメータは少し混乱するかもしれないので、下図にまとめました。

スペキュラマテリアル

スペキュラマテリアルは、照らされる光を反対方向に跳ね返すマテリアルです。specularMaterial()関数に反射するカラーを指定します。

// 陰になる部分を照らすグレーのライト
ambientLight(100);
// 右上手前からのポイントライト
pointLight(255, 255, 255, 50, -100, 80);
ambientMaterial(100);
// 白いポイントライトを逆方向に跳ね返す => 照らされた部分が白く光っているように見える
specularMaterial(255);

specularMaterial()

与えられたカラーを持つスペキュラ(鏡面)マテリアルで、光沢がある。アンビエントマテリアルと同様に、オブジェクトがアンビエントライト下で反射する色も定義する。たとえば、スペキュラマテリアルが純粋の赤で、アンビエントライトが緑の場合、オブジェクトは光を反射しない。ポイントライトやディレクショナルライトなどほかのタイプのライトに対しては、光源の色を反射する。すべてのマテリアルはこのサンプルで見ることができる。
シンタックス
specularMaterial(v1, [v2], [v3])
specularMaterial(color)
パラメータ
v1 数値: グレー値か、赤または色相の値(その時点でのカラーモードによる)
v2 数値: 緑か彩度の値
v3 数値: 青か明度の値
color 数値[]|文字列|p5.Color: カラー配列、CSSカラー文字列、p5.Color値

specularColor()とshininess()

specularMaterial()関数と、specularColor()とshininess()関数を併用すると、より自然な光沢が表現できます。

ambientLight(100);
shininess(20);
pointLight(255, 255, 255, 50, -100, 80);
specularColor(255);

ambientMaterial(100);
specularMaterial(255);

specularColor()

スペキュラマテリアルとスペキュラライトを使用しているときのスペキュラハイライトの色を設定する。
この関数をspecularMaterial()とshininess()関数と組み合わせることで、スペキュラハイライト(光沢のある強い反射)が設定できる。デフォルトのカラーは白(255,255,255)で、specularColor()がspecularMaterial()より前に呼び出されない場合に使用される。specularMaterial()なしで呼び出されると、効果はない。
ノート:specularColor()はProcessing関数のlightSpecular()と同等。

シンタックス
specularColor(v1, v2, v3)
specularColor(value)
specularColor(gray)
specularColor(values)
specularColor(color)
パラメータ
v1 数値: 現在のカラー範囲を基準にした赤か色相の値
v2 数値: 現在のカラー範囲を基準にした緑か彩度の値
v3 数値: 現在のカラー範囲を基準にした青か明度
value 文字列: カラー文字列
gray 数値: グレー値
values 数値[]: カラーの赤、緑、青、アルファ成分を含む配列
color p5.Color: アンビエントライトのp5.Color値

shininess()

シェイプ表面の光沢量を設定する。specularMaterial()と併用して、シェイプのマテリアル特性を設定する。デフォルト値と最小値はともに1。
シンタックス
shininess(shine)
パラメータ
shine 数値: 光沢の度合い。デフォルトは1。

スポットライト

ポイントライトと似ていますが、実際のスポットライトと同様、光源を頂点とする円錐の中だけを照らします。したがって、位置や照らす方向に加え、照らす角度が指定できます。

次のコードでは、ライトのカラー(255,255,255)、位置(130,10,20)、方向(-1,0,0)、角度(20)と光の集積度合い(1)を指定しています。

ambientLight(200);
spotLight(255, 255, 255, 130, 10, 20, -1, 0, 0, 20, 1);
ambientMaterial(200);

引数として渡す数値が多くて分かりづらい場合には、次のように値を個別に作成します。

const col = color(255, 255, 255);
const pos = createVector(130, 10, 20);
const dir = createVector(-1, 0, 0);
const ang = 20;
const conc = 1;
spotLight(col, pos, dir, ang, conc);
spotLight()

指定されたカラー、位置、方向、角度、光の集まり度合いを使ってスポットライトを作成する。角度はスポットライトの円錐の中心角で、集積度合いはライトをセンターに向けて集積するために用いられる。角度と集積度合いはともにオプションだが、集積度合いを指定する場合には、角度も指定する必要がある。
一度に有効にできる最大数は5個。
シンタックス
spotLight(v1, v2, v3, x, y, z, rx, ry, rz, [angle], [conc])
spotLight(color, position, direction, [angle], [conc])
spotLight(v1, v2, v3, position, direction, [angle], [conc])
spotLight(color, x, y, z, direction, [angle], [conc])
spotLight(color, position, rx, ry, rz, [angle], [conc])
spotLight(v1, v2, v3, x, y, z, direction, [angle], [conc])
spotLight(v1, v2, v3, position, rx, ry, rz, [angle], [conc])
spotLight(color, x, y, z, rx, ry, rz, [angle], [conc])
パラメータ
v1 数値: 赤または色相の値(その時点でのカラーモードによる)
v2 数値: 緑か彩度の値
v3 数値: 青か明度の値
x 数値: X位置
y 数値: Y位置
z 数値: Z位置
rx 数値: ライトのX方向
ry 数値: ライトのY方向
rz 数値: ライトのZ方向
angle 数値: 角度。デフォルトはPI/3(オプション)
conc 数値: 集積度。デフォルトは100(オプション)
color 数値[]|文字列|p5.Color: カラー配列、CSSカラー文字列、p5.Color値
position p5.Vector: ライトの位置
direction p5.Vector: ライトの向き

エミッシブマテリアル

エミッシブマテリアルを使用すると、自己発光が表現できます。次のコードでは、自転する大きな球体の周りを、黄色く光る小さな球体が回ります。なお、小さな球体は遠ざかると小さくなり、近づくと大きくなり、また大きな球体の向こうに隠れます。こういった操作はp5.js WEBGLモードの舞台裏で自動的に行われています。

ambientLight(0, 50, 255);
ambientMaterial(200);

rotateY(angle * 1.3);
sphere(50);

rotateY(angle * 0.5);
translate(100, 0, 0);
// 黄色に自己発光
emissiveMaterial(255, 255, 0);
sphere(20);
emissiveMaterial()

画面に描画されるジオメトリのマテリアルのエミッシブカラー(放出色)を設定する。これは、このマテリアルが実際には、周囲のポリゴンに影響する光を放出していないという意味で、適切な名前ではない。放出ではなく輝いて見えるといった方が正しい。エミッシブマテリアルは、反射する光がない場合でも、完全な強度で表示される。

シンタックス
emissiveMaterial(v1, [v2], [v3], [a])
emissiveMaterial(color)
パラメータ
v1 数値: グレー、赤または色相の値(その時点でのカラーモードによる)
v2 数値: 緑か彩度の値(オプション)
v3 数値: 青か明度の値(オプション)
a 数値: 不透明度 (オプション)
color 数値[]|String|p5.Color: 数値[]|文字列|p5.Color: カラー配列、CSSカラー文字列、p5.Color値

ポイントライトの減衰

lightFalloff()関数を使うと、ポイントライトの減衰、つまりライトに近い表面は明るく、遠い表面は暗くなる現象が表現できます。

次のコードでは、ポイントライトの位置をマウスの位置で決めているので、ポイントライトが移動します。左の球体の描画にはlightFalloff()関数を利用し、右の球体には使っていません。

ambientLight(25);
// マウスでポイントライトの位置を動かす
pointLight(250, 255, 0, mouseX - width / 2, mouseY - height / 2, 100);

translate(-120, 0, 0);

// 左の球体に減衰を適用
push();
lightFalloff(0.6, 0.01, 0);
ambientMaterial(250);
sphere();
pop();

translate(240, 0, 0);

// 右の球体には適用しない
push();
ambientMaterial(250);
sphere();
pop();

マウス操作でポイントライトが移動します。lightFalloff()関数を適用した左の球体の表面は、ライトが近づくと明るく、遠くなると暗くなります。

lightFalloff()

ポイントライトの減衰率を設定する。この関数は、以降に作成された要素にのみ影響する。デフォルト値はlightFalloff(1.0, 0.0, 0.0)で、パラメータは次の方程式で減衰の計算に使われる。

falloff = 1 / (CONSTANT + d * LINEAR + ( d * d ) * QUADRATIC)
* CONSTANTは定数減衰、LINEARは線形減衰、QUADRATICは2次減衰、dはライトから頂点までの距離

シンタックス
lightFalloff(constant, linear, quadratic)
パラメータ
constant 数値: 減衰を決めるための定数値
linear 数値: 減衰を決めるための線形値
quadratic 数値: 減衰を決めるための2次値

そのほかのライト関数
lights()

デフォルトのアンビエントライトとディレクショナルライトを設定する。デフォルトはambientLight(128, 128, 128)とdirectionalLight(128, 128, 128, 0, 0, -1)。ライトをループするプログラムでずっと維持しておきたい場合には、draw()関数内に置いておく必要がある。setup()内に置くと、効果はループの初回しか得られない。
シンタックス
lights()

noLights()

この関数は、この関数以降にレンダリングされるマテリアルのライトをすべて削除する。これは後続の関数すべてに影響する。noLights()以降にライト関数を呼び出すしたライトは有効になる。
シンタックス
noLights()

“p5.js WebGL入門 3 ライト、マテリアル” への5件の返信

  1. WEBのフロントをやっているものです。

    WEBのフロントの技術でデジタルアート動画を作成したいです。
    cssとp5jsはかなり違うので関数名の違い(colorがfillなど)に苦しんでいますが、processing(p5js)はjQueryのようなcssライクなものがないでしょうか?

    p5.domという物を使ってもキャンバス内でcssを使ったり生のjsでcssプロパティを使う事は出来ないでしょうか?

    無理な場合HTML5のcanvasでデジタルアート動画を作成してp5jsでないと実現が難しい3Dアートの時だけp5jsの関数を生のjsとごちゃませにして使う事は出来ますか?
    もしそうならこちらはcssを使えそうなのでp5jsメインで作成せずにHTMLcanvasをメインに使ってみようと思います。

    1. 本記事をお読みいただきありがとうございます。

      p5.jsは主に、htmlのキャンバス要素に描画するためのJavaScriptライブラリです。
      CSSは、htmlの各種要素のスタイルを設定するもので、p5.jsでは、style()関数で設定できます。

      style()関数

      p5.domは、p5.jsからhtmlの各種要素を扱えるようにするライブラリで、今ではp5.js本体に含まれていると思います。

      素のJavaScript3Dキャンバスの操作は相当面倒で、通常はthree.jsだかの専門ライブラリを使うのがいいようです。
      p5.jsは3D専門でないので、スピードや機能の面で劣るそうです。

      ごちゃまぜでもできなくはないのでしょうが、相当面倒だと思います。

  2. お返事ありがとうございます。
    下記のようにやってみましたが色が赤くなりませんでした。

    // fill(255,255,200);
    torus(width * 0.3, width * 0.1, 9, 5);
    style.color = ‘red’;

    p5jsで作った要素?物体はCSSで色を変えたりWEBのようにできないのでしょうか?

    idもふれないですよね。

    もちろんtorusのようなp5jsを使わずにdivなどで作れば当てることは可能ですが
    これだとおそらくキャンバス内でwebと同じことをしているだけになるのですよね。

    無理やりtorusにcssを適応されることは出来ないのでしょうか?

    fillなどあまりに違いすぎるのでもう一度0から覚えなおすのがあまりに大変なのと
    使っていないとcssなどのWEBの技術を忘れてしまうのですが
    無理でしょうか?

    もしそうならHTML5キャンバスでデジタルアートを作成するしかないですかね。
    これならおそらくp5jsと同じキャンバス内でデジタルアート動画を作成できるのですよね。

    もうすぐjQueryのようなcssそっくりなものが出来るならこちらを待ちますがおそらくでないでしょうね

    1. 一足飛びにp5.jsの3Dに取り組もうとしておられるのでしたら、遠回りでも、最初から学ばれるのがいいと思います。
      p5.js WebGL入門
      p5.js スピード 入門

      style()は、p5.jsで作成したHTML要素(h1とかpとか)のCSSを設定するもので、p5.jsで作成したHTML要素から呼び出します。
      キャンバスに描画される描画物は、そこにあるように見えますが、実際にはキャンバス全面が高速で描きし直されている結果です。

  3. style()は、p5.jsで作成したHTML要素(h1とかpとか)のCSSを設定するもので、p5.jsで作成したHTML要素から呼び出します。
    >>>

    キャンバスに描画される描画物にはcssを適応できないのですね。
    色を変えたくてもcolorでは無理でfillを覚えるしかないんですね。

    全部覚えなおしは大変ですが今後もこのままでしょうから仕方がないですね。

    しかし描画物ではない?HTML要素にはcssを適応できるのでキャンバス内ですべてHTML要素だけにすれば
    cssを適応できるのでp5jsで作った描画物を作らなければwebでデジタルアート動画を作成できるのですね。

    キャンバスに描画される描画物は、そこにあるように見えますが、実際にはキャンバス全面が高速で描きし直されている結果です。
    >>>
    WEBは要素は一度描画したものはそのまま表記されていますが
    p5jsで作った描画物は作られて消えて、また作られてを繰り返しているのですね。
    それでずっと存在するように錯覚するのですね。

    やはりwebとは全く違う世界なのでかなり学習コストがかかりそうですね。

    p5jsで作った描画物をCSSで色を変えたりするのは不可能なのであきらめます。

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA