Korkeuskartan valaistus

Oletko tehnyt jotain, mistä muut voisivat hyötyä. Postita vinkit tänne.
Post Reply
User avatar
CCE
Artist
Artist
Posts: 650
Joined: Mon Aug 27, 2007 9:53 pm

Korkeuskartan valaistus

Post by CCE »

Tein tämmöisen esimerkin jokin aika sitten havainnolistaakseni ns. bump-mapping -efektiä. Tämän voisi huijata nopeamminkin esilasketulla kuvalla ja palettia muuttelemalla, mutta uskoisin tämän version olevan opettavaisempi. 8-)

Code: Select all

SCREEN 512,512
// Korkeuskartan valaistus
// koodaillut cce, kiitos misthemalle parista fiksauksesta
//
// Ohjelma laskee harmaasävyisen korkeuskartan jokaiselle pisteelle ns. "pinnan normaalin",
// joka tarkoittaa suuntavektoria kohtisuoraan ylös pinnasta sillä kohdalla karttaa.
// Itse pääloopissa sitten verrataan pisteen normaalin ja valosta pisteeseen osoittavan vektorin 
// suuntia, ja annetaan pikselille väriarvo kulmien erotuksen mukaan.
// 
// 3D-vektori voidaan esittää komponenttimuodossa (x, y, z).
// Kaksi vektoria voidaan summata seuraavasti:
// 		v1 + v2 = (v1_x + v2_x, v1_y + v2_y, v1_z + v2_z)
//
// Vähennyslasku toimii samalla tavalla laskemalla komponentti kerrallaan.
//
// Vektorin pituus lasketaan pythagoraan lauseella
// 		sqrt(x^2 + y^2 + z^2)
//
// Vektori voidaan ns. "normalisoida", jolloin sen pituudeksi tulee tasan 1 (katso ylempää)
// mutta sen suunta pysyy samana. Tämä onnistuu jakamalla vektorin jokainen komponentti kyseisen
// vektorin pituudella, eli näin 

//		v = vektori; vx, vy, ja vz ovat vektorin kolme komponenttia
//
// 		v_pituus = sqrt(vx^2 + vy^2 + vz^2) 
//		vx = vx / v_pituus
// 		vy = vy / v_pituus
// 		vz = vz / v_pituus
//
//		Nyt vektorin v pituus on tasan yksi, tarkista vaikka laskemalla.
//
// Kun vektorin pituus on yksi, kutsutaan sitä nimellä "yksikkövektori" (engl. "unit vector")
// Kun puhutaan suuntavektorista, tarkoitetaan yksikkövektoria joka osoittaa ainoastaan jonkin suunnan.
// Vektorin suuntaa voi havainnollistaa piirtämällä viivan origosta pisteeseen (vx, vy, vz), jolloin
// vektorin suunta olisi viivan piirtosuunta. 
// 
// Suuntavektorien lisäksi on olemassa "paikkavektoreita", jotka yksinkertaisesti kertovat jonkin
// paikan kolmiulotteisessa avaruudessa. Esimerkiksi pelaajan paikkaa pelimaailmassa voisi kuvata
// ohjelman sisäisesti paikkavektorilla, eli siis kolmella arvolla pelaaja_x, pelaaja_y ja pelaaja_z,
// joista muodostuisi vektori (pelaaja_x, pelaaja_y, pelaaja_z).
//
// Kahden suuntavektorin välinen kulma voidaan laskea ns. "pistetulolla"
// 		Olkoon yksikkövektorit a = (ax, ay, az) ja b = (bx, by, bz), niin niiden pistetulo on
//		pistetulo# = (ax*bx) + (ay*by) + (az*bz)
// 
// Huomaa että pistetulon arvo on liukulukuarvo eikä vektori. Mikäli vektorit olivat yksikkövektoreita,
// pitäisi pistetulon arvo olla nyt välillä [-1.0, 1.0]. Mikäli se on 1.0, ovat vektorit samansuuntaiset,
// ja mikäli se on -1.0, osoittavat vektorit vastakkaisiin suuntiin. Hyödynnämme tätä valoisuuden laskemisessa,
// mikäli pinnan normaali osoittaa valoa kohti, tulee pistetulon arvoksi positiivinen arvo, ja väritämme 
// kyseisen pikselin vaaleaksi. Mikäli arvo on negatiivinen, tummennamme pikseliä.
//
// Suuntavektorin muodostus:
//
// Pisteiden A = (Ax, Ay, Az) ja B = (Bx, By, Bz) välisen vektorin (vektori AB) voi selvittää laskemalla
//		v = (Bx-Ax, By-Ay, Bz-Az)
//
// Jos tämän jälkeen vielä normalisoit vektorin (katso ylempää), niin vektori on suuntavektori, joka
// osoittaa pisteestä A pisteeseen B. Käytämme tätä kun laskemme mistä suunnasta valo paistaa kuhunkin
// kuvan pisteeseen.



