2010-07-07 11 views
16

Ich habe ein Programm, das ein physikalisches System simuliert, das sich im Laufe der Zeit ändert. Ich möchte in vorgegebenen Intervallen (etwa alle 10 Sekunden) eine Visualisierung des Zustandes der Simulation in eine Datei ausgeben. Ich möchte es so machen, dass es einfach ist, die Visualisierung auszuschalten und die Visualisierung überhaupt nicht auszugeben.Wie verwende ich GLUT/OpenGL zum Rendern in einer Datei?

Ich sehe OpenGL und GLUT als Grafik-Tools für die Visualisierung. Das Problem scheint jedoch zu sein, dass es zunächst so aussieht, als würde es nur in ein Fenster ausgeben und nicht in eine Datei ausgeben. Zweitens müssen Sie, um die Visualisierung zu generieren, GLUTMainLoop aufrufen und die Ausführung der Hauptfunktion stoppen - die einzigen Funktionen, die von da an aufgerufen werden, sind Aufrufe von der GUI. Ich möchte jedoch nicht, dass es sich um eine GUI-basierte Anwendung handelt - ich möchte, dass es nur eine Anwendung ist, die Sie über die Befehlszeile ausführen und eine Reihe von Bildern generiert. Gibt es eine Möglichkeit, dies in GLUT/OpenGL zu tun? Oder ist OpenGL das falsche Tool dafür komplett und ich sollte etwas anderes verwenden

+0

Wie groß ist der Zustand der Simulation, wenn Sie das statt der Visualisierung schreiben würden? –

Antwort

8

Sie mit ziemlicher Sicherheit nicht wollen GLUT, zu kopieren. Ihre Anforderungen passen nicht zu dem, was sie tun sollen (und selbst wenn Ihre Anforderungen ihrem beabsichtigten Zweck entsprechen, wollen Sie es normalerweise sowieso nicht).

Sie können OpenGL verwenden. Um eine Ausgabe in einer Datei zu generieren, müssen Sie OpenGL so einstellen, dass sie zu einer Textur rendert. Anschließend wird die resultierende Textur in den Hauptspeicher eingelesen und in einer Datei gespeichert. Zumindest auf einigen Systemen (z. B. Windows) bin ich ziemlich sicher, dass Sie noch ein Fenster erstellen und den Rendering-Kontext mit dem Fenster verknüpfen müssen, obwohl es wahrscheinlich in Ordnung sein wird, wenn das Fenster immer ausgeblendet ist.

1

Nicht sicher, OpenGL ist die beste Lösung.
Aber Sie können immer zu einem Offscreen-Puffer rendern.

Der typische Weg openGL Ausgabe in eine Datei zu schreiben, ist readPixels zu verwenden, um das resultierende Szene Pixel-Pixel in eine Bilddatei unabhängig

1

Sie könnten SFML http://www.sfml-dev.org/ verwenden. Sie können die Bildklasse zum Speichern der gerenderten Ausgabe verwenden.

http://www.sfml-dev.org/documentation/1.6/classsf_1_1Image.htm

gerenderten Ausgabe zu erhalten, können Sie auf eine Textur oder kopieren Sie Ihren Bildschirm übertragen.

Rendering auf eine Textur:

Kopieren Bildschirmausgabe:

37

Runnable PBO Beispiel

Das folgende Beispiel erzeugt entweder:

  • einen ppm pro Rahmen bei 200 FPS und keine zusätzlichen Abhängigkeiten
  • eine PNG pro Rahmen bei 600 FPS mit libpng
  • einen mpg für alle Rahmen bei 1200 FPS mit FFmpeg

auf einem ramfs. Je besser die Komprimierung, desto größer der FPS, also müssen wir Speicher IO gebunden sein.

FPS größer als 200 auf meinem 60 FPS-Bildschirm, und alle Bilder sind unterschiedlich, so bin ich sicher, dass sie nicht auf dem Bildschirm des FPS begrenzt ist.

glReadPixels ist die Schlüsselfunktion, die OpenGL Pixel vom Bildschirm liest. Sehen Sie sich auch das Setup unter init() an.

glReadPixels liest die untere Zeile von Pixeln zuerst, im Gegensatz zu den meisten Bildformate, so dass in der Regel erforderlich, konvertiert.

