FrameBufferObject.cpp

本文介绍了一种使用帧缓冲区对象(FBO)进行渲染的技术。通过创建并绑定FBO,可以将渲染输出指定到特定的渲染缓冲区。文中提供了一个简单的示例程序,演示了如何设置FBO,并将两个不同渲染缓冲区的内容组合显示在同一窗口的不同区域。

  帧缓冲区对象,Frame Buffer Object.创建渲染缓冲区,将渲染管线的输出可指定到渲染缓冲区,同时可以读渲染缓冲区.这些都是通过绑定帧缓冲区来做的.代码中将颜色输出分别写到两个渲染缓冲区中,再将这两个缓冲区的内容分别写到默认帧缓冲区中,效果就是左右颜色明暗不一样.书上的代码很复杂,我又实现了一个简单版的.加油吧,抓紧学习渲染,目前的进度比自己预想的慢太多太多了.

//	FrameBufferObject.cpp - 2013/09/28 - 20:35
#include "stdafx.h"
#include <GLTools.h>
#include <GLShaderManager.h>
#include <GLFrustum.h>
#include <GLBatch.h>
#include <GLMatrixStack.h>
#include <GLGeometryTransform.h>
#include <StopWatch.h>

#define FREEGLUT_STATIC
#include <GL/glut.h>

GLenum frameBufferObjectBuffers[] = {GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1,} ;
GLuint frameBufferObjectName ;
GLuint renderBufferNames[2] ;

GLFrame             viewFrame;
GLFrustum           viewFrustum;
GLTriangleBatch     sphereBatch;
GLMatrixStack       modelViewMatrix;
GLMatrixStack       projectionMatrix;
GLGeometryTransform transformPipeline;
GLShaderManager     shaderManager;

GLuint	ADSLightShader;
GLint	locAmbient;
GLint   locDiffuse;
GLint   locSpecular;
GLint	locLight;
GLint	locMVP;
GLint	locMV;
GLint	locNM;

GLfloat vEyeLight[] = { -100.0f, 100.0f, 100.0f };
GLfloat vAmbientColor[] = { 0.1f, 0.1f, 0.1f, 1.0f };
GLfloat vDiffuseColor[] = { 0.0f, 0.0f, 1.0f, 1.0f };
GLfloat vSpecularColor[] = { 1.0f, 1.0f, 1.0f, 1.0f };

GLint screenWidth ;
GLint screenHeight ;

void SetupRC(void)
{
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f );

	glEnable(GL_DEPTH_TEST | GL_CULL_FACE);

	shaderManager.InitializeStockShaders();
	viewFrame.MoveForward(4.0f);

	gltMakeSphere(sphereBatch, 1.0f, 26, 13);

	ADSLightShader = shaderManager.LoadShaderPairWithAttributes(
		"ADSGouraud.vp", "ADSGouraud.fp",
		2,
		GLT_ATTRIBUTE_VERTEX, "vVertex",
		GLT_ATTRIBUTE_NORMAL, "vNormal") ;

	locAmbient = glGetUniformLocation(ADSLightShader, "ambientColor");
	locDiffuse = glGetUniformLocation(ADSLightShader, "diffuseColor");
	locSpecular = glGetUniformLocation(ADSLightShader, "specularColor");
	locLight = glGetUniformLocation(ADSLightShader, "vLightPosition");
	locMVP = glGetUniformLocation(ADSLightShader, "mvpMatrix");
	locMV  = glGetUniformLocation(ADSLightShader, "mvMatrix");
	locNM  = glGetUniformLocation(ADSLightShader, "normalMatrix");

	glLinkProgram(ADSLightShader) ;

	glGenFramebuffers(1, &frameBufferObjectName) ;

	glGenRenderbuffers(2, renderBufferNames) ;
	glBindRenderbuffer(GL_RENDERBUFFER, renderBufferNames[0]) ;
	glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, screenWidth, screenHeight) ;
	glBindRenderbuffer(GL_RENDERBUFFER, renderBufferNames[1]) ;
	glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, screenWidth, screenHeight) ;

	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBufferObjectName) ;
	glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, renderBufferNames[0]) ;
	glFramebufferRenderbuffer(GL_DRAW_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_RENDERBUFFER, renderBufferNames[1]) ;

	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0) ;
}

