Skip to content

Instantly share code, notes, and snippets.

@DDRBoxman
Created August 18, 2021 18:51
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save DDRBoxman/d5ef7111204ccadfb73e895c4dd815cd to your computer and use it in GitHub Desktop.
Save DDRBoxman/d5ef7111204ccadfb73e895c4dd815cd to your computer and use it in GitHub Desktop.
shader panel
project(shaderpanel)
FIND_LIBRARY(GLESV2 brcmGLESv2 HINTS /opt/vc/lib)
FIND_LIBRARY(EGL brcmEGL HINTS /opt/vc/lib)
FIND_LIBRARY(RGBMATRIX rgbmatrix HINTS ${PROJECT_SOURCE_DIR}/matrix/lib)
include_directories(/opt/vc/include)
include_directories(${PROJECT_SOURCE_DIR}/matrix/include)
add_executable(shaderpanel main.cpp)
target_link_libraries(shaderpanel ${GLESV2} ${EGL} ${RGBMATRIX} pthread)
#include <EGL/egl.h>
#include <GLES2/gl2.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <chrono>
#include <math.h>
#include "led-matrix.h"
using rgb_matrix::RGBMatrix;
using rgb_matrix::Canvas;
volatile bool interrupt_received = false;
static void InterruptHandler(int signo) {
interrupt_received = true;
}
static const EGLint configAttribs[] = {
EGL_SURFACE_TYPE, EGL_PBUFFER_BIT, EGL_BLUE_SIZE, 8, EGL_GREEN_SIZE, 8,
EGL_RED_SIZE, 8, EGL_DEPTH_SIZE, 8,
// Uncomment the following to enable MSAA
// EGL_SAMPLE_BUFFERS, 1, // <-- Must be set to 1 to enable multisampling!
// EGL_SAMPLES, 4, // <-- Number of samples
// Uncomment the following to enable stencil buffer
// EGL_STENCIL_SIZE, 1,
EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT, EGL_NONE};
// Width and height of the desired framebuffer
static const EGLint pbufferAttribs[] = {
EGL_WIDTH,
64,
EGL_HEIGHT,
32,
EGL_NONE,
};
static const EGLint contextAttribs[] = {EGL_CONTEXT_CLIENT_VERSION, 2,
EGL_NONE};
// The following array holds vec3 data of
// three vertex positions
static const GLfloat vertices[] = {
-1.0f, 1.0f, 0.0f, 0.0f,// Top-left
1.0f, 1.0f, 1.0f, 0.0f,// Top-right
1.0f, -1.0f, 1.0f, 1.0f,// Bottom-right
1.0f, -1.0f, 1.0f, 1.0f, // Bottom-right
-1.0f, -1.0f, 0.0f, 1.0f,// Bottom-left
-1.0f, 1.0f, 0.0f, 0.0f, // Top-left
};
#define GLSL(version, shader) "#version " #version "\n" #shader
// The following are GLSL shaders for rendering a triangle on the screen
#define STRINGIFY(x) #x
static const char *vertexShaderCode = STRINGIFY(
attribute vec2 pos;
attribute vec2 texcoord;
varying vec2 texCoord;
void main() {
gl_Position = vec4(pos, 0.0, 1.0);
texCoord = texcoord;
}
);
static const char *fragmentShaderCode =
"uniform vec4 color; \n"
"uniform float time; \n"
"varying vec2 texCoord; \n"
"void main() { \n"
"vec2 uv = -1. +2. * texCoord;\n"
" gl_FragColor = vec4( \n"
"abs(sin(cos(time + 3.0 * uv.y)*2.0 * uv.x + time )),\n"
"abs(cos(sin(time+2.0 * uv.x) * 3.0 * uv.y + time)),\n"
" 1.0,\n"
"1.0\n"
" );\n"
"}\n";
static const char *eglGetErrorStr()
{
switch (eglGetError())
{
case EGL_SUCCESS:
return "The last function succeeded without error.";
case EGL_NOT_INITIALIZED:
return "EGL is not initialized, or could not be initialized, for the "
"specified EGL display connection.";
case EGL_BAD_ACCESS:
return "EGL cannot access a requested resource (for example a context "
"is bound in another thread).";
case EGL_BAD_ALLOC:
return "EGL failed to allocate resources for the requested operation.";
case EGL_BAD_ATTRIBUTE:
return "An unrecognized attribute or attribute value was passed in the "
"attribute list.";
case EGL_BAD_CONTEXT:
return "An EGLContext argument does not name a valid EGL rendering "
"context.";
case EGL_BAD_CONFIG:
return "An EGLConfig argument does not name a valid EGL frame buffer "
"configuration.";
case EGL_BAD_CURRENT_SURFACE:
return "The current surface of the calling thread is a window, pixel "
"buffer or pixmap that is no longer valid.";
case EGL_BAD_DISPLAY:
return "An EGLDisplay argument does not name a valid EGL display "
"connection.";
case EGL_BAD_SURFACE:
return "An EGLSurface argument does not name a valid surface (window, "
"pixel buffer or pixmap) configured for GL rendering.";
case EGL_BAD_MATCH:
return "Arguments are inconsistent (for example, a valid context "
"requires buffers not supplied by a valid surface).";
case EGL_BAD_PARAMETER:
return "One or more argument values are invalid.";
case EGL_BAD_NATIVE_PIXMAP:
return "A NativePixmapType argument does not refer to a valid native "
"pixmap.";
case EGL_BAD_NATIVE_WINDOW:
return "A NativeWindowType argument does not refer to a valid native "
"window.";
case EGL_CONTEXT_LOST:
return "A power management event has occurred. The application must "
"destroy all contexts and reinitialise OpenGL ES state and "
"objects to continue rendering.";
default:
break;
}
return "Unknown error!";
}
static void DrawOnCanvas(Canvas *canvas) {
/*
* Let's create a simple animation. We use the canvas to draw
* pixels. We wait between each step to have a slower animation.
*/
canvas->Fill(0, 0, 255);
int center_x = canvas->width() / 2;
int center_y = canvas->height() / 2;
float radius_max = canvas->width() / 2;
float angle_step = 1.0 / 360;
for (float a = 0, r = 0; r < radius_max; a += angle_step, r += angle_step) {
if (interrupt_received)
return;
float dot_x = cos(a * 2 * M_PI) * r;
float dot_y = sin(a * 2 * M_PI) * r;
canvas->SetPixel(center_x + dot_x, center_y + dot_y,
255, 0, 0);
usleep(1 * 1000); // wait a little to slow down things.
}
}
int main(int argc, char *argv[]) {
fprintf(stderr, "%s", vertexShaderCode);
RGBMatrix::Options defaults;
defaults.hardware_mapping = "adafruit-hat"; // or e.g. "adafruit-hat"
defaults.rows = 32;
defaults.cols = 64;
defaults.chain_length = 1;
defaults.parallel = 1;
defaults.show_refresh_rate = true;
defaults.brightness = 80;
Canvas *canvas = RGBMatrix::CreateFromFlags(&argc, &argv, &defaults);
if (canvas == NULL)
return 1;
EGLDisplay display;
int major, minor;
int desiredWidth, desiredHeight;
GLuint program, vert, frag, vbo;
GLint timeLoc, posLoc, colorLoc, texCoord, result;
if ((display = eglGetDisplay(EGL_DEFAULT_DISPLAY)) == EGL_NO_DISPLAY)
{
fprintf(stderr, "Failed to get EGL display! Error: %s\n",
eglGetErrorStr());
return EXIT_FAILURE;
}
if (eglInitialize(display, &major, &minor) == EGL_FALSE)
{
fprintf(stderr, "Failed to get EGL version! Error: %s\n",
eglGetErrorStr());
eglTerminate(display);
return EXIT_FAILURE;
}
printf("Initialized EGL version: %d.%d\n", major, minor);
EGLint numConfigs;
EGLConfig config;
if (!eglChooseConfig(display, configAttribs, &config, 1, &numConfigs))
{
fprintf(stderr, "Failed to get EGL config! Error: %s\n",
eglGetErrorStr());
eglTerminate(display);
return EXIT_FAILURE;
}
EGLSurface surface =
eglCreatePbufferSurface(display, config, pbufferAttribs);
if (surface == EGL_NO_SURFACE)
{
fprintf(stderr, "Failed to create EGL surface! Error: %s\n",
eglGetErrorStr());
eglTerminate(display);
return EXIT_FAILURE;
}
eglBindAPI(EGL_OPENGL_API);
EGLContext context =
eglCreateContext(display, config, EGL_NO_CONTEXT, contextAttribs);
if (context == EGL_NO_CONTEXT)
{
fprintf(stderr, "Failed to create EGL context! Error: %s\n",
eglGetErrorStr());
eglDestroySurface(display, surface);
eglTerminate(display);
return EXIT_FAILURE;
}
eglMakeCurrent(display, surface, surface, context);
desiredWidth = 64;
desiredHeight = 32;
// Set GL Viewport size, always needed!
glViewport(0, 0, desiredWidth, desiredHeight);
// Get GL Viewport size and test if it is correct.
// NOTE! DO NOT UPDATE EGL LIBRARY ON RASPBERRY PI AS IT WILL INSTALL FAKE
// EGL! If you have fake/faulty EGL library, the glViewport and
// glGetIntegerv won't work! The following piece of code checks if the gl
// functions are working as intended!
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
// viewport[2] and viewport[3] are viewport width and height respectively
printf("GL Viewport size: %dx%d\n", viewport[2], viewport[3]);
// Test if the desired width and height match the one returned by
// glGetIntegerv
if (desiredWidth != viewport[2] || desiredHeight != viewport[3])
{
fprintf(stderr, "Error! The glViewport/glGetIntegerv are not working! "
"EGL might be faulty!\n");
}
// Clear whole screen (front buffer)
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Create a shader program
// NO ERRRO CHECKING IS DONE! (for the purpose of this example)
// Read an OpenGL tutorial to properly implement shader creation
program = glCreateProgram();
glUseProgram(program);
vert = glCreateShader(GL_VERTEX_SHADER);
glShaderSource(vert, 1, &vertexShaderCode, NULL);
glCompileShader(vert);
GLint status;
glGetShaderiv(vert, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE) {
GLint infoLogLength;
glGetShaderiv(vert, GL_INFO_LOG_LENGTH, &infoLogLength);
GLchar* strInfoLog = new GLchar[infoLogLength + 1];
glGetShaderInfoLog(vert, infoLogLength, NULL, strInfoLog);
fprintf(stderr, "Compilation error in shader vert: %s\n", strInfoLog);
delete[] strInfoLog;
}
frag = glCreateShader(GL_FRAGMENT_SHADER);
glShaderSource(frag, 1, &fragmentShaderCode, NULL);
glCompileShader(frag);
glGetShaderiv(frag, GL_COMPILE_STATUS, &status);
if (status != GL_TRUE) {
GLint infoLogLength;
glGetShaderiv(frag, GL_INFO_LOG_LENGTH, &infoLogLength);
GLchar* strInfoLog = new GLchar[infoLogLength + 1];
glGetShaderInfoLog(frag, infoLogLength, NULL, strInfoLog);
fprintf(stderr, "Compilation error in shader %s\n", strInfoLog);
delete[] strInfoLog;
}
glAttachShader(program, frag);
glAttachShader(program, vert);
glLinkProgram(program);
GLint isLinked = 0;
glGetProgramiv(program, GL_LINK_STATUS, &isLinked);
if (isLinked == GL_FALSE)
{
GLint infoLogLength;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &infoLogLength);
GLchar* strInfoLog = new GLchar[infoLogLength +1];
glGetProgramInfoLog(program, infoLogLength, NULL, strInfoLog);
fprintf(stderr, "PROGRAM FAILED: %s\n", strInfoLog);
// The program is useless now. So delete it.
glDeleteProgram(program);
// Provide the infolog in whatever manner you deem best.
// Exit with failure.
return -1;
}
glUseProgram(program);
// Create Vertex Buffer Object
// Again, NO ERRRO CHECKING IS DONE! (for the purpose of this example)
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
// Get vertex attribute and uniform locations
posLoc = glGetAttribLocation(program, "pos");
texCoord = glGetAttribLocation(program, "texcoord");
colorLoc = glGetUniformLocation(program, "color");
timeLoc = glGetUniformLocation(program, "time");
fprintf(stderr, "%d\n", posLoc);
fprintf(stderr, "%d\n", texCoord);
fprintf(stderr, "%d\n", colorLoc);
// Set the desired color of the triangle to pink
// 100% red, 0% green, 50% blue, 100% alpha
glUniform4f(colorLoc, 1.0, 0.0f, 0.5, 1.0);
// Set our vertex data
glEnableVertexAttribArray(posLoc);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glVertexAttribPointer(posLoc, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float),
(void *)0);
glEnableVertexAttribArray(texCoord);
glVertexAttribPointer(texCoord, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(float),
(void *)(2*sizeof(float)));
// Render a triangle consisting of 3 vertices:
glDrawArrays(GL_TRIANGLES, 0, 6);
// Create buffer to hold entire front buffer pixels
// We multiply width and height by 3 to because we use RGB!
unsigned char *buffer =
(unsigned char *)malloc(desiredWidth * desiredHeight * 3);
// Copy entire screen
glReadPixels(0, 0, desiredWidth, desiredHeight, GL_RGB, GL_UNSIGNED_BYTE,
buffer);
// It is always good to set up a signal handler to cleanly exit when we
// receive a CTRL-C for instance. The DrawOnCanvas() routine is looking
// for that.
signal(SIGTERM, InterruptHandler);
signal(SIGINT, InterruptHandler);
// DrawOnCanvas(canvas); // Using the canvas.
//
auto t_start = std::chrono::high_resolution_clock::now();
while (!interrupt_received) {
auto t_now = std::chrono::high_resolution_clock::now();
float time = std::chrono::duration_cast<std::chrono::duration<float>>(t_now - t_start).count();
glUniform1f(timeLoc, time);
glDrawArrays(GL_TRIANGLES, 0, 6);
glReadPixels(0,0,desiredWidth, desiredHeight, GL_RGB, GL_UNSIGNED_BYTE, buffer);
for (int y=0; y<32; ++y) {
for (int x=0; x<64; ++x) {
uint8_t* data = buffer + (y * 64 * 3);
canvas->SetPixel(x, y, data[x*3] , data[x*3+1], data[x*3+2]);
}
}
usleep(35 * 1000);
}
// Animation finished. Shut down the RGB matrix.
canvas->Clear();
delete canvas;
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment