PDA

View Full Version : simple Mandelbrot shader



dingdong
11-22-2010, 03:59 AM
I'm having problems writing a simple Mandelbrot shader. For some reason it comes out looking recognizable but wrong (see attached screenshot). Strange thing is that when I use the fragment shader code (copied to C++ source file) to generate an image it looks exactly as it should. I've tried several things to figure it out but sofar I've had no luck. It may well be something obvious that I'm overlooking but whatever it is I doubt it's the shader code because I found that a similar shader from TyphoonLabs (the one in their tutorial, ch. 4) gives me the same result.

Alfonse Reinheart
11-22-2010, 04:21 AM
There's not much anyone can do to help you unless you actually post the shader code.

dingdong
11-22-2010, 05:12 AM
Right, here are the TyphoonLabs vertex and fragment shader that I used to produce the screenshot. I changed the code a bit: instead of uniform-s I simply use const-s and I added a missing line inside the loop. By the way, I'm using GL 2.1.0 and GLSL 1.20; not sure if that makes any difference though.

Vertex shader:


varying vec3 position;
void main()
{
position = vec3(gl_MultiTexCoord0 - 0.5) * 5.0;
gl_Position = ftransform();
}


Fragment shader:


varying vec3 position;
const int maxIterations = 25; //uniform
const vec2 center = vec2(0.0); //uniform
const vec3 outerColor1 = vec3(1.0); //uniform
const vec3 outerColor2 = vec3(0.0, 0.0, 1.0); //uniform
const float zoom = 1.0; //uniform

void main()
{
float real = position.x * (1.0/zoom) + center.x;
float imag = position.y * (1.0/zoom) + center.y;
float cReal = real;
float cImag = imag;

float r2 = 0.0;
int iter;

for (iter = 0; iter < maxIterations &amp;&amp; r2 < 4.0; ++iter)
{
float tempreal = real;
real = (tempreal * tempreal) - (imag * imag) + cReal;
imag = 2.0 * tempreal * imag + cImag;

r2 = real*real + imag*imag; // missing line
}

// Base the color on the number of iterations.
vec3 color;
if (r2 < 4.0)
color = vec3(0.0);
else
color = mix(outerColor1, outerColor2, fract(float(iter)*0.05));

gl_FragColor = vec4 (clamp(color, 0.0, 1.0), 1.0);
}

zeoverlord
11-22-2010, 02:25 PM
try this, i think you forgot to use a temporary imaginary variable

float tempreal = real;
float tempimag = imag;
real = (tempreal * tempreal) - (tempimag * tempimag ) + cReal;
imag = 2.0 * tempreal * tempimag + cImag;

dingdong
11-22-2010, 10:48 PM
No that's not it but I did try your code to be sure.

I started with my own implementation and when it seemed to fail I copied the frag shader main body to a routine in a C++ source file. I then generated an image using that routine and it worked fine.

I searched the web and found the TyphoonLabs shader. I gave it a try and the result turned out exactly the same as before with my own shader (see previous screenshot) even though the code is entirely different.

So now I'm stuck. I don't understand what's wrong and I'm hoping someone here can point out my mistake.

ZbuffeR
11-23-2010, 01:31 AM
Did you verify that the "position" is varying linearly across the quad ?
ie. with a simple shader like :
gl_FragColor = vec4(position.x,position.y,0,1);

dingdong
11-23-2010, 01:37 AM
Yes I did; I used a checkerboard pattern though and it turned out just fine.

Vadimgl
11-23-2010, 02:54 AM
I had the same problem in the past. But is it actually the problem? It is another type of fractal - something similar to chaos. But I can't find error in your code, so i'll post my code and show you why fractals differ, perhaps it will help you.

So..
inline Complex&amp; operator % (const Complex&amp; b)//produces your wrong mandelbrot
{
m_Img = m_Real * b.m_Img + m_Img * b.m_Real;
m_Real = m_Real * b.m_Real - m_Img * b.m_Img;//something here;)
return *this;
}
inline Complex operator * (const Complex&amp; b)//produces good manderbulb
{
return Complex(m_Real * b.m_Real - m_Img * b.m_Img, _Real * b.m_Img + m_Img * b.m_Real);//whis is the way manderbulb is build
}

This are my 2 operators to square a complex value. Notice the difference in this 2 operators. % produces wrong chaos fractal, * produces simple manderbulb.

And fuunction to build them (sorry, NOT in OpenGL):


