录制的原理就是:按照帧率,不断地采集一幅幅模拟的图像,然后通过ffmepg制作成视频


This code sample simulates the passive dynamics of a given model, renders it offscreen, reads the color and depth pixel values, and saves them into a raw data file that can then be converted into a movie file with tools such as ffmpeg. The rendering is simplified compared to simulate.cc because there is no user interaction, visualization options or timing; instead we simply render with the default settings as fast as possible. The dimensions and number of multi-samples for the offscreen buffer are specified in the MuJoCo model, while the simulation duration, frames-per-second to be rendered (usually much less than the physics simulation rate), and output file name are specified as command-line arguments. For example, a 5 second animation at 60 frames per second is created with:

render humanoid.xml 5 60 rgb.out

The default humanoid.xml model specifies offscreen rendering with 800x800 resolution. With this information in hand, we can compress the (large) raw date file into a playable movie file:

ffmpeg -f rawvideo -pixel_format rgb24 -video_size 800x800-framerate 60 -i rgb.out -vf "vflip" video.mp4

但是我试验失败好几次,发现ffmpeg里面的有尺寸的要求,我们将
模型也做个尺寸设置:

  <visual><map force="0.1" zfar="30"/><rgba haze="0.15 0.25 0.35 1"/><quality shadowsize="4096"/><global offwidth="800" offheight="800"/></visual>

这样就成功制作出视频了


将record.cc编译到自己项目的问题

目前来说,我没办法直接将录制功能嵌入到项目,感觉录制功能
很吃资源,而且录制时用OPENGL来做,测试发现会跟mujoco的UI
有点冲突,导致mujoco的UI不显示。

可能会遇到的问题

使用Mujoco自带的record.cc来改就可以
但是要注意,用g++编译,如果使用gcc编译,
可能会报cstdio找不到这样的错误。
另外,文件名不能过长,不然std::fopen会打不开
record.cc有用到mujoco的array_safe.h,这个是源码提供,可以直接复制
到自己的项目里面

例子:测试可以通过

Mujoco录制模拟视频


进一步的改进和封装

官方原始代码,用起来有点麻烦,需要将其代码摘抄出来,嵌入到我们自己的项目中。
这样子直接插入一大段录制功能的代码,容易让我们自己的项目代码不好看。
我做了代码提取和封装,这样只需在项目中简单调用几个接口就可以了

.h头文件

/*** * created by Feisy,2022-02-23*/
#ifndef RECODER_H_
#define RECODER_H_#include "mujoco.h"
#include <string>// select EGL, OSMESA or GLFW
#if defined(MJ_EGL)
#include <EGL/egl.h>
#elif defined(MJ_OSMESA)
#include <GL/osmesa.h>
OSMesaContext ctx;
unsigned char buffer[10000000];
#else
#include <GLFW/glfw3.h>
#endif#include "array_safety.h"
namespace mju = ::mujoco::sample_util;class Recoder
{
public:Recoder();virtual ~Recoder();public:int Init(mjModel* m);int Setting(std::string &save_file,int fps,int duration);/*** cur_time is d->time or other type time you used,also yi can be real elapsed time*/int Run(mjModel* m,mjData* d ,mjvCamera &cam,mjvOption &opt,double cur_time);void Release();private:void init_opengl();private:bool m_bInit;
private:int m_fps;int m_duration;std::string m_file;private:double m_frametime;int m_framecount;private:int m_W;int m_H;std::FILE *m_fp;unsigned char *m_rgb;float *m_depth;mjrRect m_viewport;mjvScene m_scn;mjrContext m_con;
};#endif

.cpp文件

