An implementation of OpenGL 3.x-ish in clean C

Overview

PortableGL

"Because of the nature of Moore's law, anything that an extremely clever graphics programmer can do at one point can be replicated by a merely competent programmer some number of years later." - John Carmack

In a nutshell, PortableGL is an implementation of OpenGL 3.x core in clean C99 as a single header library (in the style of the stb libraries).

It can theoretically be used with anything that takes a framebuffer/texture as input (including just writing images to disk manually or using something like stb_image_write) but all the demos use SDL2 and it currently only supports 8-bits per channel RGBA as a target (and also for textures).

Its goals are,

  1. Portability
  2. Matching the API within reason, at the least matching features/abilities
  3. Ease of Use
  4. Straightforward code
  5. Speed

Obviously there are tradeoffs between several of those. An example where 4 trumps 2 (and arguably 3) is with shaders. Rather than write or include a glsl parser and have a built in compiler or interpreter, shaders are just special C functions that match a specific prototype. Uniforms are another example where 3 and 4 beat 2 because it made no sense to match the API because we can do things so much simpler by just passing a pointer to a user defined struct (see the examples).

Download

Get the source from Github.

Gallery

gears pointsprites modelviewer craft

The last is a PortableGL port of Michael Fogleman's Craft.

History

PortableGL started as a very simple wireframe software renderer based on a tutorial in summer 2011. I kept playing with it and adding minor features over the next year and then in early 2013 I decided I should turn it into a software implementation of OpenGL. This would save me a huge amount of time and energy on API design since I'd just be implementing an existing good API (though some disagree) and also make the project more useful both to me and potentially others. Also, at the time Mesa3D was still years away from full 3.x support, not that I'm really competing but just the fact that there was no finished implementation was a little motivating. I made a lot of progress that year and had a few bursts here and there since, but once I got it mostly working, I was less motivated and when I did work on it I spent my time on creating new demos/examples and tweaking or fixing minor things. I could have released an MVP back in 2014 at the earliest but late 2016 would have been the best compromise. Anyway, after somewhere over 2000 hours spread out over 10 years, it is as you see it today. Software is never finished, and I'll be the first to admit PortableGL could use more polish.

Why

Aside from the fact that I just wrote it for fun and because I thought it was cool (maybe others will too), I can think of a few semi-practical purposes.

Educational

I took a 400 level 3D Graphics course in college in fall 2010 months after OpenGL 3.3/4.0 was released. It was taught based on the original Red Book using OpenGL 1.1. Fortunately, the professor let me use OpenGL 3.3 as long as I met the assignment requirements. Sadly, college graphics professors still teach 1.x and 2.x OpenGL today in 2020 far more commonly than 3.x/4.x (or Vulkan). A few are using WebGL 2.0 which I kind of consider 1 step forward 2 steps back.

While Vulkan is the newest thing (already 4 years old time flies), it really is overkill for learning 3D graphics. There is rarely anything that students make in your standard intro to 3D graphics that remotely stresses the performance of any laptop built in the last decade plus. Using modern OpenGL to introduce all the standard concepts, vertices, triangles, textures, shaders, fragments/pixels, the transformation pipeline etc. first is much better than trying to teach them Vulkan and graphics at the same time and obviously better than teaching OpenGL API's that are decades old.

PortableGL could be a very convenient base for such a class. It's easy to walk through the code and see the pipeline and how all the steps flow together. For more advanced classes or graduate students in a shared class, modifying PortableGL in some way would be a good project. It could be some optimization or algorithm, maybe a new feature. Theoretically it could be used as a base for actual research into new graphics algorithms or techniques just because it's such a convenient small base to change and share, vs trying to modify a project the size and complexity of Mesa3D or create a software renderer from scratch.

Special Cases

It's hard to imagine any hardware today that has a CPU capable of running software rendered 3D graphics at any respectable speed (especially with full IEEE floating point) that doesn't also have some kind of dedicated GPU. The GPU might only support OpenGL 2.0 give or take but for performance it'd be better to stick to whatever the hardware supported than use PortableGL. However, theoretically, there could be some platform somewhere where the CPU is relatively powerful that doesn't have GPU. Maybe some of the current and future RISC SoC's for example? In such a case PortableGL might be a useful alternative to Mesa3D or similar.

Another special case is hobby OS's. The hardware they run on might have a GPU but it might be impossible or more trouble than it's worth to get Mesa3D to run on some systems. If they have a C99 compliant compiler and standard library, they could use PortableGL to get at least some OpenGL-ish 3D support.

Documentation

There is the documentation in the comments at the top of the file (from src/header_docs.txt) but there is currently no formal documentation. Looking at the examples and demos (and comparing them to opengl_reference) should be helpful.

