Procházet zdrojové kódy

tutorial: support the Julia set in the fractal sample

legacy
Sam Hocevar před 8 roky
rodič
revize
b9ac811720
1 změnil soubory, kde provedl 118 přidání a 76 odebrání
  1. +118
    -76
      doc/tutorial/11_fractal.cpp

+ 118
- 76
doc/tutorial/11_fractal.cpp Zobrazit soubor

@@ -20,6 +20,8 @@
#include <lol/engine.h>
#include "loldebug.h"

#define USE_REAL 0

using namespace lol;

LOLFX_RESOURCE_DECLARE(11_fractal);
@@ -28,6 +30,7 @@ class Fractal : public WorldEntity
{
public:
Fractal(ivec2 const &size)
: m_julia(false)
{
/* Ensure texture size is a multiple of 16 for better aligned
* data access. Store the dimensions of a texel for our shader,
@@ -41,7 +44,8 @@ public:
m_controller = new Controller("Default");
m_profile << InputProfile::MouseKey(0, "Left")
<< InputProfile::MouseKey(1, "Right")
<< InputProfile::MouseKey(2, "Middle");
<< InputProfile::MouseKey(2, "Middle")
<< InputProfile::Keyboard(3, "Space");
m_controller->Init(m_profile);
m_mouse = InputDevice::GetMouse();

@@ -70,10 +74,10 @@ public:
m_deltascale[i] = real("1");
m_dirty[i] = 2;
}
m_center = rcmplx(-0.75, 0.0);
m_view.center = rcmplx(-0.75, 0.0);
m_zoom_speed = 0.0;
m_translate = rcmplx(0.0, 0.0);
m_radius = 5.0;
m_view.translate = rcmplx(0.0, 0.0);
m_view.radius = 5.0;
m_ready = false;
m_drag = false;

@@ -81,9 +85,9 @@ public:
{
double f = (double)i / PALETTE_STEP;

vec3 hsv(lol::fmod(i * 0.002f, 1.f),
0.4 * lol::sin(f * 0.27 + 2.0) + 0.4,
0.4 * lol::sin(f * 0.21 - 2.6) + 0.4);
vec3 hsv(lol::fmod(i * 0.001f, 1.f),
0.3 * lol::sin(f * 0.27 + 2.0) + 0.3,
0.3 * lol::sin(f * 0.21 - 2.6) + 0.6);
vec3 rgb = Color::HSVToRGB(hsv);

if (f < 7.0)
@@ -102,17 +106,17 @@ public:
}

#if !defined __native_client__
m_zoomtext = new Text("", "data/font/ascii.png");
m_zoomtext->SetPos(vec3(5, (float)m_window_size.y - 15, 1));
Ticker::Ref(m_zoomtext);

m_centertext = new Text("", "data/font/ascii.png");
m_centertext->SetPos(vec3(5, (float)m_window_size.y - 15, 1));
m_centertext->SetPos(vec3(5, (float)m_window_size.y - 29, 1));
Ticker::Ref(m_centertext);

m_mousetext = new Text("", "data/font/ascii.png");
m_mousetext->SetPos(vec3(5, (float)m_window_size.y - 29, 1));
m_mousetext->SetPos(vec3(5, (float)m_window_size.y - 43, 1));
Ticker::Ref(m_mousetext);

m_zoomtext = new Text("", "data/font/ascii.png");
m_zoomtext->SetPos(vec3(5, (float)m_window_size.y - 43, 1));
Ticker::Ref(m_zoomtext);
#endif

m_position = vec3::zero;
@@ -139,7 +143,6 @@ public:
m_donequeue.pop();
#endif

//Input::UntrackMouse(this);
#if !defined __native_client__
Ticker::Unref(m_centertext);
Ticker::Unref(m_mousetext);
@@ -147,20 +150,20 @@ public:
#endif
}

inline dcmplx TexelToWorldOffset(vec2 texel)
inline f128cmplx TexelToWorldOffset(vec2 texel)
{
double dx = (0.5 + texel.x - m_size.x / 2) * m_texel2world.x;
double dy = (0.5 + m_size.y / 2 - texel.y) * m_texel2world.y;
return m_radius * dcmplx(dx, dy);
return m_view.radius * f128cmplx(dx, dy);
}

inline dcmplx ScreenToWorldOffset(vec2 pixel)
inline f128cmplx ScreenToWorldOffset(vec2 pixel)
{
/* No 0.5 offset here, because we want to be able to position the
* mouse at (0,0) exactly. */
double dx = pixel.x - m_window_size.x / 2;
double dy = m_window_size.y / 2 - pixel.y;
return m_radius * m_window2world * dcmplx(dx, dy);
return m_view.radius * m_window2world * f128cmplx(dx, dy);
}

virtual void TickGame(float seconds)
@@ -172,8 +175,23 @@ public:
int prev_frame = (m_frame + 4) % 4;
m_frame = (m_frame + 1) % 4;

rcmplx worldmouse = m_center
+ rcmplx(ScreenToWorldOffset((vec2)mousepos));
if (m_controller->WasKeyPressedThisFrame(3))
{
m_julia = !m_julia;
if (m_julia)
{
m_saved_view = m_view;
m_view.r0 = m_view.center + rcmplx(ScreenToWorldOffset((vec2)mousepos));
}
else
{
m_view = m_saved_view;
}
for (auto & flag : m_dirty)
flag = 2;
}

rcmplx worldmouse = m_view.center + rcmplx(ScreenToWorldOffset((vec2)mousepos));

if (m_controller->IsKeyPressed(2))
{
@@ -182,24 +200,24 @@ public:
m_oldmouse = mousepos;
m_drag = true;
}
m_translate = rcmplx(ScreenToWorldOffset((vec2)m_oldmouse)
- ScreenToWorldOffset((vec2)mousepos));
m_view.translate = rcmplx(ScreenToWorldOffset((vec2)m_oldmouse)
- ScreenToWorldOffset((vec2)mousepos));
/* XXX: the purpose of this hack is to avoid translating by
* an exact number of pixels. If this were to happen, the step()
* optimisation for i915 cards in our shader would behave
* incorrectly because a quarter of the pixels in the image
* would have tied rankings in the distance calculation. */
m_translate *= real(1023.0 / 1024.0);
m_view.translate *= real(1023.0 / 1024.0);
m_oldmouse = mousepos;
}
else
{
m_drag = false;
if (m_translate != rcmplx(0.0, 0.0))
if (m_view.translate != rcmplx(0.0, 0.0))
{
m_translate *= real(std::pow(2.0, -seconds * 5.0));
if ((double)norm(m_translate) < m_radius * 1e-4)
m_translate = rcmplx(0.0, 0.0);
m_view.translate *= real(std::pow(2.0, -seconds * 5.0));
if ((double)norm(m_view.translate) < m_view.radius * 1e-4)
m_view.translate = rcmplx(0.0, 0.0);
}
}