ERLEDIGEN: einen Weg finden, es auf einer Maschine ohne GUI (z X11) zu tun. Es scheint, dass OpenGL ist nicht nur für Off-Screen-Rendering gemacht, und dass das Lesen Pixel zurück zu der GPU ist an der Grenzfläche mit dem Fenstertechnik-System implementiert (z.B. GLX). Siehe: OpenGL without X.org in linux

TODO: Verwenden Sie ein 1x1-Fenster, machen Sie es nicht resizierbar, und verstecken Sie es, um die Dinge robuster zu machen. Wenn ich eines von beiden tue, schlägt das Rendering fehl, siehe Code-Kommentare. Das Verhindern der Größenänderung scheint impossible in Glut, aber GLFW supports it. Auf jeden Fall tun diejenigen, die nicht so wichtig, wie meine FPS wird durch die Bildschirmaktualisierungsfrequenz nicht darauf beschränkt, auch wenn offscreen ausgeschaltet ist.

/* Turn output methods on and off. */ 
#define PPM 1 
#define LIBPNG 1 
#define FFMPEG 1 

#include <assert.h> 
#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> 

#define GL_GLEXT_PROTOTYPES 1 
#include <GL/gl.h> 
#include <GL/glu.h> 
#include <GL/glut.h> 
#include <GL/glext.h> 

#if LIBPNG 
#include <png.h> 
#endif 

#if FFMPEG 
#include <libavcodec/avcodec.h> 
#include <libavutil/imgutils.h> 
#include <libavutil/opt.h> 
#include <libswscale/swscale.h> 
#endif 

enum Constants { SCREENSHOT_MAX_FILENAME = 256 }; 
static GLubyte *pixels = NULL; 
static GLuint fbo; 
static GLuint rbo_color; 
static GLuint rbo_depth; 
static const unsigned int HEIGHT = 100; 
static const unsigned int WIDTH = 100; 
static int offscreen = 1; 
static unsigned int max_nframes = 100; 
static unsigned int nframes = 0; 
static unsigned int time0; 

/* Model. */ 
static double angle; 
static double delta_angle; 

#if PPM 
/* 
Take screenshot with glReadPixels and save to a file in PPM format. 
- filename: file path to save to, without extension 
- width: screen width in pixels 
- height: screen height in pixels 
- pixels: intermediate buffer to avoid repeated mallocs across multiple calls. 
    Contents of this buffer do not matter. May be NULL, in which case it is initialized. 
    You must `free` it when you won't be calling this function anymore. 
*/ 
static void screenshot_ppm(const char *filename, unsigned int width, 
     unsigned int height, GLubyte **pixels) { 
    size_t i, j, k, cur; 
    const size_t format_nchannels = 3; 
    FILE *f = fopen(filename, "w"); 
    fprintf(f, "P3\n%d %d\n%d\n", width, height, 255); 
    *pixels = realloc(*pixels, format_nchannels * sizeof(GLubyte) * width * height); 
    glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE, *pixels); 
    for (i = 0; i < height; i++) { 
     for (j = 0; j < width; j++) { 
      cur = format_nchannels * ((height - i - 1) * width + j); 
      fprintf(f, "%3d %3d %3d ", (*pixels)[cur], (*pixels)[cur + 1], (*pixels)[cur + 2]); 
     } 
     fprintf(f, "\n"); 
    } 
    fclose(f); 
} 
#endif 

#if LIBPNG 
/* Adapted from https://github.com/cirosantilli/cpp-cheat/blob/19044698f91fefa9cb75328c44f7a487d336b541/png/open_manipulate_write.c */ 
static png_byte *png_bytes = NULL; 
static png_byte **png_rows = NULL; 
static void screenshot_png(const char *filename, unsigned int width, unsigned int height, 
     GLubyte **pixels, png_byte **png_bytes, png_byte ***png_rows) { 
    size_t i, nvals; 
    const size_t format_nchannels = 4; 
    FILE *f = fopen(filename, "wb"); 
    nvals = format_nchannels * width * height; 
    *pixels = realloc(*pixels, nvals * sizeof(GLubyte)); 
    *png_bytes = realloc(*png_bytes, nvals * sizeof(png_byte)); 
    *png_rows = realloc(*png_rows, height * sizeof(png_byte*)); 
    glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, *pixels); 
    for (i = 0; i < nvals; i++) 
     (*png_bytes)[i] = (*pixels)[i]; 
    for (i = 0; i < height; i++) 
     (*png_rows)[height - i - 1] = &(*png_bytes)[i * width * format_nchannels]; 
    png_structp png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); 
    if (!png) abort(); 
    png_infop info = png_create_info_struct(png); 
    if (!info) abort(); 
    if (setjmp(png_jmpbuf(png))) abort(); 
    png_init_io(png, f); 
    png_set_IHDR(
     png, 
     info, 
     width, 
     height, 
     8, 
     PNG_COLOR_TYPE_RGBA, 
     PNG_INTERLACE_NONE, 
     PNG_COMPRESSION_TYPE_DEFAULT, 
     PNG_FILTER_TYPE_DEFAULT 
    ); 
    png_write_info(png, info); 
    png_write_image(png, *png_rows); 
    png_write_end(png, NULL); 
    png_destroy_write_struct(&png, &info); 
    fclose(f); 
} 
#endif 

#if FFMPEG 
/* Adapted from: https://github.com/cirosantilli/cpp-cheat/blob/19044698f91fefa9cb75328c44f7a487d336b541/ffmpeg/encode.c */ 

static AVCodecContext *c = NULL; 
static AVFrame *frame; 
static AVPacket pkt; 
static FILE *file; 
static struct SwsContext *sws_context = NULL; 
static uint8_t *rgb = NULL; 

static void ffmpeg_encoder_set_frame_yuv_from_rgb(uint8_t *rgb) { 
    const int in_linesize[1] = { 4 * c->width }; 
    sws_context = sws_getCachedContext(sws_context, 
      c->width, c->height, AV_PIX_FMT_RGB32, 
      c->width, c->height, AV_PIX_FMT_YUV420P, 
      0, NULL, NULL, NULL); 
    sws_scale(sws_context, (const uint8_t * const *)&rgb, in_linesize, 0, 
      c->height, frame->data, frame->linesize); 
} 

void ffmpeg_encoder_start(const char *filename, int codec_id, int fps, int width, int height) { 
    AVCodec *codec; 
    int ret; 
    avcodec_register_all(); 
    codec = avcodec_find_encoder(codec_id); 
    if (!codec) { 
     fprintf(stderr, "Codec not found\n"); 
     exit(1); 
    } 
    c = avcodec_alloc_context3(codec); 
    if (!c) { 
     fprintf(stderr, "Could not allocate video codec context\n"); 
     exit(1); 
    } 
    c->bit_rate = 400000; 
    c->width = width; 
    c->height = height; 
    c->time_base.num = 1; 
    c->time_base.den = fps; 
    c->gop_size = 10; 
    c->max_b_frames = 1; 
    c->pix_fmt = AV_PIX_FMT_YUV420P; 
    if (codec_id == AV_CODEC_ID_H264) 
     av_opt_set(c->priv_data, "preset", "slow", 0); 
    if (avcodec_open2(c, codec, NULL) < 0) { 
     fprintf(stderr, "Could not open codec\n"); 
     exit(1); 
    } 
    file = fopen(filename, "wb"); 
    if (!file) { 
     fprintf(stderr, "Could not open %s\n", filename); 
     exit(1); 
    } 
    frame = av_frame_alloc(); 
    if (!frame) { 
     fprintf(stderr, "Could not allocate video frame\n"); 
     exit(1); 
    } 
    frame->format = c->pix_fmt; 
    frame->width = c->width; 
    frame->height = c->height; 
    ret = av_image_alloc(frame->data, frame->linesize, c->width, c->height, c->pix_fmt, 32); 
    if (ret < 0) { 
     fprintf(stderr, "Could not allocate raw picture buffer\n"); 
     exit(1); 
    } 
} 

void ffmpeg_encoder_finish(void) { 
    uint8_t endcode[] = { 0, 0, 1, 0xb7 }; 
    int got_output, ret; 
    do { 
     fflush(stdout); 
     ret = avcodec_encode_video2(c, &pkt, NULL, &got_output); 
     if (ret < 0) { 
      fprintf(stderr, "Error encoding frame\n"); 
      exit(1); 
     } 
     if (got_output) { 
      fwrite(pkt.data, 1, pkt.size, file); 
      av_packet_unref(&pkt); 
     } 
    } while (got_output); 
    fwrite(endcode, 1, sizeof(endcode), file); 
    fclose(file); 
    avcodec_close(c); 
    av_free(c); 
    av_freep(&frame->data[0]); 
    av_frame_free(&frame); 
} 

void ffmpeg_encoder_encode_frame(uint8_t *rgb) { 
    int ret, got_output; 
    ffmpeg_encoder_set_frame_yuv_from_rgb(rgb); 
    av_init_packet(&pkt); 
    pkt.data = NULL; 
    pkt.size = 0; 
    ret = avcodec_encode_video2(c, &pkt, frame, &got_output); 
    if (ret < 0) { 
     fprintf(stderr, "Error encoding frame\n"); 
     exit(1); 
    } 
    if (got_output) { 
     fwrite(pkt.data, 1, pkt.size, file); 
     av_packet_unref(&pkt); 
    } 
} 

void ffmpeg_encoder_glread_rgb(uint8_t **rgb, GLubyte **pixels, unsigned int width, unsigned int height) { 
    size_t i, j, k, cur_gl, cur_rgb, nvals; 
    const size_t format_nchannels = 4; 
    nvals = format_nchannels * width * height; 
    *pixels = realloc(*pixels, nvals * sizeof(GLubyte)); 
    *rgb = realloc(*rgb, nvals * sizeof(uint8_t)); 
    /* Get RGBA to align to 32 bits instead of just 24 for RGB. May be faster for FFmpeg. */ 
    glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, *pixels); 
    for (i = 0; i < height; i++) { 
     for (j = 0; j < width; j++) { 
      cur_gl = format_nchannels * (width * (height - i - 1) + j); 
      cur_rgb = format_nchannels * (width * i + j); 
      for (k = 0; k < format_nchannels; k++) 
       (*rgb)[cur_rgb + k] = (*pixels)[cur_gl + k]; 
     } 
    } 
} 
#endif 

static int model_init(void) { 
    angle = 0; 
    delta_angle = 1; 
} 

static int model_update(void) { 
    angle += delta_angle; 
    return 0; 
} 

static int model_finished(void) { 
    return nframes >= max_nframes; 
} 

static void init(void) { 
    int glget; 

    if (offscreen) { 
     /* Framebuffer */ 
     glGenFramebuffers(1, &fbo); 
     glBindFramebuffer(GL_FRAMEBUFFER, fbo); 

     /* Color renderbuffer. */ 
     glGenRenderbuffers(1, &rbo_color); 
     glBindRenderbuffer(GL_RENDERBUFFER, rbo_color); 
     /* Storage must be one of: */ 
     /* GL_RGBA4, GL_RGB565, GL_RGB5_A1, GL_DEPTH_COMPONENT16, GL_STENCIL_INDEX8. */ 
     glRenderbufferStorage(GL_RENDERBUFFER, GL_RGB565, WIDTH, HEIGHT); 
     glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, rbo_color); 

     /* Depth renderbuffer. */ 
     glGenRenderbuffers(1, &rbo_depth); 
     glBindRenderbuffer(GL_RENDERBUFFER, rbo_depth); 
     glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, WIDTH, HEIGHT); 
     glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, rbo_depth); 

     glReadBuffer(GL_COLOR_ATTACHMENT0); 

     /* Sanity check. */ 
     assert(glCheckFramebufferStatus(GL_FRAMEBUFFER)); 
     glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &glget); 
     assert(WIDTH * HEIGHT < (unsigned int)glget); 
    } else { 
     glReadBuffer(GL_BACK); 
    } 

    glClearColor(0.0, 0.0, 0.0, 0.0); 
    glEnable(GL_DEPTH_TEST); 
    glPixelStorei(GL_UNPACK_ALIGNMENT, 1); 
    glViewport(0, 0, WIDTH, HEIGHT); 
    glMatrixMode(GL_PROJECTION); 
    glLoadIdentity(); 
    glMatrixMode(GL_MODELVIEW); 

    time0 = glutGet(GLUT_ELAPSED_TIME); 
    model_init(); 
#if FFMPEG 
    ffmpeg_encoder_start("tmp.mpg", AV_CODEC_ID_MPEG1VIDEO, 25, WIDTH, HEIGHT); 
#endif 
} 