// luodaan harmaasävyinen testikuva
img = MakeImage(255,255)
font = LoadFont("tahoma", 60)

SetFont font
reunat = 20
For u = reunat To 0 Step -1
	cc = Max(0,255-255.0/Float(reunat)*u)
	Color cc, cc, cc
	For i = 0 To 128
		
		ang# = i * (360/128.0)
		Text 64-x + cos(ang)*u*1.0, 64-y + sin(ang)*u*1.0, "peisik"
	Next i
Next u

CopyBox 0,0, 255, 255, 0, 0, SCREEN(), Image(img)

Const NORMAL_X = 0
Const NORMAL_Y = 1
Const NORMAL_Z = 2
Const HEIGHT = 3

Dim normals(ImageWidth(img), ImageHeight(img), 3) As Float

// tätä muuttamalla muuttuu korkeuskartan kovuus
depth# = 0.6

Lock(Image(img))
For y=1 To ImageWidth(img)-1
For x=1 To ImageHeight(img)-1
// Havainnollistava kuva, X = nykyinen pikseli (x,y); za1, za3, zb1 ja zb3 ovat sen ympäriltä luettavat pikselit
// luetaan siis korkeuskartan arvot ympäröivistä pikseleistä, ja muodostetaan niiden avulla suuntavektori
//    zb1
//za1  X   za3
//    zb3
//
// luetaan punaisen kanavan väriarvot ympäröivistä pikseleistä, ja skaalataan se välille [0.0, 1.0]
za1# = (((GetPixel2(x-1, y, Image(img))) Shl 8) Shr 3*8)/255.0	
'za2# = (((GetPixel2(x, y, Image(img))) Shl 8) Shr 3*8)/255.0	
za3# = (((GetPixel2(x+1, y, Image(img))) Shl 8) Shr 3*8)/255.0	

zb1# = (((GetPixel2(x, y-1, Image(img))) Shl 8) Shr 3*8)/255.0	
'zb2# = (((GetPixel2(x, y, Image(img))) Shl 8) Shr 3*8)/255.0	
zb3# = (((GetPixel2(x, y+1, Image(img))) Shl 8) Shr 3*8)/255.0

// vektori a (kolmiulotteinen vektori (xa, ya, za)
xa# = depth
ya# = 0.0
za# = za3 - za1

// vektori b
xb# = 0.0
yb# = depth
zb# = zb3 - zb1

// normalisoidaan vektorit a ja b (jaetaan jokainen komponentti pituudella)
la# = Sqrt(xa*xa + ya*ya + za*za)	// pituus saadaan pythagoraan lauseella
xna# = xa/la
yna# = ya/la
zna# = za/la

lb# = Sqrt(xb*xb + yb*yb + zb*zb)
xnb# = xb/lb
ynb# = yb/lb
znb# = zb/lb

// lopullinen suuntavektori joka on pinnan normaali
// lopullinen normaali saadaan ns. "ristitulolla", jolloin kahdesta vaakatasossa makaavasta vektorista tulee
// yksi suoraan pystyyn osoittava.
// kuva: http://dl.dropbox.com/u/5753422/paste/ristitulo.png
// kaava johdettu tästä: https://fi.wikipedia.org/wiki/Vektori#Vektoritulo_eli_ristitulo_.28Cross_Product.29
// (i, j ja k vastaavat akseleita x, y ja z)
xc# = (yna*znb-zna*ynb)
yc# = (zna*xnb-xna*znb)
zc# = (xna*ynb-yna*xnb)