I've also started porting the learnopengl tutorial code here which is or will be the best resource, combining his tutorials explaining the OpenGL aspects and my comments in the ported code explaining the differences ond PGL limitations (at least in the first time they appear).

Honestly, the official OpenGL docs and reference pages are good for 90-95% of it as far as basic usage:

4.6 Core reference 4.5 comprehensive reference tutorials and guides

Building

If you have SDL2 installed you should be able to cd into examples, demos, or testing, and just run make or make config=release for optimized builds. I use premake generated makefiles that I include in the repo, but you should be able to compile it on Windows or Mac too, there's nothing Linux specific about the code. I'll fill out this section more later.

Modifying

portablegl.h (and portablegl_unsafe.h) is generated in the src subdirectory with the python script generate_gl_h.py. You can see how it's put together and either modify the script to leave out or add files, or actually edit any of the code. Make sure if you edit gl_impl.c that you also edit gl_impl_unsafe.c.

Additionally, there is a growing set of more formal tests in /testing, one set of regression/feature tests, and one for performance. If you make any changes to core algorithms or data structures, you should definitely run those and make sure nothing broke or got drastically slower. The demos can also function as performance tests, so if one of those would be especially affected by a change, it might be worth comparing its before/after performance too.

On the other hand, if you're adding a function or feature that doesn't really affect anything else, it might be worth adding your own test if applicable. You can see how they work from looking at the code, but I'll add more details and documentation about the testing system later when it's more mature.

References

While I often used the official OpenGL documentation to make sure I was matching the spec as closely as realistically possible, what I used most, especially early on were a few textbooks.

The first was Fundamentals of Computer Graphics 3rd Edition which I used extensively early on to understand all the math involved, including the matrix transformation pipeline, barycentric coordinates and interpolation, texture mapping and more. There is now a 4th Edition and a soon to be released 5th Edition.

The second was the 5th edition of the OpenGL Superbible 5th Edition. I got this in 2010, right after OpenGL 3.3/4.0 was released, and used it for my college graphics course mentioned above. A lot of people didn't like this book because they thought it relied too much on the author`s helper libraries but I had no problems. It was my first exposure to any kind of OpenGL so I didn't have to unlearn the old stuff and all his code was free and available online so it was easy to look inside and not only see what actual OpenGL calls are used, but to then develop your own classes to your own preferences. I still use a class based on his GLFrame class for example.

In any case, that's the book I actually learned OpenGL from, and still use as a reference sometimes. I have a fork of the book repo too that I occasionally look at/update. Of course they've come out with a 6th and a 7th edition in the last decade.

Lastly, while I haven't used it as much since I got it years later, the OpenGL 4.0 Shading Language Cookbook has been useful in specific OpenGL topics occasionally. Once again, you can now get the expanded 3rd edition.

Bindings/Ports

pgl is a Go port using CXGO, and hand translating the individual examples/demos.

Similar/Related Projects

I'll probably add others to this list as I find them.

TinyGL is Fabrice Bellard's implementation of a subset of of OpenGL 1.x. If you want something like PortableGL but don't want to write shaders, just want old style glBegin/glEnd/glVertex etc. this is the closest I know of. Also I shamelessly copied his clipping code because I'm not 1/10th the programmer Bellard was even as an undergrad and I knew it would "just work". I've included his copyright and BSD license in LICENSE just in case.

Pixomatic is/was a software implementation of D3D 7 and 9 written in C and assembly by Michael Abrash and Mike Sartain. You can read a series of articles about it written by Abrash for Dr. Dobbs.

TTSIOD is an advanced software renderer written in C++.

As an aside, the way I handle interpolation in PortableGL works as a semi-rebuttal of this article. The answer is not the terrible strawman C approach he comes up with just to easily say "look how bad that is". The answer is that interpolation is an algorithm, a simple function, and it doesn't care what the data means or how many elements there are. Pass it data and let the algorithm do its job, same as graphics hardware does. While the inheritance + template functions method works ok if you only have a few "types" of data, every time you think of some new feature you want to interpolate, you need to define a new struct and a new template function specialization. Having a function/pipeline that just takes an arbitrary amount of float data to operate on takes less code and even has less runtime overhead since it's a single function that interpolates all the features at once rather than having to call a function for each feature. See lines ~1200-1250 of gl_internals.c. Obviously it looks more complicated with all the other openGL stuff going on but you can see a simpler version on line 308 that's used for interpolating between line endpoints instead of over a triangle. This is closer to his example but still longer because it has to support SMOOTH, PERSPECTIVE and FLAT. You can see the shape of a straightforward implementation even there though, and the benefits of decoupling the algorithm from the data it operates on.

