|
|
@@ -1,6 +1,6 @@
|
|
|
-#include "opengl_video_renderer.h"
|
|
|
+#include "../base/logger.h"
|
|
|
|
|
|
-// #include "../base/logger.h"
|
|
|
+#include "opengl_video_renderer.h"
|
|
|
|
|
|
#include <QPainter>
|
|
|
#include <QResizeEvent>
|
|
|
@@ -18,43 +18,33 @@ extern "C" {
|
|
|
namespace av {
|
|
|
namespace player {
|
|
|
|
|
|
-// 顶点着色器源码
|
|
|
+// 顶点着色器源码 - 参考yuvopenglwidget.cpp
|
|
|
static const char* vertexShaderSource = R"(
|
|
|
-#version 330 core
|
|
|
-layout(location = 0) in vec2 position;
|
|
|
-layout(location = 1) in vec2 texCoord;
|
|
|
-
|
|
|
-out vec2 TexCoord;
|
|
|
+attribute vec4 vertexIn;
|
|
|
+attribute vec2 textureIn;
|
|
|
+varying vec2 TexCoord;
|
|
|
|
|
|
-uniform mat4 projection;
|
|
|
-
|
|
|
-void main() {
|
|
|
- gl_Position = projection * vec4(position, 0.0, 1.0);
|
|
|
- TexCoord = texCoord;
|
|
|
+void main(void) {
|
|
|
+ gl_Position = vertexIn;
|
|
|
+ TexCoord = textureIn;
|
|
|
}
|
|
|
)";
|
|
|
|
|
|
-// 片段着色器源码 (YUV420P)
|
|
|
+// 片段着色器源码 (YUV420P) - 参考yuvopenglwidget.cpp
|
|
|
static const char* fragmentShaderSource = R"(
|
|
|
-#version 330 core
|
|
|
-in vec2 TexCoord;
|
|
|
-out vec4 FragColor;
|
|
|
-
|
|
|
+varying mediump vec2 TexCoord;
|
|
|
uniform sampler2D yTexture;
|
|
|
uniform sampler2D uTexture;
|
|
|
uniform sampler2D vTexture;
|
|
|
|
|
|
-void main() {
|
|
|
- float y = texture(yTexture, TexCoord).r;
|
|
|
- float u = texture(uTexture, TexCoord).r - 0.5;
|
|
|
- float v = texture(vTexture, TexCoord).r - 0.5;
|
|
|
-
|
|
|
- // YUV to RGB conversion
|
|
|
- float r = y + 1.402 * v;
|
|
|
- float g = y - 0.344 * u - 0.714 * v;
|
|
|
- float b = y + 1.772 * u;
|
|
|
-
|
|
|
- FragColor = vec4(r, g, b, 1.0);
|
|
|
+void main(void) {
|
|
|
+ vec3 yuv;
|
|
|
+ vec3 rgb;
|
|
|
+ yuv.r = texture2D(yTexture, TexCoord).r;
|
|
|
+ yuv.g = texture2D(uTexture, TexCoord).r - 0.5;
|
|
|
+ yuv.b = texture2D(vTexture, TexCoord).r - 0.5;
|
|
|
+ rgb = mat3(1.0, 1.0, 1.0, 0.0, -0.138, 1.816, 1.540, -0.459, 0.0) * yuv;
|
|
|
+ gl_FragColor = vec4(rgb, 1.0);
|
|
|
}
|
|
|
)";
|
|
|
|
|
|
@@ -63,6 +53,10 @@ OpenGLVideoRenderer::OpenGLVideoRenderer(QWidget* parent)
|
|
|
, m_videoWidth(0)
|
|
|
, m_videoHeight(0)
|
|
|
, m_inputFormat(AV_PIX_FMT_NONE)
|
|
|
+ , m_fps(25.0)
|
|
|
+ , m_textureY(0)
|
|
|
+ , m_textureU(0)
|
|
|
+ , m_textureV(0)
|
|
|
, m_swsContext(nullptr)
|
|
|
, m_yuvBuffer(nullptr)
|
|
|
, m_yuvBufferSize(0)
|
|
|
@@ -96,7 +90,17 @@ OpenGLVideoRenderer::OpenGLVideoRenderer(QWidget* parent)
|
|
|
|
|
|
OpenGLVideoRenderer::~OpenGLVideoRenderer()
|
|
|
{
|
|
|
+ // 清理OpenGL资源
|
|
|
+ makeCurrent();
|
|
|
cleanupOpenGLResources();
|
|
|
+
|
|
|
+ // 删除纹理
|
|
|
+ if (m_textureY) glDeleteTextures(1, &m_textureY);
|
|
|
+ if (m_textureU) glDeleteTextures(1, &m_textureU);
|
|
|
+ if (m_textureV) glDeleteTextures(1, &m_textureV);
|
|
|
+
|
|
|
+ doneCurrent();
|
|
|
+
|
|
|
if (m_swsContext) {
|
|
|
sws_freeContext(m_swsContext);
|
|
|
m_swsContext = nullptr;
|
|
|
@@ -233,11 +237,26 @@ bool OpenGLVideoRenderer::initialize(int width, int height, AVPixelFormat pixelF
|
|
|
|
|
|
bool OpenGLVideoRenderer::renderFrame(const AVFramePtr& frame)
|
|
|
{
|
|
|
- qDebug() << "------->>>>>>>>>>>>>>>>>>-" << frame.get() << m_initialized.load();
|
|
|
+ // 使用Logger输出调试信息
|
|
|
+ std::string debugMsg = "[VideoRenderer] renderFrame called - frame: " +
|
|
|
+ std::to_string(reinterpret_cast<uintptr_t>(frame.get())) +
|
|
|
+ ", initialized: " + std::to_string(m_initialized.load()) +
|
|
|
+ ", glInitialized: " + std::to_string(m_glInitialized);
|
|
|
+ av::Logger::instance().debug(debugMsg);
|
|
|
+
|
|
|
if (!frame || !m_initialized) {
|
|
|
+ av::Logger::instance().warning("[VideoRenderer] renderFrame failed - frame or not initialized");
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+ std::string frameInfo = "[VideoRenderer] Frame info - width: " + std::to_string(frame->width) +
|
|
|
+ ", height: " + std::to_string(frame->height) +
|
|
|
+ ", format: " + std::to_string(frame->format) +
|
|
|
+ ", linesize[0]: " + std::to_string(frame->linesize[0]) +
|
|
|
+ ", linesize[1]: " + std::to_string(frame->linesize[1]) +
|
|
|
+ ", linesize[2]: " + std::to_string(frame->linesize[2]);
|
|
|
+ av::Logger::instance().debug(frameInfo);
|
|
|
+
|
|
|
QMutexLocker locker(&m_mutex);
|
|
|
|
|
|
// 更新纹理数据
|
|
|
@@ -252,6 +271,7 @@ bool OpenGLVideoRenderer::renderFrame(const AVFramePtr& frame)
|
|
|
m_updateTimer->start(interval);
|
|
|
}
|
|
|
|
|
|
+ av::Logger::instance().debug("[VideoRenderer] renderFrame completed successfully");
|
|
|
return true;
|
|
|
}
|
|
|
|
|
|
@@ -355,9 +375,8 @@ void OpenGLVideoRenderer::cleanupOpenGLResources()
|
|
|
m_vertexBuffer.reset();
|
|
|
m_indexBuffer.reset();
|
|
|
m_vao.reset();
|
|
|
- m_yTexture.reset();
|
|
|
- m_uTexture.reset();
|
|
|
- m_vTexture.reset();
|
|
|
+
|
|
|
+ // 纹理在析构函数中删除,这里不需要处理
|
|
|
|
|
|
m_glInitialized = false;
|
|
|
}
|
|
|
@@ -378,6 +397,10 @@ bool OpenGLVideoRenderer::initializeShaders()
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
+ // 绑定属性位置 - 参考yuvopenglwidget.cpp
|
|
|
+ m_shaderProgram->bindAttributeLocation("vertexIn", 0);
|
|
|
+ m_shaderProgram->bindAttributeLocation("textureIn", 1);
|
|
|
+
|
|
|
// 链接着色器程序
|
|
|
if (!m_shaderProgram->link()) {
|
|
|
// av::Logger::instance().error("Failed to link shader program: " + m_shaderProgram->log().toStdString());
|
|
|
@@ -390,44 +413,20 @@ bool OpenGLVideoRenderer::initializeShaders()
|
|
|
|
|
|
bool OpenGLVideoRenderer::initializeVertexData()
|
|
|
{
|
|
|
- // 顶点数据 (位置 + 纹理坐标)
|
|
|
- static const float vertices[] = {
|
|
|
- // 位置 // 纹理坐标
|
|
|
- -1.0f, -1.0f, 0.0f, 1.0f, // 左下
|
|
|
- 1.0f, -1.0f, 1.0f, 1.0f, // 右下
|
|
|
- 1.0f, 1.0f, 1.0f, 0.0f, // 右上
|
|
|
- -1.0f, 1.0f, 0.0f, 0.0f // 左上
|
|
|
- };
|
|
|
-
|
|
|
- // 索引数据
|
|
|
- static const unsigned int indices[] = {
|
|
|
- 0, 1, 2, // 第一个三角形
|
|
|
- 2, 3, 0 // 第二个三角形
|
|
|
- };
|
|
|
+ // 参考yuvopenglwidget.cpp的顶点数据设置
|
|
|
+ static const GLfloat vertices[] = {-1.0f, -1.0f, 1.0f, -1.0f, -1.0f, 1.0f, 1.0f, 1.0f};
|
|
|
+ static const GLfloat texCoords[] = {0.0f, 1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 1.0f, 0.0f};
|
|
|
|
|
|
// 创建VAO
|
|
|
m_vao = std::make_unique<QOpenGLVertexArrayObject>();
|
|
|
m_vao->create();
|
|
|
m_vao->bind();
|
|
|
|
|
|
- // 创建顶点缓冲区
|
|
|
- m_vertexBuffer = std::make_unique<QOpenGLBuffer>(QOpenGLBuffer::VertexBuffer);
|
|
|
- m_vertexBuffer->create();
|
|
|
- m_vertexBuffer->bind();
|
|
|
- m_vertexBuffer->allocate(vertices, sizeof(vertices));
|
|
|
-
|
|
|
- // 设置顶点属性
|
|
|
- m_shaderProgram->enableAttributeArray(0);
|
|
|
- m_shaderProgram->setAttributeBuffer(0, GL_FLOAT, 0, 2, 4 * sizeof(float));
|
|
|
-
|
|
|
- m_shaderProgram->enableAttributeArray(1);
|
|
|
- m_shaderProgram->setAttributeBuffer(1, GL_FLOAT, 2 * sizeof(float), 2, 4 * sizeof(float));
|
|
|
-
|
|
|
- // 创建索引缓冲区
|
|
|
- m_indexBuffer = std::make_unique<QOpenGLBuffer>(QOpenGLBuffer::IndexBuffer);
|
|
|
- m_indexBuffer->create();
|
|
|
- m_indexBuffer->bind();
|
|
|
- m_indexBuffer->allocate(indices, sizeof(indices));
|
|
|
+ // 设置顶点,纹理数组并启用
|
|
|
+ glVertexAttribPointer(0, 2, GL_FLOAT, 0, 0, vertices);
|
|
|
+ glEnableVertexAttribArray(0);
|
|
|
+ glVertexAttribPointer(1, 2, GL_FLOAT, 0, 0, texCoords);
|
|
|
+ glEnableVertexAttribArray(1);
|
|
|
|
|
|
m_vao->release();
|
|
|
|
|
|
@@ -437,38 +436,30 @@ bool OpenGLVideoRenderer::initializeVertexData()
|
|
|
|
|
|
bool OpenGLVideoRenderer::createTextures()
|
|
|
{
|
|
|
+ // 参考yuvopenglwidget.cpp创建纹理
|
|
|
// 创建Y纹理
|
|
|
- m_yTexture = std::make_unique<QOpenGLTexture>(QOpenGLTexture::Target2D);
|
|
|
- m_yTexture->create();
|
|
|
- m_yTexture->bind();
|
|
|
- m_yTexture->setFormat(QOpenGLTexture::R8_UNorm);
|
|
|
- m_yTexture->setSize(m_videoWidth, m_videoHeight);
|
|
|
- m_yTexture->allocateStorage();
|
|
|
- m_yTexture->setMinificationFilter(QOpenGLTexture::Linear);
|
|
|
- m_yTexture->setMagnificationFilter(QOpenGLTexture::Linear);
|
|
|
- m_yTexture->setWrapMode(QOpenGLTexture::ClampToEdge);
|
|
|
+ glGenTextures(1, &m_textureY);
|
|
|
+ glBindTexture(GL_TEXTURE_2D, m_textureY);
|
|
|
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
|
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
|
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
|
|
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
|
|
|
|
|
// 创建U纹理
|
|
|
- m_uTexture = std::make_unique<QOpenGLTexture>(QOpenGLTexture::Target2D);
|
|
|
- m_uTexture->create();
|
|
|
- m_uTexture->bind();
|
|
|
- m_uTexture->setFormat(QOpenGLTexture::R8_UNorm);
|
|
|
- m_uTexture->setSize(m_videoWidth / 2, m_videoHeight / 2);
|
|
|
- m_uTexture->allocateStorage();
|
|
|
- m_uTexture->setMinificationFilter(QOpenGLTexture::Linear);
|
|
|
- m_uTexture->setMagnificationFilter(QOpenGLTexture::Linear);
|
|
|
- m_uTexture->setWrapMode(QOpenGLTexture::ClampToEdge);
|
|
|
+ glGenTextures(1, &m_textureU);
|
|
|
+ glBindTexture(GL_TEXTURE_2D, m_textureU);
|
|
|
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
|
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
|
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
|
|
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
|
|
|
|
|
// 创建V纹理
|
|
|
- m_vTexture = std::make_unique<QOpenGLTexture>(QOpenGLTexture::Target2D);
|
|
|
- m_vTexture->create();
|
|
|
- m_vTexture->bind();
|
|
|
- m_vTexture->setFormat(QOpenGLTexture::R8_UNorm);
|
|
|
- m_vTexture->setSize(m_videoWidth / 2, m_videoHeight / 2);
|
|
|
- m_vTexture->allocateStorage();
|
|
|
- m_vTexture->setMinificationFilter(QOpenGLTexture::Linear);
|
|
|
- m_vTexture->setMagnificationFilter(QOpenGLTexture::Linear);
|
|
|
- m_vTexture->setWrapMode(QOpenGLTexture::ClampToEdge);
|
|
|
+ glGenTextures(1, &m_textureV);
|
|
|
+ glBindTexture(GL_TEXTURE_2D, m_textureV);
|
|
|
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
|
|
|
+ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
|
|
|
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
|
|
|
+ glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
|
|
|
|
|
|
// av::Logger::instance().info("Textures created successfully");
|
|
|
return true;
|
|
|
@@ -477,9 +468,19 @@ bool OpenGLVideoRenderer::createTextures()
|
|
|
void OpenGLVideoRenderer::updateTextures(const AVFramePtr& frame)
|
|
|
{
|
|
|
if (!m_glInitialized || !frame) {
|
|
|
+ av::Logger::instance().warning("[VideoRenderer] updateTextures failed - glInitialized: " +
|
|
|
+ std::to_string(m_glInitialized) + ", frame: " +
|
|
|
+ std::to_string(reinterpret_cast<uintptr_t>(frame.get())));
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
+ av::Logger::instance().debug("[VideoRenderer] updateTextures - video size: " +
|
|
|
+ std::to_string(m_videoWidth) + "x" + std::to_string(m_videoHeight) +
|
|
|
+ ", input format: " + std::to_string(m_inputFormat) +
|
|
|
+ ", frame format: " + std::to_string(frame->format) +
|
|
|
+ ", linesize: [" + std::to_string(frame->linesize[0]) + ", " +
|
|
|
+ std::to_string(frame->linesize[1]) + ", " + std::to_string(frame->linesize[2]) + "]");
|
|
|
+
|
|
|
// 转换帧格式(如果需要)
|
|
|
AVFrame* yuvFrame = frame.get();
|
|
|
if (m_inputFormat != AV_PIX_FMT_YUV420P && m_swsContext) {
|
|
|
@@ -497,29 +498,60 @@ void OpenGLVideoRenderer::updateTextures(const AVFramePtr& frame)
|
|
|
|
|
|
yuvFrame = tempFrame;
|
|
|
|
|
|
- // 更新纹理数据
|
|
|
- m_yTexture->bind();
|
|
|
- m_yTexture->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, yuvFrame->data[0], nullptr);
|
|
|
+ // 更新Y纹理数据
|
|
|
+ glActiveTexture(GL_TEXTURE0);
|
|
|
+ glBindTexture(GL_TEXTURE_2D, m_textureY);
|
|
|
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, yuvFrame->linesize[0]);
|
|
|
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_videoWidth, m_videoHeight,
|
|
|
+ GL_RED, GL_UNSIGNED_BYTE, yuvFrame->data[0]);
|
|
|
|
|
|
- m_uTexture->bind();
|
|
|
- m_uTexture->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, yuvFrame->data[1], nullptr);
|
|
|
+ // 更新U纹理数据
|
|
|
+ glActiveTexture(GL_TEXTURE1);
|
|
|
+ glBindTexture(GL_TEXTURE_2D, m_textureU);
|
|
|
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, yuvFrame->linesize[1]);
|
|
|
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_videoWidth / 2, m_videoHeight / 2,
|
|
|
+ GL_RED, GL_UNSIGNED_BYTE, yuvFrame->data[1]);
|
|
|
|
|
|
- m_vTexture->bind();
|
|
|
- m_vTexture->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, yuvFrame->data[2], nullptr);
|
|
|
+ // 更新V纹理数据
|
|
|
+ glActiveTexture(GL_TEXTURE2);
|
|
|
+ glBindTexture(GL_TEXTURE_2D, m_textureV);
|
|
|
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, yuvFrame->linesize[2]);
|
|
|
+ glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, m_videoWidth / 2, m_videoHeight / 2,
|
|
|
+ GL_RED, GL_UNSIGNED_BYTE, yuvFrame->data[2]);
|
|
|
+
|
|
|
+ // 重置像素存储参数
|
|
|
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
|
|
|
|
|
av_frame_free(&tempFrame);
|
|
|
} else {
|
|
|
- // 直接更新纹理数据
|
|
|
- m_yTexture->bind();
|
|
|
- m_yTexture->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, frame->data[0], nullptr);
|
|
|
+ // 参考yuvopenglwidget.cpp的纹理更新方式
|
|
|
+ av::Logger::instance().debug("[VideoRenderer] Updating textures for YUV420P format");
|
|
|
+
|
|
|
+ // Y分量
|
|
|
+ glActiveTexture(GL_TEXTURE0);
|
|
|
+ glBindTexture(GL_TEXTURE_2D, m_textureY);
|
|
|
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, frame->linesize[0]);
|
|
|
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, frame->width, frame->height, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[0]);
|
|
|
|
|
|
- m_uTexture->bind();
|
|
|
- m_uTexture->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, frame->data[1], nullptr);
|
|
|
+ // U分量
|
|
|
+ glActiveTexture(GL_TEXTURE1);
|
|
|
+ glBindTexture(GL_TEXTURE_2D, m_textureU);
|
|
|
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, frame->linesize[1]);
|
|
|
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, frame->width >> 1, frame->height >> 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[1]);
|
|
|
|
|
|
- m_vTexture->bind();
|
|
|
- m_vTexture->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, frame->data[2], nullptr);
|
|
|
+ // V分量
|
|
|
+ glActiveTexture(GL_TEXTURE2);
|
|
|
+ glBindTexture(GL_TEXTURE_2D, m_textureV);
|
|
|
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, frame->linesize[2]);
|
|
|
+ glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, frame->width >> 1, frame->height >> 1, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[2]);
|
|
|
}
|
|
|
|
|
|
+ // 重置像素存储参数
|
|
|
+ glPixelStorei(GL_UNPACK_ROW_LENGTH, 0);
|
|
|
+
|
|
|
+ // 检查OpenGL错误
|
|
|
+ checkGLError("updateTextures");
|
|
|
+
|
|
|
// 强制更新显示
|
|
|
update();
|
|
|
}
|
|
|
@@ -579,17 +611,21 @@ void OpenGLVideoRenderer::renderCurrentFrame()
|
|
|
m_shaderProgram->bind();
|
|
|
m_vao->bind();
|
|
|
|
|
|
- // 绑定纹理
|
|
|
- m_yTexture->bind(0);
|
|
|
- m_uTexture->bind(1);
|
|
|
- m_vTexture->bind(2);
|
|
|
+ // 参考yuvopenglwidget.cpp的纹理绑定和uniform设置
|
|
|
+ glActiveTexture(GL_TEXTURE0);
|
|
|
+ glBindTexture(GL_TEXTURE_2D, m_textureY);
|
|
|
+ glUniform1i(m_shaderProgram->uniformLocation("yTexture"), 0);
|
|
|
+
|
|
|
+ glActiveTexture(GL_TEXTURE1);
|
|
|
+ glBindTexture(GL_TEXTURE_2D, m_textureU);
|
|
|
+ glUniform1i(m_shaderProgram->uniformLocation("uTexture"), 1);
|
|
|
|
|
|
- m_shaderProgram->setUniformValue("yTexture", 0);
|
|
|
- m_shaderProgram->setUniformValue("uTexture", 1);
|
|
|
- m_shaderProgram->setUniformValue("vTexture", 2);
|
|
|
+ glActiveTexture(GL_TEXTURE2);
|
|
|
+ glBindTexture(GL_TEXTURE_2D, m_textureV);
|
|
|
+ glUniform1i(m_shaderProgram->uniformLocation("vTexture"), 2);
|
|
|
|
|
|
- // 绘制
|
|
|
- glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, nullptr);
|
|
|
+ // 参考yuvopenglwidget.cpp使用GL_TRIANGLE_STRIP绘制
|
|
|
+ glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|
|
|
|
|
m_vao->release();
|
|
|
m_shaderProgram->release();
|