Browse Source

tutorial: look into previous frames for data when zooming in the Mandelbrot

viewer using only the shader.
legacy
Sam Hocevar sam 13 years ago
parent
commit
7f096b61e4
1 changed files with 152 additions and 76 deletions
  1. +152
    -76
      test/tutorial/tut03.cpp

+ 152
- 76
test/tutorial/tut03.cpp View File

@@ -46,12 +46,22 @@ class Fractal : public WorldEntity
public:
Fractal(ivec2 const &size)
{
/* Ensure size has even X and Y values */
/* Ensure texture size is a multiple of 16 for better aligned
* data access. Store the dimensions of a texel for our shader. */
m_size = size;
m_size.x = (m_size.x + 1) & ~1;
m_size.y = (m_size.y + 1) & ~1;
m_size.x = (m_size.x + 15) & ~15;
m_size.y = (m_size.y + 15) & ~15;
m_texel_settings = vec4(vec2(1.0, 1.0) / (vec2)m_size, m_size);

/* Window size decides the world aspect ratio. For instance, 640×480
* will be mapped to (-0.66,-0.5) - (0.66,0.5). */
m_window_size = Video::GetSize();
if (m_window_size.y < m_window_size.x)
m_window2world = 0.5 / m_window_size.y;
else
m_window2world = 0.5 / m_window_size.x;
m_texel2world = (vec2)m_window_size / (vec2)m_size * m_window2world;

m_pixels = new u8vec4[m_size.x * m_size.y];
m_tmppixels = new u8vec4[m_size.x / 2 * m_size.y / 2];
m_frame = -1;
@@ -62,24 +72,29 @@ public:
m_dirty[i] = 2;
}
m_center = -0.75;
m_radius = 1.5;
m_texture_radius = 0.5 * (m_size.x < m_size.y ? m_size.x : m_size.y);
m_window_radius = 0.5 * (m_window_size.x < m_window_size.y ? m_window_size.x : m_window_size.y);
m_pixel_delta = vec4(vec2(1.0, 1.0) / (vec2)m_size, m_size);
m_radius = 5.0;
m_ready = false;

m_palette = new u8vec4[(MAX_ITERATIONS + 1) * PALETTE_STEP];
for (int i = 0; i < (MAX_ITERATIONS + 1) * PALETTE_STEP; i++)
{
float f = i / (double)PALETTE_STEP;
double f = (double)i / PALETTE_STEP;

double r = 0.5 * sin(f * 0.27 - 2.5) + 0.5;
double g = 0.5 * sin(f * 0.13 + 1.1) + 0.5;
double b = 0.5 * sin(f * 0.21 + 0.4) + 0.5;

uint8_t red = r * 255.0f;
uint8_t green = g * 255.0f;
uint8_t blue = b * 255.0f;
if (f < 7.0)
{
f = f < 1.0 ? 0.0 : (f - 1.0) / 6.0;
r *= f;
g *= f;
b *= f;
}

uint8_t red = r * 255.99f;
uint8_t green = g * 255.99f;
uint8_t blue = b * 255.99f;
m_palette[i] = u8vec4(blue, green, red, 0);
}

@@ -114,24 +129,25 @@ public:

inline f64cmplx TexelToWorldOffset(ivec2 texel)
{
f64cmplx tmp = f64cmplx(0.5 + texel.x - m_size.x / 2,
0.5 + m_size.y / 2 - texel.y);
return tmp * (m_radius / m_texture_radius);
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 * f64cmplx(dx, dy);
}

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

virtual void TickGame(float deltams)
{
WorldEntity::TickGame(deltams);

int prev_frame = m_frame;
m_frame = (m_frame + 1) % 4;

f64cmplx worldmouse = m_center + ScreenToWorldOffset(mousepos);
@@ -160,23 +176,42 @@ public:
worldmouse = m_center + ScreenToWorldOffset(mousepos);
#endif

/* Store the transformation properties to go from m_frame-1
/* Store the transformation properties to go from m_frame - 1
* to m_frame. */
m_deltashift[m_frame] = (oldcenter - m_center) / m_radius;
m_deltascale[m_frame] = oldradius / m_radius;
m_deltashift[prev_frame] = (m_center - oldcenter) / 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;
}
else
{
/* If settings didn't change, set transformation from previous
* frame to identity. */
m_deltashift[m_frame] = 0.0;
m_deltascale[m_frame] = 1.0;
m_deltashift[prev_frame] = 0.0;
m_deltascale[prev_frame] = 1.0;
}

if (buttons[1])
m_dirty[0] = m_dirty[1] = m_dirty[2] = m_dirty[3] = 2;