Mesa3D is an open source implementation of OpenGL, Vulkan and other graphics APIs. It includes several different software renderers including the Gallium rasterizer (softpipe or llvmpipe depending on whether llvm is used) and Intel's OpenSWR.

bbgl is just a very interesting concept. When I first saw it soon after it was published I was very frightened that it was exactly what PortableGL is but far more polished and from a better programmer. Fortunately, it is not.

pixman I feel like you could use them together or combine useful parts of pixman with PortableGL.

fauxgl "3D software rendering in pure Go. No OpenGL, no C extensions, no nothin'."

swGL A GPL2 multithreaded software implementation of OpenGL 1.3(ish) in C++. x86 and Windows only.

LICENSE

PortableGL is licensed under the MIT License (MIT)

The code used for clipping is copyright (c) Fabrice Bellard from TinyGL under the BSD License, see LICENSE.

TODO/IDEAS

  • Render to texture; do I bother with FBOs/Renderbuffers/PixelBuffers etc.? See ch 8 of superbible 5
  • Multitexture (pointsprites and shadertoy) and texture array (Texturing) examples
  • Render to texture example program
  • Mapped buffers according to API (just wraps extensions; it's free and everything is really read/write)
  • Extension functions that avoid unecessary copying, ie user owns buffer/texture data and gl doesn't free
  • Unsafe mode (ie no gl error checking for speedup)
  • Finish duplicating NeHe style tutorial programs from learningwebgl to opengl_reference and then porting those to use PortableGL Port learnopengl instead, repo here.
  • Port medium to large open source game project as correctness/performance/API coverage test (Craft done)
  • Fix bug in cubemap demo
  • More texture and render target formats
  • Logo
  • Formal regression testing (WIP)
  • Formal performance testing (WIP)
  • Formal/organized documentation
  • Integrated documentation, license etc. a la stb libraries
Comments
  • Function prefix

    Function prefix

    I'm looking into including PortableGL as a fallback (in my 8th programming language ) when running on e.g. an RPI or other Linux system which hasn't got OpenGL (or which has GL < 2).

    To that end, it would be convenient if the glXXX functions could optionally be "prefixed", e..g #define PORTABLEGL_PREFIX "pgl_" would turn your implementation of say glEnable into pgl_glEnable.

    I haven't yet done the integration, but finding your library was exciting, since it potentially makes it easy to support previously un- or under- supported systems in 8th.

    opened by ronaaron 31
  • ported to Go but ex1 doesn't work

    ported to Go but ex1 doesn't work

    This project is really cool. I have had a fascination with graphics for a long time but haven't found something that really interests me until this. I ported this project to the GO programming language using a tool called CXGO.

    I then went ahead and converted by hand the ex1 c code found in main.go.

    Everything runs without any errors but I don't get any triangle displayed. It appears that shaders are getting the vertex data but I don't know why nothing shows. I know my code is in Go and not C but since you understand the project better than I do maybe you'll spot something wrong in my code. Or give suggestions on what to look for if cxgo introduced a bug.

    My project is here

    opened by TotallyGamerJet 8
  • Looking to include PortableGL in LVGL - roadmap for v1.0?

    Looking to include PortableGL in LVGL - roadmap for v1.0?

    I'm looking to include PortableGL as a widget in the embedded graphics/UI library LVGL. I'm not a maintainer there; will just make a pull request once I get it properly implemented. My initial proof-of-concept tests are looking pretty promising and I really appreciate how easy it's been using PortableGL for this.

    I noticed that the version number being used for PGL is 0.96. Are there any additional features you are looking to implement in order to call it 1.0? If it's embedded in a toolkit such at LVGL, it might be nice to use a "feature complete" version as it will likely get updated less frequently than your source tree. Thanks again for your efforts on this project; it's been a lot of fun to play with.

    opened by 0x0203 2
  • Add a note in Related Projects about libosmesa

    Add a note in Related Projects about libosmesa

    In case it's of interest, offering a small write-up about a more-or-less related repository that extracts part of Mesa3D from circa 2008 to make a stand-alone, software only OpenGL renderer. I was amused by your bbgl comment, because I had a very similar reaction when I saw PortableGL - your first commit in the git commit history is literally days after I had completed my initial libosmesa experiments, which would have been extremely ironic timing :-).

    This extraction has been successfully used to display renderings interactively in a Qt widget, as well as the bundled examples. Probably a little old to be of much use to PortableGL itself, but thought it might be worth a mention in case it's useful to someone who needs a bit more than TinyGL has in place.

    opened by starseeker 1
  • Unify as MIT License

    Unify as MIT License

    Recently, TinyGL was relicensed to MIT, which implies that file LICENSE can be unified as single MIT License. That is,

    The MIT License (MIT) Copyright (c) 2011-2022 Robert Winkler Copyright (c) 1997-1998 Fabrice Bellard

    Reference: https://bellard.org/TinyGL/changelog.html

    opened by jserv 1
  • glVertexAttribPointer parameter type

    glVertexAttribPointer parameter type

    The OpenGL spec gives glVertexAttribPointer the following prototype:

    void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void* pointer);
    

    However, the last parameter is "pointer is treated as a byte offset into the buffer object's data store." You can see the full description here.

    What that means is that everyone has to cast the byte offset to a void pointer like so

    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float)));
    

    I've always thought this was a stupid design decision that didn't make any sense, so when I implemented it for PortableGL I gave it the following prototype

    void glVertexAttribPointer(GLuint index, GLint size, GLenum type, GLboolean normalized, GLsizei stride, GLsizei offset)
    

    I've never really had cause to regret that but in the process of porting the LearningOpenGL code, having to delete the cast on every call is a bit of an irritation. It occurred to me that maybe I should have matched the stupid spec and had a pgl extension version or macro wrapper that took a regular int. So if I went the macro route it would look like this:

    #define pglVertexAttribPointer(index, size, type, normalized, stride, offset) \
    glVertexAttribPointer(index, size, type, normalized, stride, (void*)offset)
    

    That way you could ignore it when porting if you wanted to, and still use the pgl version from scratch for simplicity.

    It seems like it'd be an easy change, and it is logically, but unfortunately the offset member is currently a GLsizei which is in int32_t so you get a warning or error casting from a 64 bit pointer. There are several solutions to that, but I think the one that obeys the spec (or at least the spirit of it) the closest would be to add the missing GL types GLintptr and GLsizeiptr and define them as intptr_t and uintptr_t and use one of those for offset. This would work but it just feels a little wrong and maybe not worth the effort. It also would break backward compatibility but as a single header library designed to be statically compiled into projects and with a very small user base that probably shouldn't be a huge factor.

    I'm on the fence about it, and I'm in no hurry either way so I figured I'd do an informal poll. I can't guarantee I'll end up deciding in favor of the majority, but I'd welcome any strong arguments for or against the change.

    Thanks!

    enhancement question design 
    opened by rswinkle 1
  • Cubemap demo bug

    Cubemap demo bug

    I mention it in both the main README TODO list and the demo README description. Basically the sky wobbles when you look around but is fine when you move. Most likely a bug in the demo not in PortableGL but anything's possible.

    I'm putting this here to motivate me to fix it sometime this year hopefully.

    bug 
    opened by rswinkle 0
  • Multithreaded Rasterization?

    Multithreaded Rasterization?

    Hi! I just want to say that this project is excellent, and I plan on getting it running in my kernel as soon as I get the time

    While it's quite easy for a hobby kernel to take advantage of multiple CPU cores, implementing (or porting) a GPU driver is quite the sisyphean task. I was wondering if there are plans to enable parallelized rasterization in a similar way to how GPUs do so, but in software w/ multiple CPU threads - pthreads, for example. I'm not super familiar with the codebase here, but would that be a considerable undertaking/refactor of the core renderer? I'd assume this feature would be hidden behind an opt-in flag to maintain support for systems without multiple cores, or even the pthread interface.

    opened by nickwanninger 3
  • This is a great project

    This is a great project

    Just wanted to let you know I think this is a really neat project.

    I was stuck trying to compile a cross-architecture static mesa for DICOMautomaton and ... I just don't think it will work. PortableGL, in comparison, took a tiny number of changes to integrate and build. What a lifesaver!

    I still haven't been able to test the binaries work (ripped out glew init code and have not yet replaced it), but the program seems so much more tractable at this point that I had to reach out and thank you!

    opened by hdclark 6