int BuildMandelbrotChaos(TImage *Screen, long double FractMinX,
long double FractMinY,
long double FractMaxX,
long double FractMaxY,
long double MaxDistance,
int MaxIterations)
{
::FractMaxX[0] = FractMaxX; //edges of fractal vievport
::FractMaxY[0] = FractMaxY;
::FractMinX[0] = FractMinX;
::FractMinY[0] = FractMinY;
const Cols = Form1->FormScreen1->Width; //FormScreen1 - TImage which holds fractal
const Rows = Form1->FormScreen1->Height;
long double FractXAdd = (FractMaxX - FractMinX) / Cols, x = FractMinX; //complex x and y addition on every iteration
long double FractYAdd = (FractMaxY - FractMinY) / Rows, y = FractMaxY;
int i, j, k, color;
Complex Z, C(x, y); //initialize 2 complex values
//for C(x, y) - x-real fraction, y-imaginary fraction
for (i = 0; i < Cols; i++) // for each pixel in screen
{
for (j = 0; j < Rows; j++)
{
color = 5;
Z.Set(0, 0);
C.Set(x, y);
for (k = 0; k < MaxIterations; k++)
{
Z = Z % Z + C; //build wrong fractal, change this to Z * Z and you get what you need
color += 9;
if (Z.Ditance(0, 0) > MaxDistance)
break;
}
//TColor = Screen->Canvas->Pixels[0][0];
if (Z.Ditance(0, 0) <= MaxDistance)
Screen->Canvas->Pixels[i][j] = clBlack;
else
Screen->Canvas->Pixels[i][j] = GtCl(color, Z) * 2; //something to calc the color
y -= FractYAdd;
}
x += FractXAdd;
y = FractMaxY;
}
return 1;
}
//---------------------------------------------------------------------------


Hope it helps.

dingdong
11-23-2010, 03:25 AM
I see your point but unfortunately it's nothing simple like that (zeoverlord suggested the same). Again, my shader code works fine if I copy it in a C++ routine so I take that as a clue that something else must be wrong.

I'm starting to think about reentrancy: on some graphics hardware the pipeline can process multiple fragments simultaneously, right? If that is the case then I'm thinking it may be possible that the code is duplicated and run in parallel but the local variables are shared. I don't know if this can happen but if so then it could seriously mess things up.

Vadimgl
11-23-2010, 06:22 AM
I think this wouldn't happen, even if would, still the results of calculation of multiple gragments should be connected aftervards so that nothing goes wrong. But Ok, I don't know OpenGl, so I might be wrong..

But I still think that the problem is in what I have already posted. Just look at the pictures of mine. What effect operator % has done on mandelbrot in my program. My 'broken' manderbrot is a little bit different from one that you posted, but still their characteristic structure is the the same for both.

And, by the way, Julia with this 'wrong' Z computing looks quite nice.

dingdong
11-23-2010, 07:32 AM
Here is a capture of the image I generated using my shader code in C++. As you can see, it looks exactly like the Mandelbrot fractal so that tells me the computations are correct. I'm sure I didn't make a mistake there. Besides, the TyphoonLabs shader gives me the exact same result: ok when running as C++ code but wrong when running as shader language. You said yourself that you can't find fault with the code I posted so I ask: what else can you think of?

I'm hopelessly stuck here; any suggestion is helpful.

dingdong
11-24-2010, 12:01 AM
I found another Mandelbrot shader here (http://www.opengl.org/discussion_boards/ubbthreads.php?ubb=showflat&amp;Number=262713). This one seems to work for me. I compared to my code and the TyphoonLabs shader and made some changes. I got both working properly now. It turns out that I get the result I want if I change the loop body like so:


float tempreal = real;
float tempimag = imag;
tempreal = (real * real) - (tempimag * tempimag) + cReal;
imag = 2.0 * real * tempimag + cImag;
real = tempreal;


But it's messed up again if I just interchange the 3rd and 4th line like so:


float tempreal = real;
float tempimag = imag;
imag = 2.0 * real * tempimag + cImag;
tempreal = (real * real) - (tempimag * tempimag) + cReal;
real = tempreal;


This doesn't make sense at all. Now I have a solution but no explanation and I can't really move on without understanding the difference between the code that works and the code that doesn't. If anyone can please shed some light on this then I can give my tired brain a rest.

Alfonse Reinheart
11-24-2010, 12:11 AM
Seems like some kind of driver bug.

dingdong
11-24-2010, 12:52 AM
I hate when that happens. I just checked and found that an updated version is available for my chipset but the installer fails and says that I have to get the driver from my system's manufacturer. Of course, at the support website there's no updated display driver yet.

Anyway, I'll try running the shader on another system with a totally different configuration. If it works on that machine then it probably is a buggy driver that has caused me so much frustration these past few days.