HSPポータル
サイトマップ お問い合わせ


HSPTV!掲示板


未解決 解決 停止 削除要請

2016
0320
いろは三次元空間に曲面を描きたいのですが、18解決


いろは

リンク

2016/3/20(Sun) 23:32:01|NO.74996

今、二次元の平面や三次元空間にグラフや面を描ける、数学に特化したソフトを作っています。
平面における媒介変数を用いたもの等は割とすぐに出来ました。
ですが平面においての極方程式の計算方法と、空間においての曲面の描き方がわかりません。

教えてください。お願いします。
曲面の描き方は陽関数や陰関数、媒介変数表示、極方程式の計算方法を教えていただけると幸いです。
数学の知識はある程度あります。



この記事に返信する


kanamaru

リンク

2016/3/21(Mon) 00:51:29|NO.75000

とりあえず、関数(プログラムの関数じゃなくて数学のです。
以後関数とあったら数学の関数とします。)には、二つの表記があります。
陽関数…y=で始まる表記。3次元の場合はほとんど使われません。
陰関数…三次元の場合だと、ax+by+cz=定数の形で表された関数のことです。
媒介変数表示はどう説明すればいいでしょうか。3次元でいえば、媒介変数をtとすると、
x=tで書かれた式
y=tで書かれた式
z=tで書かれた式
という形で表された関数のこととでもいえばわかりますか?
で、極方程式の計算方法の説明は大変なので、
とりあえず、簡単なところだけ。
rは原点からの距離
θはX軸から正の向きにはかった角度を表します。
なので極方程式は、簡単にはグラフに出来ません。
グラフにするなら、rとθを決めた後で、
x=rcosθ
y=rsinθ
という式から、XとYを決めて、点を打つ必要があります。



kanamaru

リンク

2016/3/21(Mon) 09:13:10|NO.75001

余談ですが、
極方程式で使う座標系を極座標。
普段使う座標系を直交座標と言います。
(たまに、普段使う座標系をxy座標と説明してるのを見つけますが、それは間違いです。
計算の都合上、xy以外の軸を使う場合もありえるので)



motchy

リンク

2016/3/21(Mon) 09:25:51|NO.75003

>陰関数…三次元の場合だと、ax+by+cz=定数の形で表された関数のことです。
平面に限定するのですか?一般には f(x,y,z)=0 の形で表現されるものだと思います。

>曲面の描き方は陽関数や陰関数、媒介変数表示、極方程式の計算方法を教えていただけると幸いです。
陰関数のグラフを描く方が陽関数や媒介変数表示[x,y,z](t)のものを描くよりずっと大変だと思います。
陽関数はkanamaruさんが極座標系で説明した感じで比較的簡単に描けるのではないでしょうか?



motchy

リンク

2016/3/21(Mon) 09:50:47|NO.75005

作ろうとしているソフトの詳細が分かりませんが、ユーザーが打ち込んだ数式を解釈してグラフを描こうと思っているのならすごいですね。(数式処理システムを自作?)

陰関数をプロットする方法は私も気になったので少し検索してみました。こんなのがヒットしたので参考までに載せておきます。
1番目の方法は割と簡単そうです。
http://www.geocities.jp/ikuro_kotaro/koramu/4643_z2.htm
2番目の方法も面白そうです。



いろは

リンク

2016/3/22(Tue) 00:20:16|NO.75019

回答ありがとうございます。
motchyさんの言うとおり数式処理をし、それをグラフに反映させるというプログラムを作っています。

