
ちょっとくだらない理由で元町のジュエリー店に行ってきました。
ふと見るとカラット級の大粒ダイヤがあります。
見つめていると魂を吸い込まれていきそうになります。
値段を見ます。
ふむ、買えなくもない。
未だボーナスは手付かずだったな……。
これ、いただきます……。
はっ! 私は何を考えているのだ?
ダイヤに誘惑されている自分を必死に抑えます。
これほどまでにダイヤモンドは私の心を魅了します。
ラウンド・ブリリアントカット の大粒のダイヤ、ずっとずっと眺めていたい。
車のディーラーもそうなのですが、ジュエリー店には行ってはいけませんね。
欲しくなってしまうのです。
気が付いたら買う前提で店員と話をしていたりします。
危険極まりありません。
我に返ってジュエリー店を後にしたのですがダイヤモンドが脳裏から離れません。
欲しい、欲しい、手元に置いて眺めていたい。
しかし買うわけにはいかないのです。
子供の塾代もあるし、家のローンだってあるのです。
ということで3Dモデルで我慢することにしました。
どうせならば作りましょう。
ラウンドブリリアントカットは寸法比や角度が精密に定義されているものだとばかり思っていました。
調べてみると、比較的ゆるいルールしかないんですね。
まあ想像するに相手は自然から発掘される石ころなのだから、取れた原石に合わせてカットする自由度を残す必要があるのでしょうね。
とは言え、幾何学図形なのでだいたいの形はスクリプト生成できます。
以下ラウンドブリリアントカットの3Dオブジェクトデータを生成するpythonスクリプトです。
# -*- coding: utf-8 -*- from mpl_toolkits.mplot3d import axes3d import math import matplotlib.pyplot as plt import numpy as np def rot_z(r, P): # z軸で回転行列を計算する Rz = np.array([[ np.cos(r), np.sin(r), 0.0 ], [-np.sin(r), np.cos(r), 0.0 ], [ 0.0, 0.0, 1.0 ]]) R = Rz.dot(P.T) return R.T def rot_y(r, P): # y軸で回転行列を計算する Ry = np.array([[ np.cos(py), 0.0, -np.sin(py)], [ 0.0, 1.0, 0.0 ], [ np.sin(py), 0.0, np.cos(py)]]) R = Rz.dot(P.T) return R.T def rot_x(r, P): # x軸で回転行列を計算する Rx = np.array([[ 1.0, 0.0, 0.0 ], [ 0.0, np.cos(px), -np.sin(px)], [ 0.0, np.sin(px), np.cos(px)]]) R = Rz.dot(P.T) return R.T def mid_point(P , Q, par): pp = par / 100.0 pq = (100.0 - par) / 100.0 PP = np.array([pp, pp, pp]) PQ = np.array([pq, pq, pq]) P = P * PP Q = Q * PQ R = P + Q return R def push_index(R): global x global y global z x.append(R[0]) y.append(R[1]) z.append(R[2]) def print_v(s, P): global v_num v_num += 1 sr = s + ' ' sr += ' {:.6f}'.format(P[0]) sr += ' {:.6f}'.format(P[1]) sr += ' {:.6f}'.format(P[2]) sr += '\n' return(sr) #定数 table_r = 0.55 / 2.0 girdle_r = 1.0 / 2.0 crown_height = 0.162 pavilion_height = 0.431 girdle_height = 0.025 star_ratio = 60.0 pavilion_ratio = 40.0 culet_ratio = 2.5 center = 50.0 t = math.tan(math.pi/8.0) x = [] y = [] z = [] v_num = 0 # table(auxiliary point) P_TA = np.array([]) P_TA = np.empty((0,3), float) S = np.array([table_r, t * table_r, crown_height]) for num in range(0, 8): R = rot_z(math.pi / 4 * num, S) P_TA = np.append(P_TA, np.array([R]), axis=0) # table P_TB = np.array([]) P_TB = np.empty((0,3), float) S = mid_point(P_TA[0] , P_TA[1], 50.0) for num in range(0, 8): R = rot_z(math.pi / 4 * num, S) push_index(R) P_TB = np.append(P_TB, np.array([R]), axis=0) # upper girdle(auxiliary point) P_UGA = np.array([]) P_UGA = np.empty((0,3), float) S = np.array([girdle_r, t * girdle_r, 0]) for num in range(0, 8): R = rot_z(math.pi / 4 * num, S) # push_index(R) P_UGA = np.append(P_UGA, np.array([R]), axis=0) # upper girdle P_UG = np.array([]) P_UG = np.empty((0,3), float) S = mid_point(P_UGA[0] , P_UGA[1], center) for num in range(0, 16): R = rot_z(math.pi / 8 * num, S) push_index(R) P_UG = np.append(P_UG, np.array([R]), axis=0) # star facet P_SF = np.array([]) P_SF = np.empty((0,3), float) for num in range(0, 8): R = mid_point(P_TA[num] , P_UGA[num], star_ratio) push_index(R) P_SF = np.append(P_SF, np.array([R]), axis=0) # culet P_CU = np.array([0, 0, -pavilion_height - girdle_height]) # lower girdle(auxiliary point) P_LGA = np.array([]) P_LGA = np.empty((0,3), float) S = np.array([girdle_r, t * girdle_r, -girdle_height]) for num in range(0, 8): R = rot_z(math.pi / 4 * num, S) # push_index(R) P_LGA = np.append(P_LGA, np.array([R]), axis=0) # lower girdle P_LG = np.array([]) P_LG = np.empty((0,3), float) S = mid_point(P_LGA[0] , P_LGA[1], center) for num in range(0, 16): R = rot_z(math.pi / 8 * num, S) push_index(R) P_LG = np.append(P_LG, np.array([R]), axis=0) # pavilion facet P_PF = np.array([]) P_PF = np.empty((0,3), float) for num in range(0, 8): R = mid_point(P_LGA[num] , P_CU, pavilion_ratio) push_index(R) P_PF = np.append(P_PF, np.array([R]), axis=0) # culet table P_QT = np.array([]) P_QT = np.empty((0,3), float) for num in range(0, 8): R = mid_point(P_LGA[num] , P_CU, culet_ratio) push_index(R) P_QT = np.append(P_QT, np.array([R]), axis=0) #make obj file path_w = './test_diamond.obj' s = 'New file' with open(path_w, mode='w') as f: f.write('g diamond\n') for num in range(0, 8): f.write(print_v('v', P_TB[num])) for num in range(0, 8): f.write(print_v('v', P_SF[num])) for num in range(0, 16): f.write(print_v('v', P_UG[num])) for num in range(0, 16): f.write(print_v('v', P_LG[num])) for num in range(0, 8): f.write(print_v('v', P_PF[num])) for num in range(0, 8): f.write(print_v('v', P_QT[num])) t_base = 0 s_base = 8 ug_base = 16 lg_base = 32 p_base = 48 c_base = 56 #Table Facet: 1 f.write('#Table Facet: num = 1\n') f.write('f 1 2 3 4 5 6 7 8\n') #Bezel Facet: 8 f.write('#Bezel Facet: num = 8\n') f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 1 , s_base + 1, ug_base + 1, s_base + 2)) f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 2 , s_base + 2, ug_base + 3, s_base + 3)) f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 3 , s_base + 3, ug_base + 5, s_base + 4)) f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 4 , s_base + 4, ug_base + 7, s_base + 5)) f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 5 , s_base + 5, ug_base + 9, s_base + 6)) f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 6 , s_base + 6, ug_base + 11, s_base + 7)) f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 7 , s_base + 7, ug_base + 13, s_base + 8)) f.write('f {:d} {:d} {:d} {:d}\n'.format(t_base + 8 , s_base + 8, ug_base + 15, s_base + 1)) #Star Facet: 8 f.write('#Star Facet: num = 8\n') f.write('f {:d} {:d} {:d}\n'.format(t_base + 1 , s_base + 2, t_base + 2)) f.write('f {:d} {:d} {:d}\n'.format(t_base + 2 , s_base + 3, t_base + 3)) f.write('f {:d} {:d} {:d}\n'.format(t_base + 3 , s_base + 4, t_base + 4)) f.write('f {:d} {:d} {:d}\n'.format(t_base + 4 , s_base + 5, t_base + 5)) f.write('f {:d} {:d} {:d}\n'.format(t_base + 5 , s_base + 6, t_base + 6)) f.write('f {:d} {:d} {:d}\n'.format(t_base + 6 , s_base + 7, t_base + 7)) f.write('f {:d} {:d} {:d}\n'.format(t_base + 7 , s_base + 8, t_base + 8)) f.write('f {:d} {:d} {:d}\n'.format(t_base + 8 , s_base + 1, t_base + 1)) #Upper-Girdle Facet: 16 f.write('#Upper-Girdle Facet: num = 16\n') f.write('f {:d} {:d} {:d}\n'.format(s_base + 2 , ug_base + 1, ug_base + 2)) f.write('f {:d} {:d} {:d}\n'.format(s_base + 2 , ug_base + 2, ug_base + 3)) f.write('f {:d} {:d} {:d}\n'.format(s_base + 3 , ug_base + 3, ug_base + 4)) f.write('f {:d} {:d} {:d}\n'.format(s_base + 3 , ug_base + 4, ug_base + 5)) f.write('f {:d} {:d} {:d}\n'.format(s_base + 4 , ug_base + 5, ug_base + 6)) f.write('f {:d} {:d} {:d}\n'.format(s_base + 4 , ug_base + 6, ug_base + 7)) f.write('f {:d} {:d} {:d}\n'.format(s_base + 5 , ug_base + 7, ug_base + 8)) f.write('f {:d} {:d} {:d}\n'.format(s_base + 5 , ug_base + 8, ug_base + 9)) f.write('f {:d} {:d} {:d}\n'.format(s_base + 6 , ug_base + 9, ug_base + 10)) f.write('f {:d} {:d} {:d}\n'.format(s_base + 6 , ug_base + 10, ug_base + 11)) f.write('f {:d} {:d} {:d}\n'.format(s_base + 7 , ug_base + 11, ug_base + 12)) f.write('f {:d} {:d} {:d}\n'.format(s_base + 7 , ug_base + 12, ug_base + 13)) f.write('f {:d} {:d} {:d}\n'.format(s_base + 8 , ug_base + 13, ug_base + 14)) f.write('f {:d} {:d} {:d}\n'.format(s_base + 8 , ug_base + 14, ug_base + 15)) f.write('f {:d} {:d} {:d}\n'.format(s_base + 1 , ug_base + 15, ug_base + 16)) f.write('f {:d} {:d} {:d}\n'.format(s_base + 1 , ug_base + 16, ug_base + 1)) #Girdle: 16 f.write('#Girdle: num = 16\n') f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 1, lg_base + 1 , lg_base + 2 , ug_base + 2)) f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 2, lg_base + 2 , lg_base + 3 , ug_base + 3)) f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 3, lg_base + 3 , lg_base + 4 , ug_base + 4)) f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 4, lg_base + 4 , lg_base + 5 , ug_base + 5)) f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 5, lg_base + 5 , lg_base + 6 , ug_base + 6)) f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 6, lg_base + 6 , lg_base + 7 , ug_base + 7)) f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 7, lg_base + 7 , lg_base + 8 , ug_base + 8)) f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 8, lg_base + 8 , lg_base + 9 , ug_base + 9)) f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 9, lg_base + 9 , lg_base + 10 , ug_base + 10)) f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 10, lg_base + 10 , lg_base + 11 , ug_base + 11)) f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 11, lg_base + 11 , lg_base + 12 , ug_base + 12)) f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 12, lg_base + 12 , lg_base + 13 , ug_base + 13)) f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 13, lg_base + 13 , lg_base + 14 , ug_base + 14)) f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 14, lg_base + 14 , lg_base + 15 , ug_base + 15)) f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 15, lg_base + 15 , lg_base + 16 , ug_base + 16)) f.write('f {:d} {:d} {:d} {:d}\n'.format(ug_base + 16, lg_base + 16 , lg_base + 1 , ug_base + 1)) #Lower-Girdle Facet: 16 f.write('#Lower-Girdle Facet: num = 16\n') f.write('f {:d} {:d} {:d}\n'.format(p_base + 2 , lg_base + 1, lg_base + 2)) f.write('f {:d} {:d} {:d}\n'.format(p_base + 2 , lg_base + 2, lg_base + 3)) f.write('f {:d} {:d} {:d}\n'.format(p_base + 3 , lg_base + 3, lg_base + 4)) f.write('f {:d} {:d} {:d}\n'.format(p_base + 3 , lg_base + 4, lg_base + 5)) f.write('f {:d} {:d} {:d}\n'.format(p_base + 4 , lg_base + 5, lg_base + 6)) f.write('f {:d} {:d} {:d}\n'.format(p_base + 4 , lg_base + 6, lg_base + 7)) f.write('f {:d} {:d} {:d}\n'.format(p_base + 5 , lg_base + 7, lg_base + 8)) f.write('f {:d} {:d} {:d}\n'.format(p_base + 5 , lg_base + 8, lg_base + 9)) f.write('f {:d} {:d} {:d}\n'.format(p_base + 6 , lg_base + 9, lg_base + 10)) f.write('f {:d} {:d} {:d}\n'.format(p_base + 6 , lg_base + 10, lg_base + 11)) f.write('f {:d} {:d} {:d}\n'.format(p_base + 7 , lg_base + 11, lg_base + 12)) f.write('f {:d} {:d} {:d}\n'.format(p_base + 7 , lg_base + 12, lg_base + 13)) f.write('f {:d} {:d} {:d}\n'.format(p_base + 8 , lg_base + 13, lg_base + 14)) f.write('f {:d} {:d} {:d}\n'.format(p_base + 8 , lg_base + 14, lg_base + 15)) f.write('f {:d} {:d} {:d}\n'.format(p_base + 1 , lg_base + 15, lg_base + 16)) f.write('f {:d} {:d} {:d}\n'.format(p_base + 1 , lg_base + 16, lg_base + 1)) #Pavilion Facet: 8 f.write('#Pavilion Facet: num = 8\n') f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base + 1 , p_base + 1, c_base + 1, c_base + 2, p_base + 2)) f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base + 3 , p_base + 2, c_base + 2, c_base + 3, p_base + 3)) f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base + 5 , p_base + 3, c_base + 3, c_base + 4, p_base + 4)) f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base + 7 , p_base + 4, c_base + 4, c_base + 5, p_base + 5)) f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base + 9 , p_base + 5, c_base + 5, c_base + 6, p_base + 6)) f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base + 11 , p_base + 6, c_base + 6, c_base + 7, p_base + 7)) f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base + 13 , p_base + 7, c_base + 7, c_base + 8, p_base + 8)) f.write('f {:d} {:d} {:d} {:d} {:d}\n'.format(lg_base + 15 , p_base + 8, c_base + 8, c_base + 1, p_base + 1)) #Culet: 1 f.write('#Culet: num = 1\n') f.write('f 57 58 59 60 61 62 63 64\n') # plot fig = plt.figure() ax = fig.add_subplot(111, projection='3d') ax.set_xlabel("x") ax.set_ylabel("y") ax.set_zlabel("z") ax.scatter(x, y, z) #ax.plot(x, y, z) plt.show()
このpythonコードはカレントディレクトリにtest_diamond.objという3Dオブジェクトファイルを作成します。
幾つかの追加モジュールが必要です。
一応生成したファイルも以下に置いておきます。
g diamond v 0.275000 -0.000000 0.162000 v 0.194454 -0.194454 0.162000 v 0.000000 -0.275000 0.162000 v -0.194454 -0.194454 0.162000 v -0.275000 -0.000000 0.162000 v -0.194454 0.194454 0.162000 v -0.000000 0.275000 0.162000 v 0.194454 0.194454 0.162000 v 0.365000 0.151188 0.097200 v 0.365000 -0.151188 0.097200 v 0.151188 -0.365000 0.097200 v -0.151188 -0.365000 0.097200 v -0.365000 -0.151188 0.097200 v -0.365000 0.151188 0.097200 v -0.151188 0.365000 0.097200 v 0.151188 0.365000 0.097200 v 0.500000 -0.000000 0.000000 v 0.461940 -0.191342 0.000000 v 0.353553 -0.353553 0.000000 v 0.191342 -0.461940 0.000000 v 0.000000 -0.500000 0.000000 v -0.191342 -0.461940 0.000000 v -0.353553 -0.353553 0.000000 v -0.461940 -0.191342 0.000000 v -0.500000 -0.000000 0.000000 v -0.461940 0.191342 0.000000 v -0.353553 0.353553 0.000000 v -0.191342 0.461940 0.000000 v -0.000000 0.500000 0.000000 v 0.191342 0.461940 0.000000 v 0.353553 0.353553 0.000000 v 0.461940 0.191342 0.000000 v 0.500000 -0.000000 -0.025000 v 0.461940 -0.191342 -0.025000 v 0.353553 -0.353553 -0.025000 v 0.191342 -0.461940 -0.025000 v 0.000000 -0.500000 -0.025000 v -0.191342 -0.461940 -0.025000 v -0.353553 -0.353553 -0.025000 v -0.461940 -0.191342 -0.025000 v -0.500000 -0.000000 -0.025000 v -0.461940 0.191342 -0.025000 v -0.353553 0.353553 -0.025000 v -0.191342 0.461940 -0.025000 v -0.000000 0.500000 -0.025000 v 0.191342 0.461940 -0.025000 v 0.353553 0.353553 -0.025000 v 0.461940 0.191342 -0.025000 v 0.200000 0.082843 -0.283600 v 0.200000 -0.082843 -0.283600 v 0.082843 -0.200000 -0.283600 v -0.082843 -0.200000 -0.283600 v -0.200000 -0.082843 -0.283600 v -0.200000 0.082843 -0.283600 v -0.082843 0.200000 -0.283600 v 0.082843 0.200000 -0.283600 v 0.012500 0.005178 -0.445225 v 0.012500 -0.005178 -0.445225 v 0.005178 -0.012500 -0.445225 v -0.005178 -0.012500 -0.445225 v -0.012500 -0.005178 -0.445225 v -0.012500 0.005178 -0.445225 v -0.005178 0.012500 -0.445225 v 0.005178 0.012500 -0.445225 #Table Facet: num = 1 f 1 2 3 4 5 6 7 8 #Bezel Facet: num = 8 f 1 9 17 10 f 2 10 19 11 f 3 11 21 12 f 4 12 23 13 f 5 13 25 14 f 6 14 27 15 f 7 15 29 16 f 8 16 31 9 #Star Facet: num = 8 f 1 10 2 f 2 11 3 f 3 12 4 f 4 13 5 f 5 14 6 f 6 15 7 f 7 16 8 f 8 9 1 #Upper-Girdle Facet: num = 16 f 10 17 18 f 10 18 19 f 11 19 20 f 11 20 21 f 12 21 22 f 12 22 23 f 13 23 24 f 13 24 25 f 14 25 26 f 14 26 27 f 15 27 28 f 15 28 29 f 16 29 30 f 16 30 31 f 9 31 32 f 9 32 17 #Girdle: num = 16 f 17 33 34 18 f 18 34 35 19 f 19 35 36 20 f 20 36 37 21 f 21 37 38 22 f 22 38 39 23 f 23 39 40 24 f 24 40 41 25 f 25 41 42 26 f 26 42 43 27 f 27 43 44 28 f 28 44 45 29 f 29 45 46 30 f 30 46 47 31 f 31 47 48 32 f 32 48 33 17 #Lower-Girdle Facet: num = 16 f 50 33 34 f 50 34 35 f 51 35 36 f 51 36 37 f 52 37 38 f 52 38 39 f 53 39 40 f 53 40 41 f 54 41 42 f 54 42 43 f 55 43 44 f 55 44 45 f 56 45 46 f 56 46 47 f 49 47 48 f 49 48 33 #Pavilion Facet: num = 8 f 33 49 57 58 50 f 35 50 58 59 51 f 37 51 59 60 52 f 39 52 60 61 53 f 41 53 61 62 54 f 43 54 62 63 55 f 45 55 63 64 56 f 47 56 64 57 49 #Culet: num = 1 f 57 58 59 60 61 62 63 64
この内容を test_diamond.obj という名前で保存すればpythonのコードを実行する必要はありません。
あとはBlenderなどの3D編集ツールにインポートすれば良いわけです。
尚、Blenderでインポートする場合がZが上で、法線を外側に揃えて下さい。
マテリアルはグラスBSDFでIORは2.420あたりにセットしてください。
透過屈折するオブジェクトを綺麗にレンダリングするためには周辺環境とライティングを作りこむ必要があるようです。
クオリティティを追求するにはテクが必要ですね。

まだまだ魂が吸い込まれそうな画像には程遠いですが、楽しいものです。
この記事をご覧のあなたもぜひお試しあれ。