From 7f096b61e4b38c6b2a870c18181860b3261325de Mon Sep 17 00:00:00 2001 From: Sam Hocevar Date: Sat, 19 Nov 2011 23:34:25 +0000 Subject: [PATCH] tutorial: look into previous frames for data when zooming in the Mandelbrot viewer using only the shader. --- test/tutorial/tut03.cpp | 228 ++++++++++++++++++++++++++-------------- 1 file changed, 152 insertions(+), 76 deletions(-) diff --git a/test/tutorial/tut03.cpp b/test/tutorial/tut03.cpp index d8d4dcc2..d17f7092 100644 --- a/test/tutorial/tut03.cpp +++ b/test/tutorial/tut03.cpp @@ -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];