#include "d3m.hsp" #const double CAMERA_R 300 // 極座標のr #const double PI 3.1415 // 円周率 camera_theta = 0.0 // 極座標のθ camera_phi = PI / 3.0 // 極座標のφ old_mx = 0 // 前回処理時(約50ミリ秒前)のマウスカーソルの座標 old_my = 0 // 前回処理時(約50ミリ秒前)のマウスカーソルの座標 #include "hspmath.as" screen 0,800,600,4 di = 1.0 pai = 3.1415926535 e = M_E *main redraw 0 gosub *move_camera //カメラ操作 color:boxf:gosub *draw_axes //座標軸 //関数出力部 i = 0.0 repeat 2000 d1 = pai / 180.0 * i x1 = 0.5 * powf(e,0.3*d1) * cos(d1) y1 = 0.5 * powf(e,0.3*d1) * sin(d1) z1 = 0 d2 = pai / 180.0 * (i + 1) x2 = 0.5 * powf(e,0.3*d2) * cos(d2) y2 = 0.5 * powf(e,0.3*d2) * sin(d2) z2 = 0 color 255,0,255:d3line x1,y1,z1,x2,y2,z2 i++ loop wheel = mousew if (di < 65.0)&(wheel < 0):di += 0.009 if (di > 1.0)&(wheel > 0):di -= 0.009 if (di <= 1.0)&(di > 0.001)&(wheel > 0):di -=0.009 if (di <= 1.0)&(di > 0.001)&(wheel < 0):di +=0.009 title strf( "θ:%f / ", camera_theta ) + strf( "φ:%f", camera_phi ) redraw 1 await 5 goto *main *move_camera //カメラ操作 getkey left_click, 1 if left_click { if old_click { camera_theta += 0.01 * ( old_mx - ginfo_mx ) camera_phi = limitf( camera_phi + 0.01 * ( old_my - ginfo_my ), 0.01, PI - 0.01 ) } old_mx = ginfo_mx old_my = ginfo_my } old_click = left_click d3vrotate camera_x, camera_y, camera_z, 0, 0, CAMERA_R, 0, 1, 0, camera_phi d3vrotate camera_x, camera_y, camera_z, camera_x, camera_y, camera_z, 0, 0, 1, camera_theta d3setcam camera_x * di, camera_y * di, camera_z * di return *draw_axes //5px = 1 color 0,255,0 d3mes "X", 110, 0, 0 d3mes "Y", 0, 110, 0 d3mes "Z", 0, 0, 110 color 127,255,255 //5pxで「1」 d3mes "10", 50, 0, 0:d3mes "-10", -50, 0, 0 d3mes "10", 0, 50, 0:d3mes "-10", 0, -50, 0 d3mes "10", 0, 0, 50:d3mes "-10", 0, 0, -50 d3mes "50", 250, 0, 0:d3mes "-50", -250, 0, 0 d3mes "50", 0, 250, 0:d3mes "-50", 0, -250, 0 d3mes "50", 0, 0, 250:d3mes "-50", 0, 0, -250 color 0,255,0 d3arrow 1000, 0, 0, -1000, 0, 0 d3arrow 0, 1000, 0, 0, -1000, 0 d3arrow 0, 0, 1000, 0, 0, -1000 return

とりあえずスクリプトから一部抜粋したのですがどうでしょうか。今のところユーザーに関数を打ち込んでもらうことも頭にはありますが、まずは自身で登録した関数を出力できるようにという感じです。
関数出力部はなるべくどんな関数でも対応できるように漸化式のような形式(連続である関数しか出力は出来ないと思いますが)です。このような感じで三次元にも拡張していけたらと思っています。
数学の知識は高3くらいまでしかないので(私自身が高1ですので)三次元の拡張については資料を色々と読みあさってどんな感じかはなんとなくつかめました。皆様のおかげで平面においては何とかなりそうです。

スクリプトをみてわかるとおり関数出力部は媒介変数表示になっています。そして三次元に拡張していこうとすれば媒介変数が2つ必要になってしまいます。今の形をなるべく崩さずに三次元に拡張するにはどうすればいいでしょうか。



いろは

リンク

2016/3/22(Tue) 00:24:02|NO.75020

あ、すみません。
スクリプトの関数出力部の

i++ loop
の部分のところを直しておいてください。



motchy

リンク

2016/3/22(Tue) 10:18:00|NO.75028

 動かしてみました。螺旋のサンプルですか。細かいところは実際のソフトで色々工夫するのでしょうから、媒介変数が1つの場合の曲線はあんか感じで良いのではないかと思います。しかし、言うまでもないですがこれは曲面ではなく曲線ですよね。確認しておきたいのですが、いろはさんが今解決したい課題は、z=f(x,y) で表される関数をどうやって表示するかということでしょうか?
 こちらが勘違いしてもいけないので課題をはっきりさせておきたいのです。



いろは

リンク

2016/3/22(Tue) 12:57:58|NO.75030

はい、今の課題はz=f(x,y)の関数をどう出力すればいいかです。
処理が重くなるのが見えてますが、どうにかして曲面を描きたいです。そのためにはどうすればいいかってところです。



motchy

リンク

2016/3/22(Tue) 13:16:22|NO.75031

 メッシュの様に描くのはどうでしょうか?描画したい領域[x0,x1]×[y0,y1]を決めてx方向,y方向に十分細かく分割します。そうすると格子点が沢山できますから、各格子点上でf(x,y)を計算してz座標を求めます。最後に d3module の命令で格子点同士を宜しく線分で結びます。細かいタイルがたくさん並んだような絵ができるでしょう。細かくすればするほど、面は滑らかに見えます。

 ただし、単に線分で結んだだけだと、遠くの面を構成する線分が、よりカメラに近い面を透過して見えてしまいますから遠近感が表現できません。工夫が必要になります。

 d3module には空間上の点に対応するウィンドウ上の座標を計算してくれる機能がありますから、カメラから遠いタイルから順番にgsquare命令を使って自前で描画していきます。タイル同士の区切りがわかるように、タイルの周りはline命令で縁取っておく必要があります。タイルの角度とカメラの位置に応じてタイルの色を変えるとさらに見やすくなります。光源は+z方向の遙か彼方からxy平面を照らす平行光線で十分だと思います。この場合もちろん、光がタイルに反射してどのように見えるかを計算してやる必要があります。



motchy

リンク

2016/3/22(Tue) 13:22:19|NO.75032

