3D C/C++ tutorials - OpenGL 2.1 - God rays
3D C/C++ tutorials -> OpenGL 2.1 -> God rays
Use for personal or educational purposes only. Commercial and other profit uses strictly prohibited. Exploitation of content on a website or in a publication prohibited.
To compile and run these tutorials some or all of these libraries are required: FreeImage 3.16.0, GLEW 1.11.0, GLUT 3.7.6 / GLUT for Dev-C++, GLM 0.9.5.4
opengl_21_tutorials_win32_framework.h
...

class COpenGLRenderer
{
protected:
    ...

protected:
    CShaderProgram PerPixelLighting, GodRays;
    GLuint VBO, VerticesCount, ColorBuffer, DepthBuffer, FBO;

public:
    bool Blur, WireFrame;

public:
    ...
};

...
opengl_21_tutorials_win32_framework.cpp
...

COpenGLRenderer::COpenGLRenderer()
{
    Blur = true;
    WireFrame = false;

    Camera.SetViewMatrixPointer(&ViewMatrix);
}

COpenGLRenderer::~COpenGLRenderer()
{
}

bool COpenGLRenderer::Init()
{
    // ------------------------------------------------------------------------------------------------------------------------

    bool Error = false;

    // check OpenGL extensions ------------------------------------------------------------------------------------------------

    if(!GLEW_ARB_texture_non_power_of_two)
    {
        ErrorLog.Append("GL_ARB_texture_non_power_of_two not supported!\r\n");
        Error = true;
    }

    if(!GLEW_ARB_depth_texture)
    {
        ErrorLog.Append("GL_ARB_depth_texture not supported!\r\n");
        Error = true;
    }

    if(!GLEW_EXT_framebuffer_object)
    {
        ErrorLog.Append("GL_EXT_framebuffer_object not supported!\r\n");
        Error = true;
    }

    // load textures and shaders ----------------------------------------------------------------------------------------------

    Error |= !PerPixelLighting.Load("per_pixel_lighting.vs", "per_pixel_lighting.fs");
    Error |= !GodRays.Load("god_rays.vs", "god_rays.fs");

    // if an error occurred, return false -------------------------------------------------------------------------------------

    if(Error)
    {
        return false;
    }

    // init vertex arrays -----------------------------------------------------------------------------------------------------

    CBuffer Buffer;

    int twists = 5, ures = 64, vres = 32;
    float radius = 5.0f, thickness = 5.0f, length = 45.0f;

    float uinc = 360.0f / ures, vinc = 360.0f / vres, zinc = length / twists / ures;

    vec3 z = vec3(0.0f, 0.0f, 1.0f);
    vec3 a = vec3(radius, 0.0f, 0.0f), b = rotate(a, uinc, z), c = rotate(b, uinc, z);
    vec3 x1 = vec3(thickness / 2.0f, 0.0f, 0.0f), x2 = rotate(x1, uinc, z);

    float az = -length / 2.0f, bz = az + zinc, cz = bz + zinc;

    vec3 Normal, Vertex;

    for(float u = 0.0f; u < 360.0f * twists; u += 360.0f / ures)
    {
        vec3 m1 = vec3(a.x, a.y, az);
        vec3 m2 = vec3(b.x, b.y, bz);
        vec3 m3 = vec3(c.x, c.y, cz);

        vec3 y1 = normalize(m2 - m1);
        vec3 y2 = normalize(m3 - m2);

        for(float v = 0.0f; v < 360.0f; v += 360.0f / vres)
        {
            vec3 x1a = x1, x1b = rotate(x1, vinc, y1), x2a = x2, x2b = rotate(x2, vinc, y2);

            Normal = normalize(x1a); Buffer.AddData(&Normal, 12); Vertex = m1 + x1a; Buffer.AddData(&Vertex, 12);
            Normal = normalize(x1b); Buffer.AddData(&Normal, 12); Vertex = m1 + x1b; Buffer.AddData(&Vertex, 12);
            Normal = normalize(x2b); Buffer.AddData(&Normal, 12); Vertex = m2 + x2b; Buffer.AddData(&Vertex, 12);
            Normal = normalize(x2a); Buffer.AddData(&Normal, 12); Vertex = m2 + x2a; Buffer.AddData(&Vertex, 12);

            x1 = x1b; x2 = x2b;
        }

        x1 = rotate(x1, uinc, z);
        x2 = rotate(x2, uinc, z);

        a = b; az = bz;
        b = c; bz = cz;
        c = rotate(c, uinc, z); cz += zinc;
    }

    glGenBuffers(1, &VBO);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glBufferData(GL_ARRAY_BUFFER, Buffer.GetDataSize(), Buffer.GetData(), GL_STATIC_DRAW);
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    VerticesCount = Buffer.GetDataSize() / 24;

    Buffer.Empty();

    // generate textures ------------------------------------------------------------------------------------------------------

    glGenTextures(1, &ColorBuffer);
    glGenTextures(1, &DepthBuffer);

    // generate FBO -----------------------------------------------------------------------------------------------------------

    glGenFramebuffersEXT(1, &FBO);

    // set camera -------------------------------------------------------------------------------------------------------------

    Camera.Look(vec3(90.0f, 0.0f, 0.0f), vec3(0.0f, 0.0f, 0.0f), true);

    // ------------------------------------------------------------------------------------------------------------------------

    return true;
}