#include "Recoder.h"Recoder::Recoder()
{m_bInit = false;
}
Recoder::~Recoder()
{
}int Recoder::Init(mjModel *m)
{init_opengl();// should call firstmjv_defaultScene(&m_scn);mjr_defaultContext(&m_con);mjv_makeScene(m, &m_scn, 2000);mjr_makeContext(m, &m_con, 200);// set rendering to offscreen buffermjr_setBuffer(mjFB_OFFSCREEN, &m_con);if (m_con.currentBuffer != mjFB_OFFSCREEN){std::printf("Warning: offscreen rendering not supported, using default/window framebuffer\n");}// get size of active renderbufferm_viewport = mjr_maxViewport(&m_con);m_W = m_viewport.width;m_H = m_viewport.height;// allocate rgb and depth buffersm_rgb = (unsigned char *)std::malloc(3 * m_W * m_H);m_depth = (float *)std::malloc(sizeof(float) * m_W * m_H);if (!m_rgb || !m_depth){mju_error("Could not allocate buffers");}m_frametime = 0.0;m_framecount = 0;m_bInit = true;printf("recoder init finish\n");return 0;
}int Recoder::Setting(std::string &save_file, int fps, int duration)
{m_fps = fps;m_duration = duration;m_file = std::string(save_file.data());// create output rgb filem_fp = std::fopen(m_file.data(), "wb");if (!m_fp){mju_error("Could not open rgbfile for writing");}printf("recoder setting finish\n");return 0;
}int Recoder::Run(mjModel *m, mjData *d, mjvCamera &cam, mjvOption &opt, double cur_time)
{if (cur_time > m_duration){return 1;}// render new frame if it is time (or first frame)if ((cur_time - m_frametime) > 1 / m_fps || m_frametime == 0){// update abstract scenemjv_updateScene(m, d, &opt, NULL, &cam, mjCAT_ALL, &m_scn);// render scene in offscreen buffermjr_render(m_viewport, &m_scn, &m_con);// add time stamp in upper-left cornerchar stamp[50];mju::sprintf_arr(stamp, "Time = %.3f", cur_time);mjr_overlay(mjFONT_NORMAL, mjGRID_TOPLEFT, m_viewport, stamp, NULL, &m_con);// read rgb and depth buffersmjr_readPixels(m_rgb, m_depth, m_viewport, &m_con);// insert subsampled depth image in lower-left corner of rgb imageconst int NS = 3; // depth image sub-samplingfor (int r = 0; r < m_H; r += NS)for (int c = 0; c < m_W; c += NS){int adr = (r / NS) * m_W + c / NS;m_rgb[3 * adr] = m_rgb[3 * adr + 1] = m_rgb[3 * adr + 2] = (unsigned char)((1.0f - m_depth[r * m_W + c]) * 255.0f);}// write rgb image to filestd::fwrite(m_rgb, 3, m_W * m_H, m_fp);// print every 10 frames: '.' if ok, 'x' if OpenGL errorif (((m_framecount++) % 10) == 0){if (mjr_getError()){std::printf("x");}else{std::printf(".");}}// save simulation timem_frametime = cur_time;}return 0;
}void Recoder::Release()
{if (false == m_bInit){return;}m_bInit = false;mjr_freeContext(&m_con);mjv_freeScene(&m_scn);// close file, free buffersstd::fclose(m_fp);std::free(m_rgb);std::free(m_depth);#if defined(MJ_EGL)// get current displayEGLDisplay eglDpy = eglGetCurrentDisplay();if (eglDpy == EGL_NO_DISPLAY){return;}// get current contextEGLContext eglCtx = eglGetCurrentContext();// release contexteglMakeCurrent(eglDpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);// destroy context if validif (eglCtx != EGL_NO_CONTEXT){eglDestroyContext(eglDpy, eglCtx);}// terminate displayeglTerminate(eglDpy);//------------------------ OSMESA
#elif defined(MJ_OSMESA)OSMesaDestroyContext(ctx);//------------------------ GLFW
#else
// terminate GLFW (crashes with Linux NVidia drivers)
#if defined(__APPLE__) || defined(_WIN32)glfwTerminate();
#endif
#endif
}// create OpenGL context/window
void Recoder::init_opengl()
{//------------------------ EGL
#if defined(MJ_EGL)// desired configconst EGLint configAttribs[] = {EGL_RED_SIZE, 8,EGL_GREEN_SIZE, 8,EGL_BLUE_SIZE, 8,EGL_ALPHA_SIZE, 8,EGL_DEPTH_SIZE, 24,EGL_STENCIL_SIZE, 8,EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER,EGL_SURFACE_TYPE, EGL_PBUFFER_BIT,EGL_RENDERABLE_TYPE, EGL_OPENGL_BIT,EGL_NONE};// get default displayEGLDisplay eglDpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);if (eglDpy == EGL_NO_DISPLAY){mju_error_i("Could not get EGL display, error 0x%x\n", eglGetError());}// initializeEGLint major, minor;if (eglInitialize(eglDpy, &major, &minor) != EGL_TRUE){mju_error_i("Could not initialize EGL, error 0x%x\n", eglGetError());}// choose configEGLint numConfigs;EGLConfig eglCfg;if (eglChooseConfig(eglDpy, configAttribs, &eglCfg, 1, &numConfigs) != EGL_TRUE){mju_error_i("Could not choose EGL config, error 0x%x\n", eglGetError());}// bind OpenGL APIif (eglBindAPI(EGL_OPENGL_API) != EGL_TRUE){mju_error_i("Could not bind EGL OpenGL API, error 0x%x\n", eglGetError());}// create contextEGLContext eglCtx = eglCreateContext(eglDpy, eglCfg, EGL_NO_CONTEXT, NULL);if (eglCtx == EGL_NO_CONTEXT){mju_error_i("Could not create EGL context, error 0x%x\n", eglGetError());}// make context current, no surface (let OpenGL handle FBO)if (eglMakeCurrent(eglDpy, EGL_NO_SURFACE, EGL_NO_SURFACE, eglCtx) != EGL_TRUE){mju_error_i("Could not make EGL context current, error 0x%x\n", eglGetError());}//------------------------ OSMESA
#elif defined(MJ_OSMESA)// create contextctx = OSMesaCreateContextExt(GL_RGBA, 24, 8, 8, 0);if (!ctx){mju_error("OSMesa context creation failed");}// make currentif (!OSMesaMakeCurrent(ctx, buffer, GL_UNSIGNED_BYTE, 800, 800)){mju_error("OSMesa make current failed");}//------------------------ GLFW
#else// init GLFWif (!glfwInit()){mju_error("Could not initialize GLFW");}// create invisible window, single-bufferedglfwWindowHint(GLFW_VISIBLE, 0);glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_FALSE);GLFWwindow *window = glfwCreateWindow(800, 800, "Invisible window", NULL, NULL);if (!window){mju_error("Could not create GLFW window");}// make context currentglfwMakeContextCurrent(window);
#endif
}