いい忘れましたが、カメラの角度が変わってもf(x,y)の再計算をせずに済むよう、計算結果は配列に保存しておくのがよいでしょう。

 倍率が変われば見える範囲も変わりますから、描画する領域も広げたり縮めたりしなければいけませんし、細かさも変更しなくてはなりません。(例えばズームアップしたなら分割数を増やさないと具合が悪い。)その時には再計算しなくてはなりません。



motchy

リンク

2016/3/22(Tue) 14:00:01|NO.75033

一番簡素なやりかたで作ってみました。ワイヤーフレームです。参考になればいいですが。


#include "d3m.hsp" #include "hspmath.as" #define pi 3.1415926535 // 円周率 #define e 2.7182818284 // ネイピア数 #define UnitLengthToPx 5.0 // 1=5px #define R_CAM 300.0 // カメラと原点との距離 #define WIDTH_GRAPHAREA 800 // グラフエリアの横幅 #define HEIGHT_GRAPHAREA 600 *init // 初期化 screen 0,WIDTH_GRAPHAREA,HEIGHT_GRAPHAREA,4 theta_cam = PI/3.0 phi_cam = PI/3.0 flg_needRedraw = 1 // フラグ「グラフエリア再描画の必要有り」 flg_draggingGraphAreaToMoveCam = 0 // フラグ「カメラ移動の為にグラフエリアをドラッグ中」 x_cam = 0.0 : y_cam = 0.0 : z_cam = 0.0 d3vrotate x_cam, y_cam, z_cam, 0, 0, R_CAM, 0, 1, 0, phi_cam d3vrotate x_cam, y_cam, z_cam, x_cam, y_cam, z_cam, 0, 0, 1, theta_cam va = 1.0 // 視野角スケール d3setcam x_cam*va, y_cam*va, z_cam*va /* 曲面のzデータ */ #define X_MIN_DRAW_FUNC -10.0 // 描画するxの最小値 (※本当は倍率に応じてこれを変更せねばならない) #define X_MAX_DRAW_FUNC 10.0 #define Y_MIN_DRAW_FUNC -10.0 #define Y_MAX_DRAW_FUNC 10.0 #define DX_DRAW_FUNC 0.5 // 描画する時のx方向区切り幅 (※本当は倍率に応じてこれを変更せねばならない) #define DY_DRAW_FUNC 0.5 num_tile_x =(X_MAX_DRAW_FUNC-X_MIN_DRAW_FUNC)/DX_DRAW_FUNC : num_tile_y = (Y_MAX_DRAW_FUNC-Y_MIN_DRAW_FUNC)/DY_DRAW_FUNC ddim zData, num_tile_x+1, num_tile_y+1 x = X_MIN_DRAW_FUNC repeat num_tile_x+1 cnt_x = cnt y = Y_MIN_DRAW_FUNC repeat num_tile_y+1 #define cnt_y cnt zData(cnt_x,cnt_y) = mathFunc1(x,y) #undef cnt_y y += DY_DRAW_FUNC loop x += DX_DRAW_FUNC loop /* misc */ leftClick = 0 mx_drag_start = 0 : my_drag_start = 0 phi_cam_drag_start = 0.0 theta_cam_drag_start = 0.0 *main // メインループ #define t_mainLoop 50 // [ms]メインループ周期 repeat gosub *moveCam // カメラ移動 if flg_needRedraw { // 再描画の必要があれば title strf( "θ:%f / ", theta_cam ) + strf( "φ:%f", phi_cam ) gosub *redrawGraphArea flg_needRedraw = 0 // フラグ回収 } gosub *checkMouseCursorPosition // マウスカーソル位置監視 await t_mainLoop loop *checkMouseCursorPosition // マウスカーソル位置監視 mx_lastCheck = mousex : my_lastCheck = mousey return *redrawGraphArea // グラフエリアの再描画 logmes "Redrew graph area." redraw 0 color : boxf gosub *draw_axis // 軸の描画 gosub *draw_surface // 曲面の描画 redraw 1 return *draw_axis //軸の描画 #define k UnitLengthToPx //名前が長ったらしいので局所的に別名を使う color 0,255,0 d3mes "X", k*22, 0, 0 d3mes "Y", 0, k*22, 0 d3mes "Z", 0, 0, k*22 color 127,255,255 //5pxで「1」 d3mes "10", k*10, 0, 0:d3mes "-10", -k*10, 0, 0 d3mes "10", 0, k*10, 0:d3mes "-10", 0, -k*10, 0 d3mes "10", 0, 0, k*10:d3mes "-10", 0, 0, -k*10 d3mes "50", k*50, 0, 0:d3mes "-50", -k*50, 0, 0 d3mes "50", 0, k*50, 0:d3mes "-50", 0, -k*50, 0 d3mes "50", 0, 0, k*50:d3mes "-50", 0, 0, -k*50 color 0,255,0 d3arrow k*200, 0, 0, -k*200, 0, 0 d3arrow 0, k*200, 0, 0, -k*200, 0 d3arrow 0, 0, k*200, 0, 0, -k*200 #undef k // 片付け return *draw_surface // 関数の張る曲面を描画 #define k UnitLengthToPx redraw 0 color 0,255,255 repnum_x = (X_MAX_DRAW_FUNC-X_MIN_DRAW_FUNC)/DX_DRAW_FUNC // x方向の繰り返し回数 repnum_y = (Y_MAX_DRAW_FUNC-Y_MIN_DRAW_FUNC)/DY_DRAW_FUNC x = X_MIN_DRAW_FUNC repeat repnum_x // x方向に走査 cnt_x = cnt y = Y_MIN_DRAW_FUNC repeat repnum_y // y方向に走査 #define cnt_y cnt //d3line x, y, zData(cnt_x,cnt_y), x+DX_DRAW_FUNC, y, zData(cnt_x+1,cnt_y) //d3line x, y, zData(cnt_x,cnt_y), x, y+DY_DRAW_FUNC, zData(cnt_x,cnt_y+1) d3line k*x, k*y, k*zData(cnt_x,cnt_y), k*(x+DX_DRAW_FUNC), k*y, k*zData(cnt_x+1,cnt_y) d3line k*x, k*y, k*zData(cnt_x,cnt_y), k*x, k*(y+DY_DRAW_FUNC), k*zData(cnt_x,cnt_y+1) y += DY_DRAW_FUNC #undef cnt_y loop x += DX_DRAW_FUNC loop redraw 1 #undef k return *moveCam // カメラ移動 /* 視点移動 */ getkey leftClick, 1 if leftClick { if flg_draggingGraphAreaToMoveCam { // ドラッグ中なら if ((mousex!=mx_lastCheck)|(mousey!=my_lastCheck)) { // カーソルが動いたなら /* カメラ移動 */ theta_cam = theta_cam_drag_start + PI/HEIGHT_GRAPHAREA*(my_drag_start - my_lastCheck) phi_cam = limitf(phi_cam_drag_start + PI/WIDTH_GRAPHAREA*(mx_drag_start - mx_lastCheck), 0.01, PI-0.01) d3vrotate x_cam, y_cam, z_cam, 0, 0, R_CAM, 0, 1, 0, theta_cam d3vrotate x_cam, y_cam, z_cam, x_cam, y_cam, z_cam, 0, 0, 1, phi_cam d3setcam x_cam*va, y_cam*va, z_cam*va flg_needRedraw = 1 // 再描画の必要有り } } else { // ドラッグ中でないなら logmes "Drag started." /* ドラッグ開始 */ flg_draggingGraphAreaToMoveCam = 1 /* ドラッグ開始ポイントを記録 */ theta_cam_drag_start = theta_cam phi_cam_drag_start = phi_cam mx_drag_start = mx_lastCheck : my_drag_start = my_lastCheck } } else { if flg_draggingGraphAreaToMoveCam == 1 : flg_draggingGraphAreaToMoveCam = 0 : logmes "Drag ended." // ドラッグ解除 } /* 倍率変更 */ wheel = mousew flg = 0 if (va < 65.0)&(wheel < 0) : va += 0.009 : flg = 1 if (va > 1.0)&(wheel > 0) : va -= 0.009 : flg = 1 if (va <= 1.0)&(va > 0.001)&(wheel > 0) : va -= 0.009 : flg = 1 if (va <= 1.0)&(va > 0.001)&(wheel < 0) : va += 0.009 : flg = 1 if flg { d3setcam x_cam*va, y_cam*va, z_cam*va flg_needRedraw = 1 // 再描画の必要有り } return #module mathFuncs // 関数サンプル #define e e@ // グローバル変数をモジュール内で参照するための定義 #defcfunc mathFunc1 double x, double y r = sqrt(x*x + y*y) return 10.0*cos(r)*powf(e,-0.3*r) #global

