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