Seite 1 von 2

Kleiner Holzshader aus einem PurePunch-Projekt

Verfasst: 16.07.2014 18:12
von NicTheQuick
Hallo Leute,

hier mal ein kleiner Code, der aus einem kleinen Projekt für den PurePunch-Wettbewerb 2014 heraus gefallen ist.
Hier kam er zum Einsatz.

Code: Alles auswählen

EnableExplicit
#noiseSize = 199
Global Dim noise.d(#noiseSize, #noiseSize)
;generate Noise
Define x, y
For x = 0 To #noiseSize
	y = 0
	For y = 0 To #noiseSize
		noise(x, y) = Random(32768) / 32768.0
	Next
Next
; Mathematisch korrektes Modulo.
Macro ModI(a, b)
	(((b) + ((a) % (b))) % (b))
EndMacro

; Interpolieren von noise
Procedure.d smoothNoise(x.d, y.d)
	Protected fractX.d = x - Int(x), fractY.d = y - Int(y)
	Protected x1.i = ModI(Int(x), #noiseSize + 1)
	Protected y1.i = ModI(Int(y), #noiseSize + 1)
	Protected x2.i = ModI(Int(x) - 1, #noiseSize + 1)
	Protected y2.i = ModI(Int(y) - 1, #noiseSize + 1)
	Protected value.d = 0.0
	value + fractX       *      fractY  * noise(x1, y1)
	value + fractX       * (1 - fractY) * noise(x1, y2)
	value + (1 - fractX) *      fractY  * noise(x2, y1)
	value + (1 - fractX) * (1 - fractY) * noise(x2, y2)
	
	ProcedureReturn value
EndProcedure

;Turbulenz berechnen
Procedure.d turbulence(x.d, y.d, size.d)
	Protected value.d = 0.0, initialSize.d = size
	
	While (size >= 1.0)
		value + smoothNoise(x / size, y / size) * size
		size / 2.0
	Wend
	
	ProcedureReturn (128.0 * value / initialSize)
EndProcedure

;Parameter für die Holzstruktur
#xPeriod = 5.0
#yPeriod = 10.0
#turbPower = 5.0
#turbSize = 64.0

;Holz-Shader. Er nutzt den unterliegenden Rotkanal für Schattierungen
;und Offsetberechnungen.
Procedure MahagoniFilterCallback(xi.i, yi.i, sourceColor.i, targetColor.i)
	Protected c.d = Pow((Red(targetColor) / 255.0), 2.8)
	Protected offset.d = -(1-c) * 5
	Protected x.d = xi + 0*offset, y.d = yi + 0*offset
    Protected xyValue.d = x * #xPeriod / (#noiseSize + 1) +
                          y * #yPeriod / (#noiseSize + 1) +
                          #turbPower * turbulence(x, y, #turbSize) / 256.0
	Protected sineValue.d = 128.0 * Abs(Cos(xyValue * #PI-offset))
	c = 0.3 + c * 0.7
	Protected r.a = c * (80 + sineValue), g.a = c * (30 + sineValue), b.i = c * 30
	ProcedureReturn RGB(r, g, b)
EndProcedure

#w = 400
#h = 300
If OpenWindow(0, 0, 0, #w, #h, "Mahagoni-Shader")
	CanvasGadget(0, 0, 0, #w, #h)
	
	If StartDrawing(CanvasOutput(0))
		; Erst eine weiße Box malen.
		DrawingMode(#PB_2DDrawing_Default)
		Box(0, 0, #w, #h, $ff)
		
		; Dann Kreise mit Gradient malen
		; Wenn man ohne Kreise testen möchte, einfach die nachfolgende
		; Zeile entfernen oder gleich die ganze Schleife.
		DrawingMode(#PB_2DDrawing_Gradient)
		Define r.i = 20, x.i, y.i
		y = r
		While y < #h
			x = r
			While x < #w
				CircularGradient(x + r / 4, y + r / 4, r)
				FrontColor($ff)
				BackColor(0)
				Circle(x, y, r)
				x + 2 * r
			Wend
			y + 2 * r
		Wend
		
		; Dann Filtercallback setzen und eine einfache Box malen.
		CustomFilterCallback(@MahagoniFilterCallback())
		DrawingMode(#PB_2DDrawing_CustomFilter)
		Define.i time = ElapsedMilliseconds()
		Box(0, 0, #w, #h)
		Debug (Str(ElapsedMilliseconds() - time))
		StopDrawing()
	EndIf
	Repeat
	Until WaitWindowEvent(20) = #PB_Event_CloseWindow
EndIf

Re: Kleiner Holzshader aus einem PurePunch-Projekt

Verfasst: 16.07.2014 18:27
von RSBasic
:allright:

Re: Kleiner Holzshader aus einem PurePunch-Projekt

Verfasst: 16.07.2014 20:54
von X360 Andy
Mal so n kleiner Leistungsvergleich ... was sind eure Ergebnisse ? Ich bin hier mit 1075 gerade etwas verwundert :D

Re: Kleiner Holzshader aus einem PurePunch-Projekt

Verfasst: 16.07.2014 20:56
von RSBasic
164

Re: Kleiner Holzshader aus einem PurePunch-Projekt

Verfasst: 16.07.2014 21:00
von Derren
1700 :(

Sieht aber geil aus (zumindest ohne die Kreise) :allright:

Re: Kleiner Holzshader aus einem PurePunch-Projekt

Verfasst: 16.07.2014 21:34
von ts-soft
104 (ohne Debugger, weil alles andere ist hirnlos :mrgreen: )

Re: Kleiner Holzshader aus einem PurePunch-Projekt

Verfasst: 16.07.2014 22:08
von X360 Andy
ts-soft hat geschrieben:104 (ohne Debugger, weil alles andere ist hirnlos :mrgreen: )

Code: Alles auswählen

MessageRequester("",Str(ElapsedMilliseconds() - time))
So komm ich zumindest auf unter 300 ms :(

Re: Kleiner Holzshader aus einem PurePunch-Projekt

Verfasst: 16.07.2014 23:19
von NicTheQuick
Ich komme ohne Debugger auf 162.
Man muss aber dazu sagen, dass man da noch einiges optimieren kann. Zum Beispiel könnte man turbulence einmal für alle Pixel eines größeren Bereichs vorberechnen. Dann geht es später auch um einiges flüssiger und möglicherweise auch in Echtzeit.

Edit:
Der Rotkanal gibt das Offset an. ;)

Code: Alles auswählen

EnableExplicit
#noiseSize = 500
Global Dim noise.d(#noiseSize, #noiseSize)
;generate Noise
Define x, y
For x = 0 To #noiseSize
	For y = 0 To #noiseSize
		noise(x, y) = Random(32768) / 32768.0
	Next
Next
; Mathematisch korrektes Modulo.
Procedure.i ModI(a.i, b.i)
	;(((b) + ((a) % (b))) % (b))
	If (a < 0)
		ProcedureReturn b + (a % b)
	EndIf
	ProcedureReturn a % b
EndProcedure

; Interpolieren von noise
Procedure.d smoothNoise(x.d, y.d)
	Protected fractX.d = x - Int(x), fractY.d = y - Int(y)
	Protected x1.i = ModI(Int(x), #noiseSize + 1)
	Protected y1.i = ModI(Int(y), #noiseSize + 1)
	Protected x2.i = ModI(Int(x) - 1, #noiseSize + 1)
	Protected y2.i = ModI(Int(y) - 1, #noiseSize + 1)
	Protected value.d = 0.0
	value + fractX       *      fractY  * noise(x1, y1)
	value + fractX       * (1 - fractY) * noise(x1, y2)
	value + (1 - fractX) *      fractY  * noise(x2, y1)
	value + (1 - fractX) * (1 - fractY) * noise(x2, y2)
	
	ProcedureReturn value
EndProcedure

;Turbulenz berechnen
Procedure.d turbulence(x.d, y.d, size.d)
	Protected value.d = 0.0, initialSize.d = size
	
	While (size >= 1.0)
		value + smoothNoise(x / size, y / size) * size
		size / 2.0
	Wend
	
	ProcedureReturn (128.0 * value / initialSize)
EndProcedure

;Parameter für die Holzstruktur
#xPeriod = 5.0
#yPeriod = 10.0
#turbPower = 5.0
#turbSize = 64.0

Global Dim turb.d(0), turbWidth.i, turbHeight.i
Procedure precalculateTurbulence(width.i, height.i, turbSize.d)
	ReDim turb(width * height - 1)
	turbWidth = width
	turbHeight = height
	Protected x.i, y.i
	For x = 0 To width - 1
		For y = 0 To height - 1
			turb(x * height + y) = turbulence(x, y, turbSize)
		Next
	Next
EndProcedure

Procedure.d getTurbulence(x.i, y.i)
	x = ModI(x, turbWidth)
	y = ModI(y, turbHeight)
	ProcedureReturn turb(x * turbHeight + y)
EndProcedure

;Holz-Shader. Er nutzt den unterliegenden Rotkanal für Schattierungen
;und Offsetberechnungen.
Procedure MahagoniFilterCallback(x.i, y.i, sourceColor.i, targetColor.i)
	Protected c.d = (Red(targetColor) / 255.0)
    Protected xyValue.d = x * #xPeriod / (#noiseSize + 1) +
                          y * #yPeriod / (#noiseSize + 1) +
                          #turbPower * getTurbulence(x, y) / 256.0 +
                          c
	Protected sineValue.d = 128.0 * Abs(Cos(xyValue * #PI))
	Protected r.a = 80 + sineValue, g.a = 30 + sineValue, b.i = 30
	ProcedureReturn RGB(r, g, b)
EndProcedure

#w = 400
#h = 400

precalculateTurbulence(#w, #h, #turbSize)

If OpenWindow(0, 0, 0, #w, #h, "Mahagoni-Shader")
	CanvasGadget(0, 0, 0, #w, #h)
	
	Define offset.a = 0
	
	Repeat
		If StartDrawing(CanvasOutput(0))
			; Erst eine weiße Box malen.
			DrawingMode(#PB_2DDrawing_Default)
			Box(0, 0, #w, #h, offset)
			offset + 5
			
			; Dann Filtercallback setzen und eine einfache Box malen.
			CustomFilterCallback(@MahagoniFilterCallback())
			DrawingMode(#PB_2DDrawing_CustomFilter)
			Define.i time = ElapsedMilliseconds()
			Box(0, 0, #w, #h)
			time = ElapsedMilliseconds() - time
			StopDrawing()
		EndIf
	Until WaitWindowEvent(20) = #PB_Event_CloseWindow
EndIf

Re: Kleiner Holzshader aus einem PurePunch-Projekt

Verfasst: 17.07.2014 02:38
von NicTheQuick
Ich hab noch was schönes für euch. Und zwar habe ich jetzt mal verglichen, was schneller ist. Ein einfaches Plot oder der CustomFilterCallback. Für mich war das Ergebnis überraschend. Plot war nämlich schneller.

Zum Testen folgendes Vorgehen: Zunächst schaltet man den Debugger ab. Dann setzt man die Konstante '#USE_CUSTOM_FILTER' auf '#True'. Anschließend startet man das Programm und wartet einfach eine beliebige Zeit, aber ein paar Sekunden sollte es schon sein. Nach dem Klick auf 'X' kommt ein MessageRequester, der die durchschnittliche Zeit angibt, die zum Berechnen eines Frames gebraucht wurde. Das ist die Zeit, die es mit dem CustomFilterCallback braucht. Dann setzt man '#USE_CUSTOM_FILTER' auf '#False' und macht analog weiter um die Zeit für Plot zu bekommen.

Hier meine Werte:
CustomFilterCallback: 93 ms
Plot: 83 ms

Code: Alles auswählen

EnableExplicit
#noiseSize = 500
Global Dim noise.d(#noiseSize, #noiseSize)
;generate Noise
Define x, y
For x = 0 To #noiseSize
	For y = 0 To #noiseSize
		noise(x, y) = Random(32768) / 32768.0
	Next
Next
; Mathematisch korrektes Modulo.
Procedure.i ModI(a.i, b.i)
	;(((b) + ((a) % (b))) % (b))
	If (a < 0)
		ProcedureReturn b + (a % b)
	EndIf
	ProcedureReturn a % b
EndProcedure

; Interpolieren von noise
Procedure.d smoothNoise(x.d, y.d)
	Protected fractX.d = x - Int(x), fractY.d = y - Int(y)
	Protected x1.i = ModI(Int(x), #noiseSize + 1)
	Protected y1.i = ModI(Int(y), #noiseSize + 1)
	Protected x2.i = ModI(Int(x) - 1, #noiseSize + 1)
	Protected y2.i = ModI(Int(y) - 1, #noiseSize + 1)
	Protected value.d = 0.0
	value + fractX       *      fractY  * noise(x1, y1)
	value + fractX       * (1 - fractY) * noise(x1, y2)
	value + (1 - fractX) *      fractY  * noise(x2, y1)
	value + (1 - fractX) * (1 - fractY) * noise(x2, y2)
	
	ProcedureReturn value
EndProcedure

;Turbulenz berechnen
Procedure.d turbulence(x.d, y.d, size.d)
	Protected value.d = 0.0, initialSize.d = size
	
	While (size >= 1.0)
		value + smoothNoise(x / size, y / size) * size
		size / 2.0
	Wend
	
	ProcedureReturn (128.0 * value / initialSize)
EndProcedure

;Parameter für die Holzstruktur
#xPeriod = 5.0
#yPeriod = 10.0
#turbPower = 5.0
#turbSize = 64.0

Global Dim turb.d(0), turbWidth.i, turbHeight.i
Procedure precalculateTurbulence(width.i, height.i, turbSize.d)
	ReDim turb(width * height - 1)
	turbWidth = width
	turbHeight = height
	Protected x.i, y.i
	For x = 0 To width - 1
		For y = 0 To height - 1
			turb(x * height + y) = turbulence(x, y, turbSize)
		Next
	Next
EndProcedure

Procedure.d getTurbulence(x.i, y.i)
	x = ModI(x, turbWidth)
	y = ModI(y, turbHeight)
	ProcedureReturn turb(x * turbHeight + y)
EndProcedure

;Holz-Shader. Er nutzt den unterliegenden Rotkanal für Schattierungen
;und Offsetberechnungen.
Procedure MahagoniFilterCallback(x.i, y.i, sourceColor.i, targetColor.i)
	Protected c.d = (Red(targetColor) / 255.0)
    Protected xyValue.d = x * #xPeriod / (#noiseSize + 1) +
                          y * #yPeriod / (#noiseSize + 1) +
                          #turbPower * getTurbulence(x, y) / 256.0 +
                          c
	Protected sineValue.d = 128.0 * Abs(Cos(xyValue * #PI))
	Protected r.a = 80 + sineValue, g.a = 30 + sineValue, b.i = 30
	ProcedureReturn RGB(r, g, b)
EndProcedure

#w = 1100
#h = 700

precalculateTurbulence(#w, #h, #turbSize)

#USE_CUSTOM_FILTER = #True

Define avg_time.i = 0, avg_time_count.i = 0

If OpenWindow(0, 0, 0, #w, #h, "Mahagoni-Shader", #PB_Window_ScreenCentered)
	CanvasGadget(0, 0, 0, #w, #h)
	
	Define offset.a = 0
	AddWindowTimer(0, 0, 20)
	
	Repeat
		Define event.i = WaitWindowEvent()
		
		If event = #PB_Event_Timer And EventTimer() = 0
			If StartDrawing(CanvasOutput(0))
				Define.i time = ElapsedMilliseconds()
				CompilerIf #USE_CUSTOM_FILTER
					; Erst eine weiße Box malen.
					DrawingMode(#PB_2DDrawing_Default)
					Box(0, 0, #w, #h, offset)
					
					; Dann Filtercallback setzen und eine einfache Box malen.
					CustomFilterCallback(@MahagoniFilterCallback())
					DrawingMode(#PB_2DDrawing_CustomFilter)
					
					Box(0, 0, #w, #h)
					
				CompilerElse
					Define x.i, y.i
					For x = 0 To #w - 1
						For y = 0 To #h - 1
							Plot(x, y, MahagoniFilterCallback(x, y, 0, offset))
						Next
					Next
				CompilerEndIf
				offset + 5
				time = ElapsedMilliseconds() - time
				avg_time + time
				avg_time_count + 1
				Debug time
				StopDrawing()
			EndIf
		EndIf
	Until event = #PB_Event_CloseWindow
	Define.s title = "Time - "
	If #USE_CUSTOM_FILTER
		title + "CustomFilterCallback"
	Else
		title + "Plot"
	EndIf
	MessageRequester(title, "Average processing time: " + Str(avg_time / avg_time_count) + " ms")
EndIf

Re: Kleiner Holzshader aus einem PurePunch-Projekt

Verfasst: 17.07.2014 03:21
von NicTheQuick
Und noch eine kleine Spielerei, die mit dem CustomFilterCallback und Gradienten natürlich viel einfacher geht. :D
Maus bewegen und Scrollrad benutzen.

Code: Alles auswählen

EnableExplicit
#noiseSize = 500
Global Dim noise.d(#noiseSize, #noiseSize)
;generate Noise
Define x, y
For x = 0 To #noiseSize
	For y = 0 To #noiseSize
		noise(x, y) = Random(32768) / 32768.0
	Next
Next
; Mathematisch korrektes Modulo.
Procedure.i ModI(a.i, b.i)
	;(((b) + ((a) % (b))) % (b))
	If (a < 0)
		ProcedureReturn b + (a % b)
	EndIf
	ProcedureReturn a % b
EndProcedure

; Interpolieren von noise
Procedure.d smoothNoise(x.d, y.d)
	Protected fractX.d = x - Int(x), fractY.d = y - Int(y)
	Protected x1.i = ModI(Int(x), #noiseSize + 1)
	Protected y1.i = ModI(Int(y), #noiseSize + 1)
	Protected x2.i = ModI(Int(x) - 1, #noiseSize + 1)
	Protected y2.i = ModI(Int(y) - 1, #noiseSize + 1)
	Protected value.d = 0.0
	value + fractX       *      fractY  * noise(x1, y1)
	value + fractX       * (1 - fractY) * noise(x1, y2)
	value + (1 - fractX) *      fractY  * noise(x2, y1)
	value + (1 - fractX) * (1 - fractY) * noise(x2, y2)
	
	ProcedureReturn value
EndProcedure

;Turbulenz berechnen
Procedure.d turbulence(x.d, y.d, size.d)
	Protected value.d = 0.0, initialSize.d = size
	
	While (size >= 1.0)
		value + smoothNoise(x / size, y / size) * size
		size / 2.0
	Wend
	
	ProcedureReturn (128.0 * value / initialSize)
EndProcedure

;Parameter für die Holzstruktur
#xPeriod = 5.0
#yPeriod = 10.0
#turbPower = 5.0
#turbSize = 64.0

Global Dim turb.d(0), turbWidth.i, turbHeight.i
Procedure precalculateTurbulence(width.i, height.i, turbSize.d)
	ReDim turb(width * height - 1)
	turbWidth = width
	turbHeight = height
	Protected x.i, y.i
	For x = 0 To width - 1
		For y = 0 To height - 1
			turb(x * height + y) = turbulence(x, y, turbSize)
		Next
	Next
EndProcedure

Procedure.d getTurbulence(x.i, y.i)
	x = ModI(x, turbWidth)
	y = ModI(y, turbHeight)
	ProcedureReturn turb(x * turbHeight + y)
EndProcedure

;Holz-Shader. Er nutzt den unterliegenden Rotkanal für Schattierungen
;und Offsetberechnungen.
Procedure MahagoniFilterCallback(x.i, y.i, sourceColor.i, targetColor.i)
	Protected c.d = (Red(targetColor) / 255.0)
    Protected xyValue.d = x * #xPeriod / (#noiseSize + 1) +
                          y * #yPeriod / (#noiseSize + 1) +
                          #turbPower * getTurbulence(x, y) / 256.0 +
                          c
	Protected sineValue.d = 128.0 * Abs(Cos(xyValue * #PI))
	Protected r.a = 80 + sineValue, g.a = 30 + sineValue, b.i = 30
	ProcedureReturn RGB(r, g, b)
EndProcedure

#w = 800
#h = 600

precalculateTurbulence(#w, #h, #turbSize)

Define avg_time.i = 0, avg_time_count.i = 0

If OpenWindow(0, 0, 0, #w, #h, "Mahagoni-Shader", #PB_Window_ScreenCentered)
	CanvasGadget(0, 0, 0, #w, #h)
	
	Define offset.a = 0
	AddWindowTimer(0, 0, 20)
	Define x.i, y.i, r.i = 50
	
	Repeat
		Define event.i = WaitWindowEvent()
		
		Select event
			Case #PB_Event_Gadget
				If EventGadget() = 0
					Select EventType()
						Case #PB_EventType_MouseMove
							x = GetGadgetAttribute(0, #PB_Canvas_MouseX)
							y = GetGadgetAttribute(0, #PB_Canvas_MouseY)
						Case #PB_EventType_MouseWheel
							r + 5 * GetGadgetAttribute(0, #PB_Canvas_WheelDelta)
							If (r < 1) : r = 0 : EndIf
							If (r * r > #w * #w + #h * #h) : r = Sqr(#w * #w + #h * #h) : EndIf
					EndSelect
				EndIf
			Case #PB_Event_Timer
				If EventTimer() = 0
					If StartDrawing(CanvasOutput(0))
						Define.i time = ElapsedMilliseconds()
						; Erst eine weiße Box malen.
						DrawingMode(#PB_2DDrawing_Default)
						Box(0, 0, #w, #h, 0)
						
						DrawingMode(#PB_2DDrawing_Gradient)
						FrontColor(0)
						BackColor(255)
						CircularGradient(x, y, r)
						Circle(x, y, r)
						
						; Dann Filtercallback setzen und eine einfache Box malen.
						CustomFilterCallback(@MahagoniFilterCallback())
						DrawingMode(#PB_2DDrawing_CustomFilter)
						
						Box(0, 0, #w, #h)
							
						offset + 5
						time = ElapsedMilliseconds() - time
						avg_time + time
						avg_time_count + 1
						Debug time
						StopDrawing()
					EndIf
				EndIf
		EndSelect
	Until event = #PB_Event_CloseWindow
	
	MessageRequester("Time - CustomFilterCallback", "Average processing time: " + Str(avg_time / avg_time_count) + " ms")
EndIf