いろはさんのオリジナルのコードでは50ms毎に必ず再描画していましたが、カメラが動いた時のみ再描画するように変更しました。CPU負荷がぐんと下がるはずです。



いろは

リンク

2016/3/23(Wed) 00:59:21|NO.75041

ワイヤーフレームでやるというのはこちらもとてもいいと思いました。関数の変化が目ですぐわかるのでとてもいいです。
とりあえず陽関数の曲面は出力することに成功しました。ですが陽関数のみでは複雑な曲面出力が出来ず媒介変数を2つ使う方法を考えようという結論に至ったのですが、私の高校生にちょっと毛が生えた程度の数学力ではどう命令すればmotchyさんのスクリプトを2つの媒介変数に対応させればよいのかわかりません。
そうして対応させたら自分のスクリプトに対応させるようにプログラムをしてみようと思っています。どなたかご教授お願いできないでしょうか。それともそもそも2つの媒介変数を用いることは無理な話なのでしょうか。

とりあえず可能かの有無が知りたいです。できればアドバイス等も。



motchy

リンク

2016/3/23(Wed) 09:30:44|NO.75047

 すみません、ミスったので再投稿です。

 前回のスクリプトを改良してみました。ズームアップ/ダウンに合わせて描画範囲を変更し、隠面処理も施してみました。座標軸と関数の曲面をどう上手く同時に表示するかという課題は残っていますが。

 これはプログラム面でのアドバイスであって、数学的には前回から何も進歩していません。