/* Transformation from current frame to current frame is always
* identity. */
m_zoom_settings[m_frame][0] = 0.0f;
m_zoom_settings[m_frame][1] = 0.0f;
m_zoom_settings[m_frame][2] = 1.0f;

/* Compute transformation from other frames to current frame */
for (int i = 0; i < 3; i++)
{
int prev_index = (m_frame + 4 - i) % 4;
int cur_index = (m_frame + 3 - i) % 4;

m_zoom_settings[cur_index][0] = m_zoom_settings[prev_index][0] * m_deltascale[cur_index] + m_deltashift[cur_index].x;
m_zoom_settings[cur_index][1] = m_zoom_settings[prev_index][1] * m_deltascale[cur_index] + m_deltashift[cur_index].y;
m_zoom_settings[cur_index][2] = m_zoom_settings[prev_index][2] * m_deltascale[cur_index];
}

char buf[128];
sprintf(buf, "center: %+16.14f%+16.14fi", m_center.x, m_center.y);
m_centertext->SetText(buf);
@@ -291,43 +326,70 @@ public:

"#version 120\n"
""
"uniform vec4 in_PixelDelta;"
"uniform vec4 in_TexelSize;"
"uniform mat4 in_ZoomSettings;"
"uniform sampler2D in_Texture;"
""
"float mylen(vec2 p) {"
//" return abs(p.x) + abs(p.y);"
//" return p.x * p.x + p.y * p.y;"
" return length(p);"
"}"
""
/* Get the coordinate of the nearest point in slice 0 in xy,
* and the squared distance to that point in z.
* p is in normalised [0,1] texture coordinates.
* return value has the 0.25 Y scaling. */
"vec3 nearest0(vec2 p) {"
" vec2 q = p + 0.5 * in_PixelDelta.xy;"
" q -= mod(q, 2.0 * in_PixelDelta.xy);"
" q += 0.5 * in_PixelDelta.xy;"
" return vec3(q * vec2(1.0, 0.25),"
" length(q - p));"
" p -= vec2(0.5, 0.5);"
" p *= in_ZoomSettings[0][2];"
" p += vec2(in_ZoomSettings[0][0], -in_ZoomSettings[0][1]);"
" p += vec2(0.5, 0.5);"
" vec2 q = p + 0.5 * in_TexelSize.xy;"
" q -= mod(q, 2.0 * in_TexelSize.xy);"
" q += 0.5 * in_TexelSize.xy;"
" float l = (abs(q.x - 0.5) < 0.5 && abs(q.y - 0.5) < 0.5)"
" ? 1.0 / mylen(q - p) : 0.0;"
" return vec3(q * vec2(1.0, 0.25), l);"
"}"
""
"vec3 nearest1(vec2 p) {"
" vec2 q = p - 0.5 * in_PixelDelta.xy;"
" q -= mod(q, 2.0 * in_PixelDelta.xy);"
" q += 1.5 * in_PixelDelta.xy;"
" return vec3(q * vec2(1.0, 0.25) + vec2(0.0, 0.25),"
" length(q - p));"
" p -= vec2(0.5, 0.5);"
" p *= in_ZoomSettings[1][2];"
" p += vec2(in_ZoomSettings[1][0], -in_ZoomSettings[1][1]);"
" p += vec2(0.5, 0.5);"
" vec2 q = p + -0.5 * in_TexelSize.xy;"
" q -= mod(q, 2.0 * in_TexelSize.xy);"
" q += 1.5 * in_TexelSize.xy;"
" float l = (abs(q.x - 0.5) < 0.5 && abs(q.y - 0.5) < 0.5)"
" ? 1.0 / mylen(q - p) : 0.0;"
" return vec3(q * vec2(1.0, 0.25) + vec2(0.0, 0.25), l);"
"}"
""
"vec3 nearest2(vec2 p) {"
" vec2 q = p + vec2(0.5, -0.5) * in_PixelDelta.xy;"
" q -= mod(q, 2.0 * in_PixelDelta.xy);"
" q += vec2(0.5, 1.5) * in_PixelDelta.xy;"
" return vec3(q * vec2(1.0, 0.25) + vec2(0.0, 0.50),"
" length(q - p));"
" p -= vec2(0.5, 0.5);"
" p *= in_ZoomSettings[2][2];"
" p += vec2(in_ZoomSettings[2][0], -in_ZoomSettings[2][1]);"
" p += vec2(0.5, 0.5);"
" vec2 q = p + vec2(0.5, -0.5) * in_TexelSize.xy;"
" q -= mod(q, 2.0 * in_TexelSize.xy);"
" q += vec2(0.5, 1.5) * in_TexelSize.xy;"
" float l = (abs(q.x - 0.5) < 0.5 && abs(q.y - 0.5) < 0.5)"
" ? 1.0 / mylen(q - p) : 0.0;"
" return vec3(q * vec2(1.0, 0.25) + vec2(0.0, 0.50), l);"
"}"
""
"vec3 nearest3(vec2 p) {"
" vec2 q = p + vec2(-0.5, 0.5) * in_PixelDelta.xy;"
" q -= mod(q, 2.0 * in_PixelDelta.xy);"
" q += vec2(1.5, 0.5) * in_PixelDelta.xy;"
" return vec3(q * vec2(1.0, 0.25) + vec2(0.0, 0.75),"
" length(q - p));"
" p -= vec2(0.5, 0.5);"
" p *= in_ZoomSettings[3][2];"
" p += vec2(in_ZoomSettings[3][0], -in_ZoomSettings[3][1]);"
" p += vec2(0.5, 0.5);"
" vec2 q = p + vec2(-0.5, 0.5) * in_TexelSize.xy;"
" q -= mod(q, 2.0 * in_TexelSize.xy);"
" q += vec2(1.5, 0.5) * in_TexelSize.xy;"
" float l = (abs(q.x - 0.5) < 0.5 && abs(q.y - 0.5) < 0.5)"
" ? 1.0 / mylen(q - p) : 0.0;"
" return vec3(q * vec2(1.0, 0.25) + vec2(0.0, 0.75), l);"
"}"
""
"void main(void) {"
@@ -335,13 +397,22 @@ public:
/* Slightly shift our pixel so that it does not lie at
* an exact texel boundary. This would lead to visual
* artifacts. */
" coord -= 0.1 * in_PixelDelta.xy;"
" coord -= 0.1 * in_TexelSize.xy;"
/* Get a pixel from each slice */
" vec4 p0 = texture2D(in_Texture, nearest0(coord).xy);"
" vec4 p1 = texture2D(in_Texture, nearest1(coord).xy);"
" vec4 p2 = texture2D(in_Texture, nearest2(coord).xy);"
" vec4 p3 = texture2D(in_Texture, nearest3(coord).xy);"
" gl_FragColor = 0.25 * (p0 + p1 + p2 + p3);"
" vec3 k0 = nearest0(coord);"
" vec3 k1 = nearest1(coord);"
" vec3 k2 = nearest2(coord);"
" vec3 k3 = nearest3(coord);"
" vec4 p0 = texture2D(in_Texture, k0.xy);"
" vec4 p1 = texture2D(in_Texture, k1.xy);"
" vec4 p2 = texture2D(in_Texture, k2.xy);"
" vec4 p3 = texture2D(in_Texture, k3.xy);"
//"if (k0.z >= k1.z && k0.z >= k2.z && k0.z >= k3.z) gl_FragColor = p0;"
//"if (k1.z >= k0.z && k1.z >= k2.z && k1.z >= k3.z) gl_FragColor = p1;"
//"if (k2.z >= k0.z && k2.z >= k1.z && k2.z >= k3.z) gl_FragColor = p2;"
//"if (k3.z >= k0.z && k3.z >= k1.z && k3.z >= k2.z) gl_FragColor = p3;"
" gl_FragColor = 1.0 / (k0.z + k1.z + k2.z + k3.z)"
" * (k0.z * p0 + k1.z * p1 + k2.z * p2 + k3.z * p3);"
"}"
#else
"void main(float4 in_Position : POSITION,"
@@ -353,56 +424,57 @@ public:
" out_Position = in_Position;"
"}",

