- Understand how FUSEE deals with multiple platforms (desktop, web, Android).
- Understand the basic setup of a FUSEE application.
- Init contains initialization and startup code.
- RenderAFrame is called repeatedly for each frame to draw.
- Understand rendering pipeline basics.
- Send simple geometry through the rendering pipeline.
- Render a triangle.
-
Download and run the FUSEE project as described in Getting Started.
-
Open Fusee.Tutorial01.sln in Visual Studio (e.g. by double-clicking on the file).
-
The solution contains four projects
- Core - contains the main functionality of the application (- the "business logic")
- Desktop - contains an Application for the (Windows) desktop loading and executing the Core functionality.
- Web - contains a build process creating a JavaScript cross-compiled version of the Core functionality and generating an HTML page to load and execute that functionality.
- Android - contains a Xamarin project creating an Android APK loading and executing the Core functionality.
-
Set the Desktop project as the Startup project.
-
Right-click on the Desktop project and hit "Build".
-
If the build was successful, run the application.
-
You should see an empty application window with a greenish background.
-
If you want, also try to build the other platform Applications.
-
In the Core project, open the source code file "Tutorial.cs". This file contains the application logic (not too much at the moment...).
-
The file contains one class,
Tutorial
consisting of three methods:-
Init
- Called on application startup. You can place initizalization code here. Currently,Init
just sets the clear colorpublic override void Init() { RC.ClearColor = new float4(0.5f, 1, 0.7f, 1); }
-
RenderAFrame
- Called to generate image contents (once per frame). You can place drawing and interaction code here. Currently,RenderAFrame
just clears the background of the backbuffer (RC.Clear
) and copies the backbuffer contents to the front bufferPresent
. -
Resize
- Called when the render window is resized (or initialized). We will look at this method later.
-
-
Try to change the color of the render window background by altering the first three components of the
float4
value assigned toRC.ClearColor
. These values are red, green and blue intensities in the range from 0 to 1.
Before we can draw some geometry, we need a simple understanding how the graphics card works. You can imgine the graphics card (or the GPU) as a pipeline. On the one end, you put in (3D-) Geometry and on the other end a rendered two-dimensional pixel image drops out. The conversion from the vector geometry into pixel images is done in two major steps:
-
The coordinates of the geometry's vertices are converted into screen coordinates.
-
The vector geometry (in screen coordinates) is "rasterized" - that is, each pixel in the output buffer covered by geometry is filled with a certain color.
You can control both steps by placing small programs on the graphics card's processer (the GPU). These programs are called "Shaders". A program performing the coordinate transformation from whatever source-coordinate system to screen coordinates is called "Vertex Shader". A program performing the color calculation of each pixel to fill is called "Pixel Shader". In FUSEE you need to provide a Pixel and a Vertex Shader if you want to render geometry. The programming language for shaders used in FUSEE is GLSL, the shader language supported by OpenGL.
Now let's add a very simple pair of a Vertex- and a Pixel-Shader.
-
Add two fields to the
Tutorial
class containing strings with the respective Shader code:private const string _vertexShader = @" attribute vec3 fuVertex; void main() { gl_Position = vec4(fuVertex, 1.0); }"; private const string _pixelShader = @" #ifdef GL_ES precision highp float; #endif void main() { gl_FragColor = vec4(1, 0, 1, 1); }";
Note that the program code (containting a
main
method each) is contained in strings. Thus, the C# compiler will not recognize its contents as program code. -
Add code to the
Init
method to compile the shader code for the GPU and set it as the currently active shader on the render context (RC
).var shader = RC.CreateShader(_vertexShader, _pixelShader); RC.SetShader(shader);
Note how the pixel shader does nothing but copy the incoming vertex (fuVertex
) to the resulting vertex (gl_Position
) while adding a fourth dimension to it (constantly set to 1.0). The pixel shader fills each pixel it is called for (gl_FragColor
) with a constant color (magenta - full red, no green, full blue).
-
At the
Tutorial
class level, create a privateMesh
field.private Mesh _mesh;
-
Inside the
Init
method, initialize the mesh with a a single triangle._mesh = new Mesh { Vertices = new[] { new float3(-0.75f, -0.75f, 0), new float3(0.75f, -0.75f, 0), new float3(0, 0.75f, 0), }, Triangles = new ushort[] {0, 1, 2}, };
-
In the
RenderAFrame
method, draw the mesh after the backbuffer was cleared, but before the backbuffer contents isPresent
ed.RC.Clear(ClearFlags.Color | ClearFlags.Depth); RC.Render(_mesh); Present();
-
Compile and run the program for your favorite platform. A magenta colored triangle should fill the green background.
-
Visit the result as web application (Ctrl-Click or Long-Press to open in new tab).
-
See Tutorial.cs in the Tutorial01 Completed folder for the overall state so far.
Investigate how the vertice's coordinates relate to pixel positions within the output window.
-
What are the smallest and largest x- and y-values for vertices that can be displayed within the output window?
-
What happens to your geometry if you re-size the output window?
-
What happens if you change the z-values of your vertices (currently set to 0)?
Understand how the Triangles
are indices into the Vertices
array.
-
Add another vertex to the
Vertices
array. -
Add another triangle (three more indices) to the
Triangles
array to display a rectangle using four entries inVertices
and six entries inTriangles
. -
What happens if you change the order of the indices in the
Triangles
array? Try to explain your observation.
Understand the concept of "the current Shader".
-
Add one more geometry
Mesh
and another pixel shader string (setting a different color). -
Compile another shader using the new pixel shader (and the exisiting vertex shader). Store both shaders (resulting from the two
RC.CreateShader
calls) in fields rather than in local variables. -
Within
RenderAFrame
render each of the two meshes with a different shader (callRC.SetShader
beforeRC.Render
).