Showing posts with label Source code. Show all posts
Showing posts with label Source code. Show all posts

Sunday, 18 August 2013

The Demo #1 - Light Shafts

    Hi!

    Finally I'm ready to share my very first example of Graphics Framework usage. It is also the first demo I have created with my library - The Light Shafts!



    I wanted to create my own light shafts effect for a very long time. There are a few different techniques allowing to achieve this effect (as with any other graphical effect that can be seen in games) of which most commonly used involves placing static, blended geometry. But this technique requires geometry to prepared for a given scene. And is, of course, static. My favourite light shaft technique is the one simulating light glowing through clouds (sometimes called god rays) or through trees in misty air - in both rays change depending on the observer's point of view.
    I've been wondering how to create this effect for some time and finally I've encountered some great examples and explanations, like the one in GPU Gems 3 or in this WebGL blog. So I wanted to create similar effect with Graphics Framework. When I started working on the demo, development of my library was in early stage. I didn't have any 3D models, which I could use to create nice environment, and I didn't have 3D model loading classes (I still don't have any of those ;-)) so I had to simplify the demo a little. I thought I would create a 2D demo with Moon rising from behind the trees in the dark night. Technique I used in the demo is exactly the same as in many other games and examples: it is created as a postprocess phase, so the effect itself is really 2D. There can be many variations of shaders involved in the rendering process, of course, like blurring algorithms used but that's just tweaking the final impression.
    So here it is: source code of my very first working example created with Graphics Framework.

    Global variables:
cMesh           wood_mesh;
cEffect         wood_effect;
cTexture        wood_texture;

cMesh           moon_mesh;
cEffect         moon_effect;
cTexture        moon_texture;

cTexture        shaft_texture1;
cTexture        shaft_texture2;
cRenderTarget   shaft_rt1;
cRenderTarget   shaft_rt2;
cEffect         shaft_effect;

cTexture        scene_texture;
cRenderTarget   scene_rt;
cEffect         scene_effect;

cFullscreenQuad quad;

float           x = 0.0f;
float           y = 0.0f;

cWindow& window = cWindow::GetInstance();

    WinMain(...) function:
window.Create( "Light Shafts",
               eGraphicsDeviceType::OPENGL_3,
               eWindowMode::FULLSCREEN,
               false );

///// Wood
size_t wood_indices_data[] = { 0, 1, 2, 3 };
float wood_vertices_data[] = { -1.0f,  0.5f, 0.0f,
                               -1.0f, -1.0f, 0.0f,
                                1.0f,  0.5f, 0.0f,
                                1.0f, -1.0f, 0.0f };
float wood_texcoords0_data[] = { 0.0f, 1.0f,
                                 0.0f, 0.0f,
                                 1.0f, 1.0f,
                                 1.0f, 0.0f };

cVertexAttribute wood_positions( eVertexAttributeType::POSITION,
                                 3, 4, wood_vertices_data );
cVertexAttribute wood_texcoord0( eVertexAttributeType::TEXCOORD0,
                                 2, 4, wood_texcoords0_data );
cVertexIndices wood_indices( ePrimitiveTopologyType::TRIANGLE_STRIP,
                             4, wood_indices_data );
wood_mesh = cMesh( &wood_indices, { &wood_positions, &wood_texcoord0 } );
if( !wood_mesh.Init() )
    return 0;

cShader wood_vertex_shader( eShaderType::VERTEX_SHADER, "Data/wood.vp" );
cShader wood_fragment_shader( eShaderType::FRAGMENT_SHADER, "Data/wood.fp" );
wood_effect = cEffect( &wood_vertex_shader, &wood_fragment_shader );
if( !wood_effect.Init() )
    return 0;

cImage wood_image( "Data/wood.png" );
cSampler wood_sampler( eTextureWrapMode::CLAMP, eTextureFilteringMode::LINEAR );
cTextureImage wood_texture_image( eTextureType::IMAGE_2D, {&wood_image} );
wood_texture = cTexture( &wood_texture_image, &wood_sampler );
if( !wood_texture.Init() )
    return 0;

wood_effect.SetParameter( "uTexture", wood_texture );

///// Moon
size_t moon_indices_data[] = { 0, 1, 2, 3 };
float moon_vertices_data[] = { -100.0f,  100.0f, 1.0f,
                               -100.0f, -100.0f, 1.0f,
                                100.0f,  100.0f, 1.0f,
                                100.0f, -100.0f, 1.0f };
float moon_texcoords0_data[] = { 0.0f, 1.0f,
                                 0.0f, 0.0f,
                                 1.0f, 1.0f,
                                 1.0f, 0.0f };

cVertexAttribute moon_positions( eVertexAttributeType::POSITION,
                                 3, 4, moon_vertices_data );
cVertexAttribute moon_texcoord0( eVertexAttributeType::TEXCOORD0,
                                 2, 4, moon_texcoords0_data );
cVertexIndices moon_indices( ePrimitiveTopologyType::TRIANGLE_STRIP,
                             4, moon_indices_data );
moon_mesh = cMesh( &moon_indices, { &moon_positions, &moon_texcoord0 } );
if( !moon_mesh.Init() )
    return 0;

cShader moon_vertex_shader( eShaderType::VERTEX_SHADER, "Data/moon.vp" );
cShader moon_fragment_shader( eShaderType::FRAGMENT_SHADER, "Data/moon.fp" );
moon_effect = cEffect( &moon_vertex_shader, &moon_fragment_shader );
if( !moon_effect.Init() )
    return 0;

cImage moon_image( "Data/moon.png" );
cSampler moon_sampler( eTextureWrapMode::CLAMP, eTextureFilteringMode::LINEAR );
cTextureImage moon_texture_image( eTextureType::IMAGE_2D, { &moon_image } );
moon_texture = cTexture( &moon_texture_image, &moon_sampler );
if( !moon_texture.Init() )
    return 0;

///// Render Targets
cTextureImage shaft_texture_image1( eTextureType::IMAGE_2D, eImageFormat::COLORS_4, eImageChannelDataType::BYTE );
shaft_texture1 = cTexture( &shaft_texture_image1, &wood_sampler );
shaft_rt1 = cRenderTarget( { &shaft_texture1 } );
if( !shaft_rt1.Init() )
    return 0;

cTextureImage shaft_texture_image2( eTextureType::IMAGE_2D, eImageFormat::COLORS_4, eImageChannelDataType::BYTE );
shaft_texture2 = cTexture( &shaft_texture_image2, &wood_sampler );
shaft_rt2 = cRenderTarget( { &shaft_texture2 } );
if( !shaft_rt2.Init() )
    return 0;

cTextureImage scene_texture_image( eTextureType::IMAGE_2D, eImageFormat::COLORS_4, eImageChannelDataType::BYTE );
scene_texture = cTexture( &scene_texture_image, &wood_sampler );
scene_rt = cRenderTarget( { &scene_texture } );
if( !scene_rt.Init() )
    return 0;

// Effects
cShader scene_vs( eShaderType::VERTEX_SHADER, "Data/quad.vp" );
cShader scene_fs( eShaderType::FRAGMENT_SHADER, "Data/quad.fp" );
scene_effect = cEffect( &scene_vs, &scene_fs );
if( !scene_effect.Init() )
    return 0;

cShader shaft_vs( eShaderType::VERTEX_SHADER, "Data/shaft.vp" );
cShader shaft_fs( eShaderType::FRAGMENT_SHADER, "Data/shaft.fp" );
shaft_effect = cEffect( &shaft_vs, &shaft_fs );
if( !shaft_effect.Init() )
    return 0;

if( !quad.Init() )
    return 0;

moon_effect.SetParameter( "uTexture", moon_texture );
scene_effect.SetParameter( "uScene", scene_texture );
shaft_effect.SetParameter( "uShafts", shaft_texture1 );
scene_effect.SetParameter( "uShafts", shaft_texture2 );

window.Run( Draw );
window.Destroy();

return 0;

    Drawing function:
void Draw( iGraphicsDevice* i_GraphicsDevice,
           iInputDevice*    i_InputDevice,
           iTimer*          i_Timer ) {

const sKeyboardInputState* keys = i_InputDevice->GetKeyboardState();
const sMouseInputState* mouse = i_InputDevice->GetMouseState();

if( keys->UP.IsDown() ) {
    y += 5.0f * i_Timer->GetDeltaTime();
}
if( keys->DOWN.IsDown() ) {
    y -= 5.0f * i_Timer->GetDeltaTime();
}
if( keys->LEFT.IsDown() ) {
    x -= 5.0f * i_Timer->GetDeltaTime();
}
if( keys->RIGHT.IsDown() ) {
    x += 5.0f * i_Timer->GetDeltaTime();
}
x += mouse->Position.Delta.X*0.1f;
y += mouse->Position.Delta.Y*0.1f;

i_GraphicsDevice->StartScene();

i_GraphicsDevice->SetBackgroundColor( 0.05f, 0.05f, 0.1f, 1.0f );
scene_rt.Use();
scene_rt.Clear();

moon_effect.Use();
moon_effect.SetParameter( "MVP",
                          i_GraphicsDevice->CreateOrthographicProjectionMatrix( 0.0f, (float)window.GetWidth(),
                                                                                0.0f, (float)window.GetHeight(),
                                                                                1.0f, -1.0f ) *
                          i_GraphicsDevice->CreateTranslationMatrix( (float)mouse->Position.Window.X,
                                                                     (float)mouse->Position.Window.Y,
                                                                     0.0f ) );
moon_mesh.Draw();
wood_effect.Use();
wood_mesh.Draw();

shaft_rt1.Use();
i_GraphicsDevice->SetBackgroundColor( 0.0f, 0.0f, 0.0f, 1.0f );
shaft_rt1.Clear();

moon_effect.Use();
moon_mesh.Draw();
wood_effect.Use();
wood_mesh.Draw();

shaft_rt2.Use();
shaft_effect.Use();

for( size_t i = 0; i<5; ++i ) {
    cVector4 vec = cVector4( (float)Position.Window.X / (float)window.GetWidth(),
                             (float)mouse->Position.Window.Y / (float)window.GetHeight(),
                             0.0f,
                             1.0f );
    shaft_effect.SetParameter( "uPosition", vec );
    quad.Draw();
}
if( const cRenderTarget* rt = cRenderTarget::GetActiveResource() ) {
    rt->StopUsing();
}

scene_effect.Use();
scene_effect.SetParameter( "uScene", scene_texture );
quad.Draw();

i_GraphicsDevice->EndScene();
}

    Shaders (written in GLSL):

    Wood.vp, Quad.vp, Shaft.vp:
#version 150
precision highp float;

in vec4 inPosition;
in vec2 inTexcoord0;

out vec2 Texcoord0;

void main() {
    gl_Position = inPosition;
    Texcoord0 = inTexcoord0;
}

    Wood.fp:
#version 150
precision highp float;

in vec2 Texcoord0;
out vec4 oColor;

uniform sampler2D uTexture;

void main() {
    oColor = texture( uTexture, Texcoord0 ).rrra;
    if( oColor.a < 0.5 ) {
        discard;
    }
}

    Moon.vp:
#version 150
precision highp float;

uniform mat4 MVP;

in vec4 inPosition;
in vec2 inTexcoord0;

out vec2 Texcoord0;

void main() {
    gl_Position = MVP * inPosition;
    Texcoord0 = inTexcoord0;
}

    Moon.fp:
#version 150
precision highp float;

in vec2 Texcoord0;
out vec4 oColor;

uniform sampler2D uTexture;

void main() {
    oColor = texture( uTexture, Texcoord0 );
    if( oColor.a < 0.1 ) {
        discard;
    }
}

    Quad.fp:
#version 150
precision highp float;

in vec2 Texcoord0;
out vec4 oColor;

uniform sampler2D uScene;
uniform sampler2D uShafts;

void main() {
    oColor = texture( uScene, Texcoord0 );
    oColor += texture( uShafts, Texcoord0 ) * 0.5;
}

    Shaft.fp:
#version 150
precision highp float;
 
in vec2 Texcoord0;
out vec4 oColor;
 
uniform sampler2D uShafts;
uniform vec2 uPosition;
const float scale = 1.5;
 
void main() {
    vec2 offset = (uPosition - Texcoord0) * 0.3;
 
    oColor = texture( uShafts, Texcoord0 ) * 0.8;
    vec4 color;
    float sum_weights = 0.8;
    float weight;
    for( int i=1; i<8; ++i ) {
        weight = (8-i)*0.1;
        sum_weights += weight;
        color = texture( uShafts, Texcoord0 + offset*i*0.1*scale ) * weight;
        oColor += color;
    }
    oColor /= sum_weights;
}


    That's all - about 200 lines of code (including #includes ;-)) and a few shader files. This example has some flaws (i.e. Moon is always the same size in pixels - so if the resolution of the application window is changed, the size of the Moon will also change; and wood always covers the same portion of screen so its proportions will also change) but I wanted the source code to be as simple as possible.
    I know it is hard to analyse someone else's code, especially when one just reads it on the internet without the access to original project. That's why in the future I will share my library along with header files so anyone will be able to try it and give his or her feedback.
     [Update] I have found a better way to post source code examples... ;-) [Update]

