here is a small gimmick regarding the Mandelbrot set and the Julia set.
With two OpenGLGadgets you can move through the Mandelbrot set with your cursor and the mouse wheel,
while the corresponding Julia set is rendered on the other side.
For this, I have written a fragment shader to iterate the color at all drawn pixels.
Code: Select all
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
;}