This tutorial shows how to render a rectangle with a newton fractal shader covering the whole screen. If you are not familiar with the basics of OGLplus, it is recommended that you read the RGB triangle first for a more detailed introduction. You can also see the Basic usage with GLUT and GLEW for a complete standalone example including the code responsible for window initialization, OpenGL initialization and event handling.
The new things that this tutorial covers are:
For a full working code see the
oglplus/004_newton.cpp file in the example directory.
First we include a helper header that in turn includes
GL3/gl3.h or makes sure that the OpenGL symbols are defined.
The next line includes everything from OGLplus, except the image loaders and generators and geometric shape data generators.
Now we include the header declaring the base class for OGLplus examples.
Just like the other examples that come with the library this one is also implemented inside of the
Doing this however on the global scope may cause name clashes with other libraries especially in larger applications, so it may be a better idea to use fully qualified names or apply the
using directive only in local scopes.
As before, the example code is encapsulated in the
RectangleExample class which is derived from
Most of the member variables are the same as in the previous tutorials. There is an instance of the
oglplus::Context class wrapping the current context functions, a
oglplus::Program which define the custom functionality of the rendering pipeline and a
oglplus::VertexArray object managing the vertex data for the rendered rectangle.
In this example we'll need just one
Buffer object - to store the rectangle vertex positions.
The public interface consists of a constructor and a couple of member functions.
The constructor takes no arguments and again sets the vertex shader source, which is same as in the previous tutorials. It just passes the vertex positions down the pipeline without any significant transformations.
The vertex shader is complete and can be compiled.
The fragment shader is similar to the one from the previous tutorials. It has one input variable - the vertex coordinate that will be used in the newton fractal computations.
This time there are two uniform variables, that specify the colors of the gradient used for the colorization of the fractal.
As before, the output of the shader is a vec4 representing the color of the fragment.
We are going to visualise the newton fractal for the f(x) = x^3 - 1 polynomial. The next function computes the value of this polynomial at the specified coordinate on the complex plane (we use a vec2 as a complex number, for both the argument and the result).
We'll also need the values of the derivative of this polynomial (which is f'(x) = 3x^2) at specified complex coordinates which is what the
df function computes.
And we'll need to divide two comples numbers - hence the
The Newton or (Newton-Raphson) method was originaly devised for the finding of successivelly better approximations to the roots of real valued functions. The iterative nature of the algorithm can also be used for fractal image rendering. There are various methods for the colorization of the final image but the basic algorithm is usually the same:
Zto every pixel on a 2d surface
zcalculate a sequence of numbers such that
This example uses the number of iterations used to reach a good enough approximation to create a color gradient between the
Color2 values specified as uniform variables above.
We can now compile the fragment shader source code, attach both shaders to the shading program and try to link and use it.
Now we can start to specify the data for the individual vertex attributes of the rectangle we're going to render. The first step as before is to bind the vertex array object managing the vertex data.
Again since this is a simple example the coordinates are hardcoded.
We bind the VBO for vertex positions to the
The data are uploaded from client's memory to the server's memory by using the
Data static function.
Then we use a oglplus::VertexArrayAttrib object referencing the
Position input variable in the
prog program, to tell OpenGL about the structure of the data in the currently bound VBO and to enable this vertex attribute.
That's it for the vertex data specification. Now we specify the values of the two uniform variables referenced by the fragment shader. The uniforms were both declared as
vec3 in the shader code (i.e. a vector of three float values). The classes that OGLplus provides for manipulation of shader input variables are templated and allow to set only values of the specified type. This provides additional compile-time type checking and allows to avoid type mismatches between the C++ program and the GLSL shader code which result in runtime errors. There are several ways how to set the value of a uniform variable. The
Uniform specializations for simple types like
uint, etc. (besides setting values of the specified scalar types) also allow to set the values of vectors and arrays of the same type. There are also specializations for the
Matrix classes that can be used to specify only the values of vectors, matrices and arrays thereof and they are more generic. This tutorial uses a specialization of
GLfloat and its
SetVector member function to set the value of the 3D float vectors that store the color values.
As the last step of initialization we disable depth testing since we do not need it in this example:
Reshape function get called when the window is created and everytime the window's size changes. Here we tell the GL that the rendering viewport has changed.
This function redraws our scene and is basically the same as in the previous tutorials; (we don't clear any buffers here since it is not necessary) so we just tell the GL to draw a rectangle from the vertex data stored in the buffer objects tied to the currently bound VAO, which is still
rectangle, because we didn't bind any other VAO since initialization. More preciselly we draw the rectangle composed from 4 vertices starting at index 0 in the buffers.
RectangleExample class is complete:
The last thing in this example's source is the
makeExample function. This function is called by shared internal piece of code (that comes together with the examples) which does the initialization and event processing common to all the examples.
makeExample creates an instance of our
RectangleExample class. The common code then calls the event handler functions like
Render when appropriate. To see how this can be incorporated into a complete application see the standalone examples and the related tutorials.