原始官方的代码

// Copyright 2021 DeepMind Technologies Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.#include <cstdio>
#include <cstdlib>
#include <cstring>#include "mujoco.h"// select EGL, OSMESA or GLFW
#if defined(MJ_EGL)#include <EGL/egl.h>
#elif defined(MJ_OSMESA)#include <GL/osmesa.h>OSMesaContext ctx;unsigned char buffer[10000000];
#else#include <GLFW/glfw3.h>
#endif#include "array_safety.h"
namespace mju = ::mujoco::sample_util;//-------------------------------- global data ------------------------------------------
char xmlpath[] = "./dbpendulum/doublependulum.xml";
char rgb_file[] = "./rgb.out";
// MuJoCo model and data
mjModel* m = 0;
mjData* d = 0;// MuJoCo visualization
mjvScene scn;//关于使用OPENGL渲染的设置
mjvCamera cam;
mjvOption opt;
mjrContext con;//关于OPENGL的上下文//-------------------------------- utility functions ------------------------------------// load model, init simulation and rendering
//加载模型,以及设置相机的参数
void initMuJoCo(const char* filename) {// load and compilechar error[1000] = "Could not load binary model";if (std::strlen(filename)>4 && !std::strcmp(filename+std::strlen(filename)-4, ".mjb")) {m = mj_loadModel(filename, 0);} else {m = mj_loadXML(filename, 0, error, 1000);}if (!m) {mju_error_s("Load model error: %s", error);}// make data, run one computation to initialize all fieldsd = mj_makeData(m);mj_forward(m, d);// initialize visualization data structuresmjv_defaultCamera(&cam);mjv_defaultOption(&opt);mjv_defaultScene(&scn);mjr_defaultContext(&con);// create scene and contextmjv_makeScene(m, &scn, 2000);mjr_makeContext(m, &con, 200);// center and scale view//这里的相机是看向一个固定点,对于动态移动的物体,考虑用d->qpos等信息动态追踪double arr_view[] = {89.608063, -11.588379, 5, 0.000000, 0.000000, 2.000000};cam.azimuth = arr_view[0];cam.elevation = arr_view[1];cam.distance = arr_view[2];cam.lookat[0] = arr_view[3];cam.lookat[1] = arr_view[4];cam.lookat[2] = arr_view[5];}// deallocate everything
void closeMuJoCo(void) {mj_deleteData(d);mj_deleteModel(m);mjr_freeContext(&con);mjv_freeScene(&scn);
}// create OpenGL context/window
//OPENGL环境的构建
void initOpenGL(void) {//------------------------ EGL
#if defined(MJ_EGL)// desired configconst EGLint configAttribs[] = {EGL_RED_SIZE,           8,EGL_GREEN_SIZE,         8,EGL_BLUE_SIZE,          8,EGL_ALPHA_SIZE,         8,EGL_DEPTH_SIZE,         24,EGL_STENCIL_SIZE,       8,EGL_COLOR_BUFFER_TYPE,  EGL_RGB_BUFFER,EGL_SURFACE_TYPE,       EGL_PBUFFER_BIT,EGL_RENDERABLE_TYPE,    EGL_OPENGL_BIT,EGL_NONE};// get default displayEGLDisplay eglDpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);if (eglDpy==EGL_NO_DISPLAY) {mju_error_i("Could not get EGL display, error 0x%x\n", eglGetError());}// initializeEGLint major, minor;if (eglInitialize(eglDpy, &major, &minor)!=EGL_TRUE) {mju_error_i("Could not initialize EGL, error 0x%x\n", eglGetError());}// choose configEGLint numConfigs;EGLConfig eglCfg;if (eglChooseConfig(eglDpy, configAttribs, &eglCfg, 1, &numConfigs)!=EGL_TRUE) {mju_error_i("Could not choose EGL config, error 0x%x\n", eglGetError());}// bind OpenGL APIif (eglBindAPI(EGL_OPENGL_API)!=EGL_TRUE) {mju_error_i("Could not bind EGL OpenGL API, error 0x%x\n", eglGetError());}// create contextEGLContext eglCtx = eglCreateContext(eglDpy, eglCfg, EGL_NO_CONTEXT, NULL);if (eglCtx==EGL_NO_CONTEXT) {mju_error_i("Could not create EGL context, error 0x%x\n", eglGetError());}// make context current, no surface (let OpenGL handle FBO)if (eglMakeCurrent(eglDpy, EGL_NO_SURFACE, EGL_NO_SURFACE, eglCtx)!=EGL_TRUE) {mju_error_i("Could not make EGL context current, error 0x%x\n", eglGetError());}//------------------------ OSMESA
#elif defined(MJ_OSMESA)// create contextctx = OSMesaCreateContextExt(GL_RGBA, 24, 8, 8, 0);if (!ctx) {mju_error("OSMesa context creation failed");}// make currentif (!OSMesaMakeCurrent(ctx, buffer, GL_UNSIGNED_BYTE, 800, 800)) {mju_error("OSMesa make current failed");}//------------------------ GLFW
#else// init GLFWif (!glfwInit()) {mju_error("Could not initialize GLFW");}// create invisible window, single-bufferedglfwWindowHint(GLFW_VISIBLE, 0);glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_FALSE);GLFWwindow* window = glfwCreateWindow(800, 800, "Invisible window", NULL, NULL);if (!window) {mju_error("Could not create GLFW window");}// make context currentglfwMakeContextCurrent(window);
#endif
}// close OpenGL context/window
void closeOpenGL(void) {//------------------------ EGL
#if defined(MJ_EGL)// get current displayEGLDisplay eglDpy = eglGetCurrentDisplay();if (eglDpy==EGL_NO_DISPLAY) {return;}// get current contextEGLContext eglCtx = eglGetCurrentContext();// release contexteglMakeCurrent(eglDpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);// destroy context if validif (eglCtx!=EGL_NO_CONTEXT) {eglDestroyContext(eglDpy, eglCtx);}// terminate displayeglTerminate(eglDpy);//------------------------ OSMESA
#elif defined(MJ_OSMESA)OSMesaDestroyContext(ctx);//------------------------ GLFW
#else// terminate GLFW (crashes with Linux NVidia drivers)#if defined(__APPLE__) || defined(_WIN32)glfwTerminate();#endif
#endif
}//-------------------------------- main function ----------------------------------------int main(int argc, const char** argv) {// parse numeric argumentsdouble duration = 10, fps = 60;// initialize OpenGL and MuJoCoinitOpenGL();initMuJoCo(xmlpath);// set rendering to offscreen buffermjr_setBuffer(mjFB_OFFSCREEN, &con);if (con.currentBuffer!=mjFB_OFFSCREEN) {std::printf("Warning: offscreen rendering not supported, using default/window framebuffer\n");}// get size of active renderbuffermjrRect viewport =  mjr_maxViewport(&con);int W = viewport.width;int H = viewport.height;// allocate rgb and depth buffers//一幅图像有长和宽,每个像素个R,G,B三个通道,depth是OPENGL的概念unsigned char* rgb = (unsigned char*)std::malloc(3*W*H);float* depth = (float*)std::malloc(sizeof(float)*W*H);if (!rgb || !depth) {mju_error("Could not allocate buffers");}// create output rgb filestd::FILE* fp = std::fopen(rgb_file, "wb");if (!fp) {mju_error("Could not open rgbfile for writing");}d->qpos[0] = 0.5;// main loopdouble frametime = 0;int framecount = 0;while (d->time<duration) {// render new frame if it is time (or first frame)if ((d->time-frametime)>1/fps || frametime==0) {// update abstract scene 模型以及状态刷新到场景中mjv_updateScene(m, d, &opt, NULL, &cam, mjCAT_ALL, &scn);// render scene in offscreen buffer 将场景画出来mjr_render(viewport, &scn, &con);// add time stamp in upper-left cornerchar stamp[50];mju::sprintf_arr(stamp, "Time = %.3f", d->time);mjr_overlay(mjFONT_NORMAL, mjGRID_TOPLEFT, viewport, stamp, NULL, &con);// read rgb and depth buffers 从OPENGL的上下文中读取出当前画好的内容mjr_readPixels(rgb, depth, viewport, &con);// insert subsampled depth image in lower-left corner of rgb imageconst int NS = 3;           // depth image sub-samplingfor (int r=0; r<H; r+=NS)for (int c=0; c<W; c+=NS) {int adr = (r/NS)*W + c/NS;rgb[3*adr] = rgb[3*adr+1] = rgb[3*adr+2] = (unsigned char)((1.0f-depth[r*W+c])*255.0f);}// write rgb image to filestd::fwrite(rgb, 3, W*H, fp);// print every 10 frames: '.' if ok, 'x' if OpenGL errorif (((framecount++)%10)==0) {if (mjr_getError()) {std::printf("x");} else {std::printf(".");}}// save simulation timeframetime = d->time;}// advance simulationmj_step(m, d);}std::printf("\n");// close file, free buffersstd::fclose(fp);std::free(rgb);std::free(depth);// close MuJoCo and OpenGLcloseMuJoCo();closeOpenGL();return 1;
}

