Varying Triangle Colours

TriangleCanvas.h:

#pragma once
#include <memory>
#include <chrono>
#include "wx/glcanvas.h"

class TriangleCanvas : public wxGLCanvas
{
public:
	TriangleCanvas(wxWindow* parent, wxWindowID id = wxID_ANY,
		const int* attribList = 0, const wxPoint& pos = wxDefaultPosition,
		const wxSize& size = wxDefaultSize, long style = 0L,
		const wxString& name = L"GLCanvas", const wxPalette& palette = wxNullPalette);

	virtual ~TriangleCanvas();
	TriangleCanvas(const TriangleCanvas& tc) = delete;
	TriangleCanvas(TriangleCanvas&& tc) = delete;
	TriangleCanvas& operator=(const TriangleCanvas& tc) = delete;
	TriangleCanvas& operator=(TriangleCanvas&& tc) = delete;

private:
	void InitializeGLEW();
	void SetupGraphics();
	void BuildShaderProgram();
	void BuildVertexShader();
	void BuildFragmentShader();
	void OnPaint(wxPaintEvent& event);
	void OnTimer(wxTimerEvent& event);

	static const int INTERVAL = 1000 / 60;		// run at 60 frames/second
	static const int TIMERNUMBER = 3;

	std::unique_ptr<wxGLContext> m_context;
	GLuint m_vbo;		// vertex buffer pointer
	GLuint m_vao;		// vertex array pointer
	GLuint m_vertexShader;
	GLuint m_fragmentShader;
	GLuint m_shaderProgram;
	GLint m_uniColor;
	std::unique_ptr<wxTimer> m_timer;
	std::chrono::time_point<std::chrono::high_resolution_clock> m_startTime;
};

TriangleCanvas.cpp:

#include "wx/wxprec.h"
#include <GL/glew.h>
#include "TriangleCanvas.h"

#pragma comment(lib, "glew32.lib")

TriangleCanvas::TriangleCanvas(wxWindow* parent, wxWindowID id, const int* attribList,
	const wxPoint& pos, const wxSize& size, long style, const wxString& name,
	const wxPalette& palette)
	: wxGLCanvas(parent, id, attribList, pos, size, style, name, palette),
	m_vbo(0), m_vao(0)
{
	m_startTime = std::chrono::high_resolution_clock::now();
	m_context = std::make_unique<wxGLContext>(this);
	Bind(wxEVT_PAINT, &TriangleCanvas::OnPaint, this);
	Bind(wxEVT_TIMER, &TriangleCanvas::OnTimer, this);
	
	SetCurrent(*m_context);
	InitializeGLEW();
	SetupGraphics();

	m_timer = std::make_unique<wxTimer>(this, TIMERNUMBER);
	m_timer->Start(INTERVAL);
}


TriangleCanvas::~TriangleCanvas()
{
	SetCurrent(*m_context);
	glDeleteProgram(m_shaderProgram);
	glDeleteShader(m_fragmentShader);
	glDeleteShader(m_vertexShader);
	glDeleteVertexArrays(1, &m_vao);
	glDeleteBuffers(1, &m_vbo);
}

void TriangleCanvas::OnTimer(wxTimerEvent& event)
{
	ProcessEvent(wxPaintEvent());
}

void TriangleCanvas::OnPaint(wxPaintEvent& event)
{
	SetCurrent(*m_context);

	// set background to black
	glClearColor(0.0, 0.0, 0.0, 1.0);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	// draw the graphics
	auto t_now = std::chrono::high_resolution_clock::now();
	float time = std::chrono::duration_cast<std::chrono::duration<float>>(t_now - m_startTime).count();
	glUniform3f(m_uniColor, (sin(time * 1.0f) + 1.0f) / 2.0f, (sin(time * 0.5f) + 1.0f) / 2.0f,
		(cos(time * 0.25f) + 1.0f) / 2.0f);
	glDrawArrays(GL_TRIANGLES, 0, 3);
	// and display
	glFlush();
	SwapBuffers();
}

void TriangleCanvas::InitializeGLEW()
{
	glewExperimental = true;
	GLenum err = glewInit();
	if (err != GLEW_OK) {
		const GLubyte* msg = glewGetErrorString(err);
		throw std::exception(reinterpret_cast<const char*>(msg));
	}
}

void TriangleCanvas::SetupGraphics()
{
	// define vertices
	float points[] = {
		0.0f, 0.5f,
		0.5f, -0.5f,
		-0.5f, -0.5f,
	};
	// upload vertex data
	glGenBuffers(1, &m_vbo);
	glBindBuffer(GL_ARRAY_BUFFER, m_vbo);
	glBufferData(GL_ARRAY_BUFFER, sizeof(points), points, GL_STATIC_DRAW);
	// setup vertex array objects
	glGenVertexArrays(1, &m_vao);
	glBindVertexArray(m_vao);
	BuildShaderProgram();
}

void TriangleCanvas::BuildVertexShader()
{
	const GLchar* vertexSource =
		"#version 330 core\n"
		"in vec2 position;"
		"void main()"
		"{"
		"    gl_Position = vec4(position.x, -position.y, 0.0, 1.0);"
		"}";
	m_vertexShader = glCreateShader(GL_VERTEX_SHADER);
	glShaderSource(m_vertexShader, 1, &vertexSource, NULL);
	glCompileShader(m_vertexShader);
}

void TriangleCanvas::BuildFragmentShader()
{
	const GLchar* fragmentSource =
		"#version 330 core\n"
		"uniform vec3 triColor;"
		"out vec4 outColor;"
		"void main()"
		"{"
		"	outColor = vec4(triColor, 1.0);"
		"}";
	m_fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);
	glShaderSource(m_fragmentShader, 1, &fragmentSource, NULL);
	glCompileShader(m_fragmentShader);
}

void TriangleCanvas::BuildShaderProgram()
{
	BuildVertexShader();
	BuildFragmentShader();
	m_shaderProgram = glCreateProgram();
	glAttachShader(m_shaderProgram, m_vertexShader);
	glAttachShader(m_shaderProgram, m_fragmentShader);
	glBindFragDataLocation(m_shaderProgram, 0, "outColor");
	glLinkProgram(m_shaderProgram);
	glUseProgram(m_shaderProgram);

	GLint posAttrib = glGetAttribLocation(m_shaderProgram, "position");
	glEnableVertexAttribArray(posAttrib);
	glVertexAttribPointer(posAttrib, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(GLfloat), 0);

	m_uniColor = glGetUniformLocation(m_shaderProgram, "triColor");
}