Owner
Robert Winkler
Robert Winkler
The official Open-Asset-Importer-Library Repository. Loads 40+ 3D-file-formats into one unified and clean data structure.

Open Asset Import Library (assimp) A library to import and export various 3d-model-formats including scene-post-processing to generate missing render

Open Asset Import Library 8.6k Jan 4, 2023
Horde3D is a small 3D rendering and animation engine. It is written in an effort to create an engine being as lightweight and conceptually clean as possible.

Horde3D Horde3D is a 3D rendering engine written in C++ with an effort being as lightweight and conceptually clean as possible. Horde3D requires a ful

Volker Vogelhuber 1.3k Dec 31, 2022
A multi-platform library for OpenGL, OpenGL ES, Vulkan, window and input

GLFW Introduction GLFW is an Open Source, multi-platform library for OpenGL, OpenGL ES and Vulkan application development. It provides a simple, platf

GLFW 10k Jan 1, 2023
A legacy OpenGL simulator for OpenGL 4.4, written in C++.

the-ancient-tri A legacy OpenGL simulator for OpenGL 4.4, written in C++. Why? My Uni forces us to use legacy OpenGL (eww!), and I didn't want to lear

Mohammad Issawi 4 Feb 10, 2022
Deno gl - WIP Low-level OpenGL (GLFW) bindings and WebGL API implementation for Deno.