@@ -219,34 +237,35 @@ public:
m_zoom_speed = 0.0;
}

if (m_zoom_speed || m_translate != rcmplx(0.0, 0.0))
if (m_zoom_speed || m_view.translate != rcmplx(0.0, 0.0))
{
rcmplx oldcenter = m_center;
double oldradius = m_radius;
rcmplx oldcenter = m_view.center;
double oldradius = m_view.radius;
double zoom = std::pow(2.0, seconds * 1e3f * m_zoom_speed);
if (m_radius * zoom > 8.0)
if (m_view.radius * zoom > 8.0)
{
m_zoom_speed *= -1.0;
zoom = 8.0 / m_radius;
zoom = 8.0 / m_view.radius;
}
else if (m_radius * zoom < 1e-14)
else if (m_view.radius * zoom < MAX_ZOOM)
{
m_zoom_speed *= -1.0;
zoom = 1e-14 / m_radius;
zoom = MAX_ZOOM / m_view.radius;
}
m_radius *= zoom;
m_center += m_translate;
m_center = (m_center - worldmouse) * real(zoom) + worldmouse;
worldmouse = m_center
+ rcmplx(ScreenToWorldOffset((vec2)mousepos));
m_view.radius *= zoom;
m_view.center += m_view.translate;
m_view.center = (m_view.center - worldmouse) * real(zoom) + worldmouse;
worldmouse = m_view.center
+ rcmplx(ScreenToWorldOffset((vec2)mousepos));

/* Store the transformation properties to go from m_frame - 1
* to m_frame. */
m_deltashift[prev_frame] = (m_center - oldcenter) / real(oldradius);
m_deltashift[prev_frame] = (m_view.center - oldcenter) / real(oldradius);
m_deltashift[prev_frame].x /= m_size.x * m_texel2world.x;
m_deltashift[prev_frame].y /= m_size.y * m_texel2world.y;
m_deltascale[prev_frame] = m_radius / oldradius;
m_dirty[0] = m_dirty[1] = m_dirty[2] = m_dirty[3] = 2;
m_deltascale[prev_frame] = m_view.radius / oldradius;
for (auto & flag : m_dirty)
flag = 2;
}
else
{
@@ -283,16 +302,16 @@ public:
#if !defined __native_client__
char buf[256];
std::sprintf(buf, "center: ");
m_center.x.sprintf(buf + strlen(buf), 30);
m_view.center.x.sprintf(buf + strlen(buf), 30);
std::sprintf(buf + strlen(buf), " ");
m_center.y.sprintf(buf + strlen(buf), 30);
m_view.center.y.sprintf(buf + strlen(buf), 30);
m_centertext->SetText(buf);
std::sprintf(buf, " mouse: ");
worldmouse.x.sprintf(buf + strlen(buf), 30);
std::sprintf(buf + strlen(buf), " ");
worldmouse.y.sprintf(buf + strlen(buf), 30);
m_mousetext->SetText(buf);
std::sprintf(buf, " zoom: %g", 1.0 / m_radius);
std::sprintf(buf, "[%s] zoom: %g", m_julia ? "Julia" : "Mandelbrot", 1.0 / m_view.radius);
m_zoomtext->SetText(buf);
#endif

@@ -339,38 +358,49 @@ public:
u8vec4 *pixelstart = &m_pixels[0]
+ m_size.x * (m_size.y / 4 * m_frame + line / 4);

dcmplx c = (dcmplx)m_center;
#if USE_REAL
rcmplx c = (rcmplx)m_view.center;
rcmplx jr0 = (rcmplx)m_view.r0;
#else
f128cmplx c = (f128cmplx)m_view.center;
f128cmplx jr0 = (f128cmplx)m_view.r0;
#endif

for (int j = jmin; j < jmax; j += 2)
for (int i = m_frame % 2; i < m_size.x; i += 2)
{
double xr, yr, x0, y0, x1, y1, x2, y2, x3, y3;
dcmplx z0 = c + TexelToWorldOffset(vec2(ivec2(i, j)));
//dcmplx r0(0.28693186889504513, 0.014286693904085048);
//dcmplx r0(0.001643721971153, 0.822467633298876);
//dcmplx r0(-1.207205434596, 0.315432814901);
//dcmplx r0(-0.79192956889854, -0.14632423080102);
//dcmplx r0(0.3245046418497685, 0.04855101129280834);
dcmplx r0 = z0;
#if USE_REAL
real xr, yr, x0, y0, x1, y1, x2, y2, x3, y3;
real sqx0, sqy0, sqx1, sqy1, sqx2, sqy2, sqx3, sqy3;
rcmplx z0 = c + rcmplx(TexelToWorldOffset(vec2(ivec2(i, j))));
rcmplx r0 = m_julia ? jr0 : z0;
#else
ldouble xr, yr, x0, y0, x1, y1, x2, y2, x3, y3;
ldouble sqx0, sqy0, sqx1, sqy1, sqx2, sqy2, sqx3, sqy3;
f128cmplx z0 = c + TexelToWorldOffset(vec2(ivec2(i, j)));
f128cmplx r0 = m_julia ? jr0 : z0;
#endif

x0 = z0.x; y0 = z0.y;
xr = r0.x; yr = r0.y;
sqx0 = x0 * x0; sqy0 = y0 * y0;

int iter = MAX_ITERATIONS - 4;
for (;;)
{
/* Unroll the loop: tests are more expensive to do at each
* iteration than the few extra multiplications. */
x1 = x0 * x0 - y0 * y0 + xr;
y1 = x0 * y0 + x0 * y0 + yr;
x2 = x1 * x1 - y1 * y1 + xr;
y2 = x1 * y1 + x1 * y1 + yr;
x3 = x2 * x2 - y2 * y2 + xr;
y3 = x2 * y2 + x2 * y2 + yr;
x0 = x3 * x3 - y3 * y3 + xr;
y0 = x3 * y3 + x3 * y3 + yr;

if (x0 * x0 + y0 * y0 >= maxsqlen)
* iteration than the few extra multiplications, at least
* with floats/doubles. */
x1 = sqx0 - sqy0 + xr; y1 = x0 * y0 + x0 * y0 + yr;
sqx1 = x1 * x1; sqy1 = y1 * y1;
x2 = sqx1 - sqy1 + xr; y2 = x1 * y1 + x1 * y1 + yr;
sqx2 = x2 * x2; sqy2 = y2 * y2;
x3 = sqx2 - sqy2 + xr; y3 = x2 * y2 + x2 * y2 + yr;
sqx3 = x3 * x3; sqy3 = y3 * y3;
x0 = sqx3 - sqy3 + xr; y0 = x3 * y3 + x3 * y3 + yr;
sqx0 = x0 * x0; sqy0 = y0 * y0;

if ((double)sqx0 + (double)sqy0 >= maxsqlen)
break;
iter -= 4;
if (iter < 4)
@@ -379,19 +409,19 @@ public:

if (iter)
{
double n = x0 * x0 + y0 * y0;
double n = (double)sqx0 + (double)sqy0;

if (x1 * x1 + y1 * y1 >= maxsqlen)
if ((double)sqx1 + (double)sqy1 >= maxsqlen)
{
iter += 3; n = x1 * x1 + y1 * y1;
iter += 3; n = (double)sqx1 + (double)sqy1;
}
else if (x2 * x2 + y2 * y2 >= maxsqlen)
else if ((double)sqx2 + (double)sqy2 >= maxsqlen)
{
iter += 2; n = x2 * x2 + y2 * y2;
iter += 2; n = (double)sqx2 + (double)sqy2;
}
else if (x3 * x3 + y3 * y3 >= maxsqlen)
else if ((double)sqx3 + (double)sqy3 >= maxsqlen)
{
iter += 1; n = x3 * x3 + y3 * y3;
iter += 1; n = (double)sqx3 + (double)sqy3;
}

if (n > maxsqlen * maxsqlen)
@@ -399,7 +429,7 @@ public:

/* Approximate log(sqrt(n))/log(sqrt(maxsqlen)) */
double f = iter;
union { double n; uint64_t x; } u = { n };
union { double n; uint64_t x; } u = { (double)n };
double k = (double)(u.x >> 42) - (((1 << 10) - 1) << 10);
k *= k1;

@@ -509,11 +539,14 @@ public:
}

private:
static int const MAX_ITERATIONS = 340;
static int const MAX_ITERATIONS = 400;
static int const PALETTE_STEP = 32;
static int const MAX_THREADS = 8;
static int const MAX_LINES = 8;

// 1e-14 for doubles, 1e-17 for long doubles
static double constexpr MAX_ZOOM = 1e-17;

ivec2 m_size, m_window_size, m_oldmouse;
double m_window2world;
dvec2 m_texel2world;
@@ -530,9 +563,18 @@ private:
int m_frame, m_slices, m_dirty[4];
bool m_ready, m_drag;

rcmplx m_deltashift[4], m_center, m_translate;
struct view_settings
{
rcmplx center, translate, r0;
double radius;
};

view_settings m_view, m_saved_view;

rcmplx m_deltashift[4];
real m_deltascale[4];
double m_zoom_speed, m_radius;
double m_zoom_speed;
bool m_julia;

vec4 m_texel_settings, m_screen_settings;
mat4 m_zoom_settings;


Načítá se…
Zrušit
Uložit