void ShutdownRC(void)
{
	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0) ;
	glBindFramebuffer(GL_READ_FRAMEBUFFER, 0) ;

	glDeleteRenderbuffers(2, renderBufferNames) ;

	glDeleteFramebuffers(1, &frameBufferObjectName) ;

	glUseProgram(0) ;

	glDeleteProgram(ADSLightShader) ;
}

void ChangeSize(int w, int h)
{
	if(h == 0)
		h = 1;

	glViewport(0, 0, w, h);

	viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 100.0f);

	projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
	transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);

	screenWidth = w ;
	screenHeight = h ;

	glBindRenderbuffer(GL_RENDERBUFFER, renderBufferNames[0]);
	glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, screenWidth, screenHeight);
	glBindRenderbuffer(GL_RENDERBUFFER, renderBufferNames[1]);
	glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, screenWidth, screenHeight);
	glBindRenderbuffer(GL_RENDERBUFFER, renderBufferNames[2]);
	glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, screenWidth, screenHeight);
}

void RenderScene(void)
{
	static CStopWatch rotTimer;

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	modelViewMatrix.PushMatrix(viewFrame);
	modelViewMatrix.Rotate(rotTimer.GetElapsedSeconds() * 10.0f, 0.0f, 1.0f, 0.0f);

	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frameBufferObjectName) ;
	glDrawBuffers(2, frameBufferObjectBuffers) ;

	glUseProgram(ADSLightShader);
	glUniform4fv(locAmbient, 1, vAmbientColor);
	glUniform4fv(locDiffuse, 1, vDiffuseColor);
	glUniform4fv(locSpecular, 1, vSpecularColor);
	glUniform3fv(locLight, 1, vEyeLight);
	glUniformMatrix4fv(locMVP, 1, GL_FALSE, transformPipeline.GetModelViewProjectionMatrix());
	glUniformMatrix4fv(locMV, 1, GL_FALSE, transformPipeline.GetModelViewMatrix());
	glUniformMatrix3fv(locNM, 1, GL_FALSE, transformPipeline.GetNormalMatrix());
	sphereBatch.Draw();

	modelViewMatrix.PopMatrix();

	glBindFramebuffer(GL_READ_FRAMEBUFFER, frameBufferObjectName) ;
	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0) ;

	glReadBuffer(GL_COLOR_ATTACHMENT0) ;
	glBlitFramebuffer(
		0, 0, screenWidth / 2, screenHeight,
		0, 0, screenWidth / 2, screenHeight,
		GL_COLOR_BUFFER_BIT, GL_NEAREST) ;

	glReadBuffer(GL_COLOR_ATTACHMENT1) ;
	glBlitFramebuffer(
		screenWidth / 2, 0, screenWidth, screenHeight,
		screenWidth / 2, 0, screenWidth, screenHeight,
		GL_COLOR_BUFFER_BIT, GL_NEAREST) ;

	glutSwapBuffers();
	glutPostRedisplay();
}

int main(int argc, char* argv[])
{
	gltSetWorkingDirectory(argv[0]);

	glutInit(&argc, argv);
	glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH);
	glutInitWindowSize(800, 600);
	glutCreateWindow("Frame Buffer");
	glutReshapeFunc(ChangeSize);
	glutDisplayFunc(RenderScene);

	GLenum err = glewInit();
	if (err != GLEW_OK)
	{
		fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
		return 1;
	}

	SetupRC();    
	glutMainLoop();
	ShutdownRC();

	return 0;
}


 