deno_gl WIP Low-level OpenGL (GLFW) bindings and WebGL API implementation for Deno. Building Make dist directory if it doesn't exist. Build gl helper

DjDeveloper 14 Jun 11, 2022
SMAA is a very efficient GPU-based MLAA implementation (DX9, DX10, DX11 and OpenGL)

SMAA is a very efficient GPU-based MLAA implementation (DX9, DX10, DX11 and OpenGL), capable of handling subpixel features seamlessly, and featuring an improved and advanced pattern detection & handling mechanism.

Jorge Jimenez 848 Dec 30, 2022
Low Level Graphics Library (LLGL) is a thin abstraction layer for the modern graphics APIs OpenGL, Direct3D, Vulkan, and Metal

Low Level Graphics Library (LLGL) Documentation NOTE: This repository receives bug fixes only, but no major updates. Pull requests may still be accept

Lukas Hermanns 1.5k Jan 8, 2023
Antialiased 2D vector drawing library on top of OpenGL for UI and visualizations.

This project is not actively maintained. NanoVG NanoVG is small antialiased vector graphics rendering library for OpenGL. It has lean API modeled afte

Mikko Mononen 4.6k Jan 2, 2023
Epoxy is a library for handling OpenGL function pointer management for you

Epoxy is a library for handling OpenGL function pointer management for you. It hides the complexity of dlopen(), dlsym(), glXGetProcAddress(), eglGetP

Eric Anholt 577 Dec 19, 2022
3D engine from scratch (without OpenGL or any other 3D graphics library)

Simple 3d engine based on SFML library. I tried to make this engine powerful and easy to understand.

Vectozavr 64 Dec 10, 2022
RGL - 3D visualization device system for R using OpenGL

RGL - 3D visualization device system for R using OpenGL INTRODUCTION The RGL package is a visualization device system for R, using OpenGL or WebGL as

null 68 Dec 27, 2022
OpenGL Demo: Simulating Ocean Waves with FFT

OceanFFT Realistic ocean wave simulation, primarily based on J. Tessendorf's paper, using OpenGL compute shaders. Checkout the demo video here. In the

Achal Pandey 70 Dec 27, 2022
OBS Linux Vulkan/OpenGL game capture

OBS Linux Vulkan/OpenGL game capture OBS plugin for Vulkan/OpenGL game capture on Linux. Requires OBS with EGL support (currently unreleased, you need

David Rosca 290 Jan 1, 2023
NodeEditor basiced on Qt and OpenGL/CV

Vapour 基于Qt的轻量级的节点式几何建模工具与shader材质生成工具 可能加入图像处理(合成)的功能 日志: 2021-5-17: 完成背景绘制,右键菜单,节点连线,节点删除,初步拓扑排序 (细节)连线位于节点层级之下,使用lambda处理右键菜单slot,节点创建生成在鼠标位置处 2021

Cuimi 17 Dec 19, 2022
C Wavefront OBJ loader for OpenGL

OBJ GL Loader v2 Quite fast .OBJ loader written in C How to use it Put objgl2.c and objgl2.h files in Your project and include them. To put it simply:

null 9 Oct 14, 2022
Orbit is a multiplatform-focus graphical engine build on top of OpenGl, ImGui

Orbit Engine Orbit is a multiplatform-focus graphical engine build on top of OpenGl, ImGui and more... The development of the engine is documented via

Madsycode 11 Jul 3, 2021
Axel Gneiting 1.5k Dec 31, 2022
A 2d Graphing Calculator using OpenGL

glGraph A 2d Graphing Calculator using Modern OpenGL Demo glGraph.mp4 Information This has only been tested on Fedora 34, it should work on other OS's

Nathan Medros 16 Nov 26, 2022
Tetris written with C++ and OpenGL.

Tetrec This is yet another Tetris game, which is in 3D, written using C++ and OpenGL 2.1, aiming at being lightweight towards not-so-beefy computers (

Lê Duy Quang 14 Jan 17, 2022