ボクセルみたいなの出来たらなー。などともくもく、そのためにはいろいろ乗り越えないとイケないことがある。
ひとまずポリゴンを自前でやってみようと思ったのでPyGameをpyg経由で使って実装してみました。
#!/usr/bin/python3
import pyg
import math
class main:
def faceVect(self,p):
v1 = (p[1][0] - p[0][0], p[1][1] - p[0][1], p[1][2] - p[0][2])
v2 = (p[2][0] - p[0][0], p[2][1] - p[0][1], p[2][2] - p[0][2])
n0 = v1[1] * v2[2] - v1[2] * v2[1]
n1 = v1[2] * v2[0] - v1[0] * v2[2]
n2 = v1[0] * v2[1] - v1[1] * v2[0]
ln = math.sqrt(n0**2 + n1**2 + n2**2)
if ln != 0:
n0 = n0 / ln
n1 = n1 / ln
n2 = n2 / ln
return (n0,n1,n2)
def cl(self,p):
a = (p[1][0] - p[0][0]) * (p[2][1] - p[0][1])
b = (p[1][1] - p[0][1]) * (p[2][0] - p[0][0])
return a > b
def rotate(self, points, rx, ry, rz):
b = math.radians(ry)
a = math.radians(rx)
g = math.radians(rz)
p2 = [0]*len(points)
lc = int(len(points)/3)
for i in range(lc):
idx = i * 3
x0 = points[idx]
y0 = points[idx+1]
z0 = points[idx+2]
x1 = x0 * math.cos(b) + z0 * math.sin(b)
z1 =-x0 * math.sin(b) + z0 * math.cos(b)
y2 = y0 * math.cos(a) - z1 * math.sin(a)
z2 = y0 * math.sin(a) + z1 * math.cos(a)
x3 = x1 * math.cos(g) - y2 * math.sin(g)
y3 = x1 * math.sin(g) + y2 * math.cos(g)
p2[idx] = x3
p2[idx+1] = y3
p2[idx+2] = z2
return p2
def trans(self, points, rx, ry, rz):
p2 = [[0,0,0]] * 3
c = 0
for p in points:
x = p[0] / ( 10 + p[2] ) * 500
y = p[1] / ( 10 + p[2] ) * 500
z = p[2]
p2[c] = (x+160,y+160,z)
c += 1
return p2
def light(self,vec):
light = (0,0,-1)
a = vec[0] * light[0]
b = vec[1] * light[1]
c = vec[2] * light[2]
return abs(a+b+c)
def getTri(self, data, link):
idx = link[0] * 3
a = (data[idx],data[idx+1],data[idx+2])
idx = link[1] * 3
b = (data[idx],data[idx+1],data[idx+2])
idx = link[2] * 3
c = (data[idx],data[idx+1],data[idx+2])
return (a,b,c)
def setup(self):
pyg.setSize((320,320))
pyg.setTitle("polygon")
self.rx = 0
self.ry = 0
self.rz = 0
self.p = (-1,1,1, 1,1,1, 1,-1,1, -1,-1,1, -1,1,-1, 1,1,-1, 1,-1,-1, -1,-1,-1)
self.link = (
(0,1,2),(0,2,3), (1,5,6),(1,6,2),
(5,4,7),(5,7,6), (4,0,3),(4,3,7),
(4,1,0),(4,5,1), (3,2,6),(3,6,7))
def loop(self):
pyg.clear()
p1 = self.rotate(self.p, self.rx, self.ry, self.rz)
for l in self.link:
t = self.getTri(p1,l)
v = self.faceVect(t)
p2=self.trans(t,self.rx,self.ry,self.rz)
if self.cl(p2):
lv = self.light(v)
cl = math.floor(64 * lv + 190)
color = pyg.Color(cl,cl,cl)
pyg.getGfx().filled_polygon(pyg.getSurface(), p2, color)
if pyg.isKeyDownCode(273):
self.rx = (self.rx - 5) % 360
if pyg.isKeyDownCode(274):
self.rx = (self.rx + 5) % 360
if pyg.isKeyDownCode(275):
self.ry = (self.ry - 5) % 360
if pyg.isKeyDownCode(276):
self.ry = (self.ry + 5) % 360
m = main()
pyg.run(m)
カーソルでぐりぐり動きます。
法線ベクトルを求めるためのもの。
カリング用です。
回転式。すべてのポイントを一気に計算します。
2次元透視変換
照明効果、今の所真正面から光を当てているだけです。
点群データからリンクデータを元に3点取り出す。
今回は単純なCube一つを描画しています。点群数は8。リンクデータが12。とても小さなデータですが想像以上に計算が多い。 Pythonの場合大真面目に計算するよりも処理の速いネイティブにデータを投げてしまって手を抜いたほうが速かったりするので ひょっとするとカリングの処理なんかはやらずZソートだけやってあげたほうがより速度が出るかもしれません。
これでいくつかの計算とifの処理がなくなるので、ひょっとしなくてもそのほうが良いのかも。
理屈は分かるけどしっくり来てないというのが全体的な話でライトの処理など、ライトの持つベクトルの扱いなど釈然とせずに悶々としているといった部分が多々あります。
lightファンクションはオブジェクトの法線ベクトルとライトのベクトルを計算して 0 ~ 1のデータを返すようになっています。 これがそのオブジェクトに与えられる明るさです。ライトのベクトルをいじれば光のあたる方向がかわるはずです。 現状ライトオブジェクトを扱うような処理は入っていませんのでそういうものを作ればすぐどうにかなるのかもしれませんが。
faceVectとclは本来一つで済みそうな処理です。faceVectのz軸の方向を見ればclの返す値の代替えができるのです(というか内容としては全く一緒です) 現状なぜ2つに別れているのかというと2次元透視変換をかける前でなければ正しい法線ベクトルが得られない、変換後でないと正しいカリングが処理できない。 という理由です。非常に厄介です。 大した計算はしていないのでそれほど処理に影響はないのかもしれませんがチリツモです。いい処理方法があるなら知りたい。
といったところでしょうか。
現状は点群数も少ないので何のストレスもなく動いていますが点群が増えればちらちらと見え隠れする処理の無駄が大きくのしかかってくるのは間違いないでしょうし。 まだまだ考えないといけないことは多いようです。