#MAC
#COMMON=-O2 -I../../include -L../../bin -mavx -pthread
#LIBS = -w -lmujoco200 -lglfw.3
#CC = gcc#LINUX
#COMMON=-O2 -I../../include -L../../bin -mavx -pthread -Wl,-rpath,'$$ORIGIN'
#LIBS = -lmujoco200 -lGL -lm -lglew ../../bin/libglfw.so.3
COMMON=-O2   -I../include -L../bin -mavx -pthread -Wl,-rpath,'$$ORIGIN'
LIBS = ../bin/libmujoco.so -lGL -lm ../bin/libglew.so ../bin/libglfw.so.3
CC = g++#WINDOWS
#COMMON=/O2 /MT /EHsc /arch:AVX /I../../include /Fe../../bin/
#LIBS = ../../bin/glfw3.lib  ../../bin/mujoco200.lib
#CC = clROOT = dbpendulumRECORD=db_recordall:$(CC) $(COMMON) main.c $(LIBS) -o ../bin/$(ROOT)$(CC) $(COMMON) record.c $(LIBS) -o ../bin/$(RECORD)main.o:$(CC) $(COMMON) -c main.crecord.o:$(CC) $(COMMON) -c record.cclean:rm *.o ../../bin/$(ROOT)

record.cc代码

