VRML with JavaScript Tutorial

戻る はじめに / テクスチャー

テクスチャーの生成

VRML ( X3D ) で扱えるテクスチャーは ImageTextureMovieTexture と PixelTexture の3種類ですが、 このうち PixelTexture はスクリプトで画像を描くことができます。  ここではスクリプトによる PixelTexture の生成方法を解説します。

ピクセルテクスチャー

SFImage 型の基本

PixelTexture ノードの image フィールドは SFImage で定義されます。
PixelTexture {
image 2 2 3 0xFF0000 0x00FF00 0x0000FF 0xFFFFFF
}
SFImage は4種類のパラメータで表現されます。  最初と 2 番目のパラメータはそれぞれテクスチャーの横と縦のピクセル数です。

3 番目のパラメータは1ピクセル辺りのデータ量(バイト数)であり、1 から 4 までの整数を指定します。

3番目のパラメータ 表現できるテクスチャー 4番目以降の各パラメータがとる値
1 256段階のモノクロ 0 〜 0xFF
2 256段階のモノクロと256段階の透明度 0 〜 0xFFFF
3 R, G, B がそれぞれ256段階のフルカラー 0 〜 0xFFFFFF
4 フルカラーと256段階の透明度 0 〜 0xFFFFFFFF

4 番目以降のパラメータは各ピクセルの色(と透明度)を示す整数です。  これらは最初と 2 番目のパラメータを掛け合わせた個数(横×縦のピクセル数)が過不足なく必要です。  各ピクセルは16進数以外の整数で示してもかまいませんが、16進数で表現する方が分かり易いでしょう。
最初のピクセルはテクスチャーの左下に位置し、その次のピクセルは先のピクセルの右隣に並びます。  一番下の行が全て埋まると、次のピクセルは1つ上の行の1番左になります。  最後のピクセルはテクスチャーの右上に位置します。
下図は横と縦がそれぞれ8ピクセル、合計 64 個のピクセルで構成されたテクスチャーの場合、順番に指定された各ピクセルが何処に位置するかを示しています。  (最初のピクセルを 0 番目とします。)

横8ピクセル×縦8ピクセルのテクスチャー
( 0.0, 1.0 ) ( 1.0, 1.0 )
texture image
( 0.0, 0.0 ) ( 1.0, 0.0 )

テクスチャーの横のピクセル数を x とした場合、左から s 番目、下から t 番目のピクセルは、最初から [ s + t * x ] 番目です。  ( s と t は 0 から始まる整数です。 )  上図の点 A に位置するピクセルは 左から 4 番目、下から 3 番目の為、最初から [ 4 + 3 * 8 ] = 28 番目の要素となります。


pixcel texture
VRML - ソース / X3D - ソース
これは横 2 ピクセル、縦 2 ピクセルのフルカラー(透明度なし)のテクスチャーを、 赤 ( 0xFF0000 )、緑 ( 0x00FF00 )、青 ( 0x0000FF )、白 ( 0xFFFFFF ) の 4 ピクセルで表現した例です。  テクスチャーの左下が赤、右下が緑、左上が青、右上が白となります。

滲んで表示されるテクスチャーの対処
smooth pixcel texture グラフィックアクセラレータのテクスチャー・スムージング表示機能が働いている場合は左のように表示されます。  上の画像のようにテクスチャーを滲ませずに表示させるには以下の設定変更を行ってください。
Cosmo の場合、初期設定のOpenGLレンダラーならば、低解像度のテクスチャーに対してはスムージング機能が働きません。  GLView 4.4 の場合、グラフィックアクセラレータに関わらずテクスチャー・スムージング表示機能は働きません。)

SFImage の新規生成

pixcel texture 先の作例と同じピクセルテクスチャーをスクリプトで生成してみましょう。


VRML - ソース / X3D - ソース
スクリプトで SFImage オブジェクトを新規に生成する場合、 事前に各ピクセルの色データが収まった MFInt32 オブジェクトが必要です。
DEF Sc Script {
eventOut SFImage image
field MFInt32 array [
0xFF0000 0x00FF00 0x0000FF 0xFFFFFF
]
url "javascript:
function initialize () {

image = new SFImage ( 2, 2, 3, array );

}
"
}
VRML - ソース / X3D - ソース
先の作例ではピクセルデータをスクリプトの外に記述しましたが、勿論スクリプトの中でMFInt32 オブジェクトを生成し、 それにピクセルデータを代入しても構いません。
DEF Sc Script {
eventOut SFImage image
url "javascript:
function initialize () {

var ary = new MFInt32 ();
ary[0] = 0xFF0000; // red
ary[1] = 0x00FF00; // green
ary[2] = 0x0000FF; // blue
ary[3] = 0xFFFFFF; // white

image = new SFImage ( 2, 2, 3, ary );

}
"
}
但し、透明度付きのフルカラーテクスチャーの場合はピクセルデータの代入には注意が必要です。  先の作例に透明度のデータを付加させてみましよう。

