7
8
9
10
13#include <QtQuick3DRuntimeRender/private/qssgrendertexturedata_p.h>
14#include <QtQuick3DRuntimeRender/private/qssgrenderimage_p.h>
16#include <QtGui/qquaternion.h>
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
51
52
55
56
59
60
63
64
67
68
69
72
73
74
77
78
81
82
85
86
89
90
93
94
97
98
101
102
105
106
109
110
113
114
115
116
117
118
119
121ProceduralSkyTextureData::ProceduralSkyTextureData()
123 scheduleTextureUpdate();
126ProceduralSkyTextureData::~ProceduralSkyTextureData()
130QColor ProceduralSkyTextureData::skyTopColor()
const
132 return m_skyTopColor;
135QColor ProceduralSkyTextureData::skyHorizonColor()
const
137 return m_skyHorizonColor;
140float ProceduralSkyTextureData::skyCurve()
const
145float ProceduralSkyTextureData::skyEnergy()
const
150QColor ProceduralSkyTextureData::groundBottomColor()
const
152 return m_groundBottomColor;
155QColor ProceduralSkyTextureData::groundHorizonColor()
const
157 return m_groundHorizonColor;
160float ProceduralSkyTextureData::groundCurve()
const
162 return m_groundCurve;
165float ProceduralSkyTextureData::groundEnergy()
const
167 return m_groundEnergy;
170QColor ProceduralSkyTextureData::sunColor()
const
175float ProceduralSkyTextureData::sunLatitude()
const
177 return m_sunLatitude;
180float ProceduralSkyTextureData::sunLongitude()
const
182 return m_sunLongitude;
185float ProceduralSkyTextureData::sunAngleMin()
const
187 return m_sunAngleMin;
190float ProceduralSkyTextureData::sunAngleMax()
const
192 return m_sunAngleMax;
195float ProceduralSkyTextureData::sunCurve()
const
200float ProceduralSkyTextureData::sunEnergy()
const
205ProceduralSkyTextureData::SkyTextureQuality ProceduralSkyTextureData::textureQuality()
const
207 return m_textureQuality;
210void ProceduralSkyTextureData::setSkyTopColor(QColor skyTopColor)
212 if (m_skyTopColor == skyTopColor)
215 m_skyTopColor = skyTopColor;
216 emit skyTopColorChanged(m_skyTopColor);
217 scheduleTextureUpdate();
220void ProceduralSkyTextureData::setSkyHorizonColor(QColor skyHorizonColor)
222 if (m_skyHorizonColor == skyHorizonColor)
225 m_skyHorizonColor = skyHorizonColor;
226 emit skyHorizonColorChanged(m_skyHorizonColor);
227 scheduleTextureUpdate();
230void ProceduralSkyTextureData::setSkyCurve(
float skyCurve)
232 if (qFuzzyCompare(m_skyCurve, skyCurve))
235 m_skyCurve = skyCurve;
236 emit skyCurveChanged(m_skyCurve);
237 scheduleTextureUpdate();
240void ProceduralSkyTextureData::setSkyEnergy(
float skyEnergy)
242 if (qFuzzyCompare(m_skyEnergy, skyEnergy))
245 m_skyEnergy = skyEnergy;
246 emit skyEnergyChanged(m_skyEnergy);
247 scheduleTextureUpdate();
250void ProceduralSkyTextureData::setGroundBottomColor(QColor groundBottomColor)
252 if (m_groundBottomColor == groundBottomColor)
255 m_groundBottomColor = groundBottomColor;
256 emit groundBottomColorChanged(m_groundBottomColor);
257 scheduleTextureUpdate();
260void ProceduralSkyTextureData::setGroundHorizonColor(QColor groundHorizonColor)
262 if (m_groundHorizonColor == groundHorizonColor)
265 m_groundHorizonColor = groundHorizonColor;
266 emit groundHorizonColorChanged(m_groundHorizonColor);
267 scheduleTextureUpdate();
270void ProceduralSkyTextureData::setGroundCurve(
float groundCurve)
272 if (qFuzzyCompare(m_groundCurve, groundCurve))
275 m_groundCurve = groundCurve;
276 emit groundCurveChanged(m_groundCurve);
277 scheduleTextureUpdate();
280void ProceduralSkyTextureData::setGroundEnergy(
float groundEnergy)
282 if (qFuzzyCompare(m_groundEnergy, groundEnergy))
285 m_groundEnergy = groundEnergy;
286 emit groundEnergyChanged(m_groundEnergy);
287 scheduleTextureUpdate();
290void ProceduralSkyTextureData::setSunColor(QColor sunColor)
292 if (m_sunColor == sunColor)
295 m_sunColor = sunColor;
296 emit sunColorChanged(m_sunColor);
297 scheduleTextureUpdate();
300void ProceduralSkyTextureData::setSunLatitude(
float sunLatitude)
302 if (qFuzzyCompare(m_sunLatitude, sunLatitude))
305 m_sunLatitude = sunLatitude;
306 emit sunLatitudeChanged(m_sunLatitude);
307 scheduleTextureUpdate();
310void ProceduralSkyTextureData::setSunLongitude(
float sunLongitude)
312 if (qFuzzyCompare(m_sunLongitude, sunLongitude))
315 m_sunLongitude = sunLongitude;
316 emit sunLongitudeChanged(m_sunLongitude);
317 scheduleTextureUpdate();
320void ProceduralSkyTextureData::setSunAngleMin(
float sunAngleMin)
322 if (qFuzzyCompare(m_sunAngleMin, sunAngleMin))
325 m_sunAngleMin = sunAngleMin;
326 emit sunAngleMinChanged(m_sunAngleMin);
327 scheduleTextureUpdate();
330void ProceduralSkyTextureData::setSunAngleMax(
float sunAngleMax)
332 if (qFuzzyCompare(m_sunAngleMax, sunAngleMax))
335 m_sunAngleMax = sunAngleMax;
336 emit sunAngleMaxChanged(m_sunAngleMax);
337 scheduleTextureUpdate();
340void ProceduralSkyTextureData::setSunCurve(
float sunCurve)
342 if (qFuzzyCompare(m_sunCurve, sunCurve))
345 m_sunCurve = sunCurve;
346 emit sunCurveChanged(m_sunCurve);
347 scheduleTextureUpdate();
350void ProceduralSkyTextureData::setSunEnergy(
float sunEnergy)
352 if (qFuzzyCompare(m_sunEnergy, sunEnergy))
355 m_sunEnergy = sunEnergy;
356 emit sunEnergyChanged(m_sunEnergy);
357 scheduleTextureUpdate();
360void ProceduralSkyTextureData::setTextureQuality(ProceduralSkyTextureData::SkyTextureQuality textureQuality)
362 if (m_textureQuality == textureQuality)
365 m_textureQuality = textureQuality;
366 emit textureQualityChanged(m_textureQuality);
367 scheduleTextureUpdate();
370void ProceduralSkyTextureData::generateRGBA16FTexture()
373 switch (m_textureQuality) {
374 case SkyTextureQuality::SkyTextureQualityLow:
377 case SkyTextureQuality::SkyTextureQualityMedium:
380 case SkyTextureQuality::SkyTextureQualityHigh:
383 case SkyTextureQuality::SkyTextureQualityVeryHigh:
388 const int width = size;
389 const int height = width / 2;
390 setSize(QSize(width, height));
391 setFormat(Format::RGBA16F);
392 setHasTransparency(
false);
393 const int dataSize = width * height * 4 * 2;
394 QByteArray imageData;
395 imageData.resize(dataSize);
396 generateSkyTexture(width, height, imageData,
false);
397 setTextureData(imageData);
400QByteArray ProceduralSkyTextureData::generateSkyTexture(
int width,
int height, QByteArray &imageData,
bool isRGBE)
const
402 quint32 *data =
reinterpret_cast<quint32 *>(imageData.data());
404 LinearColor skyTopLinear(m_skyTopColor);
405 LinearColor skyHorizonLinear(m_skyHorizonColor);
406 LinearColor groundBottomLinear(m_groundBottomColor);
407 LinearColor groundHorizonLinear(m_groundHorizonColor);
408 LinearColor sunLinear(m_sunColor);
409 sunLinear.r *= m_sunEnergy;
410 sunLinear.g *= m_sunEnergy;
411 sunLinear.b *= m_sunEnergy;
413 QVector3D sun(0, 0, -1);
415 sun = QQuaternion::fromAxisAndAngle(QVector3D(1, 0, 0), m_sunLatitude) * sun;
416 sun = QQuaternion::fromAxisAndAngle(QVector3D(0, 1, 0), m_sunLongitude) * sun;
419 auto clamp = [](
float value,
float min,
float max) {
422 else if (value > max)
427 auto ease = [](
float x,
float c) {
434 return 1.0f - qPow(1.0f - x, 1.0f / c);
438 }
else if (c < 0.0f) {
440 return qPow(x * 2.0f, -c) * 0.5f;
442 return (1.0f - qPow(1.0f - (x - 0.5f) * 2.0f, -c)) * 0.5f + 0.5f;
448 for (
int i = 0; i < width; i++) {
450 float u =
float(i) / (width - 1);
451 float phi = u * 2.0 * M_PI;
453 for (
int j = 0; j < height; j++) {
454 float v =
float(j) / (height - 1);
455 float theta = v * M_PI;
457 QVector3D normal(qSin(phi) * qSin(theta) * -1.0,
459 qCos(phi) * qSin(theta) * -1.0);
461 float vAngle = qAcos(clamp(normal.y(), -1.0, 1.0));
464 if (normal.y() < 0) {
466 float c = (vAngle - (M_PI * 0.5f)) / (M_PI * 0.5f);
467 color = groundHorizonLinear.interpolate(groundBottomLinear, ease(c, m_groundCurve));
468 color.r *= m_groundEnergy;
469 color.g *= m_groundEnergy;
470 color.b *= m_groundEnergy;
473 float c = vAngle / (M_PI * 0.5f);
474 color = skyHorizonLinear.interpolate(skyTopLinear, ease(1.0 - c, m_skyCurve));
475 color.r *= m_skyEnergy;
476 color.g *= m_skyEnergy;
477 color.b *= m_skyEnergy;
479 float sunAngle = qRadiansToDegrees(qAcos(clamp(QVector3D::dotProduct(sun, normal), -1.0f, 1.0f)));
480 if (sunAngle < m_sunAngleMin) {
481 color = color.blend(sunLinear);
482 }
else if (sunAngle < m_sunAngleMax) {
483 float c2 = (sunAngle - m_sunAngleMin) / (m_sunAngleMax - m_sunAngleMin);
484 c2 = ease(c2, m_sunCurve);
485 color = color.blend(sunLinear).interpolate(color, c2);
491 data[(height - j - 1) * width + i] = color.toRGBE8();
494 const int offset = ((height - j - 1) * width + i) * 2;
495 qfloat16 *fData =
reinterpret_cast<qfloat16 *>(data + offset);
496 float pixel[4] = {color.r, color.g, color.b, color.a };
497 qFloatToFloat16(fData, pixel, 4);
505void ProceduralSkyTextureData::scheduleTextureUpdate()
507 generateRGBA16FTexture();
510ProceduralSkyTextureData::LinearColor::LinearColor(
const QColor &color)
512 const float red = color.redF();
513 const float green = color.greenF();
514 const float blue = color.blueF();
515 const float alpha = color.alphaF();
517 r = red < 0.04045 ? red * (1.0 / 12.92) : qPow((red + 0.055) * (1.0 / (1 + 0.055)), 2.4),
518 g = green < 0.04045 ? green * (1.0 / 12.92) : qPow((green + 0.055) * (1.0 / (1 + 0.055)), 2.4),
519 b = blue < 0.04045 ? blue * (1.0 / 12.92) : qPow((blue + 0.055) * (1.0 / (1 + 0.055)), 2.4),
523ProceduralSkyTextureData::LinearColor ProceduralSkyTextureData::LinearColor::interpolate(
const ProceduralSkyTextureData::LinearColor &color,
float value)
const
525 LinearColor copy = *
this;
527 copy.r += (value * (color.r - r));
528 copy.g += (value * (color.g - g));
529 copy.b += (value * (color.b - b));
530 copy.a += (value * (color.a - a));
535ProceduralSkyTextureData::LinearColor ProceduralSkyTextureData::LinearColor::blend(
const ProceduralSkyTextureData::LinearColor &color)
const
538 float sa = 1.0 - color.a;
539 copy.a = a * sa + color.a;
541 return LinearColor();
543 copy.r = (r * a * sa + color.r * color.a) / copy.a;
544 copy.g = (g * a * sa + color.g * color.a) / copy.a;
545 copy.b = (b * a * sa + color.b * color.a) / copy.a;
550quint32 ProceduralSkyTextureData::LinearColor::toRGBA8()
const
552 return (quint32(lrintf(r)) & 0xFF) |
553 ((quint32(lrintf(g)) & 0xFF) << 8) |
554 ((quint32(lrintf(b)) & 0xFF) << 16) |
555 ((quint32(lrintf(a)) & 0xFF) << 24);
558quint32 ProceduralSkyTextureData::LinearColor::toRGBE8()
const
569 v = frexp(v, &exp) * 256.0f / v;
571 quint8 *components =
reinterpret_cast<quint8*>(&result);
572 components[0] = quint8(r * v);
573 components[1] = quint8(g * v);
574 components[2] = quint8(b * v);
575 components[3] = quint8(exp + 128);
579quint32 ProceduralSkyTextureData::LinearColor::toRGBE9995()
const
581 const float pow2to9 = 512.0f;
582 const float B = 15.0f;
583 const float N = 9.0f;
585 float sharedExp = 65408.000f;
587 float cRed = qMax(0.0f, qMin(sharedExp, r));
588 float cGreen = qMax(0.0f, qMin(sharedExp, g));
589 float cBlue = qMax(0.0f, qMin(sharedExp, b));
591 float cMax = qMax(cRed, qMax(cGreen, cBlue));
593 float expp = qMax(-B - 1.0f, floor(std::log(cMax) / M_LN2)) + 1.0f + B;
595 float sMax = (
float)floor((cMax / qPow(2.0f, expp - B - N)) + 0.5f);
597 float exps = expp + 1.0f;
599 if (0.0 <= sMax && sMax < pow2to9) {
603 float sRed = qFloor((cRed / pow(2.0f, exps - B - N)) + 0.5f);
604 float sGreen = qFloor((cGreen / pow(2.0f, exps - B - N)) + 0.5f);
605 float sBlue = qFloor((cBlue / pow(2.0f, exps - B - N)) + 0.5f);
607 return (quint32(lrintf(sRed)) & 0x1FF) |
608 ((quint32(lrintf(sGreen)) & 0x1FF) << 9) |
609 ((quint32(lrintf(sBlue)) & 0x1FF) << 18) |
610 ((quint32(lrintf(exps)) & 0x1F) << 27);
Combined button and popup list for selecting options.