6#include <QtQuick3DRuntimeRender/private/qssgrenderskymaterial_p.h>
7#include <QtQuick3DRuntimeRender/private/qssgrendererimplshaders_p.h>
8#include <QtQuick3DRuntimeRender/private/qssgrhieffectsystem_p.h>
9#include <QtQuick3DRuntimeRender/private/qssgshadermaterialadapter_p.h>
11#include <QtCore/qhash.h>
12#include <QtCore/qspan.h>
13#include <QtGui/qvector4d.h>
14#include <rhi/qshaderbaker.h>
18using namespace Qt::StringLiterals;
23void main()
24{
25 vec4 qt_vertPosition = vec4(attr_pos, 1.0);
26 qt_eyeDir = qt_vertPosition.xyz;
27 gl_Position = qt_projectionMatrix * qt_viewMatrix * vec4(qt_eyeDir, 1.0);
28}
29
30)";
38void main()
39{
40 gl_Position = vec4(attr_pos, 1.0);
41#if QSHADER_VIEW_COUNT >= 2
42 vec3 qt_unprojected = (qt_inverseProjection[gl_ViewIndex] * gl_Position).xyz;
43 qt_eyeDir = (qt_viewMatrix[gl_ViewIndex] * vec4(qt_unprojected, 0.0)).xyz;
44#else
45 vec3 qt_unprojected = (qt_inverseProjection * gl_Position).xyz;
46 qt_eyeDir = (qt_viewMatrix * vec4(qt_unprojected, 0.0)).xyz;
47#endif
48 gl_Position.y *= qt_adjustY;
49}
50
51)";
54
55void main()
56{
57 qt_customMain();
58}
59
60)";
67
68void main()
69{
70 qt_customMain();
71 FRAGCOLOR = vec4(qt_tonemap(qt_exposure(FRAGCOLOR.rgb, qt_skyExposure)), 1.0);
72}
73
74)";
77// Helper: outer square border of a face
78float line(vec2 uv, float width)
79{
80 vec2 d = abs(uv - 0.5);
81 return step(max(d.x, d.y), 0.5) - step(max(d.x, d.y), 0.5 - width);
82}
83
84// Simple plus/minus symbol
85float drawPlus(vec2 uv)
86{
87 float h = step(abs(uv.y - 0.5), 0.05) * step(abs(uv.x - 0.5), 0.2);
88 float v = step(abs(uv.x - 0.5), 0.05) * step(abs(uv.y - 0.5), 0.2);
89 return max(h, v);
90}
91
92float drawMinus(vec2 uv)
93{
94 return step(abs(uv.y - 0.5), 0.05) * step(abs(uv.x - 0.5), 0.2);
95}
96
97void MAIN()
98{
99 vec3 d = normalize(qt_eyeDir);
100 vec3 ad = abs(d);
101
102 vec2 uv;
103 vec3 faceColor;
104 float label = 0.0;
105
106 // Determine dominant axis (cubemap face)
107 if (ad.x > ad.y && ad.x > ad.z)
108 {
109 uv = d.zy / ad.x * 0.5 + 0.5;
110 if (d.x > 0.0)
111 {
112 faceColor = vec3(1.0, 0.0, 0.0); // +X red
113 label = drawPlus(uv);
114 }
115 else
116 {
117 faceColor = vec3(0.5, 0.0, 0.0); // -X dark red
118 label = drawMinus(uv);
119 }
120 }
121 else if (ad.y > ad.x && ad.y > ad.z)
122 {
123 uv = d.xz / ad.y * 0.5 + 0.5;
124 if (d.y > 0.0)
125 {
126 faceColor = vec3(0.0, 1.0, 0.0); // +Y green
127 label = drawPlus(uv);
128 }
129 else
130 {
131 faceColor = vec3(0.0, 0.5, 0.0); // -Y dark green
132 label = drawMinus(uv);
133 }
134 }
135 else
136 {
137 uv = d.xy / ad.z * 0.5 + 0.5;
138 if (d.z > 0.0)
139 {
140 faceColor = vec3(0.0, 0.0, 1.0); // +Z blue
141 label = drawPlus(uv);
142 }
143 else
144 {
145 faceColor = vec3(0.0, 0.0, 0.5); // -Z dark blue
146 label = drawMinus(uv);
147 }
148 }
149
150 // Outer border
151 float border = line(uv, 0.02);
152
153 // Combine
154 vec3 color = faceColor;
155 color = mix(color, vec3(1.0), border); // white border
156 color = mix(color, vec3(1.0), label); // white + or - label
157
158 FRAGCOLOR = vec4(color, 1.0);
159}
160)";
162QSSGRenderSkyMaterial::QSSGRenderSkyMaterial()
163 : QSSGRenderGraphObject(QSSGRenderGraphObject::Type::SkyMaterial, FlagT(Flags::HasGraphicsResources))
167QSSGRenderSkyMaterial::~QSSGRenderSkyMaterial() =
default;
174 for (
const auto &u : std::as_const(propertyUniforms)) {
175 if (u.shaderDataType == QSSGRenderShaderValue::Texture) {
176 QSSGRenderImage *image = u.value.value<QSSGRenderImage *>();
177 const QSSGRenderImageTexture texture = image ? bufferManager.loadRenderImage(image) : QSSGRenderImageTexture { };
178 if (!image || !texture.m_texture)
188 QSSGRhiShaderResourceBindingList &bindings,
189 QSSGRhiContext *rhiCtx)
191 int maxSamplerBinding = -1;
192 QVector<QShaderDescription::InOutVariable> samplerVars =
193 pipeline->fragmentStage()->shader().description().combinedImageSamplers();
194 for (
const QShaderDescription::InOutVariable &var :
195 pipeline->vertexStage()->shader().description().combinedImageSamplers()) {
196 auto it = std::find_if(samplerVars.cbegin(), samplerVars.cend(), [&var](
const QShaderDescription::InOutVariable &v) {
197 return var.binding == v.binding;
199 if (it == samplerVars.cend())
200 samplerVars.append(var);
202 for (
const QShaderDescription::InOutVariable &var : std::as_const(samplerVars))
203 maxSamplerBinding = qMax(maxSamplerBinding, var.binding);
205 if (maxSamplerBinding < 0)
208 const int customTexCount = pipeline->extraTextureCount();
209 for (
int i = 0; i < customTexCount; ++i) {
210 const QSSGRhiTexture &t(pipeline->extraTextureAt(i));
211 const int samplerBinding = pipeline->bindingForTexture(t.name);
212 if (samplerBinding >= 0) {
213 QRhiSampler *sampler = rhiCtx->sampler(t.samplerDesc);
214 bindings.addTexture(samplerBinding, QRhiShaderResourceBinding::FragmentStage, t.texture, sampler);
224QSSGRhiShaderPipelinePtr QSSGRenderSkyMaterial::buildPipeline(
const QSSGRenderContextInterface &sgContext,
225 QByteArray vertexShader,
226 const QSSGShaderCustomMaterialAdapter::StringPairList &vertexViewDependentUniforms,
227 const QSSGShaderCustomMaterialAdapter::StringPairList &vertexUniforms,
228 const QByteArray &fragmentMainSnippet,
229 const QSSGShaderCustomMaterialAdapter::StringPairList &fragmentUniforms,
230 const QSSGShaderFeatures &features,
232 const QByteArray &cacheKeyTag)
234 const bool multiViewCompatible = viewCount >= 2;
236 QByteArray fragmentShader = (!fragmentShaderSource.isEmpty() ? fragmentShaderSource : QByteArray(debugFragStr)) + fragmentMainSnippet;
238 QSSGShaderCustomMaterialAdapter::StringPairList propertyBaseUniforms;
239 for (
const auto &u : std::as_const(propertyUniforms))
240 propertyBaseUniforms.append({ u.typeName, u.name });
242 QSSGShaderCustomMaterialAdapter::StringPairList inputOutputs;
243 inputOutputs.append({
"vec3",
"qt_eyeDir"_ba });
246 QSSGShaderCustomMaterialAdapter::StringPairList vertexBaseUniforms = propertyBaseUniforms;
247 vertexBaseUniforms.append(vertexUniforms.constData(), vertexUniforms.size());
249 QSSGShaderCustomMaterialAdapter::ShaderCodeAndMetaData result;
251 QSSGShaderCustomMaterialAdapter::CustomShaderPrepWorkData scratch;
252 QSSGShaderCustomMaterialAdapter::beginPrepareCustomShader(&scratch, &result, vertexShader, QSSGShaderCache::ShaderType::Vertex, multiViewCompatible);
253 QSSGShaderCustomMaterialAdapter::finishPrepareCustomShader(&buf,
256 QSSGShaderCache::ShaderType::Vertex,
262 vertexViewDependentUniforms);
263 vertexShader = result.first;
264 vertexShader.append(buf);
269 QSSGShaderCustomMaterialAdapter::StringPairList fragmentBaseUniforms = propertyBaseUniforms;
270 fragmentBaseUniforms.append(fragmentUniforms.constData(), fragmentUniforms.size());
272 QSSGShaderCustomMaterialAdapter::ShaderCodeAndMetaData result;
274 QSSGShaderCustomMaterialAdapter::CustomShaderPrepWorkData scratch;
275 QSSGShaderCustomMaterialAdapter::beginPrepareCustomShader(&scratch, &result, fragmentShader, QSSGShaderCache::ShaderType::Fragment, multiViewCompatible);
276 QSSGShaderCustomMaterialAdapter::finishPrepareCustomShader(&buf,
279 QSSGShaderCache::ShaderType::Fragment,
281 fragmentBaseUniforms,
286 fragmentShader = result.first;
287 fragmentShader.append(buf);
290 auto generator = sgContext.shaderProgramGenerator().get();
291 auto shaderLib = sgContext.shaderLibraryManager().get();
292 auto shaderCache = sgContext.shaderCache().get();
294 generator->beginProgram();
295 auto vertex = generator->getStage(QSSGShaderGeneratorStage::Vertex);
296 vertex->addIncoming(
"attr_pos"_ba,
"vec3"_ba);
297 vertex->append(vertexShader);
299 auto fragment = generator->getStage(QSSGShaderGeneratorStage::Fragment);
300 fragment->addInclude(
"tonemapping.glsllib"_ba);
301 fragment->append(fragmentShader);
303 const QByteArray key = shaderPathKey + cacheKeyTag +
':'
304 + QCryptographicHash::hash(QByteArray(vertexShader + fragmentShader), QCryptographicHash::Algorithm::Sha1).toHex();
305 return generator->compileGeneratedRhiShader(key, features, *shaderLib, *shaderCache, QSSGRhiShaderPipeline::UsedWithoutIa, { }, viewCount,
false);
308QSSGRhiShaderPipelinePtr QSSGRenderSkyMaterial::ensurePipeline(
const QSSGRenderContextInterface &sgContext)
310 if (iblPassPipeline && !isFragmentShaderDirty)
311 return iblPassPipeline;
313 if (!skyShaderTexturesReady(propertyUniforms, *sgContext.bufferManager().get()))
316 QSSGShaderCustomMaterialAdapter::StringPairList vertexViewDependentUniforms;
317 vertexViewDependentUniforms.append({
"mat4"_ba,
"qt_projectionMatrix"_ba });
318 vertexViewDependentUniforms.append({
"mat4"_ba,
"qt_viewMatrix"_ba });
323 QSSGShaderFeatures features;
324 features.set(QSSGShaderFeatures::Feature::AcesTonemapping,
true);
326 iblPassPipeline = buildPipeline(sgContext, vertexShaderStr, vertexViewDependentUniforms, { }, mainFragmentSnippet, { }, features, 1, QByteArrayLiteral(
":cube"));
328 isFragmentShaderDirty =
false;
330 return iblPassPipeline;
333QSSGRhiShaderPipelinePtr QSSGRenderSkyMaterial::ensureBackgroundPipeline(
const QSSGRenderContextInterface &sgContext,
334 const QSSGShaderFeatures &tonemapFeatures,
338 if (backgroundPipeline && !isBackgroundShaderDirty && m_backgroundTonemapKey == tonemapKey
339 && m_backgroundViewCount == viewCount) {
340 return backgroundPipeline;
343 if (!skyShaderTexturesReady(propertyUniforms, *sgContext.bufferManager().get()))
346 QSSGShaderCustomMaterialAdapter::StringPairList vertexViewDependentUniforms;
347 vertexViewDependentUniforms.append({
"mat4"_ba,
"qt_inverseProjection"_ba });
348 vertexViewDependentUniforms.append({
"mat4"_ba,
"qt_viewMatrix"_ba });
350 QSSGShaderCustomMaterialAdapter::StringPairList vertexUniforms;
351 vertexUniforms.append({
"float"_ba,
"qt_adjustY"_ba });
353 QSSGShaderCustomMaterialAdapter::StringPairList fragmentUniforms;
354 fragmentUniforms.append({
"float"_ba,
"qt_skyExposure"_ba });
356 backgroundPipeline = buildPipeline(sgContext, vertexShaderStrScreen, vertexViewDependentUniforms, vertexUniforms,
357 mainFragmentSnippetScreen, fragmentUniforms, tonemapFeatures, viewCount,
358 QByteArrayLiteral(
":screen:") + QByteArray::number(tonemapKey)
359 +
':' + QByteArray::number(viewCount));
361 m_backgroundTonemapKey = tonemapKey;
362 m_backgroundViewCount = viewCount;
363 isBackgroundShaderDirty =
false;
365 return backgroundPipeline;
368quint32 QSSGRenderSkyMaterial::updateUniforms(
const QSSGRenderContextInterface &sgContext,
370 const QMatrix4x4 &mvp,
371 const QVarLengthArray<QMatrix4x4, 6> views)
373 constexpr int cMatrixSize = 64;
375 if (!iblPassPipeline)
378 QSSGRhiContext *rhiCtx = sgContext.rhiContext().get();
379 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx);
383 QSSGRhiDrawCallData *dcd = &rhiCtxD->drawCallData({ passKey,
nullptr,
this,
nullptr, 0 });
385 const int uniformStride = rhiCtx->rhi()->ubufAligned(iblPassPipeline->ub0Size());
386 const int totalBufferSize = uniformStride * 6;
389 dcd->ubuf = rhiCtx->rhi()->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, totalBufferSize);
393 char *ubufData = dcd->ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
394 auto bufferManager = sgContext.bufferManager().get();
396 iblPassPipeline->resetExtraTextures();
398 for (
const auto &u : std::as_const(propertyUniforms))
399 iblPassPipeline->setShaderResources(ubufData, *bufferManager, u.name, u.value, u.shaderDataType);
402 iblPassPipeline->setShaderResources(ubufData, *bufferManager,
"qt_projectionMatrix"_ba, mvp, QSSGRenderShaderValue::Type::Matrix4x4);
403 iblPassPipeline->setShaderResources(ubufData, *bufferManager,
"qt_viewMatrix"_ba, views[0], QSSGRenderShaderValue::Type::Matrix4x4);
408 const int viewMatrixOffset = iblPassPipeline->offsetOfUniform(
"qt_viewMatrix"_ba);
410 Q_ASSERT(ubufData !=
nullptr);
411 Q_ASSERT(totalBufferSize >= 6 * uniformStride);
412 Q_ASSERT(uniformStride >= cMatrixSize);
413 Q_ASSERT(viewMatrixOffset >= 0);
414 Q_ASSERT(viewMatrixOffset + cMatrixSize <= uniformStride);
416 auto buffer = QSpan<
char>(ubufData, totalBufferSize);
417 auto firstFace = buffer.first(uniformStride);
419 for (
int face = 1; face < 6; ++face) {
420 auto dst = buffer.sliced(face * uniformStride, uniformStride);
423 std::copy(firstFace.begin(), firstFace.end(), dst.begin());
426 std::copy_n(
reinterpret_cast<
const char *>(views[face].constData()), cMatrixSize, dst.data() + viewMatrixOffset);
428 dcd->ubuf->endFullDynamicBufferUpdateForCurrentFrame();
430 rhiCtxD->releaseCachedSrb(bindings);
431 bindings = QSSGRhiShaderResourceBindingList();
432 bindings.addUniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, dcd->ubuf, 0, uniformStride,
true);
433 appendCustomTextureBindings(iblPassPipeline.get(), bindings, rhiCtx);
435 return uniformStride;
438void QSSGRenderSkyMaterial::updateBackgroundUniforms(
const QSSGRenderContextInterface &sgContext,
440 const QVarLengthArray<QMatrix4x4, 2> &inverseProjections,
441 const QVarLengthArray<QMatrix4x4, 2> &viewRotations,
445 if (!backgroundPipeline)
448 QSSGRhiContext *rhiCtx = sgContext.rhiContext().get();
449 QSSGRhiContextPrivate *rhiCtxD = QSSGRhiContextPrivate::get(rhiCtx);
453 QSSGRhiDrawCallData *dcd = &rhiCtxD->drawCallData({ passKey,
nullptr,
this,
nullptr, 0 });
455 const int bufferSize = rhiCtx->rhi()->ubufAligned(backgroundPipeline->ub0Size());
457 if (!dcd->ubuf ||
int(dcd->ubuf->size()) != bufferSize) {
459 dcd->ubuf = rhiCtx->rhi()->newBuffer(QRhiBuffer::Dynamic, QRhiBuffer::UniformBuffer, bufferSize);
463 char *ubufData = dcd->ubuf->beginFullDynamicBufferUpdateForCurrentFrame();
464 auto bufferManager = sgContext.bufferManager().get();
466 backgroundPipeline->resetExtraTextures();
468 for (
const auto &u : std::as_const(propertyUniforms))
469 backgroundPipeline->setShaderResources(ubufData, *bufferManager, u.name, u.value, u.shaderDataType);
471 backgroundPipeline->setShaderResources(ubufData, *bufferManager,
"qt_adjustY"_ba, adjustY, QSSGRenderShaderValue::Type::Float);
472 backgroundPipeline->setShaderResources(ubufData, *bufferManager,
"qt_skyExposure"_ba, exposure, QSSGRenderShaderValue::Type::Float);
474 constexpr int matSize = 64;
475 const int invProjOffset = backgroundPipeline->offsetOfUniform(
"qt_inverseProjection"_ba);
476 const int viewMatOffset = backgroundPipeline->offsetOfUniform(
"qt_viewMatrix"_ba);
477 const int viewCount =
int(inverseProjections.size());
478 for (
int v = 0; v < viewCount; ++v) {
479 if (invProjOffset >= 0)
480 std::copy_n(
reinterpret_cast<
const char *>(inverseProjections[v].constData()), matSize, ubufData + invProjOffset + v * matSize);
481 if (viewMatOffset >= 0)
482 std::copy_n(
reinterpret_cast<
const char *>(viewRotations[v].constData()), matSize, ubufData + viewMatOffset + v * matSize);
485 dcd->ubuf->endFullDynamicBufferUpdateForCurrentFrame();
487 rhiCtxD->releaseCachedSrb(backgroundBindings);
488 backgroundBindings = QSSGRhiShaderResourceBindingList();
489 backgroundBindings.addUniformBuffer(0, QRhiShaderResourceBinding::VertexStage | QRhiShaderResourceBinding::FragmentStage, dcd->ubuf);
490 appendCustomTextureBindings(backgroundPipeline.get(), backgroundBindings, rhiCtx);
static const char * mainFragmentSnippetScreen
static void appendCustomTextureBindings(QSSGRhiShaderPipeline *pipeline, QSSGRhiShaderResourceBindingList &bindings, QSSGRhiContext *rhiCtx)
static const char * mainFragmentSnippet
static QT_BEGIN_NAMESPACE const char * vertexShaderStr
static const char * vertexShaderStrScreen
static bool skyShaderTexturesReady(const QList< QSSGBaseTypeProperty > &propertyUniforms, QSSGBufferManager &bufferManager)
static const char * debugFragStr