static void deinit(void) { 
    printf("FPS = %f\n", 1000.0 * nframes/(double)(glutGet(GLUT_ELAPSED_TIME) - time0)); 
    free(pixels); 
#if LIBPNG 
    free(png_bytes); 
    free(png_rows); 
#endif 
#if FFMPEG 
    ffmpeg_encoder_finish(); 
    free(rgb); 
#endif 
    if (offscreen) { 
     glDeleteFramebuffers(1, &fbo); 
     glDeleteRenderbuffers(1, &rbo_color); 
     glDeleteRenderbuffers(1, &rbo_depth); 
    } 
} 

static void draw_scene(void) { 
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 
    glLoadIdentity(); 
    glRotatef(angle, 0.0f, 0.0f, -1.0f); 
    glBegin(GL_TRIANGLES); 
    glColor3f(1.0f, 0.0f, 0.0f); 
    glVertex3f(0.0f, 0.5f, 0.0f); 
    glColor3f(0.0f, 1.0f, 0.0f); 
    glVertex3f(-0.5f, -0.5f, 0.0f); 
    glColor3f(0.0f, 0.0f, 1.0f); 
    glVertex3f(0.5f, -0.5f, 0.0f); 
    glEnd(); 
} 

static void display(void) { 
    char extension[SCREENSHOT_MAX_FILENAME]; 
    char filename[SCREENSHOT_MAX_FILENAME]; 
    draw_scene(); 
    if (offscreen) { 
     glFlush(); 
    } else { 
     glutSwapBuffers(); 
    } 
#if PPM 
    snprintf(filename, SCREENSHOT_MAX_FILENAME, "tmp%d.ppm", nframes); 
    screenshot_ppm(filename, WIDTH, HEIGHT, &pixels); 
#endif 
#if LIBPNG 
    snprintf(filename, SCREENSHOT_MAX_FILENAME, "tmp%d.png", nframes); 
    screenshot_png(filename, WIDTH, HEIGHT, &pixels, &png_bytes, &png_rows); 
#endif 
# if FFMPEG 
    frame->pts = nframes; 
    ffmpeg_encoder_glread_rgb(&rgb, &pixels, WIDTH, HEIGHT); 
    ffmpeg_encoder_encode_frame(rgb); 
#endif 
    nframes++; 
    if (model_finished()) 
     exit(EXIT_SUCCESS); 
} 

static void idle(void) { 
    while (model_update()); 
    glutPostRedisplay(); 
} 

int main(int argc, char **argv) { 
    GLint glut_display; 
    glutInit(&argc, argv); 
    if (argc > 1) 
     offscreen = 0; 
    if (offscreen) { 
     /* TODO: if we use anything smaller than the window, it only renders a smaller version of things. */ 
     /*glutInitWindowSize(50, 50);*/ 
     glutInitWindowSize(WIDTH, HEIGHT); 
     glut_display = GLUT_SINGLE; 
    } else { 
     glutInitWindowSize(WIDTH, HEIGHT); 
     glutInitWindowPosition(100, 100); 
     glut_display = GLUT_DOUBLE; 
    } 
    glutInitDisplayMode(glut_display | GLUT_RGBA | GLUT_DEPTH); 
    glutCreateWindow(argv[0]); 
    if (offscreen) { 
     /* TODO: if we hide the window the program blocks. */ 
     /*glutHideWindow();*/ 
    } 
    init(); 
    glutDisplayFunc(display); 
    glutIdleFunc(idle); 
    atexit(deinit); 
    glutMainLoop(); 
    return EXIT_SUCCESS; 
} 

On GitHub.

Compile mit:

gcc main.c -lGL -lGLU -lglut #-lpng -lavcodec -lswscale -lavutil 

Run "Off-Screen" (meist TODO, arbeitet aber hat keinen Vorteil):

./a.out 

Run auf dem Bildschirm (nicht meine FPS entweder begrenzen):

Getestet auf Ubuntu 15.10, OpenGL 4.4.0 NVIDIA 352.63, Lenovo Thinkpad T430.

Andere Optionen neben PBO

  • machen zu Backbuffer (default Platz machen)
  • auf eine Textur auf ein Pixelbuffer Objekt (PBO)

