
Yay, my Raspberry Pi 400 finally arrived last week! It didn’t arrive until the last day of my christmas break, so I haven’t had much of a chance to play with it so far.
But I did have a decent hack at it today and I am super impressed so far. The keyboard is surprisingly cool – I was a little worried I’d have to plug in a ‘real’ keyboard to be able to use it seriously, which would have been pretty weird because it really only is a keyboard – but that’s definitely not the case, you just need to learn a few keyboard combos for home/end/up/down etc and you’re off.
The hardware seems to be OK, although it was definitely starting to get a bit laggy when I was using Qt Creator, to the point where you could ‘see’ the text scrolling happening in chunks. But it was still entirely usable and load/compile times were fine so I suspect this may be down to a still slightly underpowered GPU – well, compared to my desktop setup anyway!
It may also be something to do with the fact I’m using 64-bit Manjaro, as opposed to ‘stock’ 32-bit Raspbian which apparently has the most optimally tweaked drivers. I’ll probably explore that later as it’s still sitting there on an SD card waiting to be booted up again, but so far performance is definitely good enough for me to stick with the current setup.
Anyway, I had a blast writing a little GLES3 demo which I hope to expand on in future. It’s basically just a simple ‘hello triangle(s)’ demo, but I got to use a few GLES3-only tricks I learned while exploring webgpu recently. The coolest of these is being able to draw stuff without vertex/index buffers thanks to the ‘gl_VertexID’ built-in variable. This is just an int that contains the index of the vertex currently being processed by the vertex shader, but it’s great for drawing simple things like triangles/cubes etc as you can just stick vertex coordinates in the vertex shader via ‘const vec2[] vertices=…’ etc, which you can then index using gl_VertexID. Much nicer than having to create a vertex buffer, fill it with data, bind to attributes indices etc. Compiling shaders still sucks though…
There is also gl_InstanceID for instanced rendering, but in a way this is sort of redundant if you know how many vertices are in the model, as you can just draw vertices * instances vertices and use mod/div on gl_VertexId to simulate instancing. Much to play with!
I think I’ll try and turn this into a semi-regular ‘tutorial’ sort of thing. I was thinking of starting a git repos for it but it’s really only one file right now so that seems a bit overkilly! The plan for the next ‘episode’ is to add ImGui support and at that point a repos is probably worth it, so it can wait until then.
Until then, here’s the source code for hello triangles and a simple ‘CMakeLists.txt’ file for building it with. If you’re using Qt Creator, which should be available on all distros via the package manager, you can just open ‘CMakeLists.txt’ as a project, hit build/run and that should be it. If you’re not using Qt Creator, you should just be able to use ‘cmake’ directly to build it but I haven’t tried that yet. You’ll also need to install the binary tools Qt Creator does for you, so even if you don’t want to use it, install Qt Creator is probably a good way to get setup.
The only other thing you should need to install is the ‘glfw’ library, which again should available for all distros via the package manager. This package contains the headers and libraries for glfw, a simple, lightweight windowing library for creating windows you can use opengl to render to.
// File: main.cpp
#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <cstring>
#define GLFW_INCLUDE_ES31
#include <GLFW/glfw3.h>
const char* vshaderSource = R"(
#version 300 es
precision highp float;
const vec2[3] vertices=vec2[](
vec2(-1.0,-1.0),
vec2(0.0,1.0),
vec2(1.0,-1.0));
const float pi2=6.28319;
out float color;
void main() {
vec2 v = vertices[gl_VertexID] * 0.025;
float an = float(gl_InstanceID) * pi2 / 100.0;
v += vec2(cos(an),sin(an)) * 0.9;
gl_Position = vec4(v.x,v.y,0.0,1.0);
color = float(gl_InstanceID) / 100.0;
}
)";
const char* fshaderSource = R"(
#version 300 es
precision highp float;
in float color;
out vec4 fragColor;
void main() {
fragColor=vec4(1.0, color, 0.0,1.0);
}
)";
GLuint compileShader(GLenum type, const char* source) {
GLuint shader = glCreateShader(type);
glShaderSource(shader, 1, &source, nullptr);
glCompileShader(shader);
GLint status = 0;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE) {
GLint length;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &length);
char* log = new char[length];
glGetShaderInfoLog(shader, length, nullptr, log);
log[length - 1] = 0;
printf("Compile shader failed:%s\n", log);
delete[] log;
exit(1);
}
return shader;
}
int main() {
assert(glfwInit());
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 1);
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API);
GLFWwindow* window = glfwCreateWindow(640, 480, "My Title", NULL, NULL);
assert(window);
glfwMakeContextCurrent(window);
glfwSwapInterval(1);
GLuint vshader = compileShader(GL_VERTEX_SHADER, vshaderSource);
GLuint fshader = compileShader(GL_FRAGMENT_SHADER, fshaderSource);
GLuint program = glCreateProgram();
glAttachShader(program, vshader);
glAttachShader(program, fshader);
glLinkProgram(program);
glUseProgram(program);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
while (!glfwWindowShouldClose(window)) {
glClear(GL_COLOR_BUFFER_BIT);
glDrawArraysInstanced(GL_TRIANGLES, 0, 3, 100);
glfwSwapBuffers(window);
glfwPollEvents();
}
}
# File: CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
project(opengltest LANGUAGES CXX)
set(CMAKE_INCLUDE_CURRENT_DIR ON)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
add_executable(opengltest main.cpp)
target_link_libraries(opengltest glfw GLESv2)
Recent Comments