J3W ver. 5.60 でポリゴンにテクスチャを貼ることができます。 テクスチャ専用の命令を10種類追加しました。 ここではテクスチャの使い方をサンプルプログラムで解説します。(すべてJ3W 5.60 に含まれています。) 同じコードが何度もリストに出てきて冗長ですが、それぞれ実行できる完全なソースとなっています。
テクスチャはビットマップファイル(拡張子が .bmp)形式で保存された画像ファイルを使用します。 ビットマップファイルは モノクロ(1bit)、16bit(X1R5G5B5)、24ビット(R8G8B8)、32ビット(X8R8G8B8)の形式が使えます。 ビットマップファイルの大きさは最大4MBで 1024x1024ドットが可能です。 テクスチャはプログラム中最大128枚使用することができます。
テクスチャを貼り付ける方法は平面マッピングと球面マッピングの2種類をサポートしています。 また頂点のUV座標を指定することも可能です。
ビットマップファイルはコンパイルされた「???.j3d」と同じディレクトリにおいて下さい。パスを指定すれば別ディレクトリに置くこともできます。ファイル名を変更しなければ画像の内容を書き換えても再コンパイルする必要はありません。
図中で緑で示される数値をTextureAxis組み込み関数に渡すことでテクスチャを張り付ける方向を指定できます。スライドでテクスチャを投影したような形で物体に貼り付けられます。物体の裏側も物体を透過したようにテクスチャが貼られます。
テクスチャのUV座標は貼りつける方向によって、オブジェクトのローカル座標系の対応する軸の座標の値をビットマップのサイズで除した値がそのまま渡されます。大きいポリゴンの場合はテクスチャを拡大して貼る必要があります。例えばローカル座標系で512x512の大きさのポリゴンに対して256x256ドットのビットマップをテクスチャとして貼り付けた場合、縦横2枚、合計4枚分のテクスチャが貼られます。2倍に拡大すると1枚分のテクスチャとなります。
球面テクスチャマッピングは物体のローカル座標系の原点を中心としてオブジェクトを手前から奥に包み込むようにマッピングします。 UV座標は物体のローカル座標系の原点原点から見た頂点座標の経度、緯度から自動的に計算されます。
球面マッピングを使うとどのような大きさ、形の物体に対してもテクスチャをゴムの膜で包み込むように貼ることができます。中心(物体の原点)から離れるほどテクスチャは拡大され、上下に中心軸に近づくほど縮小されます。北極、南極に相当する部分は縮小され、下地のポリゴンの形や大きさを工夫しないと歪が目立つため、テクスチャの上下の端には均一な色を使うようにします。TextureAxis組み込み関数によってマッピングの中心軸をX軸(1)、Y軸(2)、Z軸(3)のように指定できます。原点を通る面に対しては正常にマッピングできないため平面マッピングを使う必要があります。
テクスチャを使う場合は、命令を使う場所に注意する必要があります。 以下の表に示す順序でテクスチャを設定します。それぞれデフォルトの値が設定されているため、TextureFile と TextureSet の2命令でテクスチャを物体に設定できます。それ以外の命令は、テクスチャをより細かくコントロールする場合に使用します。
実行順序 | デフォルト値 | 説明 | |
---|---|---|---|
共通 | GraphMode() | テクスチャ操作はグラフィックモード必須 | |
TextureAlpha() | 100 | 必要ならテクスチャファイル指定前 | |
TextureFile() | ビットマップファイルからテクスチャを生成 | ||
物体初期化 | 物体生成 | NewObject または Child | |
TextureMap() | 0 (球面マッッピング) | TextureMap, TextureAxis, TextureBias, TextureSetは物体生成後、 頂点登録前なら任意の順序でよい | |
TextureAxis() | 3 (Z軸中心) | ||
TextureBias() | (0, 0) | ||
TextureSet() | -1 (テクスチャ無効) | 平面マップの場合のみ頂点登録前 | |
頂点登録 | Point, DefPoint, PointUV, DefPointUV | ||
TextureSet() | -1 (テクスチャ無効) | テクスチャ番号を指定 | |
ポリゴン登録 | Plane, DefPlane | ||
動的に変更可能 | TextureChange() | TextureSet=TextureFile | 変更しなければ不要 |
TextureScale() | (100, 100) |
テクスチャ専用の命令が以下の10種類あります。テクスチャを使う場合 TextureFile と TextureSet の2命令は最低でも必要です。 テクスチャはビットマップファイルと結びついて、オブジェクト間で共通に使用するテクスチャファイル番号と 頂点やポリゴンを登録するときに使用するテクスチャ番号の2種類の番号で管理されます。
j3c言語を使ったテクスチャのサンプルプログラムです。 テクスチャ以外の解説はJ3Cプログラミング入門、 J3C 組み込み関数の解説J3C言語の概要を参照してください。
サンプルでテクスチャとして使用しているビットマップファイルは横256ドット、 縦256ドット、16ビットの次の画像です。
他にもモノクロの画像を4枚使っていますが、省略します。
表裏のある1枚の3角形に平面マッピングしたサンプルです。元の3角形は表側が白、 裏側がオレンジ色です。 白いポリゴンにテクスチャを貼り付けた場合は光源によって陰影はつきますが、 テクスチャの色はそのままポリゴンの色となります。元のポリゴンに色が付いていた 場合はRGBの各色の強度とテクスチャのRGB各色の強度が乗算されます。 またオレンジ側は表から貼ったテクスチャが逆に写っているのが分かります。
表
裏
三角形の原点位置がテクスチャの左上の角になっていることが確認できます
//--------------------------------------------------------------------- // tex_01.j3c 3角形への単純な平面マッピング //--------------------------------------------------------------------- // 視点用オブジェクト class eye { int INIT() { NewObject(0, 0); // オブジェクトの生成 頂点も面も不要 See(); // このオブジェクトが見る ClearRegisters(); RX = -1000; SetPosition(); // 位置と角度を視点に設定 } int RUN() { Wait(); } int EVENT() {} } // [ESC] キーで終了 class esc_key { volatile int key; int INIT() {} int RUN() { key = InKey(); // キ−コ−ド入力 if (key == 0x1B) Stop(); // 全インスタンスの終了 } int EVENT() {} } // 回転する3角形 class Triangle { final int color1 = 6; // 面の色指定 final int color2 =14; int INIT() { NewObject(6, 2); // オブジェクトの生成 6頂点 2面 Specular(0); TextureMap(1); // 平面マッピング TextureAxis(1); // X 軸方向にマッピング // 三角形の頂点座標を登録 TextureSet(0); // テクスチャ番号0 DefPoint(0, 0, 250); // 頂点0 DefPoint(0, 250,-250); // 頂点1 DefPoint(0,-250,-250); // 頂点2 DefPoint(0, 0, 250); // 頂点3 頂点0と同じ DefPoint(0, 250,-250); // 頂点4 頂点1と同じ DefPoint(0,-250,-250); // 頂点5 頂点2と同じ // 三角形の面を登録 DefPlane( 7, 3, 0, 1, 2); // 面の定義 表から見て左回り(白) DefPlane(14, 3, 2, 1, 0); // 面の定義 表から見て左回り(オレンジ) ClearRegisters(); // RX - RBに0を代入 SetPosition(); // 原点に置く } int RUN() { RotHead(12000, 360*8); // 12秒間で360度回転 } int EVENT() {} } // メインクラス class main { int INIT() { GraphMode(); // グラフィックモードに変更 Zoom(1); TextureFile(0, "num256.bmp"); new(Triangle, 50); // 3角柱のインスタンスを生成 new(eye, 50); new(esc_key, 50); // ESCキーで終了させるインスタンス BackgroundColor(0x559); // 背景色を青系に設定 } int RUN() { Wait(); } int EVENT() {} }
上のリストは今回のサンプルプログラムの基本的な形です。リスト中で赤く表示されている4つのクラスが同時に動作しています。それぞれの動作は以下のようになります。
三角形の頂点座標を登録する前に TextureSet(0) を実行しています。平面マッピングする場合は内部的にJ3Wがテクスチャのサイズを知る必要があるため TextureSet(0) 等としてテクスチャを指定する必要があります。後述の球面マッピングでは頂点登録の前にテクスチャを指定する必要はありません。
このサンプルではサンプル1と同様に平面マッピングしますが、オレンジ色の裏側には テクスチャを貼っていません。テクスチャを貼らないポリゴンを登録する前に TextureSet(-1) を実行します。一度TextureSet() を実行すると次にTextureSet() が実行されてテクスチャ番号を変更しない限り、その後のポリゴンの登録では設定されたテクスチャ番号が使用されます。
//--------------------------------------------------------------------- // tex_02.j3c 平面マッピング、片面にテクスチャを貼らない //--------------------------------------------------------------------- // 視点用オブジェクト class eye { int INIT() { NewObject(0, 0); // オブジェクトの生成 頂点も面も不要 See(); // このオブジェクトが見る ClearRegisters(); RX = -1000; SetPosition(); // 位置と角度を視点に設定 } int RUN() { Wait(); } int EVENT() {} } // [ESC] キーで終了 class esc_key { volatile int key; int INIT() {} int RUN() { key = InKey(); // キ−コ−ド入力 if (key == 0x1B) Stop(); // 全インスタンスの終了 } int EVENT() {} } // 回転する3角形 class Triangle { int INIT() { NewObject(6, 2); // オブジェクトの生成 6頂点 2面 Specular(0); TextureMap(1); // 平面マッピング TextureAxis(1); // X 軸方向にマッピング TextureSet(0); // テクスチャ番号0 // 三角形の頂点座標を登録 DefPoint(0, 0, 250); // 頂点0 DefPoint(0, 250,-250); // 頂点1 DefPoint(0,-250,-250); // 頂点2 DefPoint(0, 0, 250); // 頂点3 頂点0と同じ DefPoint(0, 250,-250); // 頂点4 頂点1と同じ DefPoint(0,-250,-250); // 頂点5 頂点2と同じ // 三角形の面を登録 DefPlane( 7, 3, 0, 1, 2); // 面の定義 表から見て左回り(白) TextureSet(-1); // テクスチャ無効 DefPlane(14, 3, 5, 4, 3); // 面の定義 表から見て左回り ClearRegisters(); // RX - RBに0を代入 SetPosition(); // 原点に置く } int RUN() { RotHead(12000, 360*8); // 12秒間で360度回転 } int EVENT() {} } // メインクラス class main { int INIT() { GraphMode(); // グラフィックモードに変更 Zoom(1); TextureAlpha(100); TextureFile(0, "num256.bmp"); new(Triangle, 50); // 3角柱のインスタンスを生成 new(eye, 50); new(esc_key, 50); // ESCキーで終了させるインスタンス BackgroundColor(0x559); // 背景色を青系に設定 } int RUN() { Wait(); } int EVENT() {} }
このサンプルではサンプル1と同様に平面マッピングしますが、オレンジ色の裏側にも正面からテクスチャを貼ります。頂点登録前にTextureAxis(-1) を実行してX軸の反対側からテクスチャを貼ります。このとき上下方向は同じで左右のみ逆転することで裏側も正常に数字が読めることが確認できます。
//--------------------------------------------------------------------- // tex_03.j3c 平面マッピング、両側から貼る //--------------------------------------------------------------------- // 視点用オブジェクト class eye { int INIT() { NewObject(0, 0); // オブジェクトの生成 頂点も面も不要 See(); // このオブジェクトが見る ClearRegisters(); RX = -1000; SetPosition(); // 位置と角度を視点に設定 } int RUN() { Wait(); } int EVENT() {} } // [ESC] キーで終了 class esc_key { volatile int key; int INIT() {} int RUN() { key = InKey(); // キ−コ−ド入力 if (key == 0x1B) Stop(); // 全インスタンスの終了 } int EVENT() {} } // 回転する3角形 class Triangle { final int color1 = 6; // 面の色指定 final int color2 =14; int INIT() { NewObject(6, 2); // オブジェクトの生成 6頂点 2面 TextureSet(0); // テクスチャ番号0 TextureMap(1); // 平面マッピング TextureAxis(1); // X 軸方向にマッピング // 三角形の頂点座標を登録 DefPoint(0, 0, 250); // 頂点0 DefPoint(0, 250,-250); // 頂点1 DefPoint(0,-250,-250); // 頂点2 TextureAxis(-1); // X 軸の逆方向からマッピング DefPoint(0, 0, 250); // 頂点3 頂点0と同じ DefPoint(0, 250,-250); // 頂点4 頂点1と同じ DefPoint(0,-250,-250); // 頂点5 頂点2と同じ TextureSet(0); // テクスチャ番号0 // 三角形の面を登録 DefPlane( 7, 3, 0, 1, 2); // 面の定義 表から見て左回り(白) DefPlane(14, 3, 5, 4, 3); // 面の定義 表から見て左回り TextureSet(0); // テクスチャ番号0 ClearRegisters(); // RX - RBに0を代入 SetPosition(); // 原点に置く Specular(0); } int RUN() { RotHead(12000, 360*8); // 12秒間で360度回転 } int EVENT() {} } // メインクラス class main { int INIT() { GraphMode(); // グラフィックモードに変更 Zoom(1); TextureAlpha(100); TextureFile(0, "num256.bmp"); new(Triangle, 50); // 3角柱のインスタンスを生成 new(eye, 50); new(esc_key, 50); // ESCキーで終了させるインスタンス BackgroundColor(0x559); // 背景色を青系に設定 } int RUN() { Wait(); } int EVENT() {} }
平面マッピングではローカル座標系の座標値がそのままテクスチャの大きさに反映されるため、大きなポリゴンではテクスチャがタイル状に何枚も貼られることになります。TextureScaleを実行するとテクスチャを拡大縮小できます。100を指定した場合はテクスチャサイズそのまま、200とした場合は2倍に拡大されます。50とするとテクスチャのドット数と同じ領域に2枚分のテクスチャが貼られます。TextureScaleは頂点やポリゴンの登録後も何度でも実行できるため、動的にテクスチャの拡大縮小が可能です。
tex_04b.j3c では3角形の左上の座標が(0, -250, -250)とテクスチャの原点(左上)とあわせるため、頂点の登録前に TextureBias(-250, -250) を実行しています。
//--------------------------------------------------------------------- // tex_04b.j3c テクスチャの位置にオフセットを加える //--------------------------------------------------------------------- // 視点用オブジェクト class eye { int INIT() { NewObject(0, 0); // オブジェクトの生成 頂点も面も不要 See(); // このオブジェクトが見る ClearRegisters(); RX = -1000; SetPosition(); // 位置と角度を視点に設定 } int RUN() { Wait(); } int EVENT() {} } // [ESC] キーで終了 class esc_key { volatile int key; int INIT() {} int RUN() { key = InKey(); // キ−コ−ド入力 if (key == 0x1B) Stop(); // 全インスタンスの終了 } int EVENT() {} } // 回転する3角形 class Triangle { final int color1 = 6; // 面の色指定 final int color2 =14; int INIT() { NewObject(6, 2); // オブジェクトの生成 6頂点 2面 TextureSet(0); // テクスチャ番号0 TextureMap(1); // 平面マッピング TextureAxis(1); // X 軸方向にマッピング TextureScale(200, 200); TextureBias(-250, -250); // 三角形の頂点座標を登録 DefPoint(0, 0, 250); // 頂点0 DefPoint(0, 250,-250); // 頂点1 DefPoint(0,-250,-250); // 頂点2 TextureAxis(-1); // X 軸方向にマッピング DefPoint(0, 0, 250); // 頂点3 頂点0と同じ DefPoint(0, 250,-250); // 頂点4 頂点1と同じ DefPoint(0,-250,-250); // 頂点5 頂点2と同じ TextureSet(0); // テクスチャ番号0 // 三角形の面を登録 DefPlane( 7, 3, 0, 1, 2); // 面の定義 表から見て左回り(白) DefPlane(14, 3, 5, 4, 3); // 面の定義 表から見て左回り TextureSet(0); // テクスチャ番号0 ClearRegisters(); // RX - RBに0を代入 SetPosition(); // 原点に置く Specular(0); } int RUN() { RotHead(12000, 360*8); // 12秒間で360度回転 } int EVENT() {} } // メインクラス class main { int INIT() { GraphMode(); // グラフィックモードに変更 Zoom(1); TextureAlpha(100); TextureFile(0, "num256.bmp"); new(Triangle, 50); // 3角柱のインスタンスを生成 new(eye, 50); new(esc_key, 50); // ESCキーで終了させるインスタンス BackgroundColor(0x559); // 背景色を青系に設定 } int RUN() { Wait(); } int EVENT() {} }
このサンプルではオブジェクト内で複数のテクスチャを使用し、またテクスチャの拡大率を動的に変化させます。画面中央で回転する4角形は表裏各2枚のポリゴンで構成されています。片面を構成する2枚のポリゴンはテクスチャ番号 0 と 1 が指定されており、モノクロ画像とカラー画像を割り当てています。TextureChange と TextureScale を実行することでテクスチャと拡大率を動的に変更します。
//--------------------------------------------------------------------- // tex_05.j3c オブジェクト内で複数テクスチャの使用と動的変更 //--------------------------------------------------------------------- // 視点用オブジェクト class eye { int INIT() { NewObject(0, 0); // オブジェクトの生成 頂点も面も不要 See(); // このオブジェクトが見る ClearRegisters(); RX = -1000; SetPosition(); // 位置と角度を視点に設定 } int RUN() { Wait(); } int EVENT() {} } // [ESC] キーで終了 class esc_key { volatile int key; int INIT() {} int RUN() { key = InKey(); // キ−コ−ド入力 if (key == 0x1B) Stop(); // 全インスタンスの終了 } int EVENT() {} } // 回転する四角形 class Rectangle { volatile int sc; int INIT() { NewObject(8, 4); // オブジェクトの生成 6頂点 2面 Specular(0); TextureMap(1); // 平面マッピング TextureScale(200, 200); // 四角形の頂点座標を登録 TextureAxis(1); // X 軸方向にマッピング TextureSet(0); // テクスチャ番号0 DefPoint(0,-250, 250); // 頂点0 DefPoint(0,-250,-250); // 頂点1 DefPoint(0, 250,-250); // 頂点2 DefPoint(0, 250, 250); // 頂点3 TextureAxis(-1); // X 軸方向にマッピング TextureSet(1); // テクスチャ番号1 DefPoint(0,-250, 250); // 頂点4 DefPoint(0,-250,-250); // 頂点5 DefPoint(0, 250,-250); // 頂点6 DefPoint(0, 250, 250); // 頂点7 // 四角形の面を登録 TextureSet(0); DefPlane( 7, 3, 0, 2, 1); // 面の定義 表から見て左回り(白) TextureSet(1); DefPlane( 7, 3, 3, 2, 0); // 面の定義 表から見て左回り(白) TextureSet(0); DefPlane(14, 3, 4, 5, 6); // 面の定義 表から見て左回り(白) TextureSet(1); DefPlane(14, 3, 4, 6, 7); // 面の定義 表から見て左回り ClearRegisters(); // RX - RBに0を代入 SetPosition(); // 原点に置く sc = 200; } int RUN() { TextureChange(0,0); TextureChange(1,1); RotHead(2000, 880); // 8秒間で360度回転 TextureChange(0,1); TextureChange(1,0); RotHead(2000, 880); // 8秒間で360度回転 TextureChange(0,-1); TextureChange(1,0); RotHead(2000, 880); // 8秒間で360度回転 sc = sc + 50; if (sc > 300) sc = 200; TextureScale(sc, sc); } int EVENT() {} } // メインクラス class main { int INIT() { GraphMode(); // グラフィックモードに変更 Zoom(1); TextureAlpha(100); TextureFile(0, "num256.bmp"); TextureFile(1, "mononum.bmp"); new(Rectangle, 50); // 3角柱のインスタンスを生成 new(eye, 50); new(esc_key, 50); // ESCキーで終了させるインスタンス BackgroundColor(0x559); // 背景色を青系に設定 } int RUN() { Wait(); } int EVENT() {} }
球体をプログラムで作成し、テクスチャを球面マッピングで貼り付けています。球の中央部 (赤道部分) に比べて上下 (極部分) は縮小されていることが確認できます。正方形の画像を球面マッピングすると横方向に拡大されます。最初は TextureScale(50,100) として横方法を50%に縮小してテクスチャを横に2枚設定し、その後100%に戻しています。
フラットシェイディングされた下地の色
//--------------------------------------------------------------------- // tex_06.j3c 球面マッピングとテクスチャのスケーリング //--------------------------------------------------------------------- // 視点用オブジェクト class eye { int INIT() { NewObject(0, 0); // オブジェクトの生成 頂点も面も不要 See(); // このオブジェクトが見る ClearRegisters(); RX = -1000; SetPosition(); // 位置と角度を視点に設定 } int RUN() { Wait(); } int EVENT() {} } // [ESC] キーで終了 class esc_key { volatile int key; int INIT() {} int RUN() { key = InKey(); // キ−コ−ド入力 if (key == 0x1B) Stop(); // 全インスタンスの終了 } int EVENT() {} } //---------------------------------------------------------------------- // 回転体を生成する //---------------------------------------------------------------------- class CRevolution { final int MAX = 30; volatile int vx[MAX]; // 頂点のX座標 volatile int vy[MAX]; // 頂点のY座標 volatile int vColor[MAX]; // 面の色 volatile int vWork[6]; // 作業用配列 volatile int T, m, i, j; int Vertex(int x, int y, int z) { // 頂点の登録 RX = x; RY = y; RZ = z; Point(); } //-------------------------------------- // 面と頂点を登録 // n は頂点数-2, m は円周の分割数 //-------------------------------------- int Setup(int n, int m) { for(i = 0; i <= n+1; i=i+1) // 初期頂点 (n+2個) の登録 Vertex(vx[i], vy[i], 0); // 頂点座標を計算して、頂点を登録 T = 360*8 / m; // 1ステップの角度 for(j=1; j<m; j=j+1) // j=0の座標は初期値で登録済み for(i=2; i<=n+1; i=i+1) Vertex(vx[i], vy[i] * cos(T*j)/10000, vy[i] * sin(T*j)/10000); // 面を登録 if (n > 1) { // 上下の錐体を除いた面の定義 vWork[1] = 4; // 4角形 for(j=0; j<=m-2; j=j+1) // 経線毎 for(i=2; i<=n; i=i+1) { // 緯線毎 vWork[0] = vColor[i]; // 面の色を指定 vWork[2] = j*n+i; // 頂点番号指定 vWork[3] = j*n+i+1; vWork[4] = (j+1)*n+i+1; vWork[5] = (j+1)*n+i; Plane(vWork); // 面の登録 } for(i=2; i<=n; i=i+1) { // m-1 から 0 への戻り vWork[0] = vColor[i]; // 面の色を指定 vWork[2] = (m-1)*n+i; // 頂点番号指定 vWork[3] = (m-1)*n+i+1; vWork[4] = i + 1; vWork[5] = i; Plane(vWork); // 面の登録 } } // 上部 (X大) の錐体 vWork[1] = 3; // 3角形 vWork[0] = vColor[0]; // 面の色を指定 for(j=0; j<=m-2; j=j+1) { vWork[2] = 0; // 頂点番号指定 vWork[3] = j*n+2; vWork[4] = (j+1)*n+2; Plane(vWork); // 面の登録 } // m-1 から 0 への戻り vWork[2] = 0; // 頂点番号指定 vWork[3] = (m-1)*n+2; vWork[4] = 2; Plane(vWork); // 面の登録 // 下部 (X小) の錐体 vWork[0] = vColor[1]; // 面の色を指定 for(j=0; j<=m-2; j=j+1) { vWork[2] = 1; // 頂点番号指定 vWork[3] = (j+2)*n+1; vWork[4] = (j+1)*n+1; Plane(vWork); } // m-1 から 0 への戻り vWork[2] = 1; // 頂点番号指定 vWork[3] = n +1; vWork[4] = m*n+1; Plane(vWork); // 面の登録 } int INIT() { } int RUN() { } int EVENT() { } } //---------------------------------------------------------------------- // 球体のクラス RH:色、RP:環境光強度、RB:スペキュラー強度を指定してNEW //---------------------------------------------------------------------- class sphere extends CRevolution { int sphere(int radius, int div, int color) { volatile int i; vx[ 0] = radius; // 頂点0 vy[ 0] = 0; vx[ 1] = -radius; // 頂点1 vy[ 1] = 0; for (i=0; i<div-1 ; i=i+1) { vColor[i] = color; } for (i=1; i<div-1 ; i=i+1) { vx[i+1] = radius * cos(i*1440/(div-1)) / 10000; vy[i+1] = radius * sin(i*1440/(div-1)) / 10000; } } int INIT() { volatile int div; div = 24; // 球体の分割数 NewObject(1000,1000); ClearRegisters(); // 位置と姿勢をすべて0に RP = 90 * 8; // 90度立てる SetPosition(); // 位置と姿勢を指定 Ambient(10); Specular(100); // スペキュラー100% TextureMap(0); // 球面マッピング TextureAxis(1); // X 軸方向にマッピング TextureSet(0); // テクスチャ番号0 sphere(250, div, 7); // 半径250の白の球体 vColor[div/2] = 1; // 赤道位置に帯状の青 vColor[div/2+1] = 1; vColor[div/2+2] = 4; // 赤道位置に帯状の赤 vColor[div/2+3] = 4; Setup(div - 2, div); // 頂点と面を登録 } int RUN() { RotPitch(8000, 360*8); // 8秒間で1周 RotBank(8000, 360*8); // 8秒間で1周 TextureScale(50,100); // 横(U)方向にテクスチャを2枚 RotPitch(8000, 360*8); // 8秒間で1周 RotHead(8000, 360*8); // 8秒間で1周 TextureScale(100,100); // 元に戻す TextureChange(0, -1); RotHead(8000, 360*8); // 8秒間で1周 TextureChange(0, 0); } } // メインクラス class main { int INIT() { GraphMode(); // グラフィックモードに変更 Zoom(1); // 視野角53度 TextureAlpha(100); // テクスチャを不透明 TextureFile(0, "num256.bmp"); TextureFile(1, "mononum.bmp"); new(sphere, 1000) // 球のインスタンスを生成 new(eye, 50); // 視点生成 new(esc_key, 50); // ESCキーで終了させるインスタンス BackgroundColor(0x559); // 背景色を青系に設定 } int RUN() { Wait(); } int EVENT() {} }
このサンプルでは球体ではない回転体に球面マッピングします。球面マッピングではどのような大きさ、形の物体に対してもテクスチャを貼ることができます。中心(物体の原点)から離れるほどテクスチャは拡大され、上下に中心軸に近づくほど縮小されます。ただし原点を通る平面では正常にマッピングできないため平面マッピングを使ってください。
//--------------------------------------------------------------------- // tex_07.j3c 非球面への球面マッピングとテクスチャのスケーリング //--------------------------------------------------------------------- // 視点用オブジェクト class eye { int INIT() { NewObject(0, 0); // オブジェクトの生成 頂点も面も不要 See(); // このオブジェクトが見る ClearRegisters(); RX = -2000; SetPosition(); // 位置と角度を視点に設定 } int RUN() { Wait(); } int EVENT() {} } // [ESC] キーで終了 class esc_key { volatile int key; int INIT() {} int RUN() { key = InKey(); // キ−コ−ド入力 if (key == 0x1B) Stop(); // 全インスタンスの終了 } int EVENT() {} } //---------------------------------------------------------------------- // 回転体を生成する //---------------------------------------------------------------------- class CRevolution { final int MAX = 30; volatile int vx[MAX]; // 頂点のX座標 volatile int vy[MAX]; // 頂点のY座標 volatile int vColor[MAX]; // 面の色 volatile int vWork[6]; // 作業用配列 volatile int T, m, i, j; int Vertex(int x, int y, int z) { // 頂点の登録 RX = x; RY = y; RZ = z; Point(); } //-------------------------------------- // 面と頂点を登録 // n は頂点数-2, m は円周の分割数 //-------------------------------------- int Setup(int n, int m) { for(i = 0; i <= n+1; i=i+1) // 初期頂点 (n+2個) の登録 Vertex(vx[i], vy[i], 0); // 頂点座標を計算して、頂点を登録 T = 360*8 / m; // 1ステップの角度 for(j=1; j<m; j=j+1) // j=0の座標は初期値で登録済み for(i=2; i<=n+1; i=i+1) Vertex(vx[i], vy[i] * cos(T*j)/10000, vy[i] * sin(T*j)/10000); // 面を登録 if (n > 1) { // 上下の錐体を除いた面の定義 vWork[1] = 4; // 4角形 for(j=0; j<=m-2; j=j+1) // 経線毎 for(i=2; i<=n; i=i+1) { // 緯線毎 vWork[0] = vColor[i]; // 面の色を指定 vWork[2] = j*n+i; // 頂点番号指定 vWork[3] = j*n+i+1; vWork[4] = (j+1)*n+i+1; vWork[5] = (j+1)*n+i; Plane(vWork); // 面の登録 } for(i=2; i<=n; i=i+1) { // m-1 から 0 への戻り vWork[0] = vColor[i]; // 面の色を指定 vWork[2] = (m-1)*n+i; // 頂点番号指定 vWork[3] = (m-1)*n+i+1; vWork[4] = i + 1; vWork[5] = i; Plane(vWork); // 面の登録 } } // 上部 (X大) の錐体 vWork[1] = 3; // 3角形 vWork[0] = vColor[0]; // 面の色を指定 for(j=0; j<=m-2; j=j+1) { vWork[2] = 0; // 頂点番号指定 vWork[3] = j*n+2; vWork[4] = (j+1)*n+2; Plane(vWork); // 面の登録 } // m-1 から 0 への戻り vWork[2] = 0; // 頂点番号指定 vWork[3] = (m-1)*n+2; vWork[4] = 2; Plane(vWork); // 面の登録 // 下部 (X小) の錐体 vWork[0] = vColor[1]; // 面の色を指定 for(j=0; j<=m-2; j=j+1) { vWork[2] = 1; // 頂点番号指定 vWork[3] = (j+2)*n+1; vWork[4] = (j+1)*n+1; Plane(vWork); } // m-1 から 0 への戻り vWork[2] = 1; // 頂点番号指定 vWork[3] = n +1; vWork[4] = m*n+1; Plane(vWork); // 面の登録 } int INIT() { } int RUN() { } int EVENT() { } } //---------------------------------------------------------------------- // 球体のクラス RH:色、RP:環境光強度、RB:スペキュラー強度を指定してNEW //---------------------------------------------------------------------- class revo extends CRevolution { int shape(int color) { volatile int i; vx[ 0] = 300; vy[ 0] = 0; vx[ 1] = -300; vy[ 1] = 0; vx[ 2] = 250; vy[ 2] = 50; vx[ 3] = 200; vy[ 3] = 200; vx[ 4] = 150; vy[ 4] = 300; vx[ 5] = 100; vy[ 5] = 300; vx[ 6] = 50; vy[ 6] = 270; vx[ 7] = 0; vy[ 7] = 170; vx[ 8] = -50; vy[ 8] = 100; vx[ 9] = -100; vy[ 9] = 80; vx[10] = -150; vy[10] = 100; vx[11] = -200; vy[11] = 150; vx[12] = -250; vy[12] = 100; for (i=0; i<12 ; i=i+1) { vColor[i] = color; } } int INIT() { volatile int div; div = 24; // 球体の分割数 NewObject(1000,1000); ClearRegisters(); // 位置と姿勢をすべて0に RP = 90 * 8; // 90度立てる RX = -800; SetPosition(); // 位置と姿勢を指定 Ambient(10); Specular(100); // スペキュラー100% TextureMap(0); // 球面マッピング TextureAxis(1); // X 軸方向にマッピング TextureSet(0); // テクスチャ番号0 shape(13); // 回転体用の頂点を登録(ピンク) Setup(10, 24); // 頂点と面を登録 } int RUN() { TextureScale(50,100); // テクスチャを横2枚、縦1枚 RotPitch(8000, 360*8); // 8秒間で1周 RotBank(8000, 360*8); // 8秒間で1周 TextureScale(25,50); // テクスチャを横4枚、縦2枚 RotPitch(8000, 360*8); // 8秒間で1周 RotHead(8000, 360*8); // 8秒間で1周 TextureScale(50,100); // 元に戻す TextureChange(0, -1); // テクスチャを無効に RotHead(8000, 360*8); // 8秒間で1周 TextureChange(0, 0); } } // メインクラス class main { int INIT() { GraphMode(); // グラフィックモードに変更 Zoom(1); // 視野角53度 TextureAlpha(100); // テクスチャの不透明100% TextureFile(0, "num256.bmp"); TextureFile(1, "mononum.bmp"); new(revo, 1000) // 回転体のインスタンスを生成 new(eye, 50); // 視点生成 new(esc_key, 50); // ESCキーで終了させるインスタンス BackgroundColor(0x559); // 背景色を青系に設定 } int RUN() { Wait(); } int EVENT() {} }
このサンプルでは同じ画像ファイルを2回読み込みますが、それぞれ TextureAlha(40) と TextureAlha(100) を直前に実行し、テクスチャの透明度を変更しています。透明度は100で不透明、40は40%不透明(60%透明) を示します。テクスチャが100%不透明でも下地の色の影響を受けます。テクスチャの色をそのまま使いたい場合は下地を白にする必要があります。
TextureAlha(40)
TextureAlha(100)
//--------------------------------------------------------------------- // tex_08.j3c 不透明度が40%と100%のテクスチャ //--------------------------------------------------------------------- // 視点用オブジェクト class eye { int INIT() { NewObject(0, 0); // オブジェクトの生成 頂点も面も不要 See(); // このオブジェクトが見る ClearRegisters(); RX = -2000; SetPosition(); // 位置と角度を視点に設定 } int RUN() { Wait(); } int EVENT() {} } // [ESC] キーで終了 class esc_key { volatile int key; int INIT() {} int RUN() { key = InKey(); // キ−コ−ド入力 if (key == 0x1B) Stop(); // 全インスタンスの終了 } int EVENT() {} } //---------------------------------------------------------------------- // 回転体を生成する //---------------------------------------------------------------------- class CRevolution { final int MAX = 30; volatile int vx[MAX]; // 頂点のX座標 volatile int vy[MAX]; // 頂点のY座標 volatile int vColor[MAX]; // 面の色 volatile int vWork[6]; // 作業用配列 volatile int T, m, i, j; int Vertex(int x, int y, int z) { // 頂点の登録 RX = x; RY = y; RZ = z; Point(); } //-------------------------------------- // 面と頂点を登録 // n は頂点数-2, m は円周の分割数 //-------------------------------------- int Setup(int n, int m) { for(i = 0; i <= n+1; i=i+1) // 初期頂点 (n+2個) の登録 Vertex(vx[i], vy[i], 0); // 頂点座標を計算して、頂点を登録 T = 360*8 / m; // 1ステップの角度 for(j=1; j<m; j=j+1) // j=0の座標は初期値で登録済み for(i=2; i<=n+1; i=i+1) Vertex(vx[i], vy[i] * cos(T*j)/10000, vy[i] * sin(T*j)/10000); // 面を登録 if (n > 1) { // 上下の錐体を除いた面の定義 vWork[1] = 4; // 4角形 for(j=0; j<=m-2; j=j+1) // 経線毎 for(i=2; i<=n; i=i+1) { // 緯線毎 vWork[0] = vColor[i]; // 面の色を指定 vWork[2] = j*n+i; // 頂点番号指定 vWork[3] = j*n+i+1; vWork[4] = (j+1)*n+i+1; vWork[5] = (j+1)*n+i; Plane(vWork); // 面の登録 } for(i=2; i<=n; i=i+1) { // m-1 から 0 への戻り vWork[0] = vColor[i]; // 面の色を指定 vWork[2] = (m-1)*n+i; // 頂点番号指定 vWork[3] = (m-1)*n+i+1; vWork[4] = i + 1; vWork[5] = i; Plane(vWork); // 面の登録 } } // 上部 (X大) の錐体 vWork[1] = 3; // 3角形 vWork[0] = vColor[0]; // 面の色を指定 for(j=0; j<=m-2; j=j+1) { vWork[2] = 0; // 頂点番号指定 vWork[3] = j*n+2; vWork[4] = (j+1)*n+2; Plane(vWork); // 面の登録 } // m-1 から 0 への戻り vWork[2] = 0; // 頂点番号指定 vWork[3] = (m-1)*n+2; vWork[4] = 2; Plane(vWork); // 面の登録 // 下部 (X小) の錐体 vWork[0] = vColor[1]; // 面の色を指定 for(j=0; j<=m-2; j=j+1) { vWork[2] = 1; // 頂点番号指定 vWork[3] = (j+2)*n+1; vWork[4] = (j+1)*n+1; Plane(vWork); } // m-1 から 0 への戻り vWork[2] = 1; // 頂点番号指定 vWork[3] = n +1; vWork[4] = m*n+1; Plane(vWork); // 面の登録 } int INIT() { } int RUN() { } int EVENT() { } } //---------------------------------------------------------------------- // 球体のクラス RH:色、RP:環境光強度、RB:スペキュラー強度を指定してNEW //---------------------------------------------------------------------- class revo extends CRevolution { int shape(int color) { volatile int i; vx[ 0] = 300; vy[ 0] = 0; vx[ 1] = -300; vy[ 1] = 0; vx[ 2] = 250; vy[ 2] = 50; vx[ 3] = 200; vy[ 3] = 200; vx[ 4] = 150; vy[ 4] = 300; vx[ 5] = 100; vy[ 5] = 300; vx[ 6] = 50; vy[ 6] = 270; vx[ 7] = 0; vy[ 7] = 170; vx[ 8] = -50; vy[ 8] = 100; vx[ 9] = -100; vy[ 9] = 80; vx[10] = -150; vy[10] = 100; vx[11] = -200; vy[11] = 150; vx[12] = -250; vy[12] = 100; for (i=0; i<12 ; i=i+1) { vColor[i] = color; } } int INIT() { volatile int div; div = 24; // 球体の分割数 NewObject(1000,1000); ClearRegisters(); // 位置と姿勢をすべて0に RP = 90 * 8; // 90度立てる RX = -800; SetPosition(); // 位置と姿勢を指定 Ambient(10); Specular(100); // スペキュラー100% TextureMap(0); // 球面マッピング TextureAxis(1); // X 軸方向にマッピング TextureSet(0); // テクスチャ番号0 shape(11); // 回転体用の頂点を登録(薄緑) Setup(10, 24); // 頂点と面を登録 } int RUN() { TextureScale(50, 100); // テクスチャを横2枚、縦1枚 RotPitch(8000, 360*8); // 8秒間で1周 RotBank(8000, 360*8); // 8秒間で1周 TextureScale(25,50); // テクスチャを横4枚、縦2枚 RotPitch(8000, 360*8); // 8秒間で1周 RotHead(8000, 360*8); // 8秒間で1周 TextureScale(50,100); // 元に戻す TextureChange(0, -1); // テクスチャを無効に RotHead(8000, 360*8); // 8秒間で1周 TextureChange(0, 1); } } // メインクラス class main { int INIT() { GraphMode(); // グラフィックモードに変更 Zoom(1); // 視野角53度 TextureAlpha(40); // テクスチャの不透明40% TextureFile(0, "num256.bmp"); TextureAlpha(100); // テクスチャの不透明100% TextureFile(1, "num256.bmp"); new(revo, 1000) // 回転体のインスタンスを生成 new(eye, 50); // 視点生成 new(esc_key, 50); // ESCキーで終了させるインスタンス BackgroundColor(0x559); // 背景色を青系に設定 } int RUN() { Wait(); } int EVENT() {} }
このサンプルでは球体に貼ったテクスチャを切り替えてアニメーションします。 モノクロビットマップを3枚用意し、適当な時間で切り替えることで「まばたき」します。 光源を右上に配置しています。ポリゴンはすべて TextureSet(0) でテクスチャ番号を0で登録し、0.3秒程度で TextureChange(0, 1) などと実行することでテクスチャ番号の 0 をテクスチャファイル番号 0 から 2に切り替えています。
//--------------------------------------------------------------------- // tex_09.j3c テクスチャアニメーション //--------------------------------------------------------------------- // 視点用オブジェクト class eye { int INIT() { NewObject(0, 0); // オブジェクトの生成 頂点も面も不要 See(); // このオブジェクトが見る ClearRegisters(); RX = -1000; SetPosition(); // 位置と角度を視点に設定 } int RUN() { Wait(); } int EVENT() {} } // 光源用オブジェクト class Lamp { int INIT() { NewObject(0, 0); // オブジェクトの生成 頂点も面も不要 RX = -2000; RY = 2000; RZ = -2000; RP = -300; RH = -360; RB = 0; SetPosition(); // 位置と角度を視点に設定 Emit(1); // 光源として発行 } int RUN() { Wait(); } int EVENT() {} } // [ESC] キーで終了 class esc_key { volatile int key; int INIT() {} int RUN() { key = InKey(); // キ−コ−ド入力 if (key == 0x1B) Stop(); // 全インスタンスの終了 } int EVENT() {} } //---------------------------------------------------------------------- // 回転体を生成する //---------------------------------------------------------------------- class CRevolution { final int MAX = 30; volatile int vx[MAX]; // 頂点のX座標 volatile int vy[MAX]; // 頂点のY座標 volatile int vColor[MAX]; // 面の色 volatile int vWork[6]; // 作業用配列 volatile int T, m, i, j; int Vertex(int x, int y, int z) { // 頂点の登録 RX = x; RY = y; RZ = z; Point(); } //-------------------------------------- // 面と頂点を登録 // n は頂点数-2, m は円周の分割数 //-------------------------------------- int Setup(int n, int m) { for(i = 0; i <= n+1; i=i+1) // 初期頂点 (n+2個) の登録 Vertex(vx[i], vy[i], 0); // 頂点座標を計算して、頂点を登録 T = 360*8 / m; // 1ステップの角度 for(j=1; j<m; j=j+1) // j=0の座標は初期値で登録済み for(i=2; i<=n+1; i=i+1) Vertex(vx[i], vy[i] * cos(T*j)/10000, vy[i] * sin(T*j)/10000); // 面を登録 if (n > 1) { // 上下の錐体を除いた面の定義 vWork[1] = 4; // 4角形 for(j=0; j<=m-2; j=j+1) // 経線毎 for(i=2; i<=n; i=i+1) { // 緯線毎 vWork[0] = vColor[i]; // 面の色を指定 vWork[2] = j*n+i; // 頂点番号指定 vWork[3] = j*n+i+1; vWork[4] = (j+1)*n+i+1; vWork[5] = (j+1)*n+i; Plane(vWork); // 面の登録 } for(i=2; i<=n; i=i+1) { // m-1 から 0 への戻り vWork[0] = vColor[i]; // 面の色を指定 vWork[2] = (m-1)*n+i; // 頂点番号指定 vWork[3] = (m-1)*n+i+1; vWork[4] = i + 1; vWork[5] = i; Plane(vWork); // 面の登録 } } // 上部 (X大) の錐体 vWork[1] = 3; // 3角形 vWork[0] = vColor[0]; // 面の色を指定 for(j=0; j<=m-2; j=j+1) { vWork[2] = 0; // 頂点番号指定 vWork[3] = j*n+2; vWork[4] = (j+1)*n+2; Plane(vWork); // 面の登録 } // m-1 から 0 への戻り vWork[2] = 0; // 頂点番号指定 vWork[3] = (m-1)*n+2; vWork[4] = 2; Plane(vWork); // 面の登録 // 下部 (X小) の錐体 vWork[0] = vColor[1]; // 面の色を指定 for(j=0; j<=m-2; j=j+1) { vWork[2] = 1; // 頂点番号指定 vWork[3] = (j+2)*n+1; vWork[4] = (j+1)*n+1; Plane(vWork); } // m-1 から 0 への戻り vWork[2] = 1; // 頂点番号指定 vWork[3] = n +1; vWork[4] = m*n+1; Plane(vWork); // 面の登録 } int INIT() { } int RUN() { } int EVENT() { } } //---------------------------------------------------------------------- // 球体のクラス RH:色、RP:環境光強度、RB:スペキュラー強度を指定してNEW //---------------------------------------------------------------------- class sphere extends CRevolution { int sphere(int radius, int div, int color) { volatile int i; vx[ 0] = radius; // 頂点0 vy[ 0] = 0; vx[ 1] = -radius; // 頂点1 vy[ 1] = 0; for (i=0; i<div-1 ; i=i+1) { vColor[i] = color; } for (i=1; i<div-1 ; i=i+1) { vx[i+1] = radius * cos(i*1440/(div-1)) / 10000; vy[i+1] = radius * sin(i*1440/(div-1)) / 10000; } } int INIT() { volatile int div; div = 24; // 球体の分割数 NewObject(1000,1000); ClearRegisters(); // 位置と姿勢をすべて0に RZ = 100; // ちょっと下 RP = 90 * 8; // 90度立てる SetPosition(); // 位置と姿勢を指定 Ambient(50); Specular(50); // スペキュラー50% TextureMap(0); // 球面マッピング TextureAxis(1); // X 軸方向にマッピング TextureSet(0); // テクスチャ番号0 sphere(250, div, 13); // 半径250の球体 Setup(div - 2, div); // 頂点と面を登録 } int RUN() { Pause(1500); // 1.5秒 TextureChange(0, 1); Pause(200); // 0.2秒 TextureChange(0, 2); Pause(200); // 0.2秒 TextureChange(0, 1); Pause(200); // 0.2秒 TextureChange(0, 0); Pause(300); // 0.3秒 TextureChange(0, 1); Pause(100); // 0.1秒 TextureChange(0, 2); Pause(100); // 0.1秒 TextureChange(0, 1); Pause(100); // 0.1秒 TextureChange(0, 0); // 以上を繰り返す } } // メインクラス class main { int INIT() { GraphMode(); // グラフィックモードに変更 Zoom(1); // 視野角53度 TextureAlpha(100); // テクスチャを不透明 TextureFile(0, "face00.bmp"); TextureFile(1, "face01.bmp"); TextureFile(2, "face02.bmp"); new(sphere, 1000) // 球のインスタンスを生成 new(eye, 50); // 視点生成 new(Lamp, 50); // 光源生成 new(esc_key, 50); // ESCキーで終了させるインスタンス BackgroundColor(0x559); // 背景色を青系に設定 } int RUN() { Wait(); } int EVENT() {} }
たった3枚のテクスチャでも、まばたきしているように見えるのは不思議です。