Framebuffer

  • render und Pixelbuffer sind besser als der Backbuffer und die Textur, da sie für das Zurücklesen von Daten auf CP gemacht werden U, während der Backbuffer und die Texturen auf der GPU bleiben und auf dem Bildschirm angezeigt werden.

    PBO ist für asynchrone Übertragungen, so dass ich denke, dass wir es nicht brauchen, finden Sie unter: What are the differences between a Frame Buffer Object and a Pixel Buffer Object in OpenGL?,

    Vielleicht Off-Screen-Mesa lohnt ein Blick in ist: http://www.mesa3d.org/osmesa.html

    apiretrace

    https://github.com/apitrace/apitrace

    Nur funktioniert, und Sie müssen Ihren Code überhaupt nicht ändern:

    git clone https://github.com/apitrace/apitrace 
    cd apitrace 
    git checkout 7.0 
    mkdir build 
    cd build 
    cmake .. 
    make 
    # Creates opengl_executable.out.trace 
    ./apitrace /path/to/opengl_executable.out 
    ./apitrace dump-images opengl_executable.out.trace 
    

    Sie haben jetzt eine Reihe von Screenshots genannt als:

    animation.out.<n>.png 
    

    ERLEDIGEN: Arbeitsprinzip.

    Die docs auch vorschlagen, das für Video:

    apitrace dump-images -o - application.trace \ 
        | ffmpeg -r 30 -f image2pipe -vcodec ppm -i pipe: -vcodec mpeg4 -y output.mp4 
    

    Vulkan

    Es scheint, dass Vulkan ausgelegt ist Offscreen Rendering besser als OpenGL zu unterstützen.

    Dies wird auf dieser NVIDIA Überblick erwähnt: https://developer.nvidia.com/transitioning-opengl-vulkan

    ein lauffähiges Beispiel gibt an ist: https://github.com/SaschaWillems/Vulkan/blob/0616eeff4e697e4cd23cb9c97f5dd83afb79d908/offscreen/offscreen.cpp aber ich habe noch nicht erhalten Vulkan laufen verwaltet.1 kloc :-)

    Verwandte: Is it possible to do offscreen rendering without Surface in Vulkan?

    Bibliographie

    FBO größer als Fenster:

    kein Fenster/X11:

  • +1

    Nach der Zeile 'png_write_end (png, NULL);' sollten Sie die PNG-Strukturen mit einer Zeile wie 'png_destroy_write_struct (& png, &info);' ausplanen Vielen Dank für das handliche Code – mgmalheiros

    +0

    @mgmalheiros boa, Marcelo –

    1

    nicht von den anderen ausgezeichneten Antworten zu nehmen, aber wenn Sie ein vorhandenes Beispiel wollen wir nun als Teil des Tests Framework in Openscad, Rendering zu tun Offscreen GL-Rendering für ein paar Jahre haben. PNG-Dateien von der Kommandozeile. Die relevanten Dateien sind unter https://github.com/openscad/openscad/tree/master/src unter dem Offscreen * .cc

    Es läuft auf OSX (CGL), Linux X11 (GLX), BSD (GLX) und Windows (WGL), mit einigen Macken aufgrund von Treiberunterschieden. Der grundlegende Trick besteht darin, zu vergessen, ein Fenster zu öffnen (wie Douglas Adams sagt, der Trick zum Fliegen ist zu vergessen, den Boden zu schlagen). Es läuft sogar auf "kopflos" linux/bsd, wenn Sie einen virtuellen X11-Server wie Xvfb oder Xvnc haben. Es gibt auch die Möglichkeit, Software Rendering auf Linux/BSD zu verwenden, indem Sie die Umgebungsvariable LIBGL_ALWAYS_SOFTWARE = ​​1 setzen, bevor Sie Ihr Programm ausführen, was in einigen Situationen hilfreich sein kann.

    Dies ist nicht das einzige System, das zu tun, ich glaube, das VTK-Imaging-System tut etwas ähnliches.

    Dieser Code ist ein bisschen alt in seiner Methoden, (ich habe den GLX-Code aus Brian Pauls glxgears ausgerissen), besonders als neue Systeme kommen, OSMesa, Mir, Wayland, EGL, Android, Vulkan, etc., aber beachten Sie Die OffscreenXXX.cc-Dateinamen, wobei XXX das GL-Kontext-Subsystem ist, können theoretisch in verschiedene Kontextgeneratoren portiert werden.

    +1

    ein Auszug.! minimal lauffähiges Beispiel und habe mein upvote :-) –

    Verwandte Themen