// ja normalisoidaan se vielä
lc# = Sqrt(xc*xc + yc*yc + zc*zc)
xnc# = xc/lc
ync# = yc/lc
znc# = zc/lc

// normalisoidun vektorin komponenttien arvot tulisivat olla välillä [-1.0, 1.0]
normals(x,y, NORMAL_X) = Max(-1.0, Min(1.0, xnc))
normals(x,y, NORMAL_Y) = Max(-1.0, Min(1.0, ync))
normals(x,y, NORMAL_Z) = Max(-1.0, Min(1.0, znc))*-1.0	// vaihdetaan normaalin z-komponentin merkki
														// että normaali osoittaisi ylös pinnasta
normals(x,y, HEIGHT) = Max(-1.0,Min(1.0, xa*2.0 - 0.5))// tallennetaan korkeuskartan nykyinen arvo
Next x
Next y

Unlock(Image(img))
offset_x = 200
offset_y = 0
start=Timer()

Repeat
t=Timer()-start

	// valon sijainti 
	lx# = Cos(t/64.0)*60 + 100
	ly# = Sin(t/32.0)*60 + 100
	lz# = 2.0 // tätä ei välttämättä tarvita (valon korkeus pinnasta)

	DrawImage img, 0, 0
	Lock()
	For y=1 To ImageWidth(img)-1
	For x=1 To ImageHeight(img)-1
		// Lasketaan suuntavektori nykyisestä pisteestä valoon (miinustetaan komponentit toisistaan)
		// mikäli valo tulee "kaukaa", voi vektori p olla kaikkialla vakio.
		// Esimerkiksi auringonvalon voisi approksimoida näin.
		px# = x-lx
		py# = y-ly
		pz# = normals(x, y, HEIGHT) - lz
		pz# = 1.0
		
		// Normalisoidaan suuntavektori p (jaetaan jokainen komponentti vektorin pituudella lp)
		lp# = Sqrt(px*px + py*py + pz*pz)
		pnx# = px/lp
		pny# = py/lp
		pnz# = pz/lp

		// lasketaan valovektorin ja taulukkoon tallennetun pinnan normaalin pistetulo
		// cosine -muuttujan arvot tulevat olemaan väliltä [-1.0, 1.0]
		// pistetulo kertoo kahden vektorin välisen kulman kosinin suuruuden, jos se ON 1
		// niin vektorit ovat samansuuntaiset, jos taas -1, ON kulmien erotus 180 astetta 
		cosine# = (normals(x,y, NORMAL_X)*pnx) + (normals(x,y, NORMAL_Y)*pny) + (normals(x,y, NORMAL_Z)*pnz)
	
		// lasketaan hatusta heitetyllä kaavalla pikselin kirkkaus 
		c = (1-(1.0+cosine)*0.5)*255.0

		// muutetaan väriarvo pikseliksi
		pix = 255 Shl 24 + c Shl 16 + c Shl 8 + c
		PutPixel2 offset_x + x, offset_y + y, pix

	Next x
	Next y
	Unlock()

	// piirretään liikkuva valolaatikko
	Color 255, 255, 255
	Box lx-2 + offset_x, ly-2 + offset_y, 5, 5, 1	
	
	SetWindow "FPS: " + FPS()
	
	DrawScreen
Forever
Edit: Korjattu korkeuskartan tallennus (xa2 muutettu muotoon xa)
Last edited by CCE on Mon Feb 18, 2013 7:56 pm, edited 1 time in total.
Wingman
Devoted Member
Posts: 594
Joined: Tue Sep 30, 2008 4:30 pm
Location: Ruudun toisella puolella

Re: Korkeuskartan valaistus

Post by Wingman »

Ohoh, lukaisin aikoja sitten kännykällä ja unohdin testiajaa. Hienohan se on!

Voisi itsekin tutustua tämmöisiin jossain vaiheessa..
- - - -
User avatar
Misthema
Advanced Member
Posts: 312
Joined: Mon Aug 27, 2007 8:32 pm
Location: Turku, Finland
Contact:

Re: Korkeuskartan valaistus