// Copyright 2021 DeepMind Technologies Limited
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.#include <cstdio>
#include <cstdlib>
#include <cstring>#include "mujoco.h"// select EGL, OSMESA or GLFW
#if defined(MJ_EGL)#include <EGL/egl.h>
#elif defined(MJ_OSMESA)#include <GL/osmesa.h>OSMesaContext ctx;unsigned char buffer[10000000];
#else#include <GLFW/glfw3.h>
#endif#include "array_safety.h"
namespace mju = ::mujoco::sample_util;//-------------------------------- global data ------------------------------------------// MuJoCo model and data
mjModel* m = 0;
mjData* d = 0;// MuJoCo visualization
mjvScene scn;
mjvCamera cam;
mjvOption opt;
mjrContext con;//-------------------------------- utility functions ------------------------------------// load model, init simulation and rendering
void initMuJoCo(const char* filename) {// load and compilechar error[1000] = "Could not load binary model";if (std::strlen(filename)>4 && !std::strcmp(filename+std::strlen(filename)-4, ".mjb")) {m = mj_loadModel(filename, 0);} else {m = mj_loadXML(filename, 0, error, 1000);}if (!m) {mju_error_s("Load model error: %s", error);}// make data, run one computation to initialize all fieldsd = mj_makeData(m);mj_forward(m, d);// initialize visualization data structuresmjv_defaultCamera(&cam);mjv_defaultOption(&opt);mjv_defaultScene(&scn);mjr_defaultContext(&con);// create scene and contextmjv_makeScene(m, &scn, 2000);mjr_makeContext(m, &con, 200);// center and scale viewcam.lookat[0] = m->stat.center[0];cam.lookat[1] = m->stat.center[1];cam.lookat[2] = m->stat.center[2];cam.distance = 1.5 * m->stat.extent;
}// deallocate everything
void closeMuJoCo(void) {mj_deleteData(d);mj_deleteModel(m);mjr_freeContext(&con);mjv_freeScene(&scn);
}// create OpenGL context/window
void initOpenGL(void) {//------------------------ EGL
#if defined(MJ_EGL)// desired configconst EGLint configAttribs[] = {EGL_RED_SIZE,           8,EGL_GREEN_SIZE,         8,EGL_BLUE_SIZE,          8,EGL_ALPHA_SIZE,         8,EGL_DEPTH_SIZE,         24,EGL_STENCIL_SIZE,       8,EGL_COLOR_BUFFER_TYPE,  EGL_RGB_BUFFER,EGL_SURFACE_TYPE,       EGL_PBUFFER_BIT,EGL_RENDERABLE_TYPE,    EGL_OPENGL_BIT,EGL_NONE};// get default displayEGLDisplay eglDpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);if (eglDpy==EGL_NO_DISPLAY) {mju_error_i("Could not get EGL display, error 0x%x\n", eglGetError());}// initializeEGLint major, minor;if (eglInitialize(eglDpy, &major, &minor)!=EGL_TRUE) {mju_error_i("Could not initialize EGL, error 0x%x\n", eglGetError());}// choose configEGLint numConfigs;EGLConfig eglCfg;if (eglChooseConfig(eglDpy, configAttribs, &eglCfg, 1, &numConfigs)!=EGL_TRUE) {mju_error_i("Could not choose EGL config, error 0x%x\n", eglGetError());}// bind OpenGL APIif (eglBindAPI(EGL_OPENGL_API)!=EGL_TRUE) {mju_error_i("Could not bind EGL OpenGL API, error 0x%x\n", eglGetError());}// create contextEGLContext eglCtx = eglCreateContext(eglDpy, eglCfg, EGL_NO_CONTEXT, NULL);if (eglCtx==EGL_NO_CONTEXT) {mju_error_i("Could not create EGL context, error 0x%x\n", eglGetError());}// make context current, no surface (let OpenGL handle FBO)if (eglMakeCurrent(eglDpy, EGL_NO_SURFACE, EGL_NO_SURFACE, eglCtx)!=EGL_TRUE) {mju_error_i("Could not make EGL context current, error 0x%x\n", eglGetError());}//------------------------ OSMESA
#elif defined(MJ_OSMESA)// create contextctx = OSMesaCreateContextExt(GL_RGBA, 24, 8, 8, 0);if (!ctx) {mju_error("OSMesa context creation failed");}// make currentif (!OSMesaMakeCurrent(ctx, buffer, GL_UNSIGNED_BYTE, 800, 800)) {mju_error("OSMesa make current failed");}//------------------------ GLFW
#else// init GLFWif (!glfwInit()) {mju_error("Could not initialize GLFW");}// create invisible window, single-bufferedglfwWindowHint(GLFW_VISIBLE, 0);glfwWindowHint(GLFW_DOUBLEBUFFER, GLFW_FALSE);GLFWwindow* window = glfwCreateWindow(800, 800, "Invisible window", NULL, NULL);if (!window) {mju_error("Could not create GLFW window");}// make context currentglfwMakeContextCurrent(window);
#endif
}// close OpenGL context/window
void closeOpenGL(void) {//------------------------ EGL
#if defined(MJ_EGL)// get current displayEGLDisplay eglDpy = eglGetCurrentDisplay();if (eglDpy==EGL_NO_DISPLAY) {return;}// get current contextEGLContext eglCtx = eglGetCurrentContext();// release contexteglMakeCurrent(eglDpy, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);// destroy context if validif (eglCtx!=EGL_NO_CONTEXT) {eglDestroyContext(eglDpy, eglCtx);}// terminate displayeglTerminate(eglDpy);//------------------------ OSMESA
#elif defined(MJ_OSMESA)OSMesaDestroyContext(ctx);//------------------------ GLFW
#else// terminate GLFW (crashes with Linux NVidia drivers)#if defined(__APPLE__) || defined(_WIN32)glfwTerminate();#endif
#endif
}//-------------------------------- main function ----------------------------------------int main(int argc, const char** argv) {// check command-line argumentsif (argc!=5) {std::printf(" USAGE:  record modelfile duration fps rgbfile\n");return 0;}// parse numeric argumentsdouble duration = 10, fps = 30;std::sscanf(argv[2], "%lf", &duration);std::sscanf(argv[3], "%lf", &fps);// initialize OpenGL and MuJoCoinitOpenGL();initMuJoCo(argv[1]);// set rendering to offscreen buffermjr_setBuffer(mjFB_OFFSCREEN, &con);if (con.currentBuffer!=mjFB_OFFSCREEN) {std::printf("Warning: offscreen rendering not supported, using default/window framebuffer\n");}// get size of active renderbuffermjrRect viewport =  mjr_maxViewport(&con);int W = viewport.width;int H = viewport.height;// allocate rgb and depth buffersunsigned char* rgb = (unsigned char*)std::malloc(3*W*H);float* depth = (float*)std::malloc(sizeof(float)*W*H);if (!rgb || !depth) {mju_error("Could not allocate buffers");}// create output rgb filestd::FILE* fp = std::fopen(argv[4], "wb");if (!fp) {mju_error("Could not open rgbfile for writing");}// main loopdouble frametime = 0;int framecount = 0;while (d->time<duration) {// render new frame if it is time (or first frame)if ((d->time-frametime)>1/fps || frametime==0) {// update abstract scenemjv_updateScene(m, d, &opt, NULL, &cam, mjCAT_ALL, &scn);// render scene in offscreen buffermjr_render(viewport, &scn, &con);// add time stamp in upper-left cornerchar stamp[50];mju::sprintf_arr(stamp, "Time = %.3f", d->time);mjr_overlay(mjFONT_NORMAL, mjGRID_TOPLEFT, viewport, stamp, NULL, &con);// read rgb and depth buffersmjr_readPixels(rgb, depth, viewport, &con);// insert subsampled depth image in lower-left corner of rgb imageconst int NS = 3;           // depth image sub-samplingfor (int r=0; r<H; r+=NS)for (int c=0; c<W; c+=NS) {int adr = (r/NS)*W + c/NS;rgb[3*adr] = rgb[3*adr+1] = rgb[3*adr+2] = (unsigned char)((1.0f-depth[r*W+c])*255.0f);}// write rgb image to filestd::fwrite(rgb, 3, W*H, fp);// print every 10 frames: '.' if ok, 'x' if OpenGL errorif (((framecount++)%10)==0) {if (mjr_getError()) {std::printf("x");} else {std::printf(".");}}// save simulation timeframetime = d->time;}// advance simulationmj_step(m, d);}std::printf("\n");// close file, free buffersstd::fclose(fp);std::free(rgb);std::free(depth);// close MuJoCo and OpenGLcloseMuJoCo();closeOpenGL();return 1;
}