"float3 nearest0(float2 p, float4 in_PixelDelta) {"
" float2 q = p + 0.5 * in_PixelDelta.xy;"
" q -= fmod(q, 2.0 * in_PixelDelta.xy);"
" q += 0.5 * in_PixelDelta.xy;"
"float3 nearest0(float2 p, float4 in_TexelSize) {"
" float2 q = p + 0.5 * in_TexelSize.xy;"
" q -= fmod(q, 2.0 * in_TexelSize.xy);"
" q += 0.5 * in_TexelSize.xy;"
" return float3(q * float2(1.0, 0.25),"
" length(q - p));"
"}"
""
"float3 nearest1(float2 p, float4 in_PixelDelta) {"
" float2 q = p - 0.5 * in_PixelDelta.xy;"
" q -= fmod(q, 2.0 * in_PixelDelta.xy);"
" q += 1.5 * in_PixelDelta.xy;"
"float3 nearest1(float2 p, float4 in_TexelSize) {"
" float2 q = p - 0.5 * in_TexelSize.xy;"
" q -= fmod(q, 2.0 * in_TexelSize.xy);"
" q += 1.5 * in_TexelSize.xy;"
" return float3(q * float2(1.0, 0.25) + float2(0.0, 0.25),"
" length(q - p));"
"}"
""
"float3 nearest2(float2 p, float4 in_PixelDelta) {"
" float2 q = p + float2(0.5, -0.5) * in_PixelDelta.xy;"
" q -= fmod(q, 2.0 * in_PixelDelta.xy);"
" q += float2(0.5, 1.5) * in_PixelDelta.xy;"
"float3 nearest2(float2 p, float4 in_TexelSize) {"
" float2 q = p + float2(0.5, -0.5) * in_TexelSize.xy;"
" q -= fmod(q, 2.0 * in_TexelSize.xy);"
" q += float2(0.5, 1.5) * in_TexelSize.xy;"
" return float3(q * float2(1.0, 0.25) + float2(0.0, 0.50),"
" length(q - p));"
"}"
""
"float3 nearest3(float2 p, float4 in_PixelDelta) {"
" float2 q = p + float2(-0.5, 0.5) * in_PixelDelta.xy;"
" q -= fmod(q, 2.0 * in_PixelDelta.xy);"
" q += float2(1.5, 0.5) * in_PixelDelta.xy;"
"float3 nearest3(float2 p, float4 in_TexelSize) {"
" float2 q = p + float2(-0.5, 0.5) * in_TexelSize.xy;"
" q -= fmod(q, 2.0 * in_TexelSize.xy);"
" q += float2(1.5, 0.5) * in_TexelSize.xy;"
" return float3(q * float2(1.0, 0.25) + float2(0.0, 0.75),"
" length(q - p));"
"}"
""
"void main(float2 in_TexCoord : TEXCOORD0,"
" uniform float4 in_PixelDelta,"
" uniform float4 in_TexelSize,"
" uniform sampler2D in_Texture,"
" out float4 out_FragColor : COLOR)"
"{"
" float2 coord = in_TexCoord.xy;"
" coord -= 0.1 * in_PixelDelta.xy;"
" float4 p0 = tex2D(in_Texture, nearest0(coord, in_PixelDelta).xy);"
" float4 p1 = tex2D(in_Texture, nearest1(coord, in_PixelDelta).xy);"
" float4 p2 = tex2D(in_Texture, nearest2(coord, in_PixelDelta).xy);"
" float4 p3 = tex2D(in_Texture, nearest3(coord, in_PixelDelta).xy);"
" coord -= 0.1 * in_TexelSize.xy;"
" float4 p0 = tex2D(in_Texture, nearest0(coord, in_TexelSize).xy);"
" float4 p1 = tex2D(in_Texture, nearest1(coord, in_TexelSize).xy);"
" float4 p2 = tex2D(in_Texture, nearest2(coord, in_TexelSize).xy);"
" float4 p3 = tex2D(in_Texture, nearest3(coord, in_TexelSize).xy);"
" out_FragColor = 0.25 * (p0 + p1 + p2 + p3);"
"}"
#endif
);
m_vertexattrib = m_shader->GetAttribLocation("in_Vertex");
m_texattrib = m_shader->GetAttribLocation("in_TexCoord");
m_pixeluni = m_shader->GetUniformLocation("in_PixelDelta");
m_texeluni = m_shader->GetUniformLocation("in_TexelSize");
m_zoomuni = m_shader->GetUniformLocation("in_ZoomSettings");
m_ready = true;