contcat bug
VRML - ソース / X3D - ソース
Contact に限り、この作例を実行すると左の画像( Smooth Textures をオフにしています)の様にテクスチャーの左下の赤と右上の白が水色になります。  Cosmo Cortona の場合は先の作例と同じ 4 色のテクスチャーが表示されます。
DEF Sc Script {
eventOut SFImage image
url "javascript:
function initialize () {

var ary = new MFInt32 ();
ary[0] = 0xFF0000FF; // red
ary[1] = 0x00FF00FF; // green
ary[2] = 0x0000FFFF; // blue
ary[3] = 0xFFFFFFFF; // white

image = new SFImage ( 2, 2, 4, ary );

}
"
}
これは変数に 0x80000000 以上の数値を直接代入すると、強制的に 0x7FFFFFFF に置き換わってしまうという Contact 特有の不具合によるものです。 
VRML - ソース / X3D - ソース
この不具合を回避するにはこの作例のように、まず変数に 0x7FFFFFFF 以下の数値を代入してから加算もしくは乗算してください。
DEF Sc Script {
eventOut SFImage image
url "javascript:
function initialize () {

var ary = new MFInt32 ();
ary[0] = 0xFF0000; // red
ary[1] = 0x00FF00; // green
ary[2] = 0x0000FF; // blue
ary[3] = 0xFFFFFF; // white

ary[0] = ary[0] * 256 + 0xFF;
ary[1] = ary[1] * 256 + 0xFF;
ary[2] = ary[2] * 256 + 0xFF;
ary[3] = ary[3] * 256 + 0xFF;

image = new SFImage ( 2, 2, 4, ary );

}
"
}

大きなテクスチャーを描く

ここまでの作例はたった 4 ピクセルだけのテクスチャーでしたが、もっと大きなサイズのテクスチャーを扱ってみましょう。

paint
VRML - ソース / X3D - ソース
64 × 64 ピクセルのテクスチャーに 4 色の直線を描き、中央を黒く塗ってみました。
横 dx ピクセル、縦 dy ピクセル、合計 dx × dy = px 個のピクセルを使ったテクスチャーに対して、ある色をある場所に適応させる例を以下に示します。
テクスチャー全体の色情報を納める配列を ary ( 要素数が px 個の MFInt32 オブジェクト)、 適応させたい色を納める変数を color (整数)、 ピクセルの場所を示す x0, y0, x1, y1 はそれぞれ 0 以上の整数であり、0 <= x0 < x1 < dx, 0 <= y0 < y1 < dy とします。
テクスチャー全体をある色で塗りつぶす
for ( i = 0; i < px; i++ ) ary[i] = color;

黒く塗りつぶすなら例えば以下の様に空の要素を px 個用意するだけでもよい。
ary = new MFInt32(); ary.length = px;
左から x0 番目、下から y0 番目のピクセルに色を塗る
ary[ x0 + y0 * dx ] = color;
下から y0 番目の水平ラインに対して、左から x0 番目のピクセルから x1 までの横線を引く
for ( s = x0; s <= x1; s++ ) ary[ s + y0 * dx ] = color;
左から x0 番目の垂直ラインに対して、下から y0 番目のピクセルから y1 までの縦線を引く
for ( t = y0; t <= y1; t++ ) ary[ x0 + t * dx ] = color;
左下のピクセル ( x0, y0 ) から右上のピクセル ( x1, y1 ) を対角線とする四角形とその内側を塗りつぶす
for ( t = y0; t <= y1; t++ ) for ( s = x0; s <= x1; s++ ) ary[ s + t * dx ] = color;


paint
VRML - ソース / X3D - ソース
テクスチャーの中央を中心とし、テクスチャーの 1 辺と同じ長さの直径を持つ円を描いてみました。 
paint
VRML - ソース / X3D - ソース
陰影のある球を描いてみました。  DirectionalLight ノードの direction フィールドの値を変えると陰影の付きかたが変化します。

ポインタドラッグによる線描

ある形状をキャンバスに見立てて、マウスなどのポインタでその表面をドラッグ操作することにより、それに貼られたピクセルテクスチャーによる自由な線を描いてみましょう。

クリックした場所に点を描く

まず簡単な例として、線ではなくクリックされた箇所に点を加えるスクリプトです。

paint
VRML - ソース / X3D - ソース
青い箱の表面をクリックすると、そこに黄色い点が加えられます。

// SFVec2fオブジェクトで示されたテクスチャー座標に penColor を適応させる為の関数。
function pset(v) {

var tv = v.multiply ( dt );
var s = Math.floor( tv.x );
var t = Math.floor( tv.y );

s = Math.max ( 0, Math.min( dt - 1, s ));
t = Math.max ( 0, Math.min( dt - 1, t ));

ary[ s + t * dt ] = penColor;

}
pset 関数の引数 v はポインタでクリックされたキャンバス(この場合、箱の表面)上のテクスチャー座標であり、2次元ベクトル ( SFVec2f オブジェクト ) です。 v はテクスチャーの一番左下を( 0.0, 0.0 )、一番右上を( 1.0, 1.0 ) とした範囲の中で表されます。  v が具体的にどいういう値をとるかはテクスチャー座標変換の最初の作例をご覧ください。