#include "d3m.hsp" #define pi 3.1415926535 // 円周率 #define e 2.7182818284 // ネイピア数 #define UnitLengthToPx 5.0 // 1=5px #define R_CAM 300.0 // カメラと原点との距離 #define WIDTH_GRAPHAREA 800 // グラフエリアの横幅 #define HEIGHT_GRAPHAREA 600 *init // 初期化 screen 0,WIDTH_GRAPHAREA,HEIGHT_GRAPHAREA,4 /* misc */ zData = 0 leftClick = 0 mx_drag_start = 0 : my_drag_start = 0 phi_cam_drag_start = 0.0 theta_cam_drag_start = 0.0 /* カメラ */ theta_cam = PI/3.0 phi_cam = PI/3.0 va = 0.5 // 視野角スケール x_cam = 0.0 : y_cam = 0.0 : z_cam = 0.0 d3vrotate x_cam, y_cam, z_cam, 0, 0, R_CAM, 0, 1, 0, phi_cam d3vrotate x_cam, y_cam, z_cam, x_cam, y_cam, z_cam, 0, 0, 1, theta_cam d3setcam x_cam*va, y_cam*va, z_cam*va /* 曲面のzデータ */ /* zDataCondition */ x_min_draw_func = 0.0 : x_max_draw_func = 0.0 : y_min_draw_func = 0.0 : y_max_draw_func = 0.0 // 描画する矩形領域の境界情報 dx_draw_func = 0.0 : dy_draw_func = 0.0 // 微小面素のサイズ num_div_x_draw_func = 0 : num_div_y_draw_func = 0 // x,y方向分割数 /* flags */ flg_needUpdate_zData = 1 // zデータ更新の必要有り flg_needRedraw = 1 // グラフエリア再描画の必要有り flg_draggingGraphAreaTooperateCam = 0 // カメラ移動の為にグラフエリアをドラッグ中 *main // メインループ #define t_mainLoop 50 // [ms]メインループ周期 repeat gosub *operateCam // カメラ移動 if flg_needUpdate_zData { // zデータ更新の必要があるならば calc_zDataCondition x_min_draw_func, x_max_draw_func, y_min_draw_func, y_max_draw_func, num_div_x_draw_func, num_div_y_draw_func // zDataConditionを計算 create_zData zData, x_min_draw_func, x_max_draw_func, y_min_draw_func, y_max_draw_func, num_div_x_draw_func, num_div_y_draw_func // zデータを生成 dx_draw_func = (x_max_draw_func-x_min_draw_func)/num_div_x_draw_func : dy_draw_func = (y_max_draw_func-y_min_draw_func)/num_div_y_draw_func flg_needUpdate_zData = 0 // フラグ回収 logmes "Updated zData." } if flg_needRedraw { // 再描画の必要があれば title strf( "θ:%f / ", theta_cam ) + strf( "φ:%f", phi_cam ) gosub *redrawGraphArea flg_needRedraw = 0 // フラグ回収 logmes "Redrew graph area." } gosub *checkMouseCursorPosition // マウスカーソル位置監視 await t_mainLoop loop *checkMouseCursorPosition // マウスカーソル位置監視 mx_lastCheck = mousex : my_lastCheck = mousey return *redrawGraphArea // グラフエリアの再描画 redraw 0 color : boxf gosub *draw_axis // 軸の描画 gosub *draw_surface // 曲面の描画 redraw 1 return *draw_axis //軸の描画 #define k UnitLengthToPx //名前が長くて面倒なので局所的に別名を使う color 0,255,0 d3mes "X", k*22, 0, 0 d3mes "Y", 0, k*22, 0 d3mes "Z", 0, 0, k*22 color 127,255,255 //5pxで「1」 d3mes "10", k*10, 0, 0:d3mes "-10", -k*10, 0, 0 d3mes "10", 0, k*10, 0:d3mes "-10", 0, -k*10, 0 d3mes "10", 0, 0, k*10:d3mes "-10", 0, 0, -k*10 d3mes "50", k*50, 0, 0:d3mes "-50", -k*50, 0, 0 d3mes "50", 0, k*50, 0:d3mes "-50", 0, -k*50, 0 d3mes "50", 0, 0, k*50:d3mes "-50", 0, 0, -k*50 color 0,255,0 d3arrow k*200, 0, 0, -k*200, 0, 0 d3arrow 0, k*200, 0, 0, -k*200, 0 d3arrow 0, 0, k*200, 0, 0, -k*200 #undef k // 後片付け return *draw_surface // 関数の張る曲面を描画 #define xmin x_min_draw_func //名前が長くて面倒なので局所的に別名を使う #define xmax x_max_draw_func #define ymin y_min_draw_func #define ymax y_max_draw_func #define nx num_div_x_draw_func #define ny num_div_y_draw_func #define dx dx_draw_func #define dy dy_draw_func gmode 3,1,1, 200 //少し透かしてみる color 0,255,255 redraw 0 repeat 1 // タイルの描画順は phi_cam の範囲によって4パターンある if (0.25*PI <= phi_cam)&(phi_cam < 0.75*PI) { repeat ny cnty = cnt repeat nx #define cntx cnt drawFuncSurfaceTile zData, cntx,cnty #undef cntx loop loop break } if (0.75*PI <= phi_cam)&(phi_cam < 1.25*PI) { repeat nx cntx = cnt repeat ny #define cnty cnt drawFuncSurfaceTile zData, nx-1-cntx, cnty #undef cnty loop loop break } if (1.25*PI <= phi_cam)&(phi_cam < 1.75*PI) { repeat ny cnty = cnt repeat nx #define cntx cnt drawFuncSurfaceTile zData, nx-1-cntx, ny-1-cnty #undef cntx loop loop break } if ((0.0 <= phi_cam)&(phi_cam < 0.25*PI))|((1.75*PI <= phi_cam)&(phi_cam < 2.0*PI)) { repeat nx cntx = cnt repeat ny #define cnty cnt drawFuncSurfaceTile zData, cntx, ny-1-cnty #undef cnty loop loop break } loop redraw 1 #undef xmin // 後片付け #undef xmax #undef ymin #undef ymax #undef nx #undef ny return *operateCam // カメラ操作 /* 視点移動 */ getkey leftClick, 1 if leftClick { if flg_draggingGraphAreaTooperateCam { // ドラッグ中なら if ((mousex!=mx_lastCheck)|(mousey!=my_lastCheck)) { // カーソルが動いたなら /* カメラ移動 */ theta_cam = limitf(theta_cam_drag_start + PI/HEIGHT_GRAPHAREA*(my_drag_start - my_lastCheck), 0.01, PI-0.01) phi_cam = limitf(phi_cam_drag_start + PI/WIDTH_GRAPHAREA*(mx_drag_start - mx_lastCheck), 0.01, 2.0*PI-0.01) d3vrotate x_cam, y_cam, z_cam, 0, 0, R_CAM, 0, 1, 0, theta_cam d3vrotate x_cam, y_cam, z_cam, x_cam, y_cam, z_cam, 0, 0, 1, phi_cam d3setcam x_cam*va, y_cam*va, z_cam*va flg_needRedraw = 1 // 再描画の必要有り } } else { // ドラッグ中でないなら logmes "Drag started." /* ドラッグ開始 */ flg_draggingGraphAreaToOperateCam = 1 /* ドラッグ開始ポイントを記録 */ theta_cam_drag_start = theta_cam phi_cam_drag_start = phi_cam mx_drag_start = mx_lastCheck : my_drag_start = my_lastCheck } } else { if flg_draggingGraphAreaToOperateCam == 1 : flg_draggingGraphAreaToOperateCam = 0 : logmes "Drag ended." // ドラッグ解除 } /* 倍率変更 */ wheel = mousew flg = 0 if (va < 65.0)&(wheel < 0) : va += 0.1*va : flg = 1 if (va > 1.0)&(wheel > 0) : va -= 0.1*va : flg = 1 if (va <= 1.0)&(va > 0.001)&(wheel > 0) : va -= 0.1*va : flg = 1 if (va <= 1.0)&(va > 0.001)&(wheel < 0) : va += 0.1*va : flg = 1 if flg { d3setcam x_cam*va, y_cam*va, z_cam*va flg_needUpdate_zData = 1 // zデータ更新の必要有り flg_needRedraw = 1 // 再描画の必要有り } return #module mathFuncs // 関数サンプル #define e e@ // グローバル変数/定数をモジュール内で参照するための定義 #defcfunc mathFunc1 double x, double y r = sqrt(x*x + y*y) return 10.0*cos(r)*powf(e,-0.3*r) #global #module module1 #define va va@ // グローバル変数/定数をモジュール内で参照するための定義 #deffunc calc_zDataCondition var xmin,var xmax,var ymin,var ymax, var nx,var ny // zDataConditionを計算 /* xmax : x_min_draw_func nx : num_div_x_draw_func */ nx = 50 : ny = 50 // ※本当はユーザーがこれを変更できるようにせねばならない。 xmin = -va*50.0 : xmax = -xmin : ymin = -va*50.0 : ymax = -ymin return #global #module module2 #deffunc create_zData array zData, double xmin,double xmax,double ymin,double ymax, int nx,int ny // zデータの生成 /* xmax : x_min_draw_func nx : num_div_x_draw_func タイルのインデックスの基点は左下(xmin,ymin)とする */ dx = (xmax-xmin)/nx : dy = (ymax-ymin)/ny // 微小面素のサイズ ddim zData, nx+1,ny+1, 2 #enum IDX_Z = 0 // 配列のインデックス #enum IDX_COSINE /* z値の書き込み */ x = xmin repeat nx+1 cnt_x = cnt y = ymin repeat ny+1 #define cnt_y cnt zData(cnt_x, cnt_y, IDX_Z) = mathFunc1(x,y) y += dy #undef cnt_y loop x += dx loop /* 微小面素の上向き法線ベクトルとz方向単位ベクトルとのなす角の余弦を計算 */ repeat nx cnt_x = cnt repeat ny #define cnt_y cnt z1 = zData(cnt_x+1,cnt_y,IDX_Z) - zData(cnt_x,cnt_y,IDX_Z) : z2 = zData(cnt_x,cnt_y+1,IDX_Z) - zData(cnt_x,cnt_y,IDX_Z) xv = -z1*dy : yv = -dx*z2 : zv = dx*dy // 法線ベクトル zData(cnt_x,cnt_y,IDX_COSINE) = zv/sqrt(xv*xv + yv*yv + zv*zv) //余弦 #undef cnt_y loop loop return #global #module module3 #define k UnitLengthToPx@ #define xmin x_min_draw_func@ #define xmax x_max_draw_func@ #define ymin y_min_draw_func@ #define ymax y_max_draw_func@ #define dx dx_draw_func@ #define dy dy_draw_func@ #enum IDX_Z = 0 // 配列のインデックス #enum IDX_COSINE dst_x_gsquare = 0 : dst_y_gsquare = 0 //未初期化変数警告回避 #deffunc drawFuncSurfaceTile array zData, int idx_x, int idx_y #define x1 xmin + dx*idx_x #define y1 ymin + dy*idx_y #define z1 zData(idx_x,idx_y,IDX_Z) #define x2 x1 + dx #define y2 y1 #define z2 zData(idx_x+1,idx_y,IDX_Z) #define x3 x2 #define y3 y2+dy #define z3 zData(idx_x+1,idx_y+1,IDX_Z) #define x4 x1 #define y4 y3 #define z4 zData(idx_x,idx_y+1,IDX_Z) brightness = absf(zData(idx_x,idx_y,IDX_COSINE)) vertex_tile = x1,y1,z1, x2,y2,z2, x3,y3,z3, x4,y4,z4 flg_error = 0 repeat 4 d3getpos x_graph,y_graph, k*vertex_tile(cnt*3),k*vertex_tile(cnt*3+1),k*vertex_tile(cnt*3+2) if stat == 0 : flg_error = 1 : break //座標変換に失敗 dst_x_gsquare(cnt) = x_graph : dst_y_gsquare(cnt) = y_graph loop if flg_error : return color limitf(brightness*255,50,220),0,0 gsquare -1, dst_x_gsquare, dst_y_gsquare /* 縁取り */ color 255,0,0 repeat 3 line dst_x_gsquare(cnt),dst_y_gsquare(cnt), dst_x_gsquare(cnt+1),dst_y_gsquare(cnt+1) loop return #global

 上のスクリプトは参考として置いておくとして、いろはさんは次の課題に挑まれるわけですね。媒介変数を2つ使うというのがピンと来ないのですが、もしかして x=f(u,v), y=g(u,v), z=h(x,y) のようなものですか?それとも、また別のものですか?具体例(数式)をひとつ挙げてもらえると助かります。



