hier mal eine kleine Spielerei zur Mandelbrot- und Juliamenge.
Mit zwei OpenGLGadgets kann man sich in der Mandelbrot mit Maus und Mausrad bewegen und sich parallel dazu die Juliamenge des jeweiligen Orts angucken. Dazu habe ich einen Fragment Shader geschrieben der die Iterationen durchführt und das sogar/immerhin mit double-Genauigkeit.
Code: Alles auswählen
EnableExplicit
;{ OpenGL
; https://www.purebasic.fr/english/viewtopic.php?p=576628#p576628
#GL_VERTEX_SHADER = $8B31
#GL_FRAGMENT_SHADER = $8B30
Prototype glCreateShader(type.l)
Prototype glCreateProgram()
Prototype glDeleteShader(shader.l)
Prototype glCompileShader(shader.l)
Prototype glLinkProgram(shader.l)
Prototype glUseProgram(shader.l)
Prototype glAttachShader(Program.l, shader.l)
Prototype glShaderSource(shader.l, numOfStrings.l, *strings, *lenOfStrings) :
Prototype glGetUniformLocation(Program.i, name.p-ascii)
Prototype glUniform1i(location.i, v0.i)
Prototype glUniform2i(location.i, v0.i, v1.i)
Prototype glUniform1f(location.i, v0.f)
Prototype glUniform1d(location.i, v0.d)
Prototype glUniform2f(location.i, v0.f, v1.f)
Prototype glUniform2d(location.i, v0.d, v1.d)
Prototype glGetShaderInfoLog(shader.i, bufSize.l, *length_l, *infoLog)
Procedure InitOpenGL()
Global glCreateShader.glCreateShader = wglGetProcAddress_("glCreateShader")
Global glCreateProgram.glCreateProgram = wglGetProcAddress_("glCreateProgram")
Global glDeleteShader.glDeleteShader = wglGetProcAddress_("glDeleteShader")
Global glCompileShader.glCompileShader = wglGetProcAddress_("glCompileShader")
Global glLinkProgram.glLinkProgram = wglGetProcAddress_("glLinkProgram")
Global glUseProgram.glUseProgram = wglGetProcAddress_("glUseProgram")
Global glAttachShader.glAttachShader = wglGetProcAddress_("glAttachShader")
Global glShaderSource.glShaderSource = wglGetProcAddress_("glShaderSource")
Global glGetUniformLocation.glGetUniformLocation = wglGetProcAddress_("glGetUniformLocation")
Global glUniform1i.glUniform1i = wglGetProcAddress_("glUniform1i")
Global glUniform2i.glUniform2i = wglGetProcAddress_("glUniform2i")
Global glUniform1f.glUniform1f = wglGetProcAddress_("glUniform1f")
Global glUniform1d.glUniform1d = wglGetProcAddress_("glUniform1d")
Global glUniform2f.glUniform2f = wglGetProcAddress_("glUniform2f")
Global glUniform2d.glUniform2d = wglGetProcAddress_("glUniform2d")
Global glGetShaderInfoLog.glGetShaderInfoLog = wglGetProcAddress_("glGetShaderInfoLog")
EndProcedure
;}
;{ Renderer
Structure Uniform
Mode.i
Iterations.i
Scale.i
Position.i
Origin.i
Center.i
Rotation.i
EndStructure
Structure Scene
Mode.i
Iterations.i
Scale.d
Rotation.f
PositionX.d
PositionY.d
OriginX.d
OriginY.d
CenterX.i
CenterY.i
EndStructure
Global Uniform.Uniform
Global Mandelbrot.Scene
Global Julia.Scene
Procedure Compile(Gadget.i)
Protected VertexShader.i, VertexShaderCode.s
Protected FragmentShader.i, FragmentShaderCode.s
Protected Program.i
Protected *Buffer, Length.i
Protected ErrorText.s
VertexShaderCode = "#version 410" + #LF$ +
"in vec3 position;" + #LF$+
"void main() {" + #LF$ +
" gl_Position = vec4( position, 1.0 );" + #LF$ +
"};"
FragmentShaderCode = "#version 410" + #LF$ +
"uniform int iterations;" + #LF$ +
"uniform int mode;" + #LF$ +
"uniform float rotation;" + #LF$ +
"uniform double scale;" + #LF$ +
"uniform dvec2 position;" + #LF$ +
"uniform dvec2 origin;" + #LF$ +
"uniform ivec2 center;" + #LF$ +
"// Gradient Color" + #LF$ +
"const int gradientLength = 12;" + #LF$ +
"const vec4 gradientColor[gradientLength] = vec4[gradientLength](" + #LF$ +
" vec4(0.000, 0.000, 0.000, 0.00000)," + #LF$ +
" vec4(0.125, 0.000, 0.250, 0.06250)," + #LF$ +
" vec4(0.500, 0.125, 0.250, 0.15625)," + #LF$ +
" vec4(0.875, 0.250, 0.000, 0.25000)," + #LF$ +
" vec4(1.000, 0.500, 0.000, 0.34375)," + #LF$ +
" vec4(1.000, 0.750, 0.000, 0.40625)," + #LF$ +
" vec4(1.000, 1.000, 1.000, 0.50000)," + #LF$ +
" vec4(0.625, 0.875, 0.000, 0.59375)," + #LF$ +
" vec4(0.250, 0.750, 0.000, 0.68750)," + #LF$ +
" vec4(0.000, 0.500, 0.625, 0.78125)," + #LF$ +
" vec4(0.000, 0.250, 0.500, 0.87500)," + #LF$ +
" vec4(0.000, 0.000, 0.000, 1.00000)" + #LF$ +
");" + #LF$ +
"vec4 gradient( float value ) {" + #LF$ +
" " + #LF$ +
" for (int i=1; i<gradientLength; i++) {" + #LF$ +
" if (value < gradientColor[i].a)" + #LF$ +
" return vec4( mix( gradientColor[i-1].rgb, gradientColor[i].rgb, (value-gradientColor[i-1].a)/(gradientColor[i].a-gradientColor[i-1].a) ), 1.0); " + #LF$ +
" }" + #LF$ +
" " + #LF$ +
"}" + #LF$ +
"// Mandelbrot & Julia" + #LF$ +
"dvec2 rotate( vec2 position, float angle ) {" + #LF$ +
" return dvec2(position.x*cos(angle)-position.y*sin(angle), position.x*sin(angle)+position.y*cos(angle));" + #LF$ +
"};" + #LF$ +
"void main( void ) {" + #LF$ +
" " + #LF$ +
" float x;" + #LF$ +
" dvec2 c;" + #LF$ +
" dvec2 z;" + #LF$ +
" " + #LF$ +
" if (mode == 0) {" + #LF$ +
" c = rotate(vec2(gl_FragCoord.x-center.x, gl_FragCoord.y-center.y), rotation) / (center.y*scale) + position;" + #LF$ +
" z = dvec2(0.0, 0.0);" + #LF$ +
" }" + #LF$ +
" else {" + #LF$ +
" c = origin;" + #LF$ +
" z = rotate(vec2(gl_FragCoord.x-center.x, gl_FragCoord.y-center.y), rotation) / (center.y*scale) + position;" + #LF$ +
" }" + #LF$ +
" dvec2 s;" + #LF$ +
" for (int i = 1; i<iterations; i++) {" + #LF$ +
" s = z*z;" + #LF$ +
" if (s.x+s.y > 64) {" + #LF$ +
" x = (i - log(log(float(s.x+s.y))*0.5*1.44269502162933)*1.44269502162933);" + #LF$ +
" //x = x*0.02;" + #LF$ +
" x = log(x+1)*sqrt(x)/50;" + #LF$ +
" break;" + #LF$ +
" }" + #LF$ +
" z = dvec2(s.x-s.y+c.x, 2.0*z.x*z.y+c.y);" + #LF$ +
" }" + #LF$ +
" gl_FragColor = gradient(mod(x, 1.0));" + #LF$ +
"};"
SetGadgetAttribute(Gadget, #PB_OpenGL_SetContext, #True)
VertexShader = glCreateShader(#GL_VERTEX_SHADER)
*Buffer = Ascii(VertexShaderCode)
glShaderSource(VertexShader, 1, @*Buffer, #Null)
glCompileShader(VertexShader)
FreeMemory(*Buffer)
*Buffer = AllocateMemory(1024)
glGetShaderInfoLog(VertexShader, 1024, @Length, *Buffer)
ErrorText + PeekS(*Buffer, Length, #PB_Ascii)
FreeMemory(*Buffer)
FragmentShader = glCreateShader(#GL_FRAGMENT_SHADER)
*Buffer = Ascii(FragmentShaderCode)
glShaderSource(FragmentShader, 1, @*Buffer, #Null)
glCompileShader(FragmentShader)
FreeMemory(*Buffer)
*Buffer = AllocateMemory(1024)
glGetShaderInfoLog(FragmentShader, 1024, @Length, *Buffer)
ErrorText + PeekS(*Buffer, Length, #PB_Ascii)
FreeMemory(*Buffer)
Program = glCreateProgram()
glAttachShader(Program, VertexShader)
glAttachShader(Program, FragmentShader)
glLinkProgram(Program)
glUseProgram(Program)
glDeleteShader(VertexShader)
glDeleteShader(FragmentShader)
With Uniform
\Mode = glGetUniformLocation(Program, "mode")
\Iterations = glGetUniformLocation(Program, "iterations")
\Scale = glGetUniformLocation(Program, "scale")
\Rotation = glGetUniformLocation(Program, "rotation")
\Position = glGetUniformLocation(Program, "position")
\Origin = glGetUniformLocation(Program, "origin")
\Center = glGetUniformLocation(Program, "center")
EndWith
If ErrorText
OpenConsole()
PrintN(ErrorText)
EndIf
EndProcedure
Procedure Render(Gadget.i)
Protected *Scene.Scene = GetGadgetData(Gadget)
SetGadgetAttribute(Gadget, #PB_OpenGL_SetContext, #True)
glUniform1i(Uniform\Mode, *Scene\Mode)
glUniform1i(Uniform\Iterations, *Scene\Iterations)
glUniform1d(Uniform\Scale, *Scene\Scale)
glUniform1f(Uniform\Rotation, *Scene\Rotation)
glUniform2d(Uniform\Position, *Scene\PositionX, *Scene\PositionY)
glUniform2d(Uniform\Origin, *Scene\OriginX, *Scene\OriginY)
glUniform2i(Uniform\Center, *Scene\CenterX, *Scene\CenterY)
glBegin_(#GL_QUADS)
glVertex2f_(-1,-1)
glVertex2f_( 1,-1)
glVertex2f_( 1, 1)
glVertex2f_(-1, 1)
glEnd_()
SetGadgetAttribute(Gadget, #PB_OpenGL_FlipBuffers, #True)
EndProcedure
;}
;{ Main Program
Enumeration
#Window
#File
#Gadget_OpenGL_Mandelbrot
#Gadget_OpenGL_Julia
#Gadget_Editor
#Gadget_Error
EndEnumeration
Procedure Callback_Size()
SetGadgetAttribute(#Gadget_OpenGL_Julia, #PB_OpenGL_SetContext, #True)
ResizeGadget(#Gadget_OpenGL_Julia, WindowWidth(#Window)/2, 0, WindowWidth(#Window)-WindowWidth(#Window)/2, WindowHeight(#Window))
SetGadgetAttribute(#Gadget_OpenGL_Mandelbrot, #PB_OpenGL_SetContext, #True)
ResizeGadget(#Gadget_OpenGL_Mandelbrot, 0, 0, WindowWidth(#Window)/2, WindowHeight(#Window))
Mandelbrot\CenterX = GadgetWidth(#Gadget_OpenGL_Mandelbrot) / 2
Mandelbrot\CenterY = GadgetHeight(#Gadget_OpenGL_Mandelbrot) / 2
Julia\CenterX = GadgetWidth(#Gadget_OpenGL_Julia) / 2
Julia\CenterY = GadgetHeight(#Gadget_OpenGL_Julia) / 2
Render(#Gadget_OpenGL_Julia)
Render(#Gadget_OpenGL_Mandelbrot)
EndProcedure
OpenWindow(#Window, 0, 0, 1600, 800, "Vector Canvas Gadget", #PB_Window_MaximizeGadget|#PB_Window_MaximizeGadget|#PB_Window_SizeGadget|#PB_Window_ScreenCentered)
OpenGLGadget(#Gadget_OpenGL_Mandelbrot, 0, 0, WindowWidth(#Window)/2, WindowHeight(#Window))
SetGadgetData(#Gadget_OpenGL_Mandelbrot, @Mandelbrot)
OpenGLGadget(#Gadget_OpenGL_Julia, WindowWidth(#Window)/2, 0, WindowWidth(#Window)/2, WindowHeight(#Window))
SetGadgetData(#Gadget_OpenGL_Julia, @Julia)
BindEvent(#PB_Event_SizeWindow, @Callback_Size(), #Window)
With Mandelbrot
\Mode = 0
\Iterations = 512
\PositionX = -0.7
\PositionY = 0.0
\Scale = 0.5
\Rotation = 0.0
\CenterX = GadgetWidth(#Gadget_OpenGL_Mandelbrot) / 2
\CenterY = GadgetHeight(#Gadget_OpenGL_Mandelbrot) / 2
EndWith
With Julia
\Mode = 1
\Iterations = 512
\PositionX = 0.0
\PositionY = 0.0
\OriginX = Mandelbrot\PositionX
\OriginY = Mandelbrot\PositionY
\Scale = 0.5
\Rotation = 0.0
\CenterX = GadgetWidth(#Gadget_OpenGL_Julia) / 2
\CenterY = GadgetHeight(#Gadget_OpenGL_Julia) / 2
EndWith
InitOpenGL()
Compile(#Gadget_OpenGL_Mandelbrot)
Compile(#Gadget_OpenGL_Julia)
Define Time.i, Text.s
Define *Scene.Scene
Define OldRotation.d, OldPositionX.d, OldPositionY.d, OldMouseX.i, OldMouseY.i
Define MouseX.i, MouseY.i
Repeat
Select WaitWindowEvent(1)
Case #PB_Event_CloseWindow
Break
Case #PB_Event_Gadget
Select EventGadget()
Case #Gadget_OpenGL_Mandelbrot, #Gadget_OpenGL_Julia
*Scene = GetGadgetData(EventGadget())
MouseX = GetGadgetAttribute(EventGadget(), #PB_OpenGL_MouseX)
MouseY = GetGadgetAttribute(EventGadget(), #PB_OpenGL_MouseY)
Select EventType()
Case #PB_EventType_MouseWheel
If GetGadgetAttribute(EventGadget(), #PB_OpenGL_Modifiers) & #PB_OpenGL_Control
*Scene\Iterations * Pow(2, GetGadgetAttribute(EventGadget(), #PB_OpenGL_WheelDelta))
Else
*Scene\PositionX + (Cos(*Scene\Rotation)*(MouseX-*Scene\CenterX) + Sin(*Scene\Rotation)*(MouseY-*Scene\CenterY)) / *Scene\Scale/*Scene\CenterY
*Scene\PositionY - (-Sin(*Scene\Rotation)*(MouseX-*Scene\CenterX) + Cos(*Scene\Rotation)*(MouseY-*Scene\CenterY)) / *Scene\Scale/*Scene\CenterY
*Scene\Scale * Pow(2, GetGadgetAttribute(EventGadget(), #PB_OpenGL_WheelDelta)*0.25)
*Scene\PositionX - (Cos(*Scene\Rotation)*(MouseX-*Scene\CenterX) + Sin(*Scene\Rotation)*(MouseY-*Scene\CenterY)) / *Scene\Scale/*Scene\CenterY
*Scene\PositionY + (-Sin(*Scene\Rotation)*(MouseX-*Scene\CenterX) + Cos(*Scene\Rotation)*(MouseY-*Scene\CenterY)) / *Scene\Scale/*Scene\CenterY
EndIf
Case #PB_EventType_LeftButtonDown
OldPositionX = *Scene\PositionX : OldPositionY = *Scene\PositionY
OldMouseX = MouseX : OldMouseY = MouseY
Case #PB_EventType_RightButtonDown
OldRotation = ATan2(MouseX-*Scene\CenterX, MouseY-*Scene\CenterY) - *Scene\Rotation
OldMouseX = MouseX : OldMouseY = MouseY
Case #PB_EventType_LeftDoubleClick
*Scene\PositionX + (Cos(*Scene\Rotation)*(MouseX-*Scene\CenterX) + Sin(*Scene\Rotation)*(MouseY-*Scene\CenterY)) / *Scene\Scale/*Scene\CenterY
*Scene\PositionY - (-Sin(*Scene\Rotation)*(MouseX-*Scene\CenterX) + Cos(*Scene\Rotation)*(MouseY-*Scene\CenterY)) / *Scene\Scale/*Scene\CenterY
Case #PB_EventType_MouseMove
If GetGadgetAttribute(EventGadget(), #PB_OpenGL_Buttons) & #PB_OpenGL_LeftButton
*Scene\PositionX = OldPositionX - (Cos(*Scene\Rotation)*(MouseX-OldMouseX) + Sin(*Scene\Rotation)*(MouseY-OldMouseY)) / *Scene\Scale/*Scene\CenterY
*Scene\PositionY = OldPositionY + (-Sin(*Scene\Rotation)*(MouseX-OldMouseX) + Cos(*Scene\Rotation)*(MouseY-OldMouseY)) / *Scene\Scale/*Scene\CenterY
ElseIf GetGadgetAttribute(EventGadget(), #PB_OpenGL_Buttons) & #PB_OpenGL_RightButton
*Scene\Rotation = ATan2(MouseX-*Scene\CenterX, MouseY-*Scene\CenterY) - OldRotation
EndIf
If EventGadget() = #Gadget_OpenGL_Mandelbrot
Julia\OriginX = Mandelbrot\PositionX + (Cos(*Scene\Rotation)*(MouseX-*Scene\CenterX) + Sin(*Scene\Rotation)*(MouseY-*Scene\CenterY)) / Mandelbrot\Scale/*Scene\CenterY
Julia\OriginY = Mandelbrot\PositionY - (-Sin(*Scene\Rotation)*(MouseX-*Scene\CenterX) + Cos(*Scene\Rotation)*(MouseY-*Scene\CenterY)) / Mandelbrot\Scale/*Scene\CenterY
EndIf
Case #PB_EventType_MouseLeave
If EventGadget() = #Gadget_OpenGL_Mandelbrot
Julia\OriginX = Mandelbrot\PositionX
Julia\OriginY = Mandelbrot\PositionY
EndIf
EndSelect
Case #Gadget_OpenGL_Julia
Select EventType()
Case #PB_EventType_MouseWheel
Julia\Scale * Pow(2, GetGadgetAttribute(#Gadget_OpenGL_Julia, #PB_OpenGL_WheelDelta)*0.25)
EndSelect
EndSelect
Case #PB_Event_None
Time = ElapsedMilliseconds()
Render(#Gadget_OpenGL_Mandelbrot)
Render(#Gadget_OpenGL_Julia)
Text = "Position: "+StrD(Julia\OriginX,17)+" + "+StrD(Julia\OriginY,17)+"i"
Text + " | Iterations: "+Str(Mandelbrot\Iterations)+" ; "+Str(Julia\Iterations)
Text + " | Render time: "+Str(ElapsedMilliseconds()-Time)+" ms"
Text + " | Zoom: mouse wheel; move: left mouse button; rotate: right mouse button; iteration depth: STRG + mouse wheel; center view: double left click"
SetWindowTitle(#Window, Text)
EndSelect
ForEver
End
;}
Insider: Theoretisch habe ich auch schon einen passenden Code geschrieben, um mit noch höherer Genauigkeit zu rechnen, allerdings bricht dann die Renderzeit selbst auf der GPU schnell zusammen wenn man sehr tief zoom und viele Iterationen hat. Somit ist dieses Beispiel nur mit Doubles.