原創(chuàng)|使用教程|編輯:龔雪|2024-06-04 10:21:03.880|閱讀 123 次
概述:本文將為大家介紹如何使用Qt Widget小部件如何實(shí)現(xiàn)一個(gè)簡(jiǎn)單的RHI小部件示例,歡迎下載最新版組件體驗(yàn)~
# 界面/圖表報(bào)表/文檔/IDE等千款熱門(mén)軟控件火熱銷(xiāo)售中 >>
相關(guān)鏈接:
Qt 是目前最先進(jìn)、最完整的跨平臺(tái)C++開(kāi)發(fā)工具。它不僅完全實(shí)現(xiàn)了一次編寫(xiě),所有平臺(tái)無(wú)差別運(yùn)行,更提供了幾乎所有開(kāi)發(fā)過(guò)程中需要用到的工具。如今,Qt已被運(yùn)用于超過(guò)70個(gè)行業(yè)、數(shù)千家企業(yè),支持?jǐn)?shù)百萬(wàn)設(shè)備及應(yīng)用。
本文將為大家演示如何使用QRhi、Qt的3D API和著色語(yǔ)言抽象層渲染三角形。
Qt技術(shù)交流群:166830288 歡迎一起進(jìn)群討論
在很多方面,這個(gè)示例都是世界中的RHI窗口示例的對(duì)應(yīng)。這個(gè)應(yīng)用程序中的子類(lèi)使用帶有基本頂點(diǎn)和片段著色器的簡(jiǎn)單圖形管道渲染單個(gè)三角形。與普通的基于的應(yīng)用程序不同,本示例不需要擔(dān)心較低級(jí)別的細(xì)節(jié),比如設(shè)置窗口和QRhi,或者處理交換鏈和窗口事件,因?yàn)檫@些都由這里的QWidget框架負(fù)責(zé)。QRhiWidget子類(lèi)的實(shí)例被添加到中,為了使示例保持最小和緊湊,沒(méi)有引入更多的小部件或3D內(nèi)容。
在上文中(點(diǎn)擊這里回顧>>),我們?yōu)榇蠹医榻B了結(jié)構(gòu)和main(),本文將繼續(xù)介紹如何完成渲染!
在examplewidget.cpp中,小部件實(shí)現(xiàn)使用一個(gè)輔助函數(shù)從.qsb文件加載一個(gè)對(duì)象,這個(gè)應(yīng)用程序通過(guò)Qt資源系統(tǒng)將預(yù)置的.qsb文件嵌入到可執(zhí)行文件中。由于模塊依賴(lài)(并且由于仍然支持qmake),本例不使用方便的CMake函數(shù)qt_add_shaders(),而是隨.qsb文件一起作為源代碼樹(shù)的一部分。我們鼓勵(lì)現(xiàn)實(shí)世界的應(yīng)用程序避免這種情況,而是使用Qt Shader Tools模塊的CMake集成功能(qt_add_shaders)。不管采用哪種方法,在c++代碼中,綁定/生成的.qsb文件的加載是相同的。
static QShader getShader(const QString &name) { QFile f(name); return f.open(QIODevice::ReadOnly) ? QShader::fromSerialized(f.readAll()) : QShader(); }
讓我們看一下initialize()的實(shí)現(xiàn),首先查詢(xún)和存儲(chǔ)QRhi對(duì)象以供以后使用,并允許在以后調(diào)用該函數(shù)時(shí)進(jìn)行比較。當(dāng)存在不匹配時(shí)(例如,當(dāng)小部件在窗口之間移動(dòng)時(shí)),需要重新創(chuàng)建圖形資源的重建,是通過(guò)銷(xiāo)毀和清空一個(gè)合適的對(duì)象來(lái)觸發(fā)的。在這種情況下是m_pipeline。該示例沒(méi)有主動(dòng)演示窗口之間的修復(fù),它還準(zhǔn)備好處理在調(diào)整窗口大小時(shí)可能發(fā)生的小部件大小變化。這不需要特殊的處理,因?yàn)閕nitialize()每次發(fā)生時(shí)都被調(diào)用,因此查詢(xún)r(jià)enderTarget()->pixelSize()或colorTexture()->pixelSize()總是給出最新的、最新的像素大小。這個(gè)例子沒(méi)有準(zhǔn)備好改變紋理格式和多樣本設(shè)置,因?yàn)樗皇褂媚J(rèn)值(RGBA8和沒(méi)有多樣本抗鋸齒)。
void ExampleRhiWidget::initialize(QRhiCommandBuffer *cb) { if (m_rhi != rhi()) { m_pipeline.reset(); m_rhi = rhi(); }
當(dāng)需要(重新)創(chuàng)建圖形資源時(shí),initialize()使用非常典型的基于qrhi的代碼來(lái)完成此工作。具有交錯(cuò)位置顏色頂點(diǎn)數(shù)據(jù)的單個(gè)頂點(diǎn)緩沖區(qū)就足夠了,而模型視圖投影矩陣則通過(guò)64字節(jié)(16個(gè)浮點(diǎn)數(shù))的統(tǒng)一緩沖區(qū)公開(kāi)。統(tǒng)一緩沖區(qū)是唯一的著色器可見(jiàn)資源,它只在頂點(diǎn)著色器中使用。圖形管道依賴(lài)于很多默認(rèn)值(例如,關(guān)閉深度測(cè)試、禁用混合、啟用顏色寫(xiě)入、禁用面部剔除、三角形的默認(rèn)拓?fù)涞?頂點(diǎn)數(shù)據(jù)布局是x, y, r, g, b,因此步幅是5個(gè)浮點(diǎn)數(shù),而第二個(gè)頂點(diǎn)輸入屬性(顏色)有2個(gè)浮點(diǎn)數(shù)的偏移量(跳過(guò)x和y)。每個(gè)圖形管道必須與一個(gè)QRhiRenderPassDescriptor相關(guān)聯(lián),這可以從基類(lèi)管理的QRhiRenderTarget中檢索。
注意:這個(gè)例子依賴(lài)于QRhiWidget的默認(rèn)autoRenderTarget設(shè)置為true,這就是為什么它不需要管理渲染目標(biāo),而可以通過(guò)調(diào)用renderTarget()來(lái)查詢(xún)現(xiàn)有的渲染目標(biāo)。
if (!m_pipeline) { m_vbuf.reset(m_rhi->newBuffer(QRhiBuffer::Immutable, QRhiBuffer::VertexBuffer, sizeof(vertexData))); m_vbuf->create(); m_ubuf.reset(m_rhi->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, 64)); m_ubuf->create(); m_srb.reset(m_rhi->newShaderResourceBindings()); m_srb->setBindings({ QRhiShaderResourceBinding::uniformBuffer(0, QRhiShaderResourceBinding::VertexStage, m_ubuf.get()), }); m_srb->create(); m_pipeline.reset(m_rhi->newGraphicsPipeline()); m_pipeline->setShaderStages({ { QRhiShaderStage::Vertex, getShader(QLatin1String(":/shader_assets/color.vert.qsb")) }, { QRhiShaderStage::Fragment, getShader(QLatin1String(":/shader_assets/color.frag.qsb")) } }); QRhiVertexInputLayout inputLayout; inputLayout.setBindings({ { 5 * sizeof(float) } }); inputLayout.setAttributes({ { 0, 0, QRhiVertexInputAttribute::Float2, 0 }, { 0, 1, QRhiVertexInputAttribute::Float3, 2 * sizeof(float) } }); m_pipeline->setVertexInputLayout(inputLayout); m_pipeline->setShaderResourceBindings(m_srb.get()); m_pipeline->setRenderPassDescriptor(renderTarget()->renderPassDescriptor()); m_pipeline->create(); QRhiResourceUpdateBatch *resourceUpdates = m_rhi->nextResourceUpdateBatch(); resourceUpdates->uploadStaticBuffer(m_vbuf.get(), vertexData); cb->resourceUpdate(resourceUpdates); }
最后,計(jì)算投影矩陣。這取決于小部件的大小,因此在每次函數(shù)調(diào)用中都無(wú)條件地完成。
注意:投影矩陣包括來(lái)自QRhi的校正矩陣,以適應(yīng)歸一化設(shè)備坐標(biāo)的3D API差異。(例如,Y向下 vs. Y向上)
應(yīng)用-4的平移只是為了確保z值為0的三角形是可見(jiàn)的。
const QSize outputSize = renderTarget()->pixelSize(); m_viewProjection = m_rhi->clipSpaceCorrMatrix(); m_viewProjection.perspective(45.0f, outputSize.width() / (float) outputSize.height(), 0.01f, 1000.0f); m_viewProjection.translate(0, 0, -4); }
小部件記錄單個(gè)呈現(xiàn)傳遞,其中包含單個(gè)繪制調(diào)用。
在初始化步驟中計(jì)算的視圖投影矩陣與模型矩陣相結(jié)合,在這種情況下,模型矩陣恰好是一個(gè)簡(jiǎn)單的旋轉(zhuǎn),然后將得到的矩陣寫(xiě)入統(tǒng)一緩沖區(qū)。注意resourceUpdates是如何傳遞給beginPass()的,這是一個(gè)不必手動(dòng)調(diào)用resourceUpdate()的快捷方式。
void ExampleRhiWidget::render(QRhiCommandBuffer *cb) { QRhiResourceUpdateBatch *resourceUpdates = m_rhi->nextResourceUpdateBatch(); m_rotation += 1.0f; QMatrix4x4 modelViewProjection = m_viewProjection; modelViewProjection.rotate(m_rotation, 0, 1, 0); resourceUpdates->updateDynamicBuffer(m_ubuf.get(), 0, 64, modelViewProjection.constData());
在渲染通道中,記錄一個(gè)帶有3個(gè)頂點(diǎn)的繪制調(diào)用。在初始化步驟中創(chuàng)建的圖形管道綁定在命令緩沖區(qū)上,并且將視口設(shè)置為覆蓋整個(gè)小部件。為了使統(tǒng)一緩沖區(qū)對(duì)(頂點(diǎn))著色器可見(jiàn),setShaderResources()調(diào)用時(shí)不帶參數(shù),這意味著使用m_srb,因?yàn)樗诠艿绖?chuàng)建時(shí)與管道相關(guān)聯(lián)。在更復(fù)雜的渲染器中,傳入不同的QRhiShaderResourceBindings對(duì)象并不罕見(jiàn),只要該對(duì)象與管道創(chuàng)建時(shí)給出的布局兼容即可。沒(méi)有索引緩沖區(qū),只有一個(gè)頂點(diǎn)緩沖區(qū)綁定(vbufBinding中的單個(gè)元素引用創(chuàng)建管道時(shí)指定的QRhiVertexInputLayout的綁定列表中的單個(gè)條目)。
const QColor clearColor = QColor::fromRgbF(0.4f, 0.7f, 0.0f, 1.0f); cb->beginPass(renderTarget(), clearColor, { 1.0f, 0 }, resourceUpdates); cb->setGraphicsPipeline(m_pipeline.get()); const QSize outputSize = renderTarget()->pixelSize(); cb->setViewport(QRhiViewport(0, 0, outputSize.width(), outputSize.height())); cb->setShaderResources(); const QRhiCommandBuffer::VertexInput vbufBinding(m_vbuf.get(), 0); cb->setVertexInput(0, 1, &vbufBinding); cb->draw(3); cb->endPass();
一旦記錄了渲染通道,就會(huì)調(diào)用update()。這將請(qǐng)求一個(gè)新的框架,并用于確保小部件不斷更新,并且三角形看起來(lái)是旋轉(zhuǎn)的。默認(rèn)情況下,呈現(xiàn)線程(在本例中為主線程)由呈現(xiàn)速率限制。在這個(gè)例子中沒(méi)有適當(dāng)?shù)膭?dòng)畫(huà)系統(tǒng),所以旋轉(zhuǎn)將在每一幀中增加,這意味著三角形將以不同的刷新率以不同的速度旋轉(zhuǎn)。
update(); }
本站文章除注明轉(zhuǎn)載外,均為本站原創(chuàng)或翻譯。歡迎任何形式的轉(zhuǎn)載,但請(qǐng)務(wù)必注明出處、不得修改原文相關(guān)鏈接,如果存在內(nèi)容上的異議請(qǐng)郵件反饋至chenjj@ke049m.cn
文章轉(zhuǎn)載自:慧都網(wǎng)