void COpenGLRenderer::Render(float FrameTime)
{
    // bind FBO ---------------------------------------------------------------------------------------------------------------

    if(Blur)
    {
        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, FBO);
        glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, ColorBuffer, 0);
        glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, DepthBuffer, 0);
    }

    // render scene -----------------------------------------------------------------------------------------------------------

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

    glMatrixMode(GL_MODELVIEW);
    glLoadMatrixf(&ViewMatrix);

    glEnable(GL_DEPTH_TEST);

    if(WireFrame)
    {
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
    }

    glUseProgram(PerPixelLighting);

    glBindBuffer(GL_ARRAY_BUFFER, VBO);

    glEnableClientState(GL_NORMAL_ARRAY);
    glNormalPointer(GL_FLOAT, 24, (void*)0);

    glEnableClientState(GL_VERTEX_ARRAY);
    glVertexPointer(3, GL_FLOAT, 24, (void*)12);

    glColor3f(0.4f, 0.2f, 0.8f);
    glDrawArrays(GL_QUADS, 0, VerticesCount);
    
    glDisableClientState(GL_VERTEX_ARRAY);
    glDisableClientState(GL_NORMAL_ARRAY);
    
    glBindBuffer(GL_ARRAY_BUFFER, 0);

    glUseProgram(0);

    if(WireFrame)
    {
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
    }
    
    glDisable(GL_DEPTH_TEST);

    // blur ColorBuffer texture -----------------------------------------------------------------------------------------------

    if(Blur)
    {
        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

        glBindTexture(GL_TEXTURE_2D, ColorBuffer);

        glUseProgram(GodRays);

        glBegin(GL_QUADS);
            glVertex2f(0.0f, 0.0f);
            glVertex2f(1.0f, 0.0f);
            glVertex2f(1.0f, 1.0f);
            glVertex2f(0.0f, 1.0f);
        glEnd();

        glUseProgram(0);

        glBindTexture(GL_TEXTURE_2D, 0);
    }
}

void COpenGLRenderer::Resize(int Width, int Height)
{
    this->Width = Width;
    this->Height = Height;

    glViewport(0, 0, Width, Height);

    ProjectionMatrix = perspective(45.0f, (float)Width / (float)Height, 0.125f, 512.0f);

    glMatrixMode(GL_PROJECTION);
    glLoadMatrixf(&ProjectionMatrix);

    glBindTexture(GL_TEXTURE_2D, ColorBuffer);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, Width, Height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);

    glBindTexture(GL_TEXTURE_2D, DepthBuffer);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH_COMPONENT24, Width, Height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);
}

void COpenGLRenderer::Destroy()
{
    PerPixelLighting.Destroy();
    GodRays.Destroy();

    glDeleteBuffers(1, &VBO);

    glDeleteTextures(1, &ColorBuffer);
    glDeleteTextures(1, &DepthBuffer);

    if(GLEW_EXT_framebuffer_object)
    {
        glDeleteFramebuffersEXT(1, &FBO);
    }
}

...

void COpenGLView::OnKeyDown(UINT Key)
{
    switch(Key)
    {
        case VK_F1:
            OpenGLRenderer.Blur = !OpenGLRenderer.Blur;
            break;

        case VK_F2:
            OpenGLRenderer.WireFrame = !OpenGLRenderer.WireFrame;
            break;
    }
}

...
per_pixel_lighting.vs
#version 120

varying vec3 LD, Normal, LDR;

void main()
{
    LD = -(gl_ModelViewMatrix * gl_Vertex).xyz;
    Normal = gl_NormalMatrix * gl_Normal;
    LDR = reflect(-LD, Normal);
    gl_FrontColor = gl_Color;
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
}
per_pixel_lighting.fs
#version 120

varying vec3 LD, Normal, LDR;

void main()
{
    vec3 LDN = normalize(LD);
    vec3 NormalN = normalize(Normal);
    vec3 LDRN = normalize(LDR);
    float NdotLD = max(dot(NormalN, LDN), 0.0);
    float EVdotLDR = pow(max(dot(LDN, LDRN), 0.0), 32.0);
    gl_FragColor = vec4(gl_Color.rgb * NdotLD + vec3(EVdotLDR), 1.0);
}
god_rays.vs
#version 120

void main()
{
    gl_TexCoord[0] = gl_Vertex;
    gl_Position = gl_Vertex * 2.0 - 1.0;
}
god_rays.fs
#version 120

uniform sampler2D ColorBuffer;

void main()
{
    int Samples = 128;
    float Intensity = 0.125, Decay = 0.96875;
    vec2 TexCoord = gl_TexCoord[0].st, Direction = vec2(0.5) - TexCoord;
    Direction /= Samples;
    vec3 Color = texture2D(ColorBuffer, TexCoord).rgb;
    
    for(int Sample = 0; Sample < Samples; Sample++)
    {
        Color += texture2D(ColorBuffer, TexCoord).rgb * Intensity;
        Intensity *= Decay;
        TexCoord += Direction;
    }
    
    gl_FragColor = vec4(Color, 1.0);
}
Download
god_rays.zip (Visual Studio 2005 Professional)
god_rays_devcpp4992.zip (Dev-C++ 4.9.9.2)

© 2010 - 2016 Bc. Michal Belanec, michalbelanec (at) centrum (dot) sk
Last update June 25, 2016
OpenGL® is a registered trademark of Silicon Graphics Inc.