Kirjoittaja: peki
Kirjoitettu: 28.05.2004 – 28.05.2004
Tagit: koodi näytille, vinkki
Linssi efekti. Perustuu aiempaan vesi efektiini.
Luodaan korkeuskenttä(Height Field) ja tehdään rendataan kuva sen "läpi".
Vaatii kuvan "koe.jpg" C:\ aseman juureen.
Koodin kääntäminen onnistuu edelleen vain .net 2003:lla johtuen renderöimisen "raytracing" osuudessa käytetystä >> operaattorista.
Exe löytyy: http://koti.mbnet.fi/peku1/Lens.exe
Älä aja ohjelmaa suoraan IEExec ohjelman läpi, vaan tallenna se kiintolevylle, muuten c: asemalla sijaitsevaa kuvaa ei voida avata. (Itselläni kävi näin)
Mietin tuota omaa vesi koodiani hieman lisää, ja tulin siihen tulokseen, että korkeuskartan läpi renderöintiä voi läyttää kaikkeen kivaan. Voit tehdä millaisen tahansa korkeuskartan ja renderöidä sen läpi.
Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices
Public Class frmLens
Inherits System.Windows.Forms.Form
' yleisiä muuttujia
Private bmp As Bitmap
Private waves As Long(,,)
Private bmpWidth As Integer
Private bmpHeight As Integer
Private activeBuffer As Integer = 0
Private weHaveWaves As Boolean
Private bmpBytes As Byte()
Private bmpBitmapData As BitmapData
' asettaa linssin säteen
Private LensRadius As Integer = 35
Dim fps As Long
Dim fnt As New Font("Arial", 20)
#Region " Windows Form Designer generated code "
Public Sub New()
MyBase.New()
'This call is required by the Windows Form Designer.
InitializeComponent()
'Haluamme piirtää itse -> ei automaattipäivitystä
SetStyle(ControlStyles.UserPaint, True)
'Piirto tapahtuu vain ja ainoastaan paint metodissa
SetStyle(ControlStyles.AllPaintingInWmPaint, True)
'Haluamme käyttää myös kaksoispuskurointia
SetStyle(ControlStyles.DoubleBuffer, True)
End Sub
'Form overrides dispose to clean up the component list.
Protected Overloads Overrides Sub Dispose(ByVal disposing As Boolean)
If disposing Then
If Not (components Is Nothing) Then
components.Dispose()
End If
End If
MyBase.Dispose(disposing)
End Sub
'Required by the Windows Form Designer
Private components As System.ComponentModel.IContainer
'NOTE: The following procedure is required by the Windows Form Designer
'It can be modified using the Windows Form Designer.
'Do not modify it using the code editor.
Friend WithEvents EffectTimer As System.Windows.Forms.Timer
Friend WithEvents picSurface As System.Windows.Forms.PictureBox
<System.Diagnostics.DebuggerStepThrough()> Private Sub InitializeComponent()
Me.components = New System.ComponentModel.Container
Me.EffectTimer = New System.Windows.Forms.Timer(Me.components)
Me.picSurface = New System.Windows.Forms.PictureBox
Me.SuspendLayout()
'
'EffectTimer
'
Me.EffectTimer.Enabled = True
Me.EffectTimer.Interval = 1
'
'picSurface
'
Me.picSurface.Anchor = CType((((System.Windows.Forms.AnchorStyles.Top Or System.Windows.Forms.AnchorStyles.Bottom) _
Or System.Windows.Forms.AnchorStyles.Left) _
Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
Me.picSurface.Location = New System.Drawing.Point(0, 0)
Me.picSurface.Name = "picSurface"
Me.picSurface.Size = New System.Drawing.Size(448, 368)
Me.picSurface.TabIndex = 0
Me.picSurface.TabStop = False
'
'frmLens
'
Me.AutoScaleBaseSize = New System.Drawing.Size(5, 13)
Me.ClientSize = New System.Drawing.Size(448, 366)
Me.Controls.Add(Me.picSurface)
Me.Name = "frmLens"
Me.Text = "Lens"
Me.ResumeLayout(False)
End Sub
#End Region
Private Sub frmWater_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
' Ladataan bittikartta
bmp = New Bitmap("C:/koe.jpg")
' otetaan ylös arvot(lukeminen suoraan muuttujasta nopeampaa)
bmpHeight = bmp.Height
bmpWidth = bmp.Width
' alustetaan aaltotaulukko
ReDim Me.waves(bmpWidth, bmpHeight, 2)
' hankitaan bittikartta dataa(NOPEA TAPA!!!)
ReDim Me.bmpBytes(bmpWidth * bmpHeight * 4)
bmpBitmapData = bmp.LockBits(New Rectangle(0, 0, bmpWidth, bmpHeight), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb)
Marshal.Copy(bmpBitmapData.Scan0, bmpBytes, 0, bmpWidth * bmpHeight * 4)
PutLens(Int(Rnd() * bmpWidth), Int(Rnd() * bmpHeight), Int(Rnd() * 100) + 100)
End Sub
Private Sub picSurface_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles picSurface.Paint
Dim tmp As Bitmap = bmp.Clone()
Dim x, y As Integer
Dim xOffset, yOffset As Integer
Dim alpha As Integer
If (weHaveWaves) Then
Dim tmpData As BitmapData = tmp.LockBits(New Rectangle(0, 0, bmpWidth, bmpHeight), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb)
Dim tmpBytes(bmpWidth * bmpHeight * 4) As Byte
Marshal.Copy(tmpData.Scan0, tmpBytes, 0, bmpWidth * bmpHeight * 4)
x = 1
Do While (x < (bmpWidth - 1))
y = 1
Do While (y < (bmpHeight - 1))
'Tämä on johdettu raytracingistä(AH!) heijastetaan oikea pikseli siten,
'että saadaan aikaan valoa taittava efekti.
xOffset = waves(x - 1, y, activeBuffer) - waves(x + 1, y, activeBuffer) >> 3
yOffset = waves(x, y - 1, activeBuffer) - waves(x, y + 1, activeBuffer) >> 3
If ((Not (xOffset = 0)) Or (Not (yOffset = 0))) Then
'tsekkaa törmailyt(heijastetuille pikseleille)
If (x + xOffset >= bmpWidth - 1) Then xOffset = bmpWidth - x - 1
If (x + xOffset < 0) Then xOffset = -x
If (y + yOffset >= bmpHeight - 1) Then yOffset = bmpHeight - y - 1
If (y + yOffset < 0) Then yOffset = -y
'luodaan alpha(tarvitaan vain, jos kuvassa on kohtia, joissa on alpha arvoja[läpinäkyvyyttä])
alpha = CInt(200 - xOffset)
If (alpha < 0) Then
alpha = 0
ElseIf (alpha > 255) Then
alpha = 254
End If
'asetetaan värit oikeisiin kohtiin(napataan oikeasta kohdasta bittitaulukkoa)
tmpBytes(4 * (x + y * bmpWidth)) = bmpBytes(4 * (x + xOffset + (y + yOffset) * bmpWidth))
tmpBytes(4 * (x + y * bmpWidth) + 1) = bmpBytes(4 * (x + xOffset + (y + yOffset) * bmpWidth) + 1)
tmpBytes(4 * (x + y * bmpWidth) + 2) = bmpBytes(4 * (x + xOffset + (y + yOffset) * bmpWidth) + 2)
tmpBytes(4 * (x + y * bmpWidth) + 3) = alpha
End If
y += 1
Loop 'y
x += 1
Loop 'x
'kopioidaan data takasin
Marshal.Copy(tmpBytes, 0, tmpData.Scan0, bmpWidth * bmpHeight * 4)
tmp.UnlockBits(tmpData)
End If
e.Graphics.DrawImage(tmp, 0, 0, picSurface.ClientRectangle.Width, picSurface.ClientRectangle.Height)
e.Graphics.DrawString("fps: " & fps, fnt, Brushes.Black, 50, 50)
End Sub
Private Sub PutLens(ByVal x As Integer, ByVal y As Integer, ByVal height As Short)
' Simuloi pyöreää linssiä
' täyttää aaltotaulukon sopivilla arvoilla
' Käytetään hyväksi kosiniaaltoa
' (Jos tykkäät geomatriasta/algebrasta RAKASTAT tätä aliohjelmaa
' Silloin tulet nauttimaan myös renderöinnin "raytracing" osuudesta)
' Perustuu samaan kaavaan, kuin vesiefekti
' Nyt meillä ON aaltoja :D
weHaveWaves = True
Dim radius As Integer = LensRadius
'etäisyyden neliö
Dim distSquared As Double
'aloitetaan "pallon" reunoilta
Dim i As Integer = -radius
Dim tmpX, tmpY As Integer
Array.Clear(waves, 0, waves.Length)
Do While (i <= radius)
Dim j As Integer = -radius
Do While (j <= radius)
tmpX = x + i
tmpY = y + j
If (((tmpX >= 0) And (tmpX < bmpWidth - 1)) And ((tmpY >= 0) And (tmpY < bmpHeight - 1))) Then
' i:n ja j:n välisen etäisyyden neliö
distSquared = Math.Sqrt(i * i + j * j)
' jos etäisyys on < säde silloin linssi on "oikein"
If (distSquared < radius) Then
' pistetään kosiniaaltoa silmukan osoittamaan paikkaan
waves(x + i, y + j, activeBuffer) = CShort(Math.Cos(distSquared * Math.PI / radius) * -height)
End If
End If
j += 1
Loop 'j
i += 1
Loop 'i
End Sub
Private Sub picSurface_MouseMove(ByVal sender As Object, ByVal e As System.Windows.Forms.MouseEventArgs) Handles picSurface.MouseMove
' Koska bittikarttaa skaalataan, täytyy hiiren koordinaatteja hieman säätää...
Dim realX As Integer = CInt((e.X / CDbl(picSurface.ClientRectangle.Width)) * bmpWidth)
Dim realY As Integer = CInt((e.Y / CDbl(picSurface.ClientRectangle.Height)) * bmpHeight)
PutLens(realX, realY, 350)
End Sub
Private Sub frmWater_Activated(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Activated
Do
Dim d As Long = Now().Millisecond
Dim ft As Long
picSurface.Invalidate()
Application.DoEvents()
ft = Now().Millisecond - d
If ft <> 0 Then fps = 1 / ft * 1000
Loop
End Sub
Private Sub frmWater_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Closed
End
End Sub
End ClassAi että oot peki hyvä tekee näitä :P
Eh??? Peki on ihan liian pro tollaiseksi otukseksi! :D
Hei, ton exen lataus ei onnistu !
hmm.
Itselläni kyllä onnistuu.
Mitä selainta käytät(itsellä Opera)
Jos käytät IE:tä paina linkkiä oikealla -> ja tallenna nimellä.
Älä siis anna IEExecin avata sitä, vaan tallenna se kiintolevylle.
IE6.0. Vesi latautui ongelmitta mutta tämä ei.
erona oli kyllä se,ettei FrameWork ollut asennettuna kun imuroin tuon vesi.exen. Kun yritän imuroida Linssiä, tulee tuo FrameWork sotkemaan jotain.
joo. itsellä käy samoin.
paina linkkiä oikealla hiirenkorvalla -> ja paina sitten: tallenna nimellä.
Jeps, noin se onnistui. Tuli tolla vesi.exellä sama ongelma.
Joo, tosi hienosti pelaa. Kuvaa voi näköjään zoomata ja pienessä koossa nopeus kasvaa. fps näyttää 50 tai 100.
Tämä näytti sopivalta kokeilulta tehdä vähä optimointia.
Option Strict Off 'Ei suositeltava
Imports System.Drawing.Imaging
Imports System.Runtime.InteropServices
Public Class frmLens
Inherits System.Windows.Forms.Form
Const LensRadius As Integer = 35 'Linssin koko
Const LensHeight As Integer = 350 'Linssi syvyys
Const wavesWidth As Integer = LensRadius * 3 'Varaa linssille riittävän ison tilan
Const wavesHeight As Integer = LensRadius * 3 'Varaa linssille riittävän ison tilan
Const filename As String = "koe.jpg" 'Tiedostoa luetaan samasta hakemistosta kuin EXE
' yleisiä muuttujia
Private bmp As Bitmap
Private waves(wavesWidth, wavesHeight) As Long 'Miksi tämä oli kolmiulotteinen?
Private bmpWidth As Integer
Private bmpHeight As Integer
'Private weHaveWaves As Boolean 'Turha
Private bmpBytes As Byte()
Private bmpBitmapData As BitmapData
Private lensX As Integer 'Linssin paikka bittikartassa
Private lensY As Integer
Dim fps As Long
Dim fnt As New Font("Arial", 20)
#Region " Windows Form Designer generated code "
'Jätin tämän pois koska se on sama (paitsi että poistin turhan Timerin)
#End Region
Private Sub frmWater_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Load
If Not IO.File.Exists(filename) Then
MsgBox("Filename " & filename & " does not exist.", MsgBoxStyle.Critical, "Serious error")
End
End If
' Ladataan bittikartta
bmp = New Bitmap(filename)
' otetaan ylös arvot(lukeminen suoraan muuttujasta nopeampaa)
bmpHeight = bmp.Height
bmpWidth = bmp.Width
' hankitaan bittikartta dataa(NOPEA TAPA!!!)
ReDim Me.bmpBytes(bmpWidth * bmpHeight * 4)
bmpBitmapData = bmp.LockBits(New Rectangle(0, 0, bmpWidth, bmpHeight), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb)
Marshal.Copy(bmpBitmapData.Scan0, bmpBytes, 0, bmpWidth * bmpHeight * 4)
CreateLens() 'Tehdään linssi, tämä tarvitsee tehdä vain kerran.
End Sub
Private Sub picSurface_Paint(ByVal sender As Object, ByVal e As System.Windows.Forms.PaintEventArgs) Handles picSurface.Paint
Dim tmp As Bitmap = bmp.Clone() 'Conversion virhe
Dim x, y As Integer
Dim xOffset, yOffset As Long
Dim alpha As Integer
Dim tmpData As BitmapData = tmp.LockBits(New Rectangle(0, 0, bmpWidth, bmpHeight), ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb)
Dim tmpBytes(bmpWidth * bmpHeight * 4) As Byte
Marshal.Copy(tmpData.Scan0, tmpBytes, 0, bmpWidth * bmpHeight * 4)
For x = 1 To wavesWidth - 1
For y = 1 To wavesHeight - 1
'Tämä on johdettu raytracingistä(AH!) heijastetaan oikea pikseli siten,
'että saadaan aikaan valoa taittava efekti.
xOffset = waves(x - 1, y) - waves(x + 1, y) >> 3
yOffset = waves(x, y - 1) - waves(x, y + 1) >> 3
If ((Not (xOffset = 0)) Or (Not (yOffset = 0))) Then
If x + lensX > 0 And y + lensY > 0 And x + lensX < bmpWidth And y + lensY < bmpHeight Then
'Tarkista että pixeleit ei oteta reunojen ulkopuolelta
If (x + lensX + xOffset >= bmpWidth - 1) Then xOffset = 0
If (x + lensX + xOffset < 0) Then xOffset = 0
If (y + lensY + yOffset >= bmpHeight - 1) Then yOffset = 0
If (y + lensY + yOffset < 0) Then yOffset = 0
'luodaan alpha(tarvitaan vain, jos kuvassa on kohtia, joissa on alpha arvoja[läpinäkyvyyttä])
alpha = CInt(200 - xOffset)
If (alpha < 0) Then
alpha = 0
ElseIf (alpha > 255) Then
alpha = 254
End If
'asetetaan värit oikeisiin kohtiin(napataan oikeasta kohdasta bittitaulukkoa)
tmpBytes(4 * (x + lensX + ((y + lensY) * bmpWidth))) = bmpBytes(CInt(4 * (x + lensX + xOffset + ((y + lensY) + yOffset) * bmpWidth)))
tmpBytes(4 * (x + lensX + ((y + lensY) * bmpWidth)) + 1) = bmpBytes(CInt(4 * (x + lensX + xOffset + ((y + lensY) + yOffset) * bmpWidth) + 1))
tmpBytes(4 * (x + lensX + ((y + lensY) * bmpWidth)) + 2) = bmpBytes(CInt(4 * (x + lensX + xOffset + ((y + lensY) + yOffset) * bmpWidth) + 2))
tmpBytes(4 * (x + lensX + ((y + lensY) * bmpWidth)) + 3) = CByte(alpha)
End If
End If
Next y
Next x
'kopioidaan data takasin
Marshal.Copy(tmpBytes, 0, tmpData.Scan0, bmpWidth * bmpHeight * 4)
tmp.UnlockBits(tmpData)
e.Graphics.DrawImage(tmp, 0, 0, picSurface.ClientRectangle.Width, picSurface.ClientRectangle.Height)
e.Graphics.DrawString("fps: " & fps, fnt, Brushes.Black, 50, 50)
End Sub
Private Sub CreateLens()
' Simuloi pyöreää linssiä
' täyttää aaltotaulukon sopivilla arvoilla
' Käytetään hyväksi kosiniaaltoa
' (Jos tykkäät geomatriasta/algebrasta RAKASTAT tätä aliohjelmaa
' Silloin tulet nauttimaan myös renderöinnin "raytracing" osuudesta)
' Perustuu samaan kaavaan, kuin vesiefekti
Dim radius As Integer = LensRadius
'etäisyyden neliö
Dim distSquared As Double
'aloitetaan "pallon" reunoilta
Dim i As Integer
Dim x, y, j As Integer
Dim tmpX, tmpY As Integer
Array.Clear(waves, 0, waves.Length)
x = CInt(wavesWidth / 2)
y = CInt(wavesHeight / 2)
For i = -radius To radius
For j = -radius To radius
tmpX = x + i
tmpY = y + j
If (((tmpX >= 0) And (tmpX < wavesWidth - 1)) And ((tmpY >= 0) And (tmpY < wavesHeight - 1))) Then
' i:n ja j:n välisen etäisyyden neliö
distSquared = Math.Sqrt(i * i + j * j)
' jos etäisyys on < säde silloin linssi on "oikein"
If (distSquared < radius) Then
' pistetään kosiniaaltoa silmukan osoittamaan paikkaan
waves(x + i, y + j) = CShort(Math.Cos(distSquared * Math.PI / radius) * -LensHeight)
End If
End If
Next j
Next i
End Sub
Private Sub PutMouse()
lensX = CInt(picSurface.Cursor.Position.X - picSurface.PointToScreen(New Point).X - (wavesWidth / 3)) 'Kaiken järjen mukaan /2 pitäisi antaa oikea kohta, en jaksanut selvittää missä vika on
lensY = CInt(picSurface.Cursor.Position.Y - picSurface.PointToScreen(New Point).Y - (wavesHeight / 3))
' Koska bittikarttaa skaalataan, täytyy hiiren koordinaatteja hieman säätää...
lensX = CInt((lensX / CDbl(picSurface.ClientRectangle.Width)) * bmpWidth)
lensY = CInt((lensY / CDbl(picSurface.ClientRectangle.Height)) * bmpHeight)
End Sub
Private Sub frmWater_Activated(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Activated
Dim Sekunnit As Long
Dim OldTime As DateTime
Dim NewTime As DateTime
Dim Frames As Long 'Lasketaan montako kertaa ruutu on piirretty
Do
PutMouse()
picSurface.Invalidate()
Application.DoEvents()
'Lasketaan FPS vain kerran sekunnissa jolloin saadaan järkeviä arvoja
If Now().Second <> Sekunnit Then
OldTime = NewTime
NewTime = Now 'GetTickCount
fps = CLng(Frames / ((NewTime.Ticks - OldTime.Ticks) / 10000000))
Frames = 0
Sekunnit = Now().Second
End If
Frames += 1
Loop
End Sub
Private Sub frmWater_Closed(ByVal sender As Object, ByVal e As System.EventArgs) Handles MyBase.Closed
End
End Sub
End ClassKäännetty EXE: http://koti.mbnet.fi/meitzi/Lens1.1.zip
Huomattava parannus nopeudessa, hienoa !
Saisko tän/veden vb6selle
miten ihmees toi valmis exe versio tilttas tosi perusteellisesti?
kumpaa exeä käytit? Meitsin vai minun?
Omani saattaa kaatua alkeellisen fps countterin takia, mutta tiltata sen ei pitäisi. Outoa.
hyvä linssiefekti kun tuli punanen rasti ruutuun
"Sovelluksen alustus epäonnistui (0x0000135). Lopeta sovellus valitsemalla OK."
,eli en tiedä VB:stä mitään ,mutta sen tiedän ,että tuo EXE ei toimi :D
lainaus:
joo. itsellä käy samoin.
paina linkkiä oikealla hiirenkorvalla -> ja paina sitten: tallenna nimellä.
Tästä olen jo kertonut ennenkin, tuo johtuu siitä, että IEExec toimii kuin java appletti tulkki.
Sinun pitää siis ladata ohjelma koneellesi ja ajaa se sieltä käsin.
Todennäköisesti tuo errorisi johtuu siitä, että fps countterini on alkeellinen. Suosittelen kokeilemaan meitzin exeä. sen fps countteri on parempi ja koodi optimoitu tehokkaammin.
Kiitos Meitzille! Opin itsekin muutaman kikan.
.NET sovellusten suorittaminen suoraan IE:stä ei toimi tässä tapauksessa, koska silloin sovellus suoritetaan suojatussa "hiekkalaatikko" tilassa jolloin se ei pysty avamaan kovalevyltä mitään.
peki: Nojuu näytti vain ihan hienolta efektiltä kun kokeilin mutta oli vaan niin turkasen hias ;)
Huomasin melkein heti että koodissa oli aika helposti tehtävissä nopeampi johtuen vanhasta "aalto" pohjasta. (mm. minun koodi laskee vain linssin alueen, ei koko ruutua jne)
saisko qb:lle tai turbo pascal 5:selle?