52void QSSGParticleRenderer::updateUniformsForParticles(
const QSSGLayerRenderData &inData,
53 QSSGRhiShaderPipeline &shaders,
54 QSSGRhiContext *rhiCtx,
56 QSSGParticlesRenderable &renderable,
57 const QSSGRenderCameraList &cameras)
59 const QMatrix4x4 clipSpaceCorrMatrix = rhiCtx->rhi()->clipSpaceCorrMatrix();
61 QSSGRhiShaderPipeline::CommonUniformIndices &cui = shaders.commonUniformIndices;
63 const int viewCount = cameras.count();
66 QMatrix4x4 camGlobalTransforms[2] { QMatrix4x4{Qt::Uninitialized}, QMatrix4x4{Qt::Uninitialized} };
68 camGlobalTransforms[0] = inData.getGlobalTransform(*cameras[0]);
70 for (size_t viewIndex = 0; viewIndex != std::size(camGlobalTransforms); ++viewIndex)
71 camGlobalTransforms[viewIndex] = inData.getGlobalTransform(*cameras[viewIndex]);
75 const QMatrix4x4 projection = clipSpaceCorrMatrix * cameras[0]->projection;
76 shaders.setUniform(ubufData,
"qt_projectionMatrix", projection.constData(), 16 *
sizeof(
float), &cui.projectionMatrixIdx);
77 const QMatrix4x4 viewMatrix = camGlobalTransforms[0].inverted();
78 shaders.setUniform(ubufData,
"qt_viewMatrix", viewMatrix.constData(), 16 *
sizeof(
float), &cui.viewMatrixIdx);
80 QVarLengthArray<QMatrix4x4, 2> projectionMatrices(viewCount);
81 QVarLengthArray<QMatrix4x4, 2> viewMatrices(viewCount);
82 for (
int viewIndex = 0; viewIndex < viewCount; ++viewIndex) {
83 projectionMatrices[viewIndex] = clipSpaceCorrMatrix * cameras[viewIndex]->projection;
84 viewMatrices[viewIndex] = camGlobalTransforms[viewIndex].inverted();
86 shaders.setUniformArray(ubufData,
"qt_projectionMatrix", projectionMatrices.constData(), viewCount, QSSGRenderShaderValue::Matrix4x4, &cui.projectionMatrixIdx);
87 shaders.setUniformArray(ubufData,
"qt_viewMatrix", viewMatrices.constData(), viewCount, QSSGRenderShaderValue::Matrix4x4, &cui.viewMatrixIdx);
90 const QMatrix4x4 &modelMatrix = renderable.globalTransform;
91 shaders.setUniform(ubufData,
"qt_modelMatrix", modelMatrix.constData(), 16 *
sizeof(
float), &cui.modelMatrixIdx);
93 const QVector2D camProperties(cameras[0]->clipPlanes);
94 shaders.setUniform(ubufData,
"qt_cameraProperties", &camProperties, 2 *
sizeof(
float), &cui.cameraPropertiesIdx);
96 QVector2D oneOverSize = QVector2D(1.0f, 1.0f);
97 auto &particleBuffer = renderable.particles.m_particleBuffer;
98 const quint32 particlesPerSlice = particleBuffer.particlesPerSlice();
99 oneOverSize = QVector2D(1.0f / particleBuffer.size().width(), 1.0f / particleBuffer.size().height());
100 shaders.setUniform(ubufData,
"qt_oneOverParticleImageSize", &oneOverSize, 2 *
sizeof(
float));
101 shaders.setUniform(ubufData,
"qt_countPerSlice", &particlesPerSlice, 1 *
sizeof(quint32));
104 shaders.setUniform(ubufData,
"qt_opacity", &renderable.opacity, 1 *
sizeof(
float));
106 float blendImages = renderable.particles.m_blendImages ? 1.0f : 0.0f;
107 float imageCount =
float(renderable.particles.m_spriteImageCount);
108 float ooImageCount = 1.0f / imageCount;
110 QVector4D spriteConfig(imageCount, ooImageCount, 0.0f, blendImages);
111 shaders.setUniform(ubufData,
"qt_spriteConfig", &spriteConfig, 4 *
sizeof(
float));
113 const float billboard = renderable.particles.m_billboard ? 1.0f : 0.0f;
114 shaders.setUniform(ubufData,
"qt_billboard", &billboard, 1 *
sizeof(
float));
117 QVector3D theLightAmbientTotal;
118 bool hasLights = !renderable.particles.m_lights.isEmpty();
122 ParticleLightData lightData;
123 auto &lights = renderable.lights;
124 for (quint32 lightIdx = 0, lightEnd = lights.size();
126 QSSGRenderLight *theLight(lights[lightIdx].light);
128 if (!renderable.particles.m_lights.contains(theLight))
131 const QMatrix4x4 lightGlobalTransform = inData.getGlobalTransform(*theLight);
133 if (theLight->type == QSSGRenderLight::Type::DirectionalLight) {
134 theLightAmbientTotal += theLight->m_diffuseColor * theLight->m_brightness;
135 }
else if (theLight->type == QSSGRenderLight::Type::PointLight && pointLight < 4) {
136 lightData.pointLightColor[pointLight] = QVector4D(theLight->m_diffuseColor * theLight->m_brightness, 1.0f);
137 lightData.pointLightPos[pointLight] = QVector4D(QSSGRenderNode::getGlobalPos(lightGlobalTransform), 1.0f);
138 lightData.pointLightConstantAtt[pointLight] = QSSGUtils::aux::translateConstantAttenuation(theLight->m_constantFade);
139 lightData.pointLightLinearAtt[pointLight] = QSSGUtils::aux::translateLinearAttenuation(theLight->m_linearFade);
140 lightData.pointLightQuadAtt[pointLight] = QSSGUtils::aux::translateQuadraticAttenuation(theLight->m_quadraticFade);
142 }
else if (theLight->type == QSSGRenderLight::Type::SpotLight && spotLight < 4) {
143 lightData.spotLightColor[spotLight] = QVector4D(theLight->m_diffuseColor * theLight->m_brightness, 1.0f);
144 lightData.spotLightPos[spotLight] = QVector4D(QSSGRenderNode::getGlobalPos(lightGlobalTransform), 1.0f);
145 lightData.spotLightDir[spotLight] = QVector4D(lights[lightIdx].direction, 0.0f);
146 lightData.spotLightConstantAtt[spotLight] = QSSGUtils::aux::translateConstantAttenuation(theLight->m_constantFade);
147 lightData.spotLightLinearAtt[spotLight] = QSSGUtils::aux::translateLinearAttenuation(theLight->m_linearFade);
148 lightData.spotLightQuadAtt[spotLight] = QSSGUtils::aux::translateQuadraticAttenuation(theLight->m_quadraticFade);
149 float coneAngle = theLight->m_coneAngle;
151 float innerConeAngle = std::min(theLight->m_innerConeAngle, coneAngle - 0.01f);
152 lightData.spotLightConeAngle[spotLight] = qDegreesToRadians(coneAngle);
153 lightData.spotLightInnerConeAngle[spotLight] = qDegreesToRadians(innerConeAngle);
156 theLightAmbientTotal += theLight->m_ambientColor;
159 int lightOffset = shaders.offsetOfUniform(
"lightData");
160 if (lightOffset >= 0)
161 memcpy(ubufData + lightOffset, &lightData,
sizeof(ParticleLightData));
163 shaders.setUniform(ubufData,
"qt_light_ambient_total", &theLightAmbientTotal, 3 *
sizeof(
float), &cui.light_ambient_totalIdx);
164 int enablePointLights = pointLight > 0 ? 1 : 0;
165 int enableSpotLights = spotLight > 0 ? 1 : 0;
166 shaders.setUniform(ubufData,
"qt_pointLights", &enablePointLights,
sizeof(
int));
167 shaders.setUniform(ubufData,
"qt_spotLights", &enableSpotLights,
sizeof(
int));
170 int segmentCount = particleBuffer.segments();
172 shaders.setUniform(ubufData,
"qt_lineSegmentCount", &segmentCount,
sizeof(
int));
173 float alphaFade = renderable.particles.m_alphaFade;
174 float sizeModifier = renderable.particles.m_sizeModifier;
175 float texcoordScale = renderable.particles.m_texcoordScale;
176 auto image = renderable.firstImage;
177 if (image && image->m_texture.m_texture) {
178 const auto size = image->m_texture.m_texture->pixelSize();
179 texcoordScale *=
float(size.height()) /
float(size.width());
181 shaders.setUniform(ubufData,
"qt_alphaFade", &alphaFade,
sizeof(
float));
182 shaders.setUniform(ubufData,
"qt_sizeModifier", &sizeModifier,
sizeof(
float));
183 shaders.setUniform(ubufData,
"qt_texcoordScale", &texcoordScale,
sizeof(
float));
186#ifdef QSSG_OIT_USE_BUFFERS
187 shaders.setOITImages((QRhiTexture*)inData.oitRenderContext.aBuffer,
188 (QRhiTexture*)inData.oitRenderContext.auxBuffer,
189 (QRhiTexture*)inData.oitRenderContext.counterBuffer);
190 if (inData.oitRenderContext.aBuffer) {
192 const QSSGRhiRenderableTexture *abuf = inData.getRenderResult(QSSGRenderResult::Key::ABufferImage);
193 const QSSGRhiRenderableTexture *aux = inData.getRenderResult(QSSGRenderResult::Key::AuxiliaryImage);
194 const QSSGRhiRenderableTexture *counter = inData.getRenderResult(QSSGRenderResult::Key::CounterImage);
195 shaders.setOITImages(abuf->texture, aux->texture, counter->texture);
198 int abufWidth = RenderHelpers::rhiCalculateABufferSize(inData.layer.oitNodeCount);
199 int listNodeCount = abufWidth * abufWidth;
200 shaders.setUniform(ubufData,
"qt_ABufImageWidth", &abufWidth,
sizeof(
int), &cui.abufImageWidth);
201 shaders.setUniform(ubufData,
"qt_listNodeCount", &listNodeCount,
sizeof(
int), &cui.listNodeCount);
202 int viewSize[2] = {inData.layerPrepResult.textureDimensions().width(), inData.layerPrepResult.textureDimensions().height()};
203 shaders.setUniform(ubufData,
"qt_viewSize", viewSize,
sizeof(
int) * 2, &cui.viewSize);
204 int samples = rhiCtx->mainPassSampleCount();
205 shaders.setUniform(ubufData,
"qt_samples", &samples,
sizeof(
int), &cui.samples);
252 const QSSGParticleBuffer &buffer,
const QSSGRenderParticles &particles,
253 const QVector3D &cameraDirection,
bool animatedParticles)
255 const QMatrix4x4 &modelMatrix = renderData.getGlobalTransform(particles);
256 const QMatrix4x4 &invModelMatrix = modelMatrix.inverted();
257 QVector3D dir = invModelMatrix.map(cameraDirection);
258 QVector3D n = dir.normalized();
259 const auto segments = buffer.segments();
260 auto particleCount = buffer.particleCount();
261 const bool lineParticles = segments > 0;
263 particleCount /= segments;
264 sortData.resize(particleCount);
267 const auto srcParticlePointer = [](
int line,
int segment,
int sc,
int ss,
int pps,
const char *source) ->
const QSSGLineParticle * {
268 int pi = (line * sc + segment) / pps;
269 int i = (line * sc + segment) % pps;
270 const QSSGLineParticle *sp =
reinterpret_cast<
const QSSGLineParticle *>(source + pi * ss);
276 const auto slices = buffer.sliceCount();
277 const auto ss = buffer.sliceStride();
278 const auto pps = buffer.particlesPerSlice();
281 const char *source = buffer.pointer();
282 const char *begin = source;
285 for (i = 0; i < particleCount; i++) {
287 const QSSGLineParticle *lineBegin = srcParticlePointer(i, 0, segments, ss, pps, source);
289 lineData.d = QVector3D::dotProduct(lineBegin->position, n);
290 for (
int j = 1; j < buffer.segments(); j++) {
291 const QSSGLineParticle *p = srcParticlePointer(i, j, segments, ss, pps, source);
292 lineData
.d = qMin(lineData
.d, QVector3D::dotProduct(p->position, n));
296 }
else if (animatedParticles) {
297 for (
int s = 0; s < slices; s++) {
298 const QSSGParticleAnimated *sp =
reinterpret_cast<
const QSSGParticleAnimated *>(source);
299 for (
int p = 0; p < pps && i < particleCount; p++) {
300 *dst = { QVector3D::dotProduct(sp->position, n),
int(
reinterpret_cast<
const char *>(sp) - begin)};
308 for (
int s = 0; s < slices; s++) {
309 const QSSGParticleSimple *sp =
reinterpret_cast<
const QSSGParticleSimple *>(source);
310 for (
int p = 0; p < pps && i < particleCount; p++) {
311 *dst = { QVector3D::dotProduct(sp->position, n),
int(
reinterpret_cast<
const char *>(sp) - begin)};
322 result.resize(buffer.bufferSize());
323 std::sort(sortData.begin(), sortData.end(), [](
const QSSGRhiSortData &a,
const QSSGRhiSortData &b){
327 auto copyParticles = [&](QByteArray &dst,
const QList<QSSGRhiSortData> &data,
const QSSGParticleBuffer &buffer) {
328 const auto slices = buffer.sliceCount();
329 const auto ss = buffer.sliceStride();
330 const auto pps = buffer.particlesPerSlice();
332 char *dest = dst.data();
333 const char *source = buffer.pointer();
337 for (
int s = 0; s < slices; s++) {
338 QSSGLineParticle *dp =
reinterpret_cast<QSSGLineParticle *>(dest);
339 for (
int p = 0; p < pps && i < particleCount; p++) {
340 *dp = *srcParticlePointer(sdata
->indexOrOffset, seg, segments, ss, pps, source);
343 if (seg == segments) {
351 }
else if (animatedParticles) {
352 for (
int s = 0; s < slices; s++) {
353 QSSGParticleAnimated *dp =
reinterpret_cast<QSSGParticleAnimated *>(dest);
354 for (
int p = 0; p < pps && i < particleCount; p++) {
355 *dp = *
reinterpret_cast<
const QSSGParticleAnimated *>(source + sdata
->indexOrOffset);
363 for (
int s = 0; s < slices; s++) {
364 QSSGParticleSimple *dp =
reinterpret_cast<QSSGParticleSimple *>(dest);
365 for (
int p = 0; p < pps && i < particleCount; p++) {
366 *dp = *
reinterpret_cast<
const QSSGParticleSimple *>(source + sdata
->indexOrOffset);
377 copyParticles(result, sortData, buffer);
391void QSSGParticleRenderer::rhiPrepareRenderable(QSSGRhiShaderPipeline &shaderPipeline,
393 QSSGRhiContext *rhiCtx,
394 QSSGRhiGraphicsPipelineState *ps,
395 QSSGParticlesRenderable &renderable,
396 const QSSGLayerRenderData &inData,
397 QRhiRenderPassDescriptor *renderPassDescriptor,
400 QSSGRenderCamera *alteredCamera,
401 QSSGRenderTextureCubeFace cubeFace,
402 QSSGReflectionMapEntry *entry,
405 const void *node = &renderable.particles;
406 const bool needsConversion = !rhiCtx->rhi()->isTextureFormatSupported(QRhiTexture::RGBA32F);
408 const auto cubeFaceIdx = QSSGBaseTypeHelpers::indexOfCubeFace(cubeFace);
409 QSSGRhiDrawCallData &dcd = QSSGRhiContextPrivate::get(rhiCtx)->drawCallData({ passKey, node, entry, cubeFaceIdx });
410 shaderPipeline.ensureUniformBuffer(&dcd.ubuf);
412 char *ubufData = dcd.ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
413 if (!alteredCamera) {
414 updateUniformsForParticles(inData, shaderPipeline, rhiCtx, ubufData, renderable, inData.renderedCameras);
416 QSSGRenderCameraList cameras({ alteredCamera });
417 updateUniformsForParticles(inData, shaderPipeline, rhiCtx, ubufData, renderable, cameras);
419 dcd.ubuf->endFullDynamicBufferUpdateForCurrentFrame();
421 QSSGRhiParticleData &particleData = QSSGRhiContextPrivate::get(rhiCtx)->particleData(&renderable.particles);
422 const QSSGParticleBuffer &particleBuffer = renderable.particles.m_particleBuffer;
423 int particleCount = particleBuffer.particleCount();
424 if (particleData.texture ==
nullptr || particleData.particleCount != particleCount) {
425 QSize size(particleBuffer.size());
426 if (!particleData.texture) {
427 particleData.texture = rhiCtx->rhi()->newTexture(needsConversion ? QRhiTexture::RGBA16F : QRhiTexture::RGBA32F, size);
428 particleData.texture->create();
430 particleData.texture->setPixelSize(size);
431 particleData.texture->create();
433 particleData.particleCount = particleCount;
436 bool sortingChanged = particleData.sorting != renderable.particles.m_depthSorting;
437 if (sortingChanged && !renderable.particles.m_depthSorting) {
438 particleData.sortData.clear();
439 particleData.sortedData.clear();
441 particleData.sorting = renderable.particles.m_depthSorting;
443 QByteArray uploadData;
445 if (renderable.particles.m_depthSorting) {
446 bool animatedParticles = renderable.particles.m_featureLevel == QSSGRenderParticles::FeatureLevel::Animated;
447 if (!alteredCamera) {
448 sortParticles(inData, particleData.sortedData, particleData.sortData, particleBuffer, renderable.particles, inData.renderedCameraData.value()[0].direction, animatedParticles);
450 const QMatrix4x4 globalTransform = inData.getGlobalTransform(*alteredCamera);
451 sortParticles(inData, particleData.sortedData, particleData.sortData, particleBuffer, renderable.particles, QSSGRenderNode::getScalingCorrectDirection(globalTransform), animatedParticles);
453 uploadData = convertParticleData(particleData.convertData, particleData.sortedData, needsConversion);
455 uploadData = convertParticleData(particleData.convertData, particleBuffer.data(), needsConversion);
458 QRhiResourceUpdateBatch *rub = rhiCtx->rhi()->nextResourceUpdateBatch();
459 QRhiTextureSubresourceUploadDescription upload;
460 upload.setData(uploadData);
461 QRhiTextureUploadDescription uploadDesc(QRhiTextureUploadEntry(0, 0, upload));
462 rub->uploadTexture(particleData.texture, uploadDesc);
463 rhiCtx->commandBuffer()->resourceUpdate(rub);
465 auto &ia = QSSGRhiInputAssemblerStatePrivate::get(*ps);
466 ia.topology = QRhiGraphicsPipeline::TriangleStrip;
467 ia.inputLayout = QRhiVertexInputLayout();
470 ps->samples = samples;
471 ps->viewCount = viewCount;
472 ps->cullMode = QRhiGraphicsPipeline::None;
474 if (inData.orderIndependentTransparencyEnabled ==
false) {
475 if (renderable.renderableFlags.hasTransparency())
476 fillTargetBlend(ps->targetBlend[0], renderable.particles.m_blendMode);
478 ps->targetBlend[0] = QRhiGraphicsPipeline::TargetBlend();
481 QSSGRhiShaderResourceBindingList bindings;
482 bindings.addUniformBuffer(0, VISIBILITY_ALL, dcd.ubuf, 0, shaderPipeline.ub0Size());
486 QSSGRenderableImage *renderableImage = renderable.firstImage;
488 int samplerBinding = shaderPipeline.bindingForTexture(
"qt_sprite");
489 if (samplerBinding >= 0) {
490 QRhiTexture *texture = renderableImage ? renderableImage->m_texture.m_texture :
nullptr;
491 if (samplerBinding >= 0 && texture) {
492 const bool mipmapped = texture->flags().testFlag(QRhiTexture::MipMapped);
493 QRhiSampler *sampler = rhiCtx->sampler({ QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_minFilterType),
494 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_magFilterType),
495 mipmapped ? QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_mipFilterType) : QRhiSampler::None,
496 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_horizontalTilingMode),
497 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_verticalTilingMode),
498 QSSGRhiHelpers::toRhi(renderableImage->m_imageNode.m_depthTilingMode)
500 bindings.addTexture(samplerBinding, QRhiShaderResourceBinding::FragmentStage, texture, sampler);
502 QRhiResourceUpdateBatch *rub = rhiCtx->rhi()->nextResourceUpdateBatch();
503 QRhiTexture *texture = rhiCtx->dummyTexture({}, rub, QSize(4, 4), Qt::white);
504 rhiCtx->commandBuffer()->resourceUpdate(rub);
505 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest,
506 QRhiSampler::Nearest,
508 QRhiSampler::ClampToEdge,
509 QRhiSampler::ClampToEdge,
512 bindings.addTexture(samplerBinding, QRhiShaderResourceBinding::FragmentStage, texture, sampler);
516 samplerBinding = shaderPipeline.bindingForTexture(
"qt_particleTexture");
517 if (samplerBinding >= 0) {
518 QRhiTexture *texture = particleData.texture;
519 if (samplerBinding >= 0 && texture) {
520 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest,
521 QRhiSampler::Nearest,
523 QRhiSampler::ClampToEdge,
524 QRhiSampler::ClampToEdge,
527 bindings.addTexture(samplerBinding, QRhiShaderResourceBinding::VertexStage, texture, sampler);
531 samplerBinding = shaderPipeline.bindingForTexture(
"qt_colorTable");
532 if (samplerBinding >= 0) {
533 bool hasTexture =
false;
534 if (renderable.colorTable) {
535 QRhiTexture *texture = renderable.colorTable->m_texture.m_texture;
538 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest,
539 QRhiSampler::Nearest,
541 QRhiSampler::ClampToEdge,
542 QRhiSampler::ClampToEdge,
545 bindings.addTexture(samplerBinding, QRhiShaderResourceBinding::FragmentStage, texture, sampler);
550 QRhiResourceUpdateBatch *rub = rhiCtx->rhi()->nextResourceUpdateBatch();
551 QRhiTexture *texture = rhiCtx->dummyTexture({}, rub, QSize(4, 4), Qt::white);
552 rhiCtx->commandBuffer()->resourceUpdate(rub);
553 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest,
554 QRhiSampler::Nearest,
556 QRhiSampler::ClampToEdge,
557 QRhiSampler::ClampToEdge,
560 bindings.addTexture(samplerBinding, QRhiShaderResourceBinding::FragmentStage, texture, sampler);
564 if (oit && inData.layer.oitMethod == QSSGRenderLayer::OITMethod::LinkedList)
565 RenderHelpers::addAccumulatorImageBindings(&shaderPipeline, bindings);
567 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx);
570 QRhiShaderResourceBindings *&srb = dcd.srb;
571 bool srbChanged =
false;
572 if (!srb || bindings != dcd.bindings) {
573 srb = rhiCtxD->srb(bindings);
574 rhiCtxD->releaseCachedSrb(dcd.bindings);
575 dcd.bindings = bindings;
579 if (cubeFace == QSSGRenderTextureCubeFaceNone)
580 renderable.rhiRenderData.mainPass.srb = srb;
582 renderable.rhiRenderData.reflectionPass.srb[cubeFaceIdx] = srb;
584 const auto pipelineKey = QSSGGraphicsPipelineStateKey::create(*ps, renderPassDescriptor, srb);
587 && dcd.renderTargetDescriptionHash == pipelineKey.extra.renderTargetDescriptionHash
588 && dcd.renderTargetDescription == pipelineKey.renderTargetDescription
591 if (cubeFace == QSSGRenderTextureCubeFaceNone)
592 renderable.rhiRenderData.mainPass.pipeline = dcd.pipeline;
594 renderable.rhiRenderData.reflectionPass.pipeline = dcd.pipeline;
596 if (cubeFace == QSSGRenderTextureCubeFaceNone) {
597 renderable.rhiRenderData.mainPass.pipeline = rhiCtxD->pipeline(pipelineKey,
598 renderPassDescriptor,
600 dcd.pipeline = renderable.rhiRenderData.mainPass.pipeline;
602 renderable.rhiRenderData.reflectionPass.pipeline = rhiCtxD->pipeline(pipelineKey,
603 renderPassDescriptor,
605 dcd.pipeline = renderable.rhiRenderData.reflectionPass.pipeline;
607 dcd.renderTargetDescriptionHash = pipelineKey.extra.renderTargetDescriptionHash;
608 dcd.renderTargetDescription = pipelineKey.renderTargetDescription;
613void QSSGParticleRenderer::prepareParticlesForModel(QSSGRhiShaderPipeline &shaderPipeline,
614 QSSGRhiContext *rhiCtx,
615 QSSGRhiShaderResourceBindingList &bindings,
616 const QSSGRenderModel *model)
618 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx);
619 QSSGRhiParticleData &particleData = rhiCtxD->particleData(model);
620 const QSSGParticleBuffer &particleBuffer = *model->particleBuffer;
621 int particleCount = particleBuffer.particleCount();
622 bool update = particleBuffer.serial() != particleData.serial;
623 const bool needsConversion = !rhiCtx->rhi()->isTextureFormatSupported(QRhiTexture::RGBA32F);
624 if (particleData.texture ==
nullptr || particleData.particleCount != particleCount) {
625 QSize size(particleBuffer.size());
626 if (!particleData.texture) {
627 particleData.texture = rhiCtx->rhi()->newTexture(needsConversion ? QRhiTexture::RGBA16F : QRhiTexture::RGBA32F, size);
628 particleData.texture->create();
630 particleData.texture->setPixelSize(size);
631 particleData.texture->create();
633 particleData.particleCount = particleCount;
638 QRhiResourceUpdateBatch *rub = rhiCtx->rhi()->nextResourceUpdateBatch();
639 QRhiTextureSubresourceUploadDescription upload;
640 upload.setData(convertParticleData(particleData.convertData, particleBuffer.data(), needsConversion));
641 QRhiTextureUploadDescription uploadDesc(QRhiTextureUploadEntry(0, 0, upload));
642 rub->uploadTexture(particleData.texture, uploadDesc);
643 rhiCtx->commandBuffer()->resourceUpdate(rub);
645 particleData.serial = particleBuffer.serial();
646 int samplerBinding = shaderPipeline.bindingForTexture(
"qt_particleTexture");
647 if (samplerBinding >= 0) {
648 QRhiTexture *texture = particleData.texture;
649 if (samplerBinding >= 0 && texture) {
650 QRhiSampler *sampler = rhiCtx->sampler({ QRhiSampler::Nearest,
651 QRhiSampler::Nearest,
653 QRhiSampler::ClampToEdge,
654 QRhiSampler::ClampToEdge,
657 bindings.addTexture(samplerBinding, QRhiShaderResourceBinding::VertexStage, texture, sampler);
764QSSGRhiShaderPipelinePtr QSSGParticleRenderer::generateRhiShaderPipeline(QSSGRenderer &renderer,
765 QSSGParticlesRenderable &inRenderable,
766 const QSSGShaderFeatures &inFeatureSet,
767 QByteArray &shaderString,
768 const QSSGShaderParticleMaterialKeyProperties &shaderKeyProperties)
770 const auto &m_contextInterface = renderer.m_contextInterface;
771 const auto &shaderCache = m_contextInterface->shaderCache();
772 const auto &shaderProgramGenerator = m_contextInterface->shaderProgramGenerator();
773 const auto &shaderLibraryManager = m_contextInterface->shaderLibraryManager();
775 shaderString = particlesLogPrefix();
776 QSSGShaderParticleMaterialKey theKey(inRenderable.shaderDescription);
778 theKey.toString(shaderString, shaderKeyProperties);
780 if (s_shaderCacheEnabled) {
781 if (
const auto &maybePipeline = shaderCache->tryGetRhiShaderPipeline(shaderString, inFeatureSet))
782 return maybePipeline;
784 const QByteArray qsbcKey = QQsbCollection::EntryDesc::generateSha(shaderString, QQsbCollection::toFeatureSet(inFeatureSet));
786 if (renderer.m_currentLayer) {
787 QQsbCollection::EntryMap &pregenEntries = renderer.m_currentLayer->m_particleShaderEntries;
788 if (pregenEntries.isEmpty()) {
789 renderer.m_currentLayer->m_particleShaderEntries = shaderLibraryManager->getParticleShaderEntries();
790 pregenEntries = renderer.m_currentLayer->m_particleShaderEntries;
792 if (!pregenEntries.isEmpty()) {
793 const auto foundIt = pregenEntries.constFind(QQsbCollection::Entry(qsbcKey));
794 if (foundIt != pregenEntries.cend())
795 return shaderCache->newPipelineFromPregenerated(shaderString, inFeatureSet, *foundIt, inRenderable.particles);
800 if (
const auto &maybePipeline = shaderCache->tryNewPipelineFromPersistentCache(qsbcKey, shaderString, inFeatureSet))
801 return maybePipeline;
805 shaderProgramGenerator->beginProgram();
806 auto &vertex = *shaderProgramGenerator->getStage(QSSGShaderGeneratorStage::Vertex);
807 auto &fragment = *shaderProgramGenerator->getStage(QSSGShaderGeneratorStage::Fragment);
808 ShaderGeneratorCommon common(*shaderProgramGenerator);
809 const bool isSpriteLinear = shaderKeyProperties.m_isSpriteLinear.getValue(theKey);
810 const bool isColorTableLinear = shaderKeyProperties.m_isColorTableLinear.getValue(theKey);
811 const bool lighting = shaderKeyProperties.m_hasLighting.getValue(theKey);
812 const bool isMapped = shaderKeyProperties.m_isMapped.getValue(theKey);
813 const bool isLineParticle = shaderKeyProperties.m_isLineParticle.getValue(theKey);
814 const bool isAnimated = shaderKeyProperties.m_isAnimated.getValue(theKey);
815 const int viewCount = inFeatureSet.isSet(QSSGShaderFeatures::Feature::DisableMultiView)
816 ? 1 : shaderKeyProperties.m_viewCount.getValue(theKey);
817 const int oit = shaderKeyProperties.m_orderIndependentTransparency.getValue(theKey);
818 const bool oitMSAA = shaderKeyProperties.m_oitMSAA.getValue(theKey);
821 common.addDefinition(
"QSSG_PARTICLES_ENABLE_ANIMATED",
"1");
823 common.addDefinition(
"QSSG_PARTICLES_ENABLE_LINE_PARTICLE",
"1");
825 common.addDefinition(
"QSSG_OIT_METHOD", QStringLiteral(
"%1").arg(oit).toUtf8());
827 common.addDefinition(
"QSSG_MULTISAMPLE",
"1");
829 common.addUniform(
"qt_modelMatrix",
"mat4");
830 if (viewCount >= 2) {
831 common.addUniformArray(
"qt_viewMatrix",
"mat4", viewCount);
832 common.addUniformArray(
"qt_projectionMatrix",
"mat4", viewCount);
834 common.addUniform(
"qt_viewMatrix",
"mat4");
835 common.addUniform(
"qt_projectionMatrix",
"mat4");
838 common.addPrefix(s_lightPrefix);
839 common.addUniform(
"lightData",
"ParticleLightData");
841 common.addUniform(
"qt_spriteConfig",
"vec4");
842 common.addUniform(
"qt_light_ambient_total",
"vec3");
843 common.addUniform(
"qt_oneOverParticleImageSize",
"vec2");
844 common.addUniform(
"qt_countPerSlice",
"uint");
846 common.addUniform(
"qt_lineSegmentCount",
"uint");
847 common.addUniform(
"qt_billboard",
"float");
848 common.addUniform(
"qt_opacity",
"float");
849 if (isLineParticle) {
850 common.addUniform(
"qt_alphaFade",
"float");
851 common.addUniform(
"qt_sizeModifier",
"float");
852 common.addUniform(
"qt_texcoordScale",
"float");
855 common.addUniform(
"qt_pointLights",
"bool");
856 common.addUniform(
"qt_spotLights",
"bool");
858 if (oit == (
int)QSSGRenderLayer::OITMethod::WeightedBlended) {
859 common.addUniform(
"qt_cameraProperties",
"vec2");
860 }
else if (oit == (
int)QSSGRenderLayer::OITMethod::LinkedList) {
861 common.addUniform(
"qt_viewSize",
"ivec2");
862 common.addUniform(
"qt_ABufImageWidth",
"uint");
863 common.addUniform(
"qt_listNodeCount",
"uint");
864#ifdef QSSG_OIT_USE_BUFFERS
865 common.addUniform(
"qt_samples",
"uint");
870 vertex.addInclude(
"particleLighting.glsllib");
872 vertex.addUniform(
"qt_particleTexture",
"sampler2D");
874 vertex.addInclude(
"particleLoading.glsllib");
876 common.addInterpolant(
"color",
"vec4");
877 common.addInterpolant(
"spriteData",
"vec2");
878 common.addInterpolant(
"texcoord",
"vec2");
879 common.addFlatInterpolant(
"instanceIndex",
"uint");
880 if (oit == (
int)QSSGRenderLayer::OITMethod::LinkedList && viewCount >= 2)
881 common.addFlatInterpolant(
"v_viewIndex",
"uint");
883 AutoFormatGenerator vg(vertex);
885 vg <<
"const vec2 corners[4] = {{0.0, 0.0}, {1.0, 0.0}, {0.0, 1.0}, {1.0, 1.0}};";
888 vg <<
"out gl_PerVertex {"
889 <<
" vec4 gl_Position;"
892 vg <<
"void main() {";
894 if (isLineParticle) {
895 vg <<
"uint segmentIndex = gl_VertexIndex / 2;"
896 <<
"uint particleIndex = segmentIndex + gl_InstanceIndex * qt_lineSegmentCount;";
898 vg <<
"uint particleIndex = gl_InstanceIndex;"
899 <<
"uint cornerIndex = gl_VertexIndex;"
900 <<
"vec2 corner = corners[cornerIndex];";
902 vg <<
"instanceIndex = particleIndex;"
903 <<
"Particle p = qt_loadParticle(particleIndex);";
905 if (isLineParticle) {
906 vg <<
"float side = float(mod(gl_VertexIndex, 2)) - 0.5;"
907 <<
"float size = p.size * qt_calcSizeFactor(segmentIndex);"
908 <<
"vec3 o = p.binormal * side * size;"
909 <<
"vec4 worldPos = qt_modelMatrix * vec4(p.position + (1.0 - qt_billboard) * o, 1.0);";
910 if (viewCount >= 2) {
911 vg <<
"vec4 viewPos = qt_viewMatrix[gl_ViewIndex] * worldPos;"
912 <<
"vec3 viewBN = (transpose(inverse(qt_viewMatrix[gl_ViewIndex] * qt_modelMatrix)) * vec4(p.binormal, 0.0)).xyz;";
914 vg <<
"vec4 viewPos = qt_viewMatrix * worldPos;"
915 <<
"vec3 viewBN = (transpose(inverse(qt_viewMatrix * qt_modelMatrix)) * vec4(p.binormal, 0.0)).xyz;";
917 vg <<
"viewBN.z = 0;"
918 <<
"viewBN = normalize(viewBN) * side * size;"
919 <<
"viewPos = viewPos + vec4(viewBN, 0.0) * qt_billboard;"
920 <<
"texcoord.x = p.segmentLength * qt_texcoordScale;"
921 <<
"texcoord.y = float(mod(gl_VertexIndex, 2));"
922 <<
"color = p.color;"
923 <<
"color.a *= qt_opacity * qt_calcAlphaFade(segmentIndex);";
925 vg <<
"mat3 rotMat = qt_fromEulerRotation(p.rotation);"
926 <<
"vec4 worldPos = qt_modelMatrix * vec4(p.position, 1.0);"
927 <<
"vec4 viewPos = worldPos;"
928 <<
"vec3 offset = (p.size * vec3(corner - vec2(0.5), 0.0));"
929 <<
"viewPos.xyz += offset * rotMat * (1.0 - qt_billboard);";
931 vg <<
"viewPos = qt_viewMatrix[gl_ViewIndex] * viewPos;";
933 vg <<
"viewPos = qt_viewMatrix * viewPos;";
934 vg <<
"viewPos.xyz += rotMat * offset * qt_billboard;"
935 <<
"texcoord = corner;"
936 <<
"color = p.color;"
937 <<
"color.a *= qt_opacity;";
940 vg <<
"color.rgb *= qt_calcLightColor(worldPos.xyz);";
941 if (viewCount >= 2) {
942 if (oit ==
int(QSSGRenderLayer::OITMethod::LinkedList))
943 vg <<
"v_viewIndex = gl_ViewIndex;";
944 vg <<
"gl_Position = qt_projectionMatrix[gl_ViewIndex] * viewPos;";
946 vg <<
"gl_Position = qt_projectionMatrix * viewPos;";
949 vg <<
"spriteData.x = qt_ageToSpriteFactor(p.age);";
951 vg <<
"spriteData.y = p.animationFrame;";
956 fragment.addInclude(
"tonemapping.glsllib");
957 fragment.addInclude(
"particleSprite.glsllib");
958 fragment.addUniform(
"qt_sprite",
"sampler2D");
960 AutoFormatGenerator fg(fragment);
962 if (oit ==
int(QSSGRenderLayer::OITMethod::WeightedBlended)) {
963 fragment.addInclude(
"oitweightedblended.glsllib");
964 fg <<
"layout(location = 1) out vec4 revealageOutput;";
965 }
else if (oit ==
int(QSSGRenderLayer::OITMethod::LinkedList)) {
966#ifdef QSSG_OIT_USE_BUFFERS
967 fragment.addInclude(
"oitlinkedlist_buf.glsllib");
968 QSSGShaderResourceMergeContext::setAdditionalBufferAmount(3);
970 fragment.addInclude(
"oitlinkedlist.glsllib");
974 fg <<
"#define spriteFunc";
976 fg <<
"#define spriteFunc qt_sRGBToLinear";
978 fragment.addUniform(
"qt_colorTable",
"sampler2D");
979 if (isColorTableLinear)
980 fg <<
"#define colorTableFunc";
982 fg <<
"#define colorTableFunc qt_sRGBToLinear";
985 fg <<
"vec4 qt_readSprite() {";
988 fg <<
"vec2 coords[2];"
989 <<
"float factor = qt_spriteCoords(coords);"
990 <<
"return mix(spriteFunc(texture(qt_sprite, coords[0])), spriteFunc(texture(qt_sprite, coords[1])), factor);";
992 fg <<
"return spriteFunc(texture(qt_sprite, texcoord));";
996 fg <<
"vec4 qt_readColor() {";
999 fg <<
"return color * colorTableFunc(texture(qt_colorTable, vec2(spriteData.x, qt_rand(vec2(instanceIndex, 0)))));";
1001 fg <<
"return color;";
1004 fg <<
"void main() {";
1006 fg <<
"vec4 ret = qt_readColor() * qt_readSprite();";
1008 if (oit ==
int(QSSGRenderLayer::OITMethod::WeightedBlended)) {
1009 fg <<
"float z = abs(gl_FragCoord.z);"
1010 <<
"float distWeight = qt_transparencyWeight(z, ret.a, qt_cameraProperties.y);"
1011 <<
"fragOutput = distWeight * qt_tonemap(ret);"
1012 <<
"revealageOutput = vec4(ret.a);";
1013 }
else if (oit ==
int(QSSGRenderLayer::OITMethod::LinkedList)) {
1014#ifdef QSSG_OIT_USE_BUFFERS
1016 fg <<
"fragOutput = qt_oitLinkedList(ret, qt_listNodeCount, qt_ABufImageWidth, qt_viewSize, v_viewIndex, qt_samples);";
1018 fg <<
"fragOutput = qt_oitLinkedList(ret, qt_listNodeCount, qt_ABufImageWidth, qt_viewSize, 0, qt_samples);";
1021 fg <<
"fragOutput = qt_oitLinkedList(ret, qt_listNodeCount, qt_ABufImageWidth, qt_viewSize, v_viewIndex);";
1023 fg <<
"fragOutput = qt_oitLinkedList(ret, qt_listNodeCount, qt_ABufImageWidth, qt_viewSize, 0);";
1027 fg <<
"fragOutput = qt_tonemap(ret);";
1033 return shaderProgramGenerator->compileGeneratedRhiShader(shaderString,
1035 *shaderLibraryManager,
1037 QSSGRhiShaderPipeline::UsedWithoutIa,
1039 shaderKeyProperties.m_viewCount.getValue(inRenderable.shaderDescription),