5
6
7
8
11#include <QtQuick3DRuntimeRender/private/qssgrendertexturedata_p.h>
12#include <QtQuick3DRuntimeRender/private/qssgrenderimage_p.h>
14#include <QtGui/qquaternion.h>
19
20
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
49
50
53
54
57
58
61
62
65
66
67
70
71
72
75
76
79
80
83
84
87
88
91
92
95
96
99
100
103
104
107
108
111
112
113
114
115
116
117
119ProceduralSkyTextureData::ProceduralSkyTextureData()
121 scheduleTextureUpdate();
124ProceduralSkyTextureData::~ProceduralSkyTextureData()
128QColor ProceduralSkyTextureData::skyTopColor()
const
130 return m_skyTopColor;
133QColor ProceduralSkyTextureData::skyHorizonColor()
const
135 return m_skyHorizonColor;
138float ProceduralSkyTextureData::skyCurve()
const
143float ProceduralSkyTextureData::skyEnergy()
const
148QColor ProceduralSkyTextureData::groundBottomColor()
const
150 return m_groundBottomColor;
153QColor ProceduralSkyTextureData::groundHorizonColor()
const
155 return m_groundHorizonColor;
158float ProceduralSkyTextureData::groundCurve()
const
160 return m_groundCurve;
163float ProceduralSkyTextureData::groundEnergy()
const
165 return m_groundEnergy;
168QColor ProceduralSkyTextureData::sunColor()
const
173float ProceduralSkyTextureData::sunLatitude()
const
175 return m_sunLatitude;
178float ProceduralSkyTextureData::sunLongitude()
const
180 return m_sunLongitude;
183float ProceduralSkyTextureData::sunAngleMin()
const
185 return m_sunAngleMin;
188float ProceduralSkyTextureData::sunAngleMax()
const
190 return m_sunAngleMax;
193float ProceduralSkyTextureData::sunCurve()
const
198float ProceduralSkyTextureData::sunEnergy()
const
203ProceduralSkyTextureData::SkyTextureQuality ProceduralSkyTextureData::textureQuality()
const
205 return m_textureQuality;
208void ProceduralSkyTextureData::setSkyTopColor(QColor skyTopColor)
210 if (m_skyTopColor == skyTopColor)
213 m_skyTopColor = skyTopColor;
214 emit skyTopColorChanged(m_skyTopColor);
215 scheduleTextureUpdate();
218void ProceduralSkyTextureData::setSkyHorizonColor(QColor skyHorizonColor)
220 if (m_skyHorizonColor == skyHorizonColor)
223 m_skyHorizonColor = skyHorizonColor;
224 emit skyHorizonColorChanged(m_skyHorizonColor);
225 scheduleTextureUpdate();
228void ProceduralSkyTextureData::setSkyCurve(
float skyCurve)
230 if (qFuzzyCompare(m_skyCurve, skyCurve))
233 m_skyCurve = skyCurve;
234 emit skyCurveChanged(m_skyCurve);
235 scheduleTextureUpdate();
238void ProceduralSkyTextureData::setSkyEnergy(
float skyEnergy)
240 if (qFuzzyCompare(m_skyEnergy, skyEnergy))
243 m_skyEnergy = skyEnergy;
244 emit skyEnergyChanged(m_skyEnergy);
245 scheduleTextureUpdate();
248void ProceduralSkyTextureData::setGroundBottomColor(QColor groundBottomColor)
250 if (m_groundBottomColor == groundBottomColor)
253 m_groundBottomColor = groundBottomColor;
254 emit groundBottomColorChanged(m_groundBottomColor);
255 scheduleTextureUpdate();
258void ProceduralSkyTextureData::setGroundHorizonColor(QColor groundHorizonColor)
260 if (m_groundHorizonColor == groundHorizonColor)
263 m_groundHorizonColor = groundHorizonColor;
264 emit groundHorizonColorChanged(m_groundHorizonColor);
265 scheduleTextureUpdate();
268void ProceduralSkyTextureData::setGroundCurve(
float groundCurve)
270 if (qFuzzyCompare(m_groundCurve, groundCurve))
273 m_groundCurve = groundCurve;
274 emit groundCurveChanged(m_groundCurve);
275 scheduleTextureUpdate();
278void ProceduralSkyTextureData::setGroundEnergy(
float groundEnergy)
280 if (qFuzzyCompare(m_groundEnergy, groundEnergy))
283 m_groundEnergy = groundEnergy;
284 emit groundEnergyChanged(m_groundEnergy);
285 scheduleTextureUpdate();
288void ProceduralSkyTextureData::setSunColor(QColor sunColor)
290 if (m_sunColor == sunColor)
293 m_sunColor = sunColor;
294 emit sunColorChanged(m_sunColor);
295 scheduleTextureUpdate();
298void ProceduralSkyTextureData::setSunLatitude(
float sunLatitude)
300 if (qFuzzyCompare(m_sunLatitude, sunLatitude))
303 m_sunLatitude = sunLatitude;
304 emit sunLatitudeChanged(m_sunLatitude);
305 scheduleTextureUpdate();
308void ProceduralSkyTextureData::setSunLongitude(
float sunLongitude)
310 if (qFuzzyCompare(m_sunLongitude, sunLongitude))
313 m_sunLongitude = sunLongitude;
314 emit sunLongitudeChanged(m_sunLongitude);
315 scheduleTextureUpdate();
318void ProceduralSkyTextureData::setSunAngleMin(
float sunAngleMin)
320 if (qFuzzyCompare(m_sunAngleMin, sunAngleMin))
323 m_sunAngleMin = sunAngleMin;
324 emit sunAngleMinChanged(m_sunAngleMin);
325 scheduleTextureUpdate();
328void ProceduralSkyTextureData::setSunAngleMax(
float sunAngleMax)
330 if (qFuzzyCompare(m_sunAngleMax, sunAngleMax))
333 m_sunAngleMax = sunAngleMax;
334 emit sunAngleMaxChanged(m_sunAngleMax);
335 scheduleTextureUpdate();
338void ProceduralSkyTextureData::setSunCurve(
float sunCurve)
340 if (qFuzzyCompare(m_sunCurve, sunCurve))
343 m_sunCurve = sunCurve;
344 emit sunCurveChanged(m_sunCurve);
345 scheduleTextureUpdate();
348void ProceduralSkyTextureData::setSunEnergy(
float sunEnergy)
350 if (qFuzzyCompare(m_sunEnergy, sunEnergy))
353 m_sunEnergy = sunEnergy;
354 emit sunEnergyChanged(m_sunEnergy);
355 scheduleTextureUpdate();
358void ProceduralSkyTextureData::setTextureQuality(ProceduralSkyTextureData::SkyTextureQuality textureQuality)
360 if (m_textureQuality == textureQuality)
363 m_textureQuality = textureQuality;
364 emit textureQualityChanged(m_textureQuality);
365 scheduleTextureUpdate();
368void ProceduralSkyTextureData::generateRGBA16FTexture()
371 switch (m_textureQuality) {
372 case SkyTextureQuality::SkyTextureQualityLow:
375 case SkyTextureQuality::SkyTextureQualityMedium:
378 case SkyTextureQuality::SkyTextureQualityHigh:
381 case SkyTextureQuality::SkyTextureQualityVeryHigh:
386 const int width = size;
387 const int height = width / 2;
388 setSize(QSize(width, height));
389 setFormat(Format::RGBA16F);
390 setHasTransparency(
false);
391 const int dataSize = width * height * 4 * 2;
392 QByteArray imageData;
393 imageData.resize(dataSize);
394 generateSkyTexture(width, height, imageData,
false);
395 setTextureData(imageData);
398QByteArray ProceduralSkyTextureData::generateSkyTexture(
int width,
int height, QByteArray &imageData,
bool isRGBE)
const
400 quint32 *data =
reinterpret_cast<quint32 *>(imageData.data());
402 LinearColor skyTopLinear(m_skyTopColor);
403 LinearColor skyHorizonLinear(m_skyHorizonColor);
404 LinearColor groundBottomLinear(m_groundBottomColor);
405 LinearColor groundHorizonLinear(m_groundHorizonColor);
406 LinearColor sunLinear(m_sunColor);
407 sunLinear.r *= m_sunEnergy;
408 sunLinear.g *= m_sunEnergy;
409 sunLinear.b *= m_sunEnergy;
411 QVector3D sun(0, 0, -1);
413 sun = QQuaternion::fromAxisAndAngle(QVector3D(1, 0, 0), m_sunLatitude) * sun;
414 sun = QQuaternion::fromAxisAndAngle(QVector3D(0, 1, 0), m_sunLongitude) * sun;
417 auto clamp = [](
float value,
float min,
float max) {
420 else if (value > max)
425 auto ease = [](
float x,
float c) {
432 return 1.0f - qPow(1.0f - x, 1.0f / c);
436 }
else if (c < 0.0f) {
438 return qPow(x * 2.0f, -c) * 0.5f;
440 return (1.0f - qPow(1.0f - (x - 0.5f) * 2.0f, -c)) * 0.5f + 0.5f;
446 for (
int i = 0; i < width; i++) {
448 float u =
float(i) / (width - 1);
449 float phi = u * 2.0 * M_PI;
451 for (
int j = 0; j < height; j++) {
452 float v =
float(j) / (height - 1);
453 float theta = v * M_PI;
455 QVector3D normal(qSin(phi) * qSin(theta) * -1.0,
457 qCos(phi) * qSin(theta) * -1.0);
459 float vAngle = qAcos(clamp(normal.y(), -1.0, 1.0));
462 if (normal.y() < 0) {
464 float c = (vAngle - (M_PI * 0.5f)) / (M_PI * 0.5f);
465 color = groundHorizonLinear.interpolate(groundBottomLinear, ease(c, m_groundCurve));
466 color.r *= m_groundEnergy;
467 color.g *= m_groundEnergy;
468 color.b *= m_groundEnergy;
471 float c = vAngle / (M_PI * 0.5f);
472 color = skyHorizonLinear.interpolate(skyTopLinear, ease(1.0 - c, m_skyCurve));
473 color.r *= m_skyEnergy;
474 color.g *= m_skyEnergy;
475 color.b *= m_skyEnergy;
477 float sunAngle = qRadiansToDegrees(qAcos(clamp(QVector3D::dotProduct(sun, normal), -1.0f, 1.0f)));
478 if (sunAngle < m_sunAngleMin) {
479 color = color.blend(sunLinear);
480 }
else if (sunAngle < m_sunAngleMax) {
481 float c2 = (sunAngle - m_sunAngleMin) / (m_sunAngleMax - m_sunAngleMin);
482 c2 = ease(c2, m_sunCurve);
483 color = color.blend(sunLinear).interpolate(color, c2);
489 data[(height - j - 1) * width + i] = color.toRGBE8();
492 const int offset = ((height - j - 1) * width + i) * 2;
493 qfloat16 *fData =
reinterpret_cast<qfloat16 *>(data + offset);
494 float pixel[4] = {color.r, color.g, color.b, color.a };
495 qFloatToFloat16(fData, pixel, 4);
503void ProceduralSkyTextureData::scheduleTextureUpdate()
505 generateRGBA16FTexture();
508ProceduralSkyTextureData::LinearColor::LinearColor(
const QColor &color)
510 const float red = color.redF();
511 const float green = color.greenF();
512 const float blue = color.blueF();
513 const float alpha = color.alphaF();
515 r = red < 0.04045 ? red * (1.0 / 12.92) : qPow((red + 0.055) * (1.0 / (1 + 0.055)), 2.4),
516 g = green < 0.04045 ? green * (1.0 / 12.92) : qPow((green + 0.055) * (1.0 / (1 + 0.055)), 2.4),
517 b = blue < 0.04045 ? blue * (1.0 / 12.92) : qPow((blue + 0.055) * (1.0 / (1 + 0.055)), 2.4),
521ProceduralSkyTextureData::LinearColor ProceduralSkyTextureData::LinearColor::interpolate(
const ProceduralSkyTextureData::LinearColor &color,
float value)
const
523 LinearColor copy = *
this;
525 copy.r += (value * (color.r - r));
526 copy.g += (value * (color.g - g));
527 copy.b += (value * (color.b - b));
528 copy.a += (value * (color.a - a));
533ProceduralSkyTextureData::LinearColor ProceduralSkyTextureData::LinearColor::blend(
const ProceduralSkyTextureData::LinearColor &color)
const
536 float sa = 1.0 - color.a;
537 copy.a = a * sa + color.a;
539 return LinearColor();
541 copy.r = (r * a * sa + color.r * color.a) / copy.a;
542 copy.g = (g * a * sa + color.g * color.a) / copy.a;
543 copy.b = (b * a * sa + color.b * color.a) / copy.a;
548quint32 ProceduralSkyTextureData::LinearColor::toRGBA8()
const
550 return (quint32(lrintf(r)) & 0xFF) |
551 ((quint32(lrintf(g)) & 0xFF) << 8) |
552 ((quint32(lrintf(b)) & 0xFF) << 16) |
553 ((quint32(lrintf(a)) & 0xFF) << 24);
556quint32 ProceduralSkyTextureData::LinearColor::toRGBE8()
const
567 v = frexp(v, &exp) * 256.0f / v;
569 quint8 *components =
reinterpret_cast<quint8*>(&result);
570 components[0] = quint8(r * v);
571 components[1] = quint8(g * v);
572 components[2] = quint8(b * v);
573 components[3] = quint8(exp + 128);
577quint32 ProceduralSkyTextureData::LinearColor::toRGBE9995()
const
579 const float pow2to9 = 512.0f;
580 const float B = 15.0f;
581 const float N = 9.0f;
583 float sharedExp = 65408.000f;
585 float cRed = qMax(0.0f, qMin(sharedExp, r));
586 float cGreen = qMax(0.0f, qMin(sharedExp, g));
587 float cBlue = qMax(0.0f, qMin(sharedExp, b));
589 float cMax = qMax(cRed, qMax(cGreen, cBlue));
591 float expp = qMax(-B - 1.0f, floor(std::log(cMax) / M_LN2)) + 1.0f + B;
593 float sMax = (
float)floor((cMax / qPow(2.0f, expp - B - N)) + 0.5f);
595 float exps = expp + 1.0f;
597 if (0.0 <= sMax && sMax < pow2to9) {
601 float sRed = qFloor((cRed / pow(2.0f, exps - B - N)) + 0.5f);
602 float sGreen = qFloor((cGreen / pow(2.0f, exps - B - N)) + 0.5f);
603 float sBlue = qFloor((cBlue / pow(2.0f, exps - B - N)) + 0.5f);
605 return (quint32(lrintf(sRed)) & 0x1FF) |
606 ((quint32(lrintf(sGreen)) & 0x1FF) << 9) |
607 ((quint32(lrintf(sBlue)) & 0x1FF) << 18) |
608 ((quint32(lrintf(exps)) & 0x1F) << 27);