Wednesday, 24 July 2013

Graphics Framework - The Beginning

   Hello.

    My name is Paul (or PaweÅ‚ in my language) and I thought I would write a blog about 3D graphics and a small project I'm developing. For a very long time now I was interested in 3D graphics. During my studies I learned OpenGL through nehe.gamedev.net and that's how one of my biggest "loves" started. I worked on different projects connected with 3D realtime graphics - both in my jobs and at home - and in the 2012 I thought I would create a library that would allow me to quickly create simple tech demos, interactive logos or other, small 3D applications. In the mid 2012 I started developing it. I didn't have any name for it so I called it just "Graphics Framework".
    Now, after over a year of development, I thought I could share it with the rest of the world. I don't have many examples of how to use my library but that's why I wanted to start a blog. If there will be someone who would like to read and see more of it, maybe I will have motivation to create new apps and improve my library.
    Something more about my creation - Graphics Framework is a C++ library designed to ease development of small, realtime 3D applications like interactive logos or tech demos. It wraps graphics api calls into set of simple to use classes (i.e. shader, render target, vertex attribute, texture) that are independent of the graphics api. Currently it only supports OpenGL 3.3 core profile (not whole of course). In the future it will support other versions of OpenGL (with some extensions) and, hopefully, different versions of Direct3D. Library also contains additional class, which allows to easily create and display application window, and a set of interfaces that help track size of a application window, begin and end of a frame rendering, mouse and keyboard input and passage of time.
    So it's not only a 3D api wrapper but it also includes other classes that help create small 3D apps in a relatively short time. And that's why I didn't call it "Graphics Library" but "Graphics Framework". Besides, there are many different graphics libraries out there so I didn't want to create one more.