Mujoco制作模拟视频相关推荐

  1. 国产科幻惊悚电影《太空群落》视效制作大放送

    8月31日,国产科幻惊悚电影<太空群落>登录爱奇艺! "国产"."科幻"."惊悚"几个词眼充分调动了观众的好奇心,很多观众看到海 ...

  2. 音响设备常用连接头及音视频线材的制作方法

    一套可使用的音响设备无论是专业系统还是非专业的民用音响设备除了设备本身外还需要各种连接线材将设备进行连接才能够使用.通常民用的设备从简单的DVD机到一套组合音响的线材都是附带的,也就是不用另加购买或制 ...

  3. 【计算机基础】多媒体技术、网页制作、计算机网络

    目录 第一章 思维 什么是计算思维 计算思维的目的 计算机思维的特征 计算机思维的本质 计算思维的应用 计算机的诞生 主要杰出贡献人物 计算机分代 计算机未来发展趋势 技术方向 硬件方向 我国计算机发 ...

  4. Ubuntu16.04 搭建mujoco环境+强化学习gym

    (2022)Ubuntu16.04 搭建mujoco环境+强化学习gym 前言 一.Ubuntu16.04系统安装 二.mujoco安装 1.安装教程 2.程序验证 三.mujoco-py 安装 1. ...

  5. 基于javaGUI的文档识别工具制作

    基于javaGUI的文档识别工具制作 对于某些文本,其中富含了一些标志,需要去排除,以及去获得段落字数,以下是我个人写的一个比较简单的文档识别工具,含导入文件.导出文件以及一个简单的识别功能. 1.功 ...

  6. 2021年大数据ELK(二十八):制作Dashboard

    全网最详细的大数据ELK文章系列,强烈建议收藏加关注! 新文章都已经列出历史文章目录,帮助大家回顾前面的知识重点. 目录 制作Dashboard 一.点击第三个组件图标,并创建一个新的Dashboar ...

  7. ucgui下制作漂亮按键

    源:ucgui下制作漂亮按键 转载于:https://www.cnblogs.com/LittleTiger/p/10313161.html

  8. 纯CSS制作的图形效果

    纯CSS制作的图形效果 很少会有人意识到,当浏览器绘制的border,会有一个角度的问题.我们就是得用这样的一个技巧来制作三角的效果.我们只需要保证一边的边框是有色,其他边框色为透明色,这样我们就很容 ...

  9. 使用 U 盘制作 Ubuntu 系统启动盘

    1. 总体概述 使用 Ubuntu USB 启动盘有以下作用: 安装或升级 Ubuntu 在不接触你的电脑配置的情况下测试 Ubuntu 桌面体验 在借来的机器上或从网吧启动到 Ubuntu 使用默认 ...

  10. 【BZOJ-30391057】玉蟾宫棋盘制作 悬线法

    3039: 玉蟾宫 Time Limit: 2 Sec  Memory Limit: 128 MB Submit: 753  Solved: 444 [Submit][Status][Discuss] ...

