Skip to content

OpenGL Example#

Example's page

This page describes an example listed in ui category.

Demonstrates how to integrate custom OpenGL rendering with AUI Framework.

It shows a simple triangle rendered using OpenGL ES 2.0 shaders while maintaining compatibility with AUI's rendering system.

Features#

  • Custom OpenGL rendering within AUI view
  • FPS counter display
  • Proper OpenGL state management
  • Basic shader compilation and usage
  • Vertex buffer creation and rendering

Implementation Details#

The example consists of a custom renderer (MyRenderer) that:

  1. Sets up basic vertex and fragment shaders
  2. Creates and manages OpenGL resources (shaders, program, VBO)
  3. Renders a colored triangle
  4. Calculates and displays FPS
  5. Properly cleans up OpenGL state to maintain compatibility with AUI

The rendering is performed in real-time with continuous updates, demonstrating smooth integration between custom OpenGL code and AUI's widget system.

Source Code#

Repository

CMakeLists.txt#

1
2
3
4
get_filename_component(_t ${CMAKE_CURRENT_SOURCE_DIR} NAME)

aui_executable("aui.example.${_t}")
aui_link("aui.example.${_t}" PRIVATE aui::core aui::views)

src/main.cpp#

/// [OpenGL_example]
#include "AUI/GL/State.h"
#include "AUI/Platform/ARenderingContextOptions.h"

#include <AUI/Platform/Entry.h>
#include <AUI/Platform/AWindow.h>
#include <AUI/Util/UIBuildingHelpers.h>

using namespace ass;
using namespace declarative;

namespace {

struct State {
    AProperty<float> fps = 0;
};

class MyRenderer : public AView {
public:
    MyRenderer(_<State> state) : mState(std::move(state)) {
        // Simple passthrough ES 2.0 shaders
        const char* vsSrc =
            "attribute vec2 aPos;\n"
            "void main(){\n"
            "  gl_Position = vec4(aPos, 0.0, 1.0);\n"
            "}\n";
        const char* fsSrc =
            "precision mediump float;\n"
            "void main(){\n"
            "  gl_FragColor = vec4(1.0, 0.4, 0.2, 1.0);\n"
            "}\n";

        // Compile vertex shader
        mShaderVertex = glCreateShader(GL_VERTEX_SHADER);
        glShaderSource(mShaderVertex, 1, &vsSrc, nullptr);
        glCompileShader(mShaderVertex);

        // Compile fragment shader
        mShaderFragment = glCreateShader(GL_FRAGMENT_SHADER);
        glShaderSource(mShaderFragment, 1, &fsSrc, nullptr);
        glCompileShader(mShaderFragment);

        // Link program
        mProgram = glCreateProgram();
        glAttachShader(mProgram, mShaderVertex);
        glAttachShader(mProgram, mShaderFragment);
        glBindAttribLocation(mProgram, 0, "aPos");
        glLinkProgram(mProgram);

        // Triangle vertices (clip space)
        const GLfloat verts[] = {
            -0.6f, -0.6f, 0.0f, 0.6f, 0.6f, -0.6f,
        };

        // Create VBO
        glGenBuffers(1, &mVertexBufferObject);
        glBindBuffer(GL_ARRAY_BUFFER, mVertexBufferObject);
        glBufferData(GL_ARRAY_BUFFER, sizeof(verts), verts, GL_STATIC_DRAW);
        glBindBuffer(GL_ARRAY_BUFFER, 0);
    }
    ~MyRenderer() override {
        if (mVertexBufferObject) {
            glDeleteBuffers(1, &mVertexBufferObject);
            mVertexBufferObject = 0;
        }
        if (mProgram) {
            if (mShaderVertex)
                glDetachShader(mProgram, mShaderVertex);
            if (mShaderFragment)
                glDetachShader(mProgram, mShaderFragment);
            glDeleteProgram(mProgram);
            mProgram = 0;
        }
        if (mShaderVertex) {
            glDeleteShader(mShaderVertex);
            mShaderVertex = 0;
        }
        if (mShaderFragment) {
            glDeleteShader(mShaderFragment);
            mShaderFragment = 0;
        }
    }

    void cleanupAUIGLState() {
        gl::State::activeTexture(0);
        gl::State::bindTexture(GL_TEXTURE_2D, 0);
        gl::State::bindVertexArray(0);
        gl::State::useProgram(0);
    }

    void render(ARenderContext ctx) override {
        AView::render(ctx);
        cleanupAUIGLState();

        // Draw triangle
        glUseProgram(mProgram);
        glBindBuffer(GL_ARRAY_BUFFER, mVertexBufferObject);
        glEnableVertexAttribArray(0);
        glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), (const void*) 0);
        glDrawArrays(GL_TRIANGLES, 0, 3);

        cleanupAUIGLState();

        // calculate fps
        auto now = std::chrono::high_resolution_clock::now();
        mState->fps = 1'000'000.f / std::chrono::duration_cast<std::chrono::microseconds>(now - mLastFrame).count();
        mLastFrame = now;

        // request redraw if you'd like to show animations
        redraw();
    }

private:
    _<State> mState;
    std::chrono::high_resolution_clock::time_point mLastFrame {};
    GLuint mProgram {};
    GLuint mShaderVertex {};
    GLuint mShaderFragment {};
    GLuint mVertexBufferObject {};
};
}   // namespace

AUI_ENTRY {
    // ask aui to provide opengl context for us. (no fallback to software rendering)
    ARenderingContextOptions::set({
        .initializationOrder = {
            ARenderingContextOptions::OpenGL{},
        },
        .flags = ARenderContextFlags::NO_SMOOTH | ARenderContextFlags::NO_VSYNC,
    });

    auto window = _new<AWindow>("OpenGL Demo", 600_dp, 300_dp);
    auto state = _new<State>();
    window->setContents(
        Stacked {
          _new<MyRenderer>(state) AUI_OVERRIDE_STYLE { Expanding() },
          Horizontal::Expanding {
            Vertical {
              Vertical {
                Label { AUI_REACT(fmt::format("FPS: {:.1f}", *state->fps)) },
              } AUI_OVERRIDE_STYLE {
                    Padding(16_dp),
                    BackgroundSolid { AColor::WHITE.transparentize(0.5f) },
                    MinSize { 150_dp, {} },
                  },
            },
          },
        } AUI_OVERRIDE_STYLE { Padding(0) });
    window->show();
    return 0;
}
/// [OpenGL_example]