Qt中使用OpenGL渲染视频

该博客介绍了如何在Qt5.4之后利用QOpenGLWidget和QOpenGLFunctions进行OpenGL编程,以解码AVFrame并进行渲染。通过设置顶点和片元着色器,以及处理纹理坐标,最终在QOpenGLWidget上显示视频帧。博客内容包括初始化OpenGL,创建和编译Shader,以及在paintGL()函数中进行渲染操作。
摘要由CSDN通过智能技术生成

Qt5.4之后,OpenGL在Qt中可以通过QOpenGLWidget和QOpenGLFunctions来实现,以下Demo(只展示OpenGL相关部分)解码出AVFrame后对其进行渲染。

//顶点Shader
static const char vertexShaderSource[] =
"attribute vec4 position;           \n"
"attribute vec2 texcoord;           \n"
"varying vec2 v_texcoord;           \n"
"void main()                        \n"
"{                                  \n"
"   gl_Position = position;         \n"
"   v_texcoord = texcoord.xy;       \n"
"}                                  \n";

//片元Shader
static const char yuvFragmentShaderSource[] =
"precision mediump float;                              \n"
"varying vec2 v_texcoord;                              \n"
"uniform sampler2D s_texture_y;                        \n"
"uniform sampler2D s_texture_u;                        \n"
"uniform sampler2D s_texture_v;                        \n"
"void main()                                           \n"
"{                                                     \n"
"   vec3 yuv;                                          \n"
"   vec3 rgb;                                          \n"
"   yuv.r = texture2D(s_texture_y, v_texcoord).r;      \n"
"   yuv.g = texture2D(s_texture_u, v_texcoord).r - 0.5;\n"
"   yuv.b = texture2D(s_texture_v, v_texcoord).r - 0.5;\n"
"   rgb = mat3(1.0, 1.0, 1.0,                          \n"
"              0.0, -0.39465, 2.03211,                 \n"
"              1.13983, -0.58060, 0.0)*yuv;            \n"
"   gl_FragColor = vec4(rgb,1.0);                      \n"
"}                                                     \n";

//Vertex coordinates
static const GLfloat verCoords[] = {
	-1.0f, -1.0f,
	 1.0f, -1.0f,
	-1.0f,  1.0f,
	 1.0f,  1.0f
};

//Material coordinates
static const GLfloat texCoords[] = {
	0.0f, 1.0f,
	1.0f, 1.0f,
	0.0f, 0.0f,
	1.0f, 0.0f
};

头文件部分

class VideoWidget : public QOpenGLWidget, protected QOpenGLFunctions {
	Q_OBJECT

public:
	explicit VideoWidget(QWidget *parent = 0);
	~VideoWidget() override;
	GLuint VideoWidget::InitShader(GLenum type, const GLchar *code);

protected:
	void initializeGL() override;
	void paintGL() override;
	void resizeGL(int w, int h) override;

private:
	int viewPortWidth_ = 0;   
	int viewPortHeight_ = 0;
	GLuint program;
	std::array<GLuint, 3> textures;

};

在initializeGL函数中需要添加initializeOpenGLFunctions()完成初始化,然后启动定时器QTimer

void VideoWidget::initializeGL() {
	initializeOpenGLFunctions();

    //初始化等相关加载工作
    // ......
    // ......

	QTimer *time = new QTimer(this);
	connect(time, SIGNAL(timeout()), this, SLOT(update()));
	time->start(15);
}
void VideoWidget::resizeGL(int w, int h) {
	LOG(INFO) << "resizeGL: " << w << "," << h;
	viewPortWidth_ = w;
	viewPortHeight_ = h;
}

Shader的创建、编译