motchy

リンク

2016/3/23(Wed) 09:42:40|NO.75048

※↑のスクリプトではわざとタイルを少し透過させていますが、105行目 gmode 3,1,1, 200 の 200を255に変えることで全く透けなくなります。こういった数学のグラフに限れば面素群をzソートしなくてもいいので高速に描画できます。



いろは

リンク

2016/3/23(Wed) 13:33:40|NO.75049

x=f(u,v),y=g(u,v),z=h(u,v)といった形式のものです。
1つ例を挙げるとならば、x=rcos(s)cos(t),y=rcos(s)sin(t),z=rsin(s)といった球体等です。



motchy

リンク

2016/3/23(Wed) 14:57:52|NO.75050

 なるほど、そういう類の曲面ですか。複雑な図形も表現できますね。(※球ですと私の周りでは x=r*sin(θ)*cos(φ), y=r*sin(θ)*sin(φ), z=r*cos(θ) の方を良く目にします(本質的には一緒)。電磁理論の教科書なんかでは専らこちら。)

 結論から言うと、描けます。実は私も嘗てそんなことをHSPでやっていた者です。言葉で説明すると結構わかりにくいのですが、まぁやってみましょうか。私の馴染みのあるタイプの球で説明しましょう。

 2つの独立変数(θ,φ)があるのなら、当然、定義域があるわけです。θ∈[-π,π], φ∈[0,2π]。同時に動かすのは無茶ですから片方θを固定しておいて他方φを動かします。φが一回りしたらθを微小に動かしてまた固定。φを一回りさせます。これをどんどん繰り返してθの定義域を舐めつくしたら終了です。(※θ,φどちらを固定するかは人間の都合なので入れ替えてもOK) それで先程のz=f(x,y)と同じ要領でメッシュを張れば良いのです。

 日本語だとわかりにくいですね。ソースコードで表すなら、