pset 関数は v がテクスチャーのどのピクセルを指すのかを求めます。  dt はテクスチャーの縦もしくは横のピクセル数です。  v と dt から、クリックされた場所がテクスチャーの左から何番目のピクセルか、下から何番目のピクセルかを求め、それぞれを変数 s と t に代入します。  s と t はそれぞれ 0 以上 dt 未満の整数となります。

最後にテクスチャーの各ピクセルの色情報が納められている配列 ary の目標となる要素を penColor に変更して、この関数は終わります。



initialize 関数で生成された SFImage オブジェクトに対して、penUpDowned 関数ではそれに対して配列の要素を変更しただけなのだから、 以下のように array プロパティへの代入で構わないのではと思うかもしれません。
function penUpDowned (active) {

if (active) {

// キャンバス上でポインタのボタンが押されたら、ポインタの座標に点を描く。
pset( hitPoint );
image.array = ary;

}

}
しかし、これが正常に機能するのは Cosmo の場合のみです。  Cortona Contact ではエラーを出すなど機能いたしません。  よって、SFImage オブジェクトは配列要素の変更だけであっても新規生成するようにしています。  この例に限らず、何れのVRMLブラウザーであっても SFImage オブジェクトのプロパティの読み書きは何らかの不具合があります。  SFImage オブジェクト



Script {
field SFColor colA 0.3 1.0 0.3 # 球の色
}
先までの作例では上記のように色を SFColor オブジェクトで定義し、スクリプト内の SFColor2Int 関数で整数に変換していましたが、 この作例では以下のように SFInt32 オブジェクトで色を定義しています。 
Script {
field SFInt32 penColor 0xFFFF00 # 点の色
}
どちらのオブジェクトで色を定義するかは、スクリプトの作り易さや処理速度などを理由に自由に決めていただいて構いませんが、 X3D の場合 SFInt32 オブジェクトの定義は注意が必要です。
<Script>
<field accessType='initializeOnly' type='SFInt32' name='penColor' value='0xFFFF00'/>
<field accessType='initializeOnly' type='SFInt32' name='bgColor' value='255'/>
<field accessType='initializeOnly' type='MFInt32' name='array' value='0xFF0000 0x00FF00 0x0000FF 0xFFFFFF'/>
</Script>
BS Contact VRML/X3D の場合、SFInt32 オブジェクトの初期値を 0xFFFF00 のように16進数で記述すると、 初期値に関わらずスクリプトから見たこのオブジェクトの値は 0 になります。  尚、X3Dのエンコーディング仕様によると SFInt32 は16進数による記述も認められているので、これは BS Contact VRML/X3D の不具合だと思われます。  この不具合は値を10進数で記述するとにより回避できます。  ちなみに MFInt32 オブジェクトは16進数による記述でも正しくスクリプトから読み込めます。

ドラッグした2点間に直線を引く

paint
VRML - ソース / X3D - ソース
青い箱の表面をドラッグすると、ドラッグ開始点から終点までの間に直線が引かれます。
スクリプトの lineset 関数は開始点から終点まで必要な数だけ pset 関数を呼び出すことにより、点を繋げて直線を描いています。

ドラッグによるフリーハンド曲線を描く

paint
VRML - ソース / X3D - ソース
青い箱の表面のドラッグに従って曲線が描かれます。
ドラッグ中のカーソル移動ごとに点が描かれるのではなく、カーソルがある程度の小距離を移動するごとに短い直線を描いています。  これは SFImage オブジェクトを凄く大量に新規生成すると、環境によってはスワップファイルでハードディスクが埋まりハングアップすることを出来るだけ避ける為の措置です。

綺麗な斜め線を描く

先の2例は描かれた斜め線がギザギザと汚かったのですが、アンチエイリアスっぽい処理を加えて綺麗にしてみましょう。

paint
VRML - ソース / X3D - ソース
青い箱の表面をドラッグすると、ドラッグ開始点から終点までの間に滑らかな直線が引かれます。

paint
VRML - ソース / X3D - ソース
青い箱の表面のドラッグに従って滑らかな曲線が描かれます。
psetAA 関数ではまず、本来描かれるピクセルとそれを取り囲む8個のピクセルに対して、テクスチャーコード座標とピクセルの中心座標との距離を導きます。  その距離が短い程ペンの色に、遠ざかるほど元のピクセルの色に近くなるように、9個の各ピクセルの色を決めています。
このページのトップ | 前へ 貼り方の指定 | 次へ サンプル集