GLuint VideoWidget::InitShader(GLenum type, const GLchar *code) {
	GLuint shader = glCreateShader(type);
	if (shader == 0 || shader == GL_INVALID_ENUM) {
		LOG(ERROR) << "Failed to create shader";
		return 0;
	}

	glShaderSource(shader, 1, &code, NULL);
	glCompileShader(shader);

#ifdef DEBUG
	GLint logLength;
	glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
	if (logLength > 0) {
		GLchar *log = (GLchar *)malloc(logLength);
		glGetShaderInfoLog(shader, logLength, &logLength, log);
		LOG(INFO) << "Shader compile log : " << log;
		free(log);
	}
#endif

	GLint status;
	glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
	if (status == GL_FALSE) {
		glDeleteShader(shader);
		LOG(ERROR) << "Failed to compile shader";
		return 0;
	}

	return shader;
}
void VideoWidget::paintGL() {
	//需要获取frame
	//......
	//......
	auto frame = mediaSamples[0]->GetFrame();
	
	glViewport(0, 0, viewPortWidth_, viewPortHeight_);
	glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
	glGenTextures(3, textures.data());
	int width = frame->width;
	int height = frame->height;
	int widths[3] = { width, width >> 1, width >> 1 };
	int heights[3] = { height, height >> 1, height >> 1 };
	//支持的是yuv格式数据
	for (int i = 0; i < 3; ++i) {
		glPixelStorei(GL_UNPACK_ROW_LENGTH, frame->linesize[i]);
		glBindTexture(GL_TEXTURE_2D, textures[i]);
		glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, widths[i], heights[i],
			0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[i]);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
		glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	}
	glBindTexture(GL_TEXTURE_2D, 0);

	program = glCreateProgram();
	GLuint vShader = InitShader(GL_VERTEX_SHADER, vertexShaderSource);
	if (!vShader) {
		glDeleteShader(vShader);
		CHECK(false) << "CreateShader GL_VERTEX_SHADER failed";
	}
	GLuint fShader = InitShader(GL_FRAGMENT_SHADER, yuvFragmentShaderSource);
	if (!fShader) {
		glDeleteShader(fShader);
		CHECK(false) << "CreateShader GL_VERTEX_SHADER failed";
	}
	glAttachShader(program, vShader);
	glAttachShader(program, fShader);
	glBindAttribLocation(program, 0, "position");
	glBindAttribLocation(program, 1, "texcoord");
	glLinkProgram(program);
	GLint status;
	glGetProgramiv(program, GL_LINK_STATUS, &status);
	if (status == GL_FALSE) {
		glDeleteProgram(program);
		CHECK(false) << "failed to link program : " << program;
	}
	glValidateProgram(program);
#ifdef DEBUG
	GLint logLength;
	glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
	if (logLength > 0) {
		GLchar *log = (GLchar *)malloc(logLength);
		glGetProgramInfoLog(program, logLength, &logLength, log);
		LOG(INFO) << "program validate log : " << log;
		free(log);
	}
#endif
	glGetProgramiv(program, GL_VALIDATE_STATUS, &status);
	if (status == GL_FALSE) {
		LOG(INFO) << "ValidateProgram failed";
		exit(1);
	}
	glUseProgram(program);
	glUniform1i(glGetUniformLocation(program, "s_texture_y"), 0);
	glUniform1i(glGetUniformLocation(program, "s_texture_u"), 1);
	glUniform1i(glGetUniformLocation(program, "s_texture_v"), 2);

	for (int i = 0; i < 3; ++i) {
		glActiveTexture(GL_TEXTURE0 + i);
		glBindTexture(GL_TEXTURE_2D, textures[i]);
	}
	glVertexAttribPointer((GLuint)ATTRIBUTE_COORD::ATTRIBUTE_VERTEX, 2, GL_FLOAT, GL_FALSE, 0, verCoords);
	glEnableVertexAttribArray((GLuint)ATTRIBUTE_COORD::ATTRIBUTE_VERTEX);
	glVertexAttribPointer((GLuint)ATTRIBUTE_COORD::ATTRIBUTE_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, texCoords);
	glEnableVertexAttribArray((GLuint)ATTRIBUTE_COORD::ATTRIBUTE_TEXCOORD);
	
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
要在QT使用OpenGL渲染一个OBJ模型,可以按照以下步骤进行: 1. 创建一个QT项目,选择OpenGL窗口模板。 2. 在OpenGL窗口初始化OpenGLQT的集成,可以使用以下代码: ```c++ void GLWidget::initializeGL() { initializeOpenGLFunctions(); glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glEnable(GL_DEPTH_TEST); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glEnable(GL_COLOR_MATERIAL); } void GLWidget::resizeGL(int w, int h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(45.0, (double)w / (double)h, 0.01, 100.0); glMatrixMode(GL_MODELVIEW); } void GLWidget::paintGL() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glLoadIdentity(); glTranslatef(0.0f, 0.0f, -5.0f); glRotatef(rotationX, 1.0, 0.0, 0.0); glRotatef(rotationY, 0.0, 1.0, 0.0); drawModel(); } ``` 3. 加载OBJ模型,并将其渲染OpenGL窗口,可以使用以下代码: ```c++ void GLWidget::loadModel(QString filename) { model = glmReadOBJ(filename.toStdString().c_str()); glmUnitize(model); glmFacetNormals(model); glmVertexNormals(model, 90); } void GLWidget::drawModel() { if (model != NULL) { glmDraw(model, GLM_SMOOTH); } } ``` 4. 在QT的主窗口添加一个QPushButton,点击按钮后调用loadModel()函数加载OBJ模型。 ```c++ void MainWindow::on_btnLoad_clicked() { QString filename = QFileDialog::getOpenFileName(this, tr("Open File"), ".", tr("OBJ Files (*.obj)")); if (!filename.isEmpty()) { ui->glWidget->loadModel(filename); } } ``` 5. 运行程序,点击按钮加载OBJ模型并在OpenGL窗口显示。 以上就是在QT使用OpenGL渲染OBJ模型的基本步骤,需要注意的是,这里使用OpenGL Utility Toolkit(GLUT)和OpenGL Mathematics(GLM)库来帮助加载OBJ模型和进行矩阵变换等操作。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值