min_theta = -PI : max_theta = PI //θの定義域 min_phi = 0 : max_phi = 2.0*PI //φの定義域 num_div_theta = 100 //θの分割数 num_div_phi = 100 //φの分割数 d_theta = (max_theta-min_theta)/num_div_theta //dθ d_phi = (max_phi-min_phi)/num_div_phi //dφ ddim vertexData, num_div_theta, num_div_phi, 3 //頂点データ用の配列 #enum IDX_X_vertexData = 0 #enum IDX_Y_vertexData #unum IDX_Z_vertexData theta = min_theta repeat num_div_theta cnt_theta = cnt phi = min_phi repeat num_div_phi #define cnt_phi cnt vertexData(cnt_theta,cnt_phi,IDX_X_vertexData) = r*sin(theta)*cos(phi) vertexData(cnt_theta,cnt_phi,IDX_Y_vertexData) = r*sin(theta)*sin(phi) vertexData(cnt_theta,cnt_phi,IDX_Z_vertexData) = r*cos(theta) phi += d_phi loop theta += d_theta loop

こうやって用意した頂点データを上手く線分で結ぶのが少し難しいですが、大抵は安直なやりかたでそこそこの見栄えにできます。
(theta,phi)に対応する頂点を(theta,phi+d_phi)に対応する頂点を線分で結びます。それから、(theta,phi)に対応する頂点と(theta+d_theta,phi)に対応する頂点を線分で結びます。これを終わりまで繰り返せばいい感じにメッシュを張れます。ただし定義域の端には気をつけてください。theta+d_thetaが定義域をオーバーランしますので、そこは諦めるのが一番簡単です。布地の端っこみたいですね。ほつれそうな感じの。