One more thing - right now library is developed for Windows only. But most of the classes are system independent so it should be possible to port it to some other OS.
    Here is some example of a usage:
cWindow& window = cWindow::GetInstance();
if( !window.Create( "Shaiddir - Graphics Test",
                    eGraphicsDeviceType::OPENGL_3,
                    eWindowMode::WINDOWED,
                    eAntialiasingMode::OFF,
                    nullptr,
                    nullptr ) ) {
    return 0;
}

cImage image( "image.jpg" );
cTextureImage texture_image( eTextureType::IMAGE_2D, { &image } );
cSampler sampler( eTextureWrapMode::MIRROR, eTextureFilteringMode::LINEAR );
cTexture texture( &texture_image, &sampler );

cShader vs( eShaderType::VERTEX_SHADER, "shader.vp" );
cShader fs( eShaderType::FRAGMENT_SHADER, "shader.fp" );
cEffect effect( &vs, &fs );
if( !effect.Init() ) {
    window.Destroy();
    return 0;
}
effect.SetParameter( "uImage", texture );

cFullscreenQuad quad;
quad.Init();

window.Run( Draw );
And a drawing function could look like this:
void Draw( iGraphicsDevice* i_GraphicsDevice, iInputDevice* i_InputDevice, iTimer* i_Timer ) {
    i_GraphicsDevice->StartScene();
    effect.Use();
    quad.Draw();
    i_GraphicsDevice->EndScene();
}
And with this code image should be drawn covering whole application window (but I didn't test it ;-)). Of course You would need proper vertex and fragment shaders. But I hope this gives You an overview of an approach I have taken when I started creating this library.
    That's all for now. I hope I'll have time to write something new soon.
    Best regards.