Skip to content
/ kreogl Public

A simple OpenGL renderer, aimed to get any game project up and running with simple but satisfying graphics.

License

Notifications You must be signed in to change notification settings

phisko/kreogl

Repository files navigation

kreogl

build

A simple OpenGL renderer, aimed to get any game project up and running with simple but satisfying graphics.

Motivation

I'm not a graphics programmer and, although I do find it fun when I get pretty things to appear on-screen, I much prefer metaprogramming and engine architecture. However, whenever I've wanted to get some proof-of-concept graphics, I've always been frustrated to see there's no 3D equivalent to SFML: a simple 3D graphics library, letting me load meshes and draw them without having to write my own shaders.

Eventually I came to accept this, and started implementing a custom OpenGL system for the kengine. This was a learning process for me, as it was my first time toying with graphics programming.

Now that I'm relatively satisfied with the final result (although it's far from perfect), I figured it was time to move the functionality out of the kengine and have it become what I was initially looking for, so that future engine programmers don't have to write their own renderer from scratch.

Usage

Take a look at the example code. There are two examples:

  • a simple one which shows how to quickly draw and animate a model
  • a more complex one, which makes use of most available features but may be a bit harder to follow

The examples can be built by setting the KREOGL_EXAMPLE CMake option.

Below is a snippet of the important parts of the simple example:

kreogl::window window; // create a window
window.get_default_camera().set_position({ 0.f, 0.f, -5.f }); // move the camera back to see the centered scene

kreogl::world world; // the world that will be used to draw into the window

const kreogl::skybox_texture skybox_texture{ // load the skybox
	"resources/skybox/left.jpg",
	"resources/skybox/right.jpg",
	"resources/skybox/top.jpg",
	"resources/skybox/bottom.jpg",
	"resources/skybox/front.jpg",
	"resources/skybox/back.jpg",
};
world.skybox.texture = &skybox_texture; // add it to the world

kreogl::directional_light light; // create a light
world.add(light); // add it to the world
light.direction = { 0.f, -1.f, -1.f };
light.cast_shadows = false; // disable shadows for our scene

const auto model = kreogl::assimp::load_animated_model("resources/funnyman/funnyman.fbx"); // load a 3d model
assert(model && model->animations.size() == 1);

kreogl::animated_object object; // create an object
object.model = model.get(); // base it on the loaded 3d model
object.transform = glm::translate(glm::mat4{1.f}, glm::vec3{ 0.f, -2.5f, 5.f }); // move it forward and down a bit
object.transform = glm::rotate(object.transform, glm::pi<float>(), glm::vec3{ 0.f, 1.f, 0.f }); // rotate it to face the camera
object.animation = kreogl::animation{ // play an animation
	.model = model->animations->animations[0].get(), // use the animation that was baked into the 3d model
	.loop = true
};
world.add(object); // add the object to the world

// main loop
auto previous_time = std::chrono::system_clock::now();
while (!window.should_close()) {
	const auto now = std::chrono::system_clock::now();
	const auto delta_time = float(std::chrono::duration_cast<std::chrono::milliseconds>(now - previous_time).count()) / 1000.f;
	previous_time = now;

	object.tick_animation(delta_time); // play the object's animation

	window.poll_events(); // process input
	window.draw(world); // draw the world into the window
	window.display(); // present the new window contents
}

And here's a screenshot of the result:

API

High level objects

These are objects that will typically live for as long as the application is running.

Basic drawables

Lights

Loaders

These provide functions to load models from files.

Animation

The rest of these types are less user-facing, and understanding them isn't required for basic animation code.

Implementation details

This describe the internal implementation of the rendering engine. You don't need to be aware of these to make use of kreogl, but if you wish to improve/extend/understand its behavior, this is a good starting point.

Each camera has an associated viewport, which represents the on-screen area used to display the camera.

Each viewport has an underlying G-buffer, which contains the intermediate rendering data generated when drawing the camera content. It can be used to query the position, color, or custom user data that was drawn in a specific pixel.

RAII wrappers to OpenGL resources

Shadow maps

Textures

Shapes

Shaders

Shaders are instances of shader, grouped into a shader_pipeline, which can be passed to window::draw. The default pipeline contains all the pre-implemented shaders:

gbuffer shaders

These are shaders in charge of filling the gbuffer.

Lighting shaders

These are shaders in charge of applying lighting to what was previously written into the gbuffer, and writing the result to the main framebuffer.

Post-lighting shaders

These are shaders that run after the lighting pass, and can render effects to alter the lighting of the scene.

Post-process shaders

These are shaders that run after the post-lighting pass, and can add the "final touches" to the scene.

Shadowmap shaders

These are shaders that implement the shadow_map_shader or shadow_cube_shader interfaces, and are called by the lighting shaders to fill lights' ShadowMaps.

Profiling

The code is instrumented using Tracy. Profiling can be enabled by setting the KREOGL_PROFILING CMake option.

About

A simple OpenGL renderer, aimed to get any game project up and running with simple but satisfying graphics.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published