motchy

リンク

2016/3/23(Wed) 15:31:39|NO.75051

 ただし、これくらい複雑になってくると色々と面倒なことが起こり始めます。上の球なんかではワイヤーフレームで描いてもまぁいいのですが、奥の面と手前の面が一緒くたに描画されますから少々わかりにくくなります。そこで隠面処理を行おうとすると、微小面素をカメラからの距離に応じて遠い順に並べ替えて(zソート)、奥のものから順番に描いていかねばなりません。(画家のアルゴリズム)
分割を細かくすれば微小面素の枚数はあっという間に数千,数万に達します。これだけのものをzソートしようとすると膨大な計算量になりますから処理時間が長くなります。マウスでグリグリ気持よく回すことは期待できなくなります。ガクガクです。

 実感がわかないと思うのでこれを見てください。私が昔作ったものです。
http://hsproom.me/program/view/?p=23
 分割数を増やすと急激に遅くなるのがわかると思います。(※50x50でポリゴンが欠け始めるのはHSP部屋の特性(設定次第で修正できるものですが)なので気にしないでください。)

少しでも面素の枚数を減らそうと思えば、カメラがズームアップした時に、分割数を増やすと同時に、見えている範囲のみに計算を絞る必要があります。これが恐ろしく大変です。見えている範囲に対応する(θ,φ)の領域を算出しなければなりませんから。しかもその領域は(θ,φ)空間の矩形領域にはまずなりません。θとφの陰関数で表現される非常に厄介な領域になるでしょう。こんな領域をどうやって上手く舐めつくすか考えるのは頭が痛いことです。この辺りにくると工学系の私は非常に苦しくなってきます(笑)。要するに、メッシュのきめ細かさをそう簡単には変更できないことになります。多峰性を有する曲面を表現するにはきめ細かいメッシュが必要なのに、そうもいかないということです。

 そこでですが、HSPの標準命令に頼る3D描画をやめて(=d3moduleから離れて)、プラグインもしくは拡張ランタイムを使う高速描画でゴリ押すというのも1つの手かと思います。なんだか最近はHGIMG4なるランタイムが熱いみたいですよ。頂点と微小面素の計算は標準命令で間に合います。算出したポリゴンをHGIMG4等に叩き込んで撮影させるのはどうでしょう? 尤も、私は使ったことがないのでそういうことが可能なのかどうかはわかりませんが。多分ポリゴンの登録機能くらいは付いているんじゃないでしょうか?



いろは

リンク

2016/3/24(Thu) 00:47:31|NO.75052

ありがとうございます。motchyさんの提案も少し検討してみます。こちらのほうが自分にあっていれば乗り換えてみようかと。
とりあえず、ある程度曲面は出力することが出来たので今回は一応これで解決にします。

他に何かいい案や何かこうした法がいいとかがありましたら続きき書いていってもらえると嬉しいです。
是非今後の開発の参考にさせていただきます。



ONION software Copyright 1997-2021(c) All rights reserved.