Post by Misthema »

Taas jälleen kun näiden kanssa kikkailen, niin löysin erheen koodista:

Code: Select all

normals(x,y, HEIGHT) = Max(-1.0,Min(1.0, xa2*2.0 - 0.5))// tallennetaan korkeuskartan nykyinen arvo
...mutta mistään muualta en löytänyt muuttujaa xa2, paitsi tuosta. Pitäisi vissiinkin olla pelkkä xa?
User avatar
CCE
Artist
Artist
Posts: 650
Joined: Mon Aug 27, 2007 9:53 pm

Re: Korkeuskartan valaistus

Post by CCE »

Misthema wrote:Taas jälleen kun näiden kanssa kikkailen, niin löysin erheen koodista:

Code: Select all

normals(x,y, HEIGHT) = Max(-1.0,Min(1.0, xa2*2.0 - 0.5))// tallennetaan korkeuskartan nykyinen arvo
...mutta mistään muualta en löytänyt muuttujaa xa2, paitsi tuosta. Pitäisi vissiinkin olla pelkkä xa?
Oikeassa olet, korjattu. Muutos on kyllä lähes huomaamaton :P
User avatar
Misthema
Advanced Member
Posts: 312
Joined: Mon Aug 27, 2007 8:32 pm
Location: Turku, Finland
Contact:

Re: Korkeuskartan valaistus

Post by Misthema »

CCE wrote:
Misthema wrote:Taas jälleen kun näiden kanssa kikkailen, niin löysin erheen koodista:

Code: Select all

normals(x,y, HEIGHT) = Max(-1.0,Min(1.0, xa2*2.0 - 0.5))// tallennetaan korkeuskartan nykyinen arvo
...mutta mistään muualta en löytänyt muuttujaa xa2, paitsi tuosta. Pitäisi vissiinkin olla pelkkä xa?
Oikeassa olet, korjattu. Muutos on kyllä lähes huomaamaton :P
Juu, tuli huomattua sama kun tällä kikkailin. :D Mutta ajattelin vain kysäistä, kun en ole vieläkään varma mitä se oikein tekee. Kopipaste + edit on niin kiva kokonaisuus, aina! :D
User avatar
axu
Devoted Member
Posts: 854
Joined: Tue Sep 18, 2007 6:50 pm

Re: Korkeuskartan valaistus

Post by axu »

Misthema wrote:Juu, tuli huomattua sama kun tällä kikkailin. :D Mutta ajattelin vain kysäistä, kun en ole vieläkään varma mitä se oikein tekee. Kopipaste + edit on niin kiva kokonaisuus, aina! :D
Nopeasti koodia luettuna se ei vaikuta yhtään mihinkään, koska ainoa paikka, jossa tätä arvoa käytetään on rivillä 177, kun taasen rivi 178 päällekirjoittaa samaan muuttujaan:

Code: Select all

pz# = normals(x, y, HEIGHT) - lz
pz# = 1.0
Jos tuon jälkimmäisen rivin poistaa, näyttäisi valon ympärille tulevan pimeää?
EDIT:

Näyttää siltä, mitä isompi tuo lz on sen tummempi, negatiiviset arvot vuorostaan tekevät kirkkautta. Mitä tulee itse tähän korkeusarvoon, se näyttäisi olevan joka sijainnissa sama. Onkohan se xa sittenkään oikea muuttuja, olisikohan za2 tai zb2 ollut siihen alunperin tulossa, mutta nämä jätetään laskematta lopullisessa versiossa. Kokeilin vaihtaa tähän ja muuttaa rivin 177 seuraavaksi:

Code: Select all

pz# = Max(0, normals(x, y, HEIGHT) * 5)//Kerroin hyvin tieteellisin menetelmin asetettu viiteen vaikutuksen selventämiseksi.
Nyt näyttää oikealta, eli korkeammalla kohdalla valoa on enemmän. Jouduin lisäämään siihen tuon Max(0,...), jottei valon kohdalle tulisi sitä pimeää hehkua.[/edit]
Jos tämä viesti on kirjoitettu alle 5 min. sitten, päivitä sivu. Se on saattanut jo muuttua :roll:
Image
Post Reply