最新文章

  1. node mysql 批量写入_请问如何使用node.js在MySQL中进行批量插入
  2. http://www.od85c.com.cn/html/,OllyDbg script for unpacking Enigma 4.xx and 5.xx
  3. 你的导师是哪种类型?
  4. 倒计时 7 天 | 完整议程大揭秘!来 20 个 AI 论坛,与百名大咖携手玩转人工智能...
  5. Lua_手册_代码版
  6. Java编程:排序算法——选择排序
  7. python面向对象的编程_不会面向对象,肯定学不好Python!简易的面向对象攻略来啦...
  8. Unity音频常用插件
  9. 主成分分析 SPSS、python实例分析
  10. YDOOK:Maxwell 电磁场仿真 最新版的 Maxwell 软件 使用什么软件进行电磁场仿真
  11. 快递鸟电子面单对接文档(顺丰、ESM、圆通通用)
  12. 2017阿里巴巴实习生C/C++研发内推一面、二面经历
  13. iOS定位经纬度转换
  14. 微信公众号JS-SDK多图上传爬坑指南
  15. 开源库MusicPlayManager - 封装StarrySky音乐库
  16. Tensorflow用SVM(高斯核函数)分类非线性数据
  17. Tiled地图编辑器 Tiled Map Editor 的使用(二)动画效果
  18. 【论文翻译】Transferring GANs: generating images from limited data
  19. 计算机网络基础案例启示,《计算机网络基础及典型案例》理工大学出版社.pdf...
  20. 自动获取指定路径文件夹,删除文件夹及子文件

热门文章

  1. Android Kotlin关于新增本地数据库对象表字段问题
  2. 十次方——父工程子模块、公共模块
  3. 逻辑回归(LR) 算法模型简介
  4. 计网学习记录,burp抓包等学习记录
  5. 主流的推荐系统算法总结
  6. Mbed记录 STM32F207ZG板子引脚图
  7. JSzip 前端处理下载打包文件夹
  8. Java实现视频编码格式转换(转libx264编码格式)
  9. Windows常用密码的破解方法
  10. 2022年4月国产数据库大事记