<think>我们正在讨论的是在QML中嵌入传统的QLineEdit组件(C++部件),而其他界面元素使用QML组件。这涉及到Qt的混合编程(C++与QML集成)。以下是实现方案的关键步骤: 1. **创建自定义的QLineEdit包装器(C++部分)**: 我们需要创建一个继承自`QQuickItem`或`QQuickPaintedItem`的类,并在其中嵌入一个QLineEdit作为子控件。但是,更常见的做法是使用`QWidget`与QML的集成方式,即通过`QQuickWindow`的`contentItem`来嵌入QWidget。然而,在QML中直接嵌入QWidget并不是官方推荐的方式,因为性能可能不佳,且可能在不同平台上有兼容性问题。 另一种推荐的方式是使用`QQuickItem`的子类,然后在其中使用`QWidget`的封装。但是,从Qt 5开始,推荐使用`QQuickPaintedItem`来绘制自定义内容,或者使用场景图(Scene Graph)进行更底层的渲染。不过,对于QLineEdit这样的控件,我们可以将其渲染到一个离屏表面(如QPixmap),然后在QQuickPaintedItem中绘制这个QPixmap,并处理鼠标事件转发给QLineEdit。这种方式相对复杂。 2. **使用QtQuick.Controls 1.x(已弃用)**: 在QtQuick.Controls 1.x中,有`QQuickWidget`可以将QWidget嵌入到QML中,但它是基于QWidget的,性能不如原生的QtQuick控件。而且,在Qt5.3.2中,QtQuick.Controls 1.x是存在的,但需要注意,它已经被标记为废弃。 3. **使用Qt的Window嵌入方式(QWidget与QML混合)**: 我们可以将整个窗口分为两部分:一部分是QWidget区域(用于放置QLineEdit),另一部分是QML区域(用于其他QML组件)。这需要在一个QWidget窗口中同时使用QWidget和QQuickWidget(用于显示QML)。但是,这样就不是在一个QML界面中嵌入QLineEdit,而是将窗口分割。 4. **推荐方案:使用QML的原生TextInput代替QLineEdit**: 根据引用[2]和[3],QML本身提供了TextInput组件用于单行文本输入,功能与QLineEdit类似。除非有特殊需求(比如必须使用QLineEdit的某些特性),否则建议使用TextInput组件。这样整个界面都是QML,可以避免混合编程的复杂性。 5. **如果必须使用QLineEdit**: 我们可以通过创建一个C++类,将这个QLineEdit的属性暴露给QML,然后通过QQuickPaintedItem或QQuickItem的子类来将这个QLineEdit绘制到QML场景中。同时,还需要处理事件转发(鼠标、键盘等)。但这种方法较为复杂。 另一种相对简单的方法是使用`QQuickView`和`QWidget::createWindowContainer`。在Qt 5中,我们可以使用`QWidget::createWindowContainer`将一个QWidget嵌入到QML中作为一个窗口。但是,这要求该QWidget是一个窗口(即具有Qt::Window标志)。我们可以将QLineEdit放在一个QWidget窗口中,然后使用`QWidget::createWindowContainer`将其包装成一个QML可以识别的组件。 示例步骤: - 创建一个QWidget窗口(例如`QLineEditWindow`),其中包含一个QLineEdit。 - 在C++中,使用`QWindow *container = QWidget::createWindowContainer(lineEditWindow);`创建一个容器。 - 将这个容器暴露给QML引擎,作为QML可以使用的类型。 - 在QML中,使用这个自定义的类型,就像一个普通的QML元素一样。 但是,请注意,`createWindowContainer`在嵌入式Linux等平台可能有问题,而且在不同平台上的行为可能不一致。 6. **使用QML的QtObject进行属性绑定**: 如果只是需要QLineEdit的数据,而不需要显示在QML界面上(比如后台处理),那么可以通过将QLineEdit的数据暴露给QML,然后在QML中用一个TextInput来显示和编辑,并通过信号槽同步数据。这样,用户操作的是QML的TextInput,而实际数据存储在C++的QLineEdit中。 考虑到用户要求的是在QML中使用QLineEdit(显示出来),而其他组件用QML,我们采用第5种方案中的`createWindowContainer`方法。以下是具体步骤: ### 实现方案:在QML中嵌入QLineEdit #### 步骤1:创建可嵌入的QLineEdit窗口(C++) 首先,创建一个继承自`QWidget`的窗口类,并设置其窗口标志为`Qt::Tool`或`Qt::Window`,使其成为一个独立的窗口。然后,在这个窗口中放置QLineEdit,并调整大小和样式。 ```cpp // lineeditwindow.h #include <QWidget> #include <QLineEdit> class LineEditWindow : public QWidget { Q_OBJECT public: explicit LineEditWindow(QWidget *parent = nullptr); QLineEdit *lineEdit() const; private: QLineEdit *m_lineEdit; }; ``` ```cpp // lineeditwindow.cpp #include "lineeditwindow.h" LineEditWindow::LineEditWindow(QWidget *parent) : QWidget(parent, Qt::Window | Qt::FramelessWindowHint) // 设置窗口标志,并且去掉边框 { m_lineEdit = new QLineEdit(this); m_lineEdit->setGeometry(0, 0, 200, 30); // 设置初始大小 // 设置样式等 setAttribute(Qt::WA_TranslucentBackground); // 如果需要透明背景 } QLineEdit *LineEditWindow::lineEdit() const { return m_lineEdit; } ``` 注意:我们设置了`Qt::FramelessWindowHint`,这样就不会有窗口边框,并且设置透明背景(如果需要)可以让它更好地融入QML界面。 #### 步骤2:创建容器并暴露给QML 在C++主程序中,我们需要创建一个容器,将这个窗口包装起来,并暴露给QML上下文。 ```cpp #include <QGuiApplication> #include <QQmlApplicationEngine> #include <QQuickWindow> #include <QWidget> #include <QQmlContext> #include "lineeditwindow.h" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); // 创建LineEditWindow LineEditWindow *lineWin = new LineEditWindow(); // 创建容器 QWindow *container = QWidget::createWindowContainer(lineWin); // 由于LineEditWindow设置了Frameless,所以我们需要自己处理移动等事件(可选) QQmlApplicationEngine engine; // 将容器暴露给QML engine.rootContext()->setContextProperty("lineEditContainer", container); engine.load(QUrl(QStringLiteral("qrc:/main.qml"))); return app.exec(); } ``` #### 步骤3:在QML中使用容器 在QML中,我们可以这样使用: ```qml // main.qml import QtQuick 2.15 import QtQuick.Window 2.15 Window { visible: true width: 400 height: 300 // 这是一个普通的QML组件 Rectangle { width: 200 height: 50 color: "lightblue" Text { text: "QML组件" } } // 嵌入的QLineEdit容器 Item { // 注意:container是一个QWindow,在QML中表现为一个Item id: lineEditItem // 注意:这里设置的位置和大小应该与C++中创建的容器关联 x: 50 y: 100 width: 200 height: 30 // 将C++暴露的container对象作为该Item的子对象 data: [lineEditContainer] // 将容器放入该Item的data中,作为其子节点 // 调整container的大小与Item一致 Component.onCompleted: { lineEditContainer.parent = lineEditItem; lineEditContainer.width = Qt.binding(function() { return lineEditItem.width; }); lineEditContainer.height = Qt.binding(function() { return lineEditItem.height; }); } } } ``` 注意:这种方法在桌面平台上通常可行,但在移动平台(如Android)上可能存在问题,因为窗口嵌套管理在不同平台上的支持程度不同。 ### 替代方案:使用QML的TextInput 考虑到跨平台和稳定性,建议尽可能使用QML的TextInput组件(如引用[2]和[3])。如果是因为某些功能TextInput无法实现,那么可以尝试扩展TextInput,或者通过C++实现自定义属性暴露给TextInput。 ### 总结 在QML中嵌入QLineEdit的方法主要有两种: 1. **使用QWidgetContainer**:通过`QWidget::createWindowContainer`将包含QLineEdit的窗口嵌入到QML中。这种方法在桌面平台可行,但在移动平台可能不稳定。 2. **使用纯QML组件**:使用TextInput替代QLineEdit,并通过C++后端提供数据绑定和业务逻辑。 根据实际需求选择。如果用户界面主要在桌面平台运行,且必须使用QLineEdit,则采用方案1。否则,强烈建议使用方案2。 §§相关问题§§ 1. 如何解决在嵌入式Linux平台上使用QWidgetContainer时的渲染问题? 2. 如何在QML的TextInput中实现QLineEdit的特定功能(如输入掩码)? 3. 在移动平台上嵌入QWidget有哪些限制? </think>### 在QML中集成QLineEdit组件的混合实现方案 在Qt框架中实现QML界面嵌入传统QWidget组件(如QLineEdit)的混合方案,需通过C++封装桥接层实现。以下是具体实现步骤: #### 1. **创建QLineEdit封装类(C++层)** ```cpp // QmlLineEditBridge.h #include <QObject> #include <QLineEdit> #include <QQuickPaintedItem> class QmlLineEditBridge : public QQuickPaintedItem { Q_OBJECT Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged) public: explicit QmlLineEditBridge(QQuickItem *parent = nullptr); void paint(QPainter *painter) override; QString text() const { return m_lineEdit.text(); } void setText(const QString &text); signals: void textChanged(); void returnPressed(); private: QLineEdit m_lineEdit; // 核心QLineEdit组件 QWidget *m_container; // 容器窗口 }; ``` ```cpp // QmlLineEditBridge.cpp QmlLineEditBridge::QmlLineEditBridge(QQuickItem *parent) : QQuickPaintedItem(parent) { // 创建透明容器窗口 m_container = QWindow::fromWinId(0); m_container->setFlags(Qt::FramelessWindowHint); m_container->setColor(Qt::transparent); // 将QLineEdit嵌入容器 m_lineEdit.setParent(QWidget::find(m_container->winId())); setAcceptedMouseButtons(Qt::AllButtons); } void QmlLineEditBridge::paint(QPainter *painter) { // 将QLineEdit渲染到QML场景 m_lineEdit.render(painter, QPoint(0, 0), QRegion(0, 0, width(), height())); } void QmlLineEditBridge::setText(const QString &text) { if (m_lineEdit.text() != text) { m_lineEdit.setText(text); update(); // 触发重绘 emit textChanged(); } } ``` #### 2. **注册C++类到QML引擎(main.cpp)** ```cpp // main.cpp #include <QGuiApplication> #include <QQmlApplicationEngine> #include "QmlLineEditBridge.h" int main(int argc, char *argv[]) { QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); QGuiApplication app(argc, argv); // 注册桥接类 qmlRegisterType<QmlLineEditBridge>("CustomWidgets", 1, 0, "LineEditBridge"); QQmlApplicationEngine engine; engine.load(QUrl("qrc:/main.qml")); return app.exec(); } ``` #### 3. **QML集成方案** ```qml // main.qml import QtQuick 2.15 import QtQuick.Window 2.15 import CustomWidgets 1.0 // 自定义组件模块 Window { width: 600 height: 400 visible: true // 原生QML组件(如按钮) Rectangle { width: 200; height: 50 color: "lightblue" Text { text: "QML按钮" } anchors.top: parent.top } // 封装的QLineEdit组件 LineEditBridge { id: nativeLineEdit width: 300; height: 40 anchors.centerIn: parent // 双向数据绑定 text: qmlTextField.text onTextChanged: qmlTextField.text = text // 事件处理 onReturnPressed: console.log("Enter pressed:", text) } // 原生QML文本输入框(对比) TextInput { id: qmlTextField width: 300; height: 40 anchors.bottom: parent.bottom font.pixelSize: 16 } } ``` #### 4. **关键技术点解析** 1. **渲染机制**: - 使用`QQuickPaintedItem`实现C++组件的渲染桥接 - 通过`paint()`方法将QLineEdit绘制到QML场景图 - 透明容器窗口确保无视觉残留(`Qt::FramelessWindowHint`) 2. **事件转发**: ```cpp // 在QmlLineEditBridge中重写事件处理 void mousePressEvent(QMouseEvent *event) override { QPointF globalPos = mapToGlobal(event->localPos()); QMouseEvent newEvent(event->type(), m_lineEdit.mapFromGlobal(globalPos), globalPos, event->button(), event->buttons(), event->modifiers()); QCoreApplication::sendEvent(&m_lineEdit, &newEvent); update(); } ``` 3. **尺寸同步**: ```cpp void QmlLineEditBridge::geometryChanged( const QRectF &newGeometry, const QRectF &oldGeometry) { QQuickPaintedItem::geometryChanged(newGeometry, oldGeometry); m_lineEdit.setGeometry(0, 0, width(), height()); } ``` #### 5. **备选优化方案** 如果性能要求较高,可改用**纹理共享方案**: ```cpp // OpenGL纹理共享示例 void QmlLineEditBridge::synchronize() { if (m_texture.isNull()) { m_texture.reset(new QQuickFramebufferObject); m_lineEdit.render(m_texture.data()); } } QSGNode *QmlLineEditBridge::updatePaintNode(QSGNode *node, UpdatePaintNodeData *) { if (!node) node = new QSGSimpleTextureNode; static_cast<QSGSimpleTextureNode*>(node)->setTexture(m_texture.data()); return node; } ``` ### 适用场景分析 | 方案 | 优点 | 缺点 | 适用场景 | |------|------|------|----------| | **QQuickPaintedItem** | 实现简单,兼容性好 | 性能较低(软件渲染) | 低频输入表单 | | **FramebufferObject** | GPU加速渲染,性能高 | 实现复杂,跨平台问题 | 高频交互界面 | | **纯QML方案** | 最佳性能,原生集成 | 功能受限(如输入掩码) | 推荐首选方案[^2] | > **关键建议**:若非必需QLineEdit特性(如输入掩码、验证器等),优先使用QML原生`TextInput`组件[^2][^3]。混合方案适用于: > 1. 需复用现有C++验证逻辑 > 2. 项目渐进式迁移场景 > 3. 特殊输入需求(如银行密码框)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值