commit 1fafa50a4833e1c448f3773c37d44466bdad90f0
parent 3cde30ce082208b0cd8f485d13209fb1e6c3ea9a
Author: bsandro <email@bsandro.tech>
Date: Wed, 26 Nov 2025 01:47:54 +0200
First geometry shader
Diffstat:
| A | obj2d.c | | | 215 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
1 file changed, 215 insertions(+), 0 deletions(-)
diff --git a/obj2d.c b/obj2d.c
@@ -0,0 +1,215 @@
+/* 2d object from single vertex */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <math.h>
+#include <X11/X.h>
+#include <X11/Xlib.h>
+#define GL_GLEXT_PROTOTYPES 1
+#include <GL/gl.h>
+#include <GL/glx.h>
+#include <GL/glu.h>
+
+#define LEN(x) (sizeof(x)/sizeof(x[0]))
+
+#define GL_CHECK(x) \
+x; \
+{ \
+ GLenum glError = glGetError(); \
+ if (glError!=GL_NO_ERROR) { \
+ printf("glGetError() = %i (%#.8x) at %s:%i\n", glError, glError, __FILE__, __LINE__); \
+ exit(EXIT_FAILURE); \
+ } \
+}
+
+static const double current_ts() {
+ struct timespec ts;
+ timespec_get(&ts, TIME_UTC);
+ return (double)ts.tv_sec + (double)ts.tv_nsec/1e9;
+}
+
+static double t_started;
+
+//@todo move shaders to separate files
+static const GLchar *vertexSrc = R"(
+#version 420
+layout(location=0) in vec2 position;
+void main() {
+ gl_Position = vec4(position, 0.0, 1.0);
+}
+)";
+
+static const GLchar *geometrySrc = R"(
+#version 420
+layout(points) in;
+layout(line_strip,max_vertices=6) out;
+void main() {
+ vec4 p = gl_in[0].gl_Position;
+ gl_Position = p+vec4(-0.1, -0.1, 0.0, 0.0);
+ EmitVertex();
+ gl_Position = p+vec4(0.1, -0.1, 0.0, 0.0);
+ EmitVertex();
+ gl_Position = p+vec4(0.1, 0.1, 0.0, 0.0);
+ EmitVertex();
+ gl_Position = p+vec4(-0.1, 0.1, 0.0, 0.0);
+ EmitVertex();
+ gl_Position = p+vec4(-0.1, -0.1, 0.0, 0.0);
+ EmitVertex();
+
+ EndPrimitive();
+}
+)";
+
+static const GLchar *fragmentSrc = R"(
+#version 420
+uniform double iTime;
+out vec4 outColor;
+void main() {
+ vec3 color;
+ color.r = 0.2;
+ color.g = 0.6;
+ color.b = 0.1;
+ outColor = vec4(color, 1.0);
+}
+)";
+
+static GLuint mkShader(GLenum type, const GLchar *src) {
+ GLuint shader = glCreateShader(type);
+ glShaderSource(shader, 1, &src, NULL);
+ glCompileShader(shader);
+ GLint ok = 0;
+ glGetShaderiv(shader, GL_COMPILE_STATUS, &ok);
+ if (!ok) {
+ static char buf[512] = {0};
+ glGetShaderInfoLog(shader, sizeof(buf)-1, NULL, buf);
+ fprintf(stderr, "Shader %d compile error: %s\n", (int)type, buf);
+ }
+ return shader;
+}
+
+static GLuint linkProgram(GLuint vertex, GLuint geometry, GLuint fragment) {
+ GLuint shaderProgram = glCreateProgram();
+ glAttachShader(shaderProgram, vertex);
+ glAttachShader(shaderProgram, geometry);
+ glAttachShader(shaderProgram, fragment);
+ glBindAttribLocation(shaderProgram, 0, "position");
+ glLinkProgram(shaderProgram);
+ return shaderProgram;
+}
+
+void __attribute__((constructor)) start() {
+ printf("start()\n");
+ t_started = current_ts();
+}
+
+void __attribute((destructor)) end() {
+ printf("end()\n");
+}
+
+int main(int argc, char *argv[]) {
+ Display *dpy = XOpenDisplay(NULL);
+ XSetWindowAttributes swa;
+ Window win;
+ XWindowAttributes gwa;
+ GLint attr[] = { GLX_RGBA, GLX_DEPTH_SIZE, 24, GLX_DOUBLEBUFFER, None };
+
+ if (dpy == NULL) {
+ printf("\n\tcannot connect to X server\n\n");
+ exit(0);
+ }
+
+ Window root = DefaultRootWindow(dpy);
+ XVisualInfo *vi = glXChooseVisual(dpy, 0, attr);
+
+ if (vi == NULL) {
+ printf("\n\tno appropriate visual found\n\n");
+ exit(0);
+ } else {
+ printf("\n\tvisual %p selected\n", (void *)vi->visualid); /* %p creates hexadecimal output like in glxinfo */
+ }
+
+ Colormap cmap = XCreateColormap(dpy, root, vi->visual, AllocNone);
+ swa.colormap = cmap;
+ swa.event_mask = ExposureMask | KeyPressMask;
+ win = XCreateWindow(dpy, root, 0, 0, 500, 500, 0, vi->depth, InputOutput, vi->visual, CWColormap | CWEventMask, &swa);
+ XMapWindow(dpy, win);
+ XStoreName(dpy, win, "OPENGL");
+ GLXContext ctx = glXCreateContext(dpy, vi, NULL, GL_TRUE);
+ glXMakeCurrent(dpy, win, ctx);
+
+ // USEFUL DEBUG INFO
+ //@todo validate ASTC support
+ /*GLint extn;
+ GL_CHECK(glGetIntegerv(GL_NUM_EXTENSIONS, &extn));
+ printf("%d GL Extensions:\n", extn);
+ for (GLint i=0; i<extn; ++i) {
+ printf("\t%s\n", glGetStringi(GL_EXTENSIONS, i));
+ }*/
+
+ GLuint vao;
+ glGenVertexArrays(1, &vao);
+ glBindVertexArray(vao);
+
+ // x,y
+ static const GLfloat vertices[] = {
+ -0.5, -0.5,
+ 0.5, -0.5,
+ 0.5, 0.5
+ };
+
+ GLuint vbo;
+ glGenBuffers(1, &vbo);
+ glBindBuffer(GL_ARRAY_BUFFER, vbo);
+ glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
+
+ GLuint vertex = mkShader(GL_VERTEX_SHADER, vertexSrc);
+ GLuint geometry = mkShader(GL_GEOMETRY_SHADER, geometrySrc);
+ GLuint fragment = mkShader(GL_FRAGMENT_SHADER, fragmentSrc);
+ GLuint shaderPrg = linkProgram(vertex, geometry, fragment);
+
+ GLint prgStatus;
+ glGetProgramiv(shaderPrg, GL_LINK_STATUS, &prgStatus);
+ printf("shader link status: %s\n", prgStatus == GL_TRUE ? "ok" : "error");
+
+ GLint position = glGetAttribLocation(shaderPrg, "position");
+ glVertexAttribPointer(position, 2, GL_FLOAT, GL_FALSE, 2*sizeof(GLfloat), (void *)0);
+ glEnableVertexAttribArray(position);
+
+ while (1) {
+ while (XPending(dpy)) {
+ XEvent xev;
+ XNextEvent(dpy, &xev);
+ if (xev.type == Expose) {
+ XGetWindowAttributes(dpy, win, &gwa);
+ glViewport(0, 0, gwa.width, gwa.height);
+ } else if (xev.type == KeyPress) {
+ printf("btn:%3d\n", xev.xbutton.button);
+ if (xev.xbutton.button==24||xev.xbutton.button==9) {
+ glXMakeCurrent(dpy, None, NULL);
+ glXDestroyContext(dpy, ctx);
+ XDestroyWindow(dpy, win);
+ XCloseDisplay(dpy);
+ exit(0);
+ }
+ } else if (xev.type == ConfigureNotify) {
+ XConfigureEvent *ce = (XConfigureEvent *)&xev;
+ glViewport(0, 0, ce->width, ce->height);
+ }
+ }
+
+ glClearColor(0.1f, 0.1f, 0.15f, 1.0f);
+ glClear(GL_COLOR_BUFFER_BIT);
+
+ GLfloat iTime = glGetUniformLocation(shaderPrg, "iTime");
+ glUniform1d(iTime, current_ts()-t_started);
+ //printf("%f\n", fabsf(sinf(current_ts()-t_started)));
+
+ glUseProgram(shaderPrg);
+ glDrawArrays(GL_POINTS, 0, LEN(vertices));
+
+ glXSwapBuffers(dpy, win);
+ usleep(1000*7); // 144hz
+ }
+}