#if !defined __CELLOS_LV2__ && !defined __ANDROID__
@@ -445,7 +517,8 @@ public:
}

m_shader->Bind();
m_shader->SetUniform(m_pixeluni, m_pixel_delta);
m_shader->SetUniform(m_texeluni, m_texel_settings);
m_shader->SetUniform(m_zoomuni, m_zoom_settings);
#if !defined __CELLOS_LV2__ && !defined __ANDROID__
glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
glEnableVertexAttribArray(m_vertexattrib);
@@ -486,6 +559,8 @@ private:
static int const PALETTE_STEP = 32;

ivec2 m_size, m_window_size;
double m_window2world;
f64vec2 m_texel2world;
u8vec4 *m_pixels, *m_tmppixels, *m_palette;
Shader *m_shader;
GLuint m_texid;
@@ -493,13 +568,14 @@ private:
GLuint m_vbo, m_tbo;
GLuint m_tco;
#endif
int m_vertexattrib, m_texattrib, m_pixeluni;
int m_vertexattrib, m_texattrib, m_texeluni, m_zoomuni;
int m_frame, m_dirty[4];
bool m_ready;

f64cmplx m_center;
double m_radius, m_texture_radius, m_window_radius;
vec4 m_pixel_delta;
double m_radius;
vec4 m_texel_settings;
mat4 m_zoom_settings;
f64cmplx m_deltashift[4];
double m_deltascale[4];



Loading…
Cancel
Save