| /**************************************************************************** |
| ** |
| ** Copyright (C) 2008-2012 NVIDIA Corporation. |
| ** Copyright (C) 2019 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of Qt Quick 3D. |
| ** |
| ** $QT_BEGIN_LICENSE:GPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 3 or (at your option) any later version |
| ** approved by the KDE Free Qt Foundation. The licenses are as published by |
| ** the Free Software Foundation and appearing in the file LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qssgrendermaterialshadergenerator_p.h" |
| #include <QtQuick3DRuntimeRender/private/qssgrenderdefaultmaterialshadergenerator_p.h> |
| #include <QtQuick3DRuntimeRender/private/qssgrendercontextcore_p.h> |
| #include <QtQuick3DRuntimeRender/private/qssgrendershadercodegeneratorv2_p.h> |
| #include <QtQuick3DRuntimeRender/private/qssgrenderableimage_p.h> |
| #include <QtQuick3DRuntimeRender/private/qssgrenderimage_p.h> |
| #include <QtQuick3DRender/private/qssgrendercontext_p.h> |
| #include <QtQuick3DRuntimeRender/private/qssgrenderlight_p.h> |
| #include <QtQuick3DRender/private/qssgrendershaderprogram_p.h> |
| #include <QtQuick3DRuntimeRender/private/qssgrendercamera_p.h> |
| #include <QtQuick3DRuntimeRender/private/qssgrendershadowmap_p.h> |
| #include <QtQuick3DRuntimeRender/private/qssgrendercustommaterial_p.h> |
| #include "qssgrendercustommaterialsystem_p.h" |
| #include <QtQuick3DRuntimeRender/private/qssgrenderlightconstantproperties_p.h> |
| #include <QtQuick3DRuntimeRender/private/qssgrendershaderkeys_p.h> |
| #include <QtQuick3DRuntimeRender/private/qssgrendererimplshaders_p.h> |
| #include <QtQuick3DUtils/private/qssgutils_p.h> |
| |
| QT_BEGIN_NAMESPACE |
| |
| namespace { |
| struct QSSGShaderLightProperties |
| { |
| QAtomicInt ref; |
| QSSGRef<QSSGRenderShaderProgram> m_shader; |
| QSSGRenderLight::Type m_lightType; |
| QSSGLightSourceShader m_lightData; |
| |
| QSSGShaderLightProperties(const QSSGRef<QSSGRenderShaderProgram> &inShader) |
| : m_shader(inShader), m_lightType(QSSGRenderLight::Type::Directional) |
| { |
| } |
| |
| void set(const QSSGRenderLight *inLight) |
| { |
| QVector3D dir(0, 0, 1); |
| if (inLight->m_lightType == QSSGRenderLight::Type::Directional) { |
| dir = inLight->getScalingCorrectDirection(); |
| // we lit in world sapce |
| dir *= -1; |
| m_lightData.position = QVector4D(dir, 0.0); |
| } else if (inLight->m_lightType == QSSGRenderLight::Type::Area) { |
| dir = inLight->getScalingCorrectDirection(); |
| m_lightData.position = QVector4D(inLight->getGlobalPos(), 1.0); |
| } else { |
| dir = inLight->getGlobalPos(); |
| m_lightData.position = QVector4D(dir, 1.0); |
| } |
| |
| m_lightType = inLight->m_lightType; |
| |
| m_lightData.direction = QVector4D(dir, 0.0); |
| |
| float normalizedBrightness = aux::translateBrightness(inLight->m_brightness); |
| m_lightData.diffuse = QVector4D(inLight->m_diffuseColor * normalizedBrightness, 1.0); |
| m_lightData.specular = QVector4D(inLight->m_specularColor * normalizedBrightness, 1.0); |
| |
| if (inLight->m_lightType == QSSGRenderLight::Type::Area) { |
| m_lightData.width = inLight->m_areaWidth; |
| m_lightData.height = inLight->m_areaWidth; |
| |
| QMatrix3x3 theDirMatrix(mat44::getUpper3x3(inLight->globalTransform)); |
| m_lightData.right = QVector4D(mat33::transform(theDirMatrix, QVector3D(1, 0, 0)), inLight->m_areaWidth); |
| m_lightData.up = QVector4D(mat33::transform(theDirMatrix, QVector3D(0, 1, 0)), inLight->m_areaHeight); |
| } else { |
| m_lightData.width = 0.0; |
| m_lightData.height = 0.0; |
| m_lightData.right = QVector4D(); |
| m_lightData.up = QVector4D(); |
| |
| // These components only apply to CG lights |
| m_lightData.ambient = QVector4D(inLight->m_ambientColor, 1.0); |
| |
| m_lightData.constantAttenuation |
| = aux::translateConstantAttenuation(inLight->m_constantFade); |
| m_lightData.linearAttenuation = aux::translateLinearAttenuation(inLight->m_linearFade); |
| m_lightData.quadraticAttenuation |
| = aux::translateQuadraticAttenuation(inLight->m_quadraticFade); |
| m_lightData.spotCutoff = 180.0; |
| } |
| |
| if (m_lightType == QSSGRenderLight::Type::Point) { |
| memcpy(m_lightData.shadowView, QMatrix4x4().constData(), 16 * sizeof(float)); |
| } else { |
| memcpy(m_lightData.shadowView, inLight->globalTransform.constData(), 16 * sizeof(float)); |
| } |
| } |
| |
| static QSSGShaderLightProperties createLightEntry(const QSSGRef<QSSGRenderShaderProgram> &inShader) |
| { |
| return QSSGShaderLightProperties(inShader); |
| } |
| }; |
| |
| /* We setup some shared state on the custom material shaders */ |
| struct QSSGShaderGeneratorGeneratedShader |
| { |
| typedef QHash<QSSGImageMapTypes, QSSGShaderTextureProperties> TCustomMaterialImagMap; |
| |
| QAtomicInt ref; |
| QSSGRef<QSSGRenderShaderProgram> m_shader; |
| // Specific properties we know the shader has to have. |
| QSSGRenderCachedShaderProperty<QMatrix4x4> m_modelMatrix; |
| QSSGRenderCachedShaderProperty<QMatrix4x4> m_viewProjMatrix; |
| QSSGRenderCachedShaderProperty<QMatrix4x4> m_viewMatrix; |
| QSSGRenderCachedShaderProperty<QMatrix3x3> m_normalMatrix; |
| QSSGRenderCachedShaderProperty<QVector3D> m_cameraPos; |
| QSSGRenderCachedShaderProperty<QMatrix4x4> m_projMatrix; |
| QSSGRenderCachedShaderProperty<QMatrix4x4> m_viewportMatrix; |
| QSSGRenderCachedShaderProperty<QVector2D> m_camProperties; |
| QSSGRenderCachedShaderProperty<QSSGRenderTexture2D *> m_depthTexture; |
| QSSGRenderCachedShaderProperty<QSSGRenderTexture2D *> m_aoTexture; |
| QSSGRenderCachedShaderProperty<QSSGRenderTexture2D *> m_lightProbe; |
| QSSGRenderCachedShaderProperty<QVector4D> m_lightProbeProps; |
| QSSGRenderCachedShaderProperty<QVector4D> m_lightProbeOpts; |
| QSSGRenderCachedShaderProperty<QVector4D> m_lightProbeRot; |
| QSSGRenderCachedShaderProperty<QVector4D> m_lightProbeOfs; |
| QSSGRenderCachedShaderProperty<QSSGRenderTexture2D *> m_lightProbe2; |
| QSSGRenderCachedShaderProperty<QVector4D> m_lightProbe2Props; |
| QSSGRenderCachedShaderProperty<qint32> m_lightCount; |
| QSSGRenderCachedShaderProperty<qint32> m_areaLightCount; |
| QSSGRenderCachedShaderProperty<qint32> m_shadowMapCount; |
| QSSGRenderCachedShaderProperty<qint32> m_shadowCubeCount; |
| QSSGRenderCachedShaderProperty<float> m_opacity; |
| QSSGRenderCachedShaderBuffer<QSSGRenderShaderConstantBuffer> m_aoShadowParams; |
| QSSGRenderCachedShaderBuffer<QSSGRenderShaderConstantBuffer> m_lightsBuffer; |
| QSSGRenderCachedShaderBuffer<QSSGRenderShaderConstantBuffer> m_areaLightsBuffer; |
| |
| QSSGLightConstantProperties<QSSGShaderGeneratorGeneratedShader> *m_lightsProperties; |
| QSSGLightConstantProperties<QSSGShaderGeneratorGeneratedShader> *m_areaLightsProperties; |
| |
| typedef QSSGRenderCachedShaderPropertyArray<QSSGRenderTexture2D *, QSSG_MAX_NUM_SHADOWS> ShadowMapPropertyArray; |
| typedef QSSGRenderCachedShaderPropertyArray<QSSGRenderTextureCube *, QSSG_MAX_NUM_SHADOWS> ShadowCubePropertyArray; |
| |
| ShadowMapPropertyArray m_shadowMaps; |
| ShadowCubePropertyArray m_shadowCubes; |
| |
| // Cache the image property name lookups |
| TCustomMaterialImagMap m_images; // Images external to custom material usage |
| |
| explicit QSSGShaderGeneratorGeneratedShader(const QSSGRef<QSSGRenderShaderProgram> &inShader) |
| : m_shader(inShader) |
| , m_modelMatrix("modelMatrix", inShader) |
| , m_viewProjMatrix("modelViewProjection", inShader) |
| , m_viewMatrix("viewMatrix", inShader) |
| , m_normalMatrix("normalMatrix", inShader) |
| , m_cameraPos("cameraPosition", inShader) |
| , m_projMatrix("viewProjectionMatrix", inShader) |
| , m_viewportMatrix("viewportMatrix", inShader) |
| , m_camProperties("cameraProperties", inShader) |
| , m_depthTexture("depthTexture", inShader) |
| , m_aoTexture("aoTexture", inShader) |
| , m_lightProbe("lightProbe", inShader) |
| , m_lightProbeProps("lightProbeProperties", inShader) |
| , m_lightProbeOpts("lightProbeOptions", inShader) |
| , m_lightProbeRot("lightProbeRotation", inShader) |
| , m_lightProbeOfs("lightProbeOffset", inShader) |
| , m_lightProbe2("lightProbe2", inShader) |
| , m_lightProbe2Props("lightProbe2Properties", inShader) |
| , m_lightCount("lightCount", inShader) |
| , m_areaLightCount("areaLightCount", inShader) |
| , m_shadowMapCount("shadowMapCount", inShader) |
| , m_shadowCubeCount("shadowCubeCount", inShader) |
| , m_opacity("objectOpacity", inShader) |
| , m_aoShadowParams("aoShadow", inShader) |
| , m_lightsBuffer("lightsBuffer", inShader) |
| , m_areaLightsBuffer("areaLightsBuffer", inShader) |
| , m_lightsProperties(nullptr) |
| , m_areaLightsProperties(nullptr) |
| , m_shadowMaps("shadowMaps[0]", inShader) |
| , m_shadowCubes("shadowCubes[0]", inShader) |
| { |
| } |
| |
| ~QSSGShaderGeneratorGeneratedShader() |
| { |
| delete m_lightsProperties; |
| delete m_areaLightsProperties; |
| } |
| |
| QSSGLightConstantProperties<QSSGShaderGeneratorGeneratedShader> *getLightProperties(int count) |
| { |
| if (!m_lightsProperties || m_areaLightsProperties->m_lightCountInt < count) { |
| if (m_lightsProperties) |
| delete m_lightsProperties; |
| m_lightsProperties = new QSSGLightConstantProperties<QSSGShaderGeneratorGeneratedShader>("lights", "lightCount", this, false, count); |
| } |
| return m_lightsProperties; |
| } |
| QSSGLightConstantProperties<QSSGShaderGeneratorGeneratedShader> *getAreaLightProperties(int count) |
| { |
| if (!m_areaLightsProperties || m_areaLightsProperties->m_lightCountInt < count) { |
| if (m_areaLightsProperties) |
| delete m_areaLightsProperties; |
| m_areaLightsProperties = new QSSGLightConstantProperties< |
| QSSGShaderGeneratorGeneratedShader>("areaLights", "areaLightCount", this, false, count); |
| } |
| return m_areaLightsProperties; |
| } |
| }; |
| |
| struct QSSGShaderGenerator : public QSSGMaterialShaderGeneratorInterface |
| { |
| typedef QPair<qint32, QSSGRef<QSSGShaderLightProperties>> TCustomMaterialLightEntry; |
| typedef QPair<qint32, QSSGRenderCachedShaderProperty<QSSGRenderTexture2D *>> TShadowMapEntry; |
| typedef QPair<qint32, QSSGRenderCachedShaderProperty<QSSGRenderTextureCube *>> TShadowCubeEntry; |
| |
| typedef QHash<QSSGRef<QSSGRenderShaderProgram>, QSSGRef<QSSGShaderGeneratorGeneratedShader>> ProgramToShaderMap; |
| ProgramToShaderMap m_programToShaderMap; |
| |
| const QSSGRenderCustomMaterial *m_currentMaterial; |
| |
| QByteArray m_imageSampler; |
| QByteArray m_imageFragCoords; |
| QByteArray m_imageRotScale; |
| QByteArray m_imageOffset; |
| |
| QVector<TCustomMaterialLightEntry> m_lightEntries; |
| |
| explicit QSSGShaderGenerator(QSSGRenderContextInterface *inRc) |
| : QSSGMaterialShaderGeneratorInterface (inRc) |
| , m_currentMaterial(nullptr) |
| { |
| } |
| |
| const QSSGRef<QSSGShaderProgramGeneratorInterface> &programGenerator() { return m_programGenerator; } |
| QSSGDefaultMaterialVertexPipelineInterface &vertexGenerator() { return *m_currentPipeline; } |
| QSSGShaderStageGeneratorInterface &fragmentGenerator() |
| { |
| return *m_programGenerator->getStage(QSSGShaderGeneratorStage::Fragment); |
| } |
| QSSGShaderDefaultMaterialKey &key() { return *m_currentKey; } |
| const QSSGRenderCustomMaterial &material() { return *m_currentMaterial; } |
| bool hasTransparency() const { return m_hasTransparency; } |
| |
| quint32 convertTextureTypeValue(QSSGImageMapTypes inType) |
| { |
| QSSGRenderTextureTypeValue retVal = QSSGRenderTextureTypeValue::Unknown; |
| |
| switch (inType) { |
| case QSSGImageMapTypes::LightmapIndirect: |
| retVal = QSSGRenderTextureTypeValue::LightmapIndirect; |
| break; |
| case QSSGImageMapTypes::LightmapRadiosity: |
| retVal = QSSGRenderTextureTypeValue::LightmapRadiosity; |
| break; |
| case QSSGImageMapTypes::LightmapShadow: |
| retVal = QSSGRenderTextureTypeValue::LightmapShadow; |
| break; |
| case QSSGImageMapTypes::Bump: |
| retVal = QSSGRenderTextureTypeValue::Bump; |
| break; |
| case QSSGImageMapTypes::Diffuse: |
| retVal = QSSGRenderTextureTypeValue::Diffuse; |
| break; |
| case QSSGImageMapTypes::Displacement: |
| retVal = QSSGRenderTextureTypeValue::Displace; |
| break; |
| default: |
| retVal = QSSGRenderTextureTypeValue::Unknown; |
| break; |
| } |
| |
| Q_ASSERT(retVal != QSSGRenderTextureTypeValue::Unknown); |
| |
| return static_cast<quint32>(retVal); |
| } |
| |
| ImageVariableNames getImageVariableNames(uint imageIdx) override |
| { |
| // convert to QSSGRenderTextureTypeValue |
| QSSGRenderTextureTypeValue texType = QSSGRenderTextureTypeValue(imageIdx); |
| QByteArray imageStem = toString(texType); |
| imageStem.append("_"); |
| m_imageSampler = imageStem; |
| m_imageSampler.append("sampler"); |
| m_imageFragCoords = imageStem; |
| m_imageFragCoords.append("uv_coords"); |
| m_imageRotScale = imageStem; |
| m_imageRotScale.append("rot_scale"); |
| m_imageOffset = imageStem; |
| m_imageOffset.append("offset"); |
| |
| ImageVariableNames retVal; |
| retVal.m_imageSampler = m_imageSampler; |
| retVal.m_imageFragCoords = m_imageFragCoords; |
| return retVal; |
| } |
| |
| void setImageShaderVariables(const QSSGRef<QSSGShaderGeneratorGeneratedShader> &inShader, QSSGRenderableImage &inImage) |
| { |
| // skip displacement and emissive mask maps which are handled differently |
| if (inImage.m_mapType == QSSGImageMapTypes::Displacement || inImage.m_mapType == QSSGImageMapTypes::Emissive) |
| return; |
| |
| QSSGShaderGeneratorGeneratedShader::TCustomMaterialImagMap::iterator iter = inShader->m_images.find(inImage.m_mapType); |
| if (iter == inShader->m_images.end()) { |
| ImageVariableNames names = getImageVariableNames(convertTextureTypeValue(inImage.m_mapType)); |
| inShader->m_images.insert(inImage.m_mapType, |
| QSSGShaderTextureProperties(inShader->m_shader, names.m_imageSampler, m_imageOffset, m_imageRotScale)); |
| iter = inShader->m_images.find(inImage.m_mapType); |
| } |
| |
| QSSGShaderTextureProperties &theShaderProps = iter.value(); |
| const QMatrix4x4 &textureTransform = inImage.m_image.m_textureTransform; |
| const float *dataPtr(textureTransform.constData()); |
| QVector3D offsets(dataPtr[12], dataPtr[13], 0.0f); |
| // Grab just the upper 2x2 rotation matrix from the larger matrix. |
| QVector4D rotations(dataPtr[0], dataPtr[4], dataPtr[1], dataPtr[5]); |
| |
| // The image horizontal and vertical tiling modes need to be set here, before we set texture |
| // on the shader. |
| // because setting the image on the texture forces the textue to bind and immediately apply |
| // any tex params. |
| inImage.m_image.m_textureData.m_texture->setTextureWrapS(inImage.m_image.m_horizontalTilingMode); |
| inImage.m_image.m_textureData.m_texture->setTextureWrapT(inImage.m_image.m_verticalTilingMode); |
| |
| theShaderProps.sampler.set(inImage.m_image.m_textureData.m_texture.data()); |
| theShaderProps.offsets.set(offsets); |
| theShaderProps.rotations.set(rotations); |
| } |
| |
| void generateImageUVCoordinates(QSSGShaderStageGeneratorInterface &, quint32, quint32, QSSGRenderableImage &) override |
| { |
| } |
| |
| ///< get the light constant buffer and generate if necessary |
| QSSGRef<QSSGRenderConstantBuffer> getLightConstantBuffer(const QByteArray &name, qint32 inLightCount) |
| { |
| const QSSGRef<QSSGRenderContext> &theContext(m_renderContext->renderContext()); |
| |
| // we assume constant buffer support |
| Q_ASSERT(inLightCount >= 0); |
| Q_ASSERT(theContext->supportsConstantBuffer()); |
| // we only create if if we have lights |
| if (!inLightCount || !theContext->supportsConstantBuffer()) |
| return nullptr; |
| |
| QSSGRef<QSSGRenderConstantBuffer> pCB = theContext->getConstantBuffer(name); |
| if (pCB) |
| return pCB; |
| |
| // create with size of all structures + int for light count |
| const size_t size = sizeof(QSSGLightSourceShader) * QSSG_MAX_NUM_LIGHTS + (4 * sizeof(qint32)); |
| quint8 stackData[size]; |
| memset(stackData, 0, 4 * sizeof(qint32)); |
| new (stackData + 4*sizeof(qint32)) QSSGLightSourceShader[QSSG_MAX_NUM_LIGHTS]; |
| QSSGByteView cBuffer(stackData, size); |
| pCB = *m_constantBuffers.insert(name, new QSSGRenderConstantBuffer(theContext, name, QSSGRenderBufferUsageType::Static, cBuffer)); |
| if (Q_UNLIKELY(!pCB)) { |
| Q_ASSERT(false); |
| return nullptr; |
| } |
| |
| return pCB; |
| } |
| |
| bool generateVertexShader(QSSGShaderDefaultMaterialKey &, const QByteArray &inShaderPathName) |
| { |
| const QSSGRef<QSSGDynamicObjectSystem> &theDynamicSystem(m_renderContext->dynamicObjectSystem()); |
| QByteArray vertSource = theDynamicSystem->getShaderSource(inShaderPathName); |
| |
| Q_ASSERT(!vertSource.isEmpty()); |
| |
| // Check if the vertex shader portion already contains a main function |
| // The same string contains both the vertex and the fragment shader |
| // The last "#ifdef FRAGMENT_SHADER" should mark the start of the fragment shader |
| int fragmentDefStart = vertSource.indexOf("#ifdef FRAGMENT_SHADER"); |
| int nextIndex = fragmentDefStart; |
| while (nextIndex != -1) { |
| nextIndex = vertSource.indexOf("#ifdef FRAGMENT_SHADER", nextIndex + 1); |
| if (nextIndex != -1) |
| fragmentDefStart = nextIndex; |
| } |
| const int mainStart = vertSource.indexOf("void main()"); |
| |
| auto &vertGenerator = vertexGenerator(); |
| |
| if (mainStart != -1 && (fragmentDefStart == -1 || mainStart < fragmentDefStart)) { |
| programGenerator()->beginProgram(); |
| vertGenerator << "#define VERTEX_SHADER\n\n"; |
| vertGenerator << vertSource; |
| return true; |
| } |
| // vertex displacement |
| quint32 imageIdx = 0; |
| QSSGRenderableImage *displacementImage = nullptr; |
| quint32 displacementImageIdx = 0; |
| |
| for (QSSGRenderableImage *img = m_firstImage; img != nullptr; img = img->m_nextImage, ++imageIdx) { |
| if (img->m_mapType == QSSGImageMapTypes::Displacement) { |
| displacementImage = img; |
| displacementImageIdx = imageIdx; |
| break; |
| } |
| } |
| |
| // the pipeline opens/closes up the shaders stages |
| vertexGenerator().beginVertexGeneration(displacementImageIdx, displacementImage); |
| return false; |
| } |
| |
| QSSGRef<QSSGShaderGeneratorGeneratedShader> getShaderForProgram(const QSSGRef<QSSGRenderShaderProgram> &inProgram) |
| { |
| auto inserter = m_programToShaderMap.constFind(inProgram); |
| if (inserter == m_programToShaderMap.constEnd()) |
| inserter = m_programToShaderMap.insert(inProgram, |
| QSSGRef<QSSGShaderGeneratorGeneratedShader>( |
| new QSSGShaderGeneratorGeneratedShader(inProgram))); |
| |
| return *inserter; |
| } |
| |
| virtual QSSGRef<QSSGShaderLightProperties> setLight(const QSSGRef<QSSGRenderShaderProgram> &inShader, |
| qint32 lightIdx, |
| qint32 /*shadeIdx*/, |
| const QSSGRenderLight *inLight, |
| QSSGShadowMapEntry *inShadow, |
| qint32 shadowIdx, |
| float shadowDist) |
| { |
| auto it = m_lightEntries.cbegin(); |
| const auto end = m_lightEntries.cend(); |
| for (; it != end; ++it) { |
| if (it->first == lightIdx && it->second->m_shader == inShader && it->second->m_lightType == inLight->m_lightType) |
| break; |
| } |
| |
| if (it == end) { |
| // create a new name |
| #if 0 |
| QString lightName; |
| if (inLight->m_lightType == QSSGRenderLight::Type::Area) |
| lightName = QStringLiteral("arealights"); |
| else |
| lightName = QStringLiteral("lights"); |
| char buf[16]; |
| qsnprintf(buf, 16, "[%d]", int(shadeIdx)); |
| lightName.append(QString::fromLocal8Bit(buf)); |
| #endif |
| |
| m_lightEntries.push_back(TCustomMaterialLightEntry(lightIdx, new QSSGShaderLightProperties(QSSGShaderLightProperties::createLightEntry(inShader)))); |
| it = m_lightEntries.cend() - 1; |
| } |
| it->second->set(inLight); |
| it->second->m_lightData.shadowControls = QVector4D(inLight->m_shadowBias, inLight->m_shadowFactor, shadowDist, 0.0); |
| it->second->m_lightData.shadowIdx = (inShadow) ? shadowIdx : -1; |
| |
| return it->second; |
| } |
| |
| void setShadowMaps(const QSSGRef<QSSGRenderShaderProgram> &inProgram, |
| QSSGShadowMapEntry *inShadow, |
| qint32 &numShadowMaps, |
| qint32 &numShadowCubes, |
| bool shadowMap, |
| QSSGShaderGeneratorGeneratedShader::ShadowMapPropertyArray &shadowMaps, |
| QSSGShaderGeneratorGeneratedShader::ShadowCubePropertyArray &shadowCubes) |
| { |
| Q_UNUSED(inProgram) |
| if (inShadow) { |
| if (shadowMap == false && inShadow->m_depthCube && (numShadowCubes < QSSG_MAX_NUM_SHADOWS)) { |
| shadowCubes.m_array[numShadowCubes] = inShadow->m_depthCube.data(); |
| ++numShadowCubes; |
| } else if (shadowMap && inShadow->m_depthMap && (numShadowMaps < QSSG_MAX_NUM_SHADOWS)) { |
| shadowMaps.m_array[numShadowMaps] = inShadow->m_depthMap.data(); |
| ++numShadowMaps; |
| } |
| } |
| } |
| |
| void setGlobalProperties(const QSSGRef<QSSGRenderShaderProgram> &inProgram, |
| const QSSGRenderLayer & /*inLayer*/, |
| QSSGRenderCamera &inCamera, |
| const QVector3D &, |
| const QVector<QSSGRenderLight *> &inLights, |
| const QVector<QVector3D> &, |
| const QSSGRef<QSSGRenderShadowMap> &inShadowMaps, |
| bool receivesShadows = true) |
| { |
| const QSSGRef<QSSGShaderGeneratorGeneratedShader> &theShader(getShaderForProgram(inProgram)); |
| m_renderContext->renderContext()->setActiveShader(inProgram); |
| |
| QSSGRenderCamera &theCamera(inCamera); |
| |
| QVector2D camProps(theCamera.clipNear, theCamera.clipFar); |
| theShader->m_camProperties.set(camProps); |
| theShader->m_cameraPos.set(theCamera.getGlobalPos()); |
| |
| if (theShader->m_viewMatrix.isValid()) |
| theShader->m_viewMatrix.set(theCamera.globalTransform.inverted()); |
| |
| if (theShader->m_projMatrix.isValid()) { |
| QMatrix4x4 vProjMat; |
| inCamera.calculateViewProjectionMatrix(vProjMat); |
| theShader->m_projMatrix.set(vProjMat); |
| } |
| |
| // set lights separate for area lights |
| qint32 cgLights = 0, areaLights = 0; |
| qint32 numShadowMaps = 0, numShadowCubes = 0; |
| |
| // this call setup the constant buffer for ambient occlusion and shadow |
| theShader->m_aoShadowParams.set(); |
| |
| if (m_renderContext->renderContext()->supportsConstantBuffer()) { |
| // Count area lights before processing |
| for (int lightIdx = 0; lightIdx < inLights.size(); ++lightIdx) { |
| if (inLights[lightIdx]->m_lightType == QSSGRenderLight::Type::Area) |
| areaLights++; |
| else |
| cgLights++; |
| } |
| |
| const QSSGRef<QSSGRenderConstantBuffer> &pLightCb |
| = getLightConstantBuffer(QByteArrayLiteral("lightsBuffer"), |
| cgLights); |
| const QSSGRef<QSSGRenderConstantBuffer> &pAreaLightCb |
| = getLightConstantBuffer(QByteArrayLiteral("areaLightsBuffer"), |
| areaLights); |
| |
| areaLights = 0; |
| cgLights = 0; |
| // Split the count between CG lights and area lights |
| for (int lightIdx = 0; lightIdx < inLights.size(); ++lightIdx) { |
| QSSGShadowMapEntry *theShadow = nullptr; |
| qint32 shdwIdx = 0; |
| |
| if (receivesShadows) { |
| if (inShadowMaps && inLights[lightIdx]->m_castShadow) |
| theShadow = inShadowMaps->getShadowMapEntry(lightIdx); |
| |
| shdwIdx = (inLights[lightIdx]->m_lightType != QSSGRenderLight::Type::Directional) ? numShadowCubes : numShadowMaps; |
| setShadowMaps(inProgram, |
| theShadow, |
| numShadowMaps, |
| numShadowCubes, |
| inLights[lightIdx]->m_lightType == QSSGRenderLight::Type::Directional, |
| theShader->m_shadowMaps, |
| theShader->m_shadowCubes); |
| } |
| if (inLights[lightIdx]->m_lightType == QSSGRenderLight::Type::Area) { |
| const QSSGRef<QSSGShaderLightProperties> &theAreaLightEntry = setLight(inProgram, |
| lightIdx, |
| areaLights, |
| inLights[lightIdx], |
| theShadow, |
| shdwIdx, |
| inCamera.clipFar); |
| |
| if (theAreaLightEntry && pAreaLightCb) { |
| pAreaLightCb->updateRaw(areaLights * sizeof(QSSGLightSourceShader) + (4 * sizeof(qint32)), |
| toByteView(theAreaLightEntry->m_lightData)); |
| } |
| areaLights++; |
| } else { |
| const QSSGRef<QSSGShaderLightProperties> &theLightEntry = setLight(inProgram, |
| lightIdx, |
| cgLights, |
| inLights[lightIdx], |
| theShadow, |
| shdwIdx, |
| inCamera.clipFar); |
| |
| if (theLightEntry && pLightCb) { |
| pLightCb->updateRaw(cgLights * sizeof(QSSGLightSourceShader) + (4 * sizeof(qint32)), |
| toByteView(theLightEntry->m_lightData)); |
| } |
| |
| cgLights++; |
| } |
| } |
| |
| if (pLightCb) { |
| pLightCb->updateRaw(0, toByteView(cgLights)); |
| theShader->m_lightsBuffer.set(); |
| } |
| if (pAreaLightCb) { |
| pAreaLightCb->updateRaw(0, toByteView(areaLights)); |
| theShader->m_areaLightsBuffer.set(); |
| } |
| |
| theShader->m_lightCount.set(cgLights); |
| theShader->m_areaLightCount.set(areaLights); |
| } else { |
| QVector<QSSGRef<QSSGShaderLightProperties>> lprop; |
| QVector<QSSGRef<QSSGShaderLightProperties>> alprop; |
| for (int lightIdx = 0; lightIdx < inLights.size(); ++lightIdx) { |
| |
| QSSGShadowMapEntry *theShadow = nullptr; |
| qint32 shdwIdx = 0; |
| |
| if (receivesShadows) { |
| if (inShadowMaps && inLights[lightIdx]->m_castShadow) |
| theShadow = inShadowMaps->getShadowMapEntry(lightIdx); |
| |
| shdwIdx = (inLights[lightIdx]->m_lightType != QSSGRenderLight::Type::Directional) ? numShadowCubes : numShadowMaps; |
| setShadowMaps(inProgram, |
| theShadow, |
| numShadowMaps, |
| numShadowCubes, |
| inLights[lightIdx]->m_lightType == QSSGRenderLight::Type::Directional, |
| theShader->m_shadowMaps, |
| theShader->m_shadowCubes); |
| } |
| |
| const QSSGRef<QSSGShaderLightProperties> &p = setLight(inProgram, lightIdx, areaLights, inLights[lightIdx], theShadow, shdwIdx, inCamera.clipFar); |
| if (inLights[lightIdx]->m_lightType == QSSGRenderLight::Type::Area) |
| alprop.push_back(p); |
| else |
| lprop.push_back(p); |
| } |
| QSSGLightConstantProperties<QSSGShaderGeneratorGeneratedShader> *lightProperties = theShader->getLightProperties( |
| lprop.size()); |
| QSSGLightConstantProperties<QSSGShaderGeneratorGeneratedShader> *areaLightProperties = theShader->getAreaLightProperties( |
| alprop.size()); |
| |
| lightProperties->updateLights(lprop); |
| areaLightProperties->updateLights(alprop); |
| |
| theShader->m_lightCount.set(lprop.size()); |
| theShader->m_areaLightCount.set(alprop.size()); |
| } |
| for (int i = numShadowMaps; i < QSSG_MAX_NUM_SHADOWS; ++i) |
| theShader->m_shadowMaps.m_array[i] = nullptr; |
| for (int i = numShadowCubes; i < QSSG_MAX_NUM_SHADOWS; ++i) |
| theShader->m_shadowCubes.m_array[i] = nullptr; |
| theShader->m_shadowMaps.set(numShadowMaps); |
| theShader->m_shadowCubes.set(numShadowCubes); |
| theShader->m_shadowMapCount.set(numShadowMaps); |
| theShader->m_shadowCubeCount.set(numShadowCubes); |
| } |
| |
| void setMaterialProperties(const QSSGRef<QSSGRenderShaderProgram> &inProgram, |
| const QSSGRenderCustomMaterial &inMaterial, |
| const QVector2D &, |
| const QMatrix4x4 &inModelViewProjection, |
| const QMatrix3x3 &inNormalMatrix, |
| const QMatrix4x4 &inGlobalTransform, |
| QSSGRenderableImage *inFirstImage, |
| float inOpacity, |
| const QSSGRef<QSSGRenderTexture2D> &inDepthTexture, |
| const QSSGRef<QSSGRenderTexture2D> &inSSaoTexture, |
| QSSGRenderImage *inLightProbe, |
| QSSGRenderImage *inLightProbe2, |
| float inProbeHorizon, |
| float inProbeBright, |
| float inProbe2Window, |
| float inProbe2Pos, |
| float inProbe2Fade, |
| float inProbeFOV) |
| { |
| const QSSGRef<QSSGMaterialSystem> &theMaterialSystem(m_renderContext->customMaterialSystem()); |
| const QSSGRef<QSSGShaderGeneratorGeneratedShader> &theShader(getShaderForProgram(inProgram)); |
| |
| theShader->m_viewProjMatrix.set(inModelViewProjection); |
| theShader->m_normalMatrix.set(inNormalMatrix); |
| theShader->m_modelMatrix.set(inGlobalTransform); |
| |
| theShader->m_depthTexture.set(inDepthTexture.data()); |
| theShader->m_aoTexture.set(inSSaoTexture.data()); |
| |
| theShader->m_opacity.set(inOpacity); |
| |
| QSSGRenderImage *theLightProbe = inLightProbe; |
| QSSGRenderImage *theLightProbe2 = inLightProbe2; |
| |
| if (inMaterial.m_iblProbe && inMaterial.m_iblProbe->m_textureData.m_texture) { |
| theLightProbe = inMaterial.m_iblProbe; |
| } |
| |
| if (theLightProbe) { |
| if (theLightProbe->m_textureData.m_texture) { |
| QSSGRenderTextureCoordOp theHorzLightProbeTilingMode = QSSGRenderTextureCoordOp::Repeat; |
| QSSGRenderTextureCoordOp theVertLightProbeTilingMode = theLightProbe->m_verticalTilingMode; |
| theLightProbe->m_textureData.m_texture->setTextureWrapS(theHorzLightProbeTilingMode); |
| theLightProbe->m_textureData.m_texture->setTextureWrapT(theVertLightProbeTilingMode); |
| |
| const QMatrix4x4 &textureTransform = theLightProbe->m_textureTransform; |
| // We separate rotational information from offset information so that just maybe the |
| // shader |
| // will attempt to push less information to the card. |
| const float *dataPtr(textureTransform.constData()); |
| // The third member of the offsets contains a flag indicating if the texture was |
| // premultiplied or not. |
| // We use this to mix the texture alpha. |
| // lightProbeOffsets.w is now no longer being used to enable/disable fast IBL, |
| // (it's now the only option) |
| // So now, it's storing the number of mip levels in the IBL image. |
| QVector4D offsets(dataPtr[12], |
| dataPtr[13], |
| theLightProbe->m_textureData.m_textureFlags.isPreMultiplied() ? 1.0f : 0.0f, |
| float(theLightProbe->m_textureData.m_texture->numMipmaps())); |
| // Fast IBL is always on; |
| // inRenderContext.m_Layer.m_FastIbl ? 1.0f : 0.0f ); |
| // Grab just the upper 2x2 rotation matrix from the larger matrix. |
| QVector4D rotations(dataPtr[0], dataPtr[4], dataPtr[1], dataPtr[5]); |
| |
| theShader->m_lightProbeRot.set(rotations); |
| theShader->m_lightProbeOfs.set(offsets); |
| |
| if ((!inMaterial.m_iblProbe) && (inProbeFOV < 180.f)) { |
| theShader->m_lightProbeOpts.set(QVector4D(0.01745329251994329547f * inProbeFOV, 0.0f, 0.0f, 0.0f)); |
| } |
| |
| // Also make sure to add the secondary texture, but it should only be added if the |
| // primary |
| // (i.e. background) texture is also there. |
| if (theLightProbe2 && theLightProbe2->m_textureData.m_texture) { |
| theLightProbe2->m_textureData.m_texture->setTextureWrapS(theHorzLightProbeTilingMode); |
| theLightProbe2->m_textureData.m_texture->setTextureWrapT(theVertLightProbeTilingMode); |
| theShader->m_lightProbe2.set(theLightProbe2->m_textureData.m_texture.data()); |
| theShader->m_lightProbe2Props.set(QVector4D(inProbe2Window, inProbe2Pos, inProbe2Fade, 1.0f)); |
| |
| const QMatrix4x4 &xform2 = theLightProbe2->m_textureTransform; |
| const float *dataPtr(xform2.constData()); |
| |
| theShader->m_lightProbeProps.set(QVector4D(dataPtr[12], dataPtr[13], inProbeHorizon, inProbeBright * 0.01f)); |
| } else { |
| theShader->m_lightProbe2Props.set(QVector4D(0.0f, 0.0f, 0.0f, 0.0f)); |
| theShader->m_lightProbeProps.set(QVector4D(0.0f, 0.0f, inProbeHorizon, inProbeBright * 0.01f)); |
| } |
| } else { |
| theShader->m_lightProbeProps.set(QVector4D(0.0f, 0.0f, -1.0f, 0.0f)); |
| theShader->m_lightProbe2Props.set(QVector4D(0.0f, 0.0f, 0.0f, 0.0f)); |
| } |
| |
| theShader->m_lightProbe.set(theLightProbe->m_textureData.m_texture.data()); |
| |
| } else { |
| theShader->m_lightProbeProps.set(QVector4D(0.0f, 0.0f, -1.0f, 0.0f)); |
| theShader->m_lightProbe2Props.set(QVector4D(0.0f, 0.0f, 0.0f, 0.0f)); |
| } |
| |
| // finally apply custom material shader properties |
| theMaterialSystem->applyShaderPropertyValues(inMaterial, inProgram); |
| |
| // additional textures |
| for (QSSGRenderableImage *theImage = inFirstImage; theImage; theImage = theImage->m_nextImage) |
| setImageShaderVariables(theShader, *theImage); |
| } |
| |
| void setMaterialProperties(const QSSGRef<QSSGRenderShaderProgram> &inProgram, |
| const QSSGRenderGraphObject &inMaterial, |
| const QVector2D &inCameraVec, |
| const QMatrix4x4 &inModelViewProjection, |
| const QMatrix3x3 &inNormalMatrix, |
| const QMatrix4x4 &inGlobalTransform, |
| QSSGRenderableImage *inFirstImage, |
| float inOpacity, |
| const QSSGLayerGlobalRenderProperties &inRenderProperties, |
| bool receivesShadows) override |
| { |
| const QSSGRenderCustomMaterial &theCustomMaterial = static_cast<const QSSGRenderCustomMaterial &>(inMaterial); |
| Q_ASSERT(inMaterial.type == QSSGRenderGraphObject::Type::CustomMaterial); |
| |
| setGlobalProperties(inProgram, |
| inRenderProperties.layer, |
| inRenderProperties.camera, |
| inRenderProperties.cameraDirection, |
| inRenderProperties.lights, |
| inRenderProperties.lightDirections, |
| inRenderProperties.shadowMapManager, |
| receivesShadows); |
| |
| setMaterialProperties(inProgram, |
| theCustomMaterial, |
| inCameraVec, |
| inModelViewProjection, |
| inNormalMatrix, |
| inGlobalTransform, |
| inFirstImage, |
| inOpacity, |
| inRenderProperties.depthTexture, |
| inRenderProperties.ssaoTexture, |
| inRenderProperties.lightProbe, |
| inRenderProperties.lightProbe2, |
| inRenderProperties.probeHorizon, |
| inRenderProperties.probeBright, |
| inRenderProperties.probe2Window, |
| inRenderProperties.probe2Pos, |
| inRenderProperties.probe2Fade, |
| inRenderProperties.probeFOV); |
| } |
| |
| void generateLightmapIndirectFunc(QSSGShaderStageGeneratorInterface &inFragmentShader, QSSGRenderImage *pEmissiveLightmap) |
| { |
| inFragmentShader << "\n" |
| "vec3 computeMaterialLightmapIndirect()\n{\n" |
| " vec4 indirect = vec4( 0.0, 0.0, 0.0, 0.0 );\n"; |
| if (pEmissiveLightmap) { |
| ImageVariableNames names = getImageVariableNames(convertTextureTypeValue(QSSGImageMapTypes::LightmapIndirect)); |
| inFragmentShader.addUniform(names.m_imageSampler, "sampler2D"); |
| inFragmentShader.addUniform(m_imageOffset, "vec3"); |
| inFragmentShader.addUniform(m_imageRotScale, "vec4"); |
| |
| inFragmentShader << "\n indirect = evalIndirectLightmap( " << m_imageSampler << ", varTexCoord1, " |
| << m_imageRotScale << ", " |
| << m_imageOffset << " );\n\n"; |
| } |
| |
| inFragmentShader << " return indirect.rgb;\n" |
| "}\n\n"; |
| } |
| |
| void generateLightmapRadiosityFunc(QSSGShaderStageGeneratorInterface &inFragmentShader, QSSGRenderImage *pRadiosityLightmap) |
| { |
| inFragmentShader << "\n" |
| "vec3 computeMaterialLightmapRadiosity()\n{\n" |
| " vec4 radiosity = vec4( 1.0, 1.0, 1.0, 1.0 );\n"; |
| if (pRadiosityLightmap) { |
| ImageVariableNames names = getImageVariableNames(convertTextureTypeValue(QSSGImageMapTypes::LightmapRadiosity)); |
| inFragmentShader.addUniform(names.m_imageSampler, "sampler2D"); |
| inFragmentShader.addUniform(m_imageOffset, "vec3"); |
| inFragmentShader.addUniform(m_imageRotScale, "vec4"); |
| |
| inFragmentShader << "\n radiosity = evalRadiosityLightmap( " << m_imageSampler << ", varTexCoord1, " |
| << m_imageRotScale << ", " |
| << m_imageOffset << " );\n\n"; |
| } |
| |
| inFragmentShader << " return radiosity.rgb;\n" |
| "}\n\n"; |
| } |
| |
| void generateLightmapShadowFunc(QSSGShaderStageGeneratorInterface &inFragmentShader, QSSGRenderImage *pBakedShadowMap) |
| { |
| inFragmentShader << "\n" |
| "vec4 computeMaterialLightmapShadow()\n{\n" |
| " vec4 shadowMask = vec4( 1.0, 1.0, 1.0, 1.0 );\n"; |
| if (pBakedShadowMap) { |
| ImageVariableNames names = getImageVariableNames(static_cast<quint32>(QSSGRenderTextureTypeValue::LightmapShadow)); |
| // Add uniforms |
| inFragmentShader.addUniform(names.m_imageSampler, "sampler2D"); |
| inFragmentShader.addUniform(m_imageOffset, "vec3"); |
| inFragmentShader.addUniform(m_imageRotScale, "vec4"); |
| |
| inFragmentShader << "\n shadowMask = evalShadowLightmap( " << m_imageSampler << ", texCoord0, " |
| << m_imageRotScale << ", " |
| << m_imageOffset << " );\n\n"; |
| } |
| |
| inFragmentShader << " return shadowMask;\n" |
| "}\n\n"; |
| } |
| |
| void generateLightmapIndirectSetupCode(QSSGShaderStageGeneratorInterface &inFragmentShader, |
| QSSGRenderableImage *pIndirectLightmap, |
| QSSGRenderableImage *pRadiosityLightmap) |
| { |
| if (!pIndirectLightmap && !pRadiosityLightmap) |
| return; |
| |
| QByteArray finalValue; |
| |
| inFragmentShader << "\n" |
| "void initializeLayerVariablesWithLightmap(void)\n{\n"; |
| if (pIndirectLightmap) { |
| inFragmentShader << " vec3 lightmapIndirectValue = computeMaterialLightmapIndirect( );\n"; |
| finalValue.append("vec4(lightmapIndirectValue, 1.0)"); |
| } |
| if (pRadiosityLightmap) { |
| inFragmentShader << " vec3 lightmapRadisoityValue = computeMaterialLightmapRadiosity( );\n"; |
| if (finalValue.isEmpty()) |
| finalValue.append("vec4(lightmapRadisoityValue, 1.0)"); |
| else |
| finalValue.append(" + vec4(lightmapRadisoityValue, 1.0)"); |
| } |
| |
| finalValue.append(";\n"); |
| |
| inFragmentShader << " layer.base += " << finalValue; |
| inFragmentShader << " layer.layer += " << finalValue; |
| |
| inFragmentShader << "}\n\n"; |
| } |
| |
| void generateLightmapShadowCode(QSSGShaderStageGeneratorInterface &inFragmentShader, QSSGRenderableImage *pBakedShadowMap) |
| { |
| if (pBakedShadowMap) { |
| inFragmentShader << " tmpShadowTerm *= computeMaterialLightmapShadow( );\n\n"; |
| } |
| } |
| |
| void applyEmissiveMask(QSSGShaderStageGeneratorInterface &inFragmentShader, QSSGRenderImage *pEmissiveMaskMap) |
| { |
| inFragmentShader << "\n" |
| "vec3 computeMaterialEmissiveMask()\n{\n" |
| " vec3 emissiveMask = vec3( 1.0, 1.0, 1.0 );\n"; |
| if (pEmissiveMaskMap) { |
| inFragmentShader << " texture_coordinate_info tci;\n" |
| " texture_coordinate_info transformed_tci;\n" |
| " tci = textureCoordinateInfo( texCoord0, tangent, binormal );\n" |
| " transformed_tci = transformCoordinate( " |
| "rotationTranslationScale( vec3( 0.000000, 0.000000, 0.000000 ), " |
| "vec3( 0.000000, 0.000000, 0.000000 ), vec3( 1.000000, 1.000000, " |
| "1.000000 ) ), tci );\n" |
| " emissiveMask = fileTexture( " << pEmissiveMaskMap->m_imageShaderName.toUtf8() |
| << ", vec3( 0, 0, 0 ), vec3( 1, 1, 1 ), mono_alpha, transformed_tci, " |
| << "vec2( 0.000000, 1.000000 ), vec2( 0.000000, 1.000000 ), " |
| "wrap_repeat, wrap_repeat, gamma_default ).tint;\n"; |
| } |
| |
| inFragmentShader << " return emissiveMask;\n" |
| "}\n\n"; |
| } |
| |
| bool generateFragmentShader(QSSGShaderDefaultMaterialKey &, |
| const QByteArray &inShaderPathName, |
| bool hasCustomVertShader) |
| { |
| const QSSGRef<QSSGDynamicObjectSystem> &theDynamicSystem(m_renderContext->dynamicObjectSystem()); |
| QByteArray fragSource = theDynamicSystem->getShaderSource(inShaderPathName); |
| |
| Q_ASSERT(!fragSource.isEmpty()); |
| |
| // light maps |
| bool hasLightmaps = false; |
| QSSGRenderableImage *lightmapShadowImage = nullptr; |
| QSSGRenderableImage *lightmapIndirectImage = nullptr; |
| QSSGRenderableImage *lightmapRadisoityImage = nullptr; |
| |
| for (QSSGRenderableImage *img = m_firstImage; img != nullptr; img = img->m_nextImage) { |
| if (img->m_mapType == QSSGImageMapTypes::LightmapIndirect) { |
| lightmapIndirectImage = img; |
| hasLightmaps = true; |
| } else if (img->m_mapType == QSSGImageMapTypes::LightmapRadiosity) { |
| lightmapRadisoityImage = img; |
| hasLightmaps = true; |
| } else if (img->m_mapType == QSSGImageMapTypes::LightmapShadow) { |
| lightmapShadowImage = img; |
| } |
| } |
| |
| if (!hasCustomVertShader) { |
| vertexGenerator().generateUVCoords(0); |
| // for lightmaps we expect a second set of uv coordinates |
| if (hasLightmaps) |
| vertexGenerator().generateUVCoords(1); |
| } |
| |
| QSSGDefaultMaterialVertexPipelineInterface &vertexShader(vertexGenerator()); |
| QSSGShaderStageGeneratorInterface &fragmentShader(fragmentGenerator()); |
| |
| QByteArray srcString(fragSource); |
| |
| if (m_renderContext->renderContext()->renderContextType() == QSSGRenderContextType::GLES2) { |
| QString::size_type pos = 0; |
| while ((pos = srcString.indexOf("out vec4 fragColor", pos)) != -1) { |
| srcString.insert(pos, "//"); |
| pos += int(strlen("//out vec4 fragColor")); |
| } |
| } |
| |
| fragmentShader << "#define FRAGMENT_SHADER\n\n"; |
| |
| const bool hasCustomFragShader = srcString.contains("void main()"); |
| |
| if (!hasCustomFragShader) |
| fragmentShader.addInclude("evalLightmaps.glsllib"); |
| |
| // check dielectric materials |
| if (!material().isDielectric()) |
| fragmentShader << "#define MATERIAL_IS_NON_DIELECTRIC 1\n\n"; |
| else |
| fragmentShader << "#define MATERIAL_IS_NON_DIELECTRIC 0\n\n"; |
| |
| fragmentShader << "#define QSSG_ENABLE_RNM 0\n\n"; |
| |
| fragmentShader << srcString << "\n"; |
| |
| // If a "main()" is already |
| // written, we'll assume that the |
| // shader |
| // pass is already written out and we don't need to add anything. |
| // Nothing beyond the basics, anyway |
| if (hasCustomFragShader) { |
| fragmentShader << "#define FRAGMENT_SHADER\n\n"; |
| if (!hasCustomVertShader) { |
| vertexShader.generateWorldNormal(); |
| vertexShader.generateVarTangentAndBinormal(); |
| vertexShader.generateWorldPosition(); |
| |
| vertexShader.generateViewVector(); |
| } |
| return true; |
| } |
| |
| if (material().hasLighting() && lightmapIndirectImage) { |
| generateLightmapIndirectFunc(fragmentShader, &lightmapIndirectImage->m_image); |
| } |
| if (material().hasLighting() && lightmapRadisoityImage) { |
| generateLightmapRadiosityFunc(fragmentShader, &lightmapRadisoityImage->m_image); |
| } |
| if (material().hasLighting() && lightmapShadowImage) { |
| generateLightmapShadowFunc(fragmentShader, &lightmapShadowImage->m_image); |
| } |
| |
| if (material().hasLighting() && (lightmapIndirectImage || lightmapRadisoityImage)) |
| generateLightmapIndirectSetupCode(fragmentShader, lightmapIndirectImage, lightmapRadisoityImage); |
| |
| if (material().hasLighting()) { |
| applyEmissiveMask(fragmentShader, material().m_emissiveMap); |
| } |
| |
| // setup main |
| vertexGenerator().beginFragmentGeneration(); |
| |
| // since we do pixel lighting we always need this if lighting is enabled |
| // We write this here because the functions below may also write to |
| // the fragment shader |
| if (material().hasLighting()) { |
| vertexShader.generateWorldNormal(); |
| vertexShader.generateVarTangentAndBinormal(); |
| vertexShader.generateWorldPosition(); |
| |
| if (material().isSpecularEnabled()) { |
| vertexShader.generateViewVector(); |
| } |
| } |
| |
| fragmentShader << " initializeBaseFragmentVariables();\n" |
| " computeTemporaries();\n" |
| " normal = normalize( computeNormal() );\n" |
| " initializeLayerVariables();\n" |
| " float alpha = clamp( evalCutout(), 0.0, 1.0 );\n"; |
| |
| if (material().isCutOutEnabled()) { |
| fragmentShader << " if ( alpha <= 0.0f )\n" |
| " discard;\n"; |
| } |
| |
| // indirect / direct lightmap init |
| if (material().hasLighting() && (lightmapIndirectImage || lightmapRadisoityImage)) |
| fragmentShader << " initializeLayerVariablesWithLightmap();\n"; |
| |
| // shadow map |
| generateLightmapShadowCode(fragmentShader, lightmapShadowImage); |
| |
| // main Body |
| fragmentShader << "#include \"customMaterialFragBodyAO.glsllib\"\n"; |
| |
| // for us right now transparency means we render a glass style material |
| if (m_hasTransparency && !material().isTransmissive()) |
| fragmentShader << " rgba = computeGlass( normal, materialIOR, alpha, rgba );\n"; |
| if (material().isTransmissive()) |
| fragmentShader << " rgba = computeOpacity( rgba );\n"; |
| |
| if (vertexGenerator().hasActiveWireframe()) { |
| fragmentShader.append("vec3 edgeDistance = varEdgeDistance * gl_FragCoord.w;\n" |
| " float d = min(min(edgeDistance.x, edgeDistance.y), edgeDistance.z);\n" |
| " float mixVal = smoothstep(0.0, 1.0, d);\n" // line width 1.0 |
| " rgba = mix( vec4(0.0, 1.0, 0.0, 1.0), rgba, mixVal);"); |
| } |
| fragmentShader << " rgba.a *= objectOpacity;\n"; |
| if (m_renderContext->renderContext()->renderContextType() == QSSGRenderContextType::GLES2) |
| fragmentShader << " gl_FragColor = rgba;\n"; |
| else |
| fragmentShader << " fragColor = rgba;\n"; |
| return false; |
| } |
| |
| QSSGRef<QSSGRenderShaderProgram> generateCustomMaterialShader(const QByteArray &inShaderPrefix, const QByteArray &inCustomMaterialName) |
| { |
| // build a string that allows us to print out the shader we are generating to the log. |
| // This is time consuming but I feel like it doesn't happen all that often and is very |
| // useful to users |
| // looking at the log file. |
| QByteArray generatedShaderString; |
| generatedShaderString = inShaderPrefix; |
| generatedShaderString.append(inCustomMaterialName); |
| QSSGShaderDefaultMaterialKey theKey(key()); |
| theKey.toString(generatedShaderString, m_defaultMaterialShaderKeyProperties); |
| |
| const bool hasCustomVertShader = generateVertexShader(theKey, inCustomMaterialName); |
| // TODO: The material name shouldn't need to be a QString |
| const bool hasCustomFragShader = generateFragmentShader(theKey, inCustomMaterialName, hasCustomVertShader); |
| |
| vertexGenerator().endVertexGeneration(hasCustomVertShader); |
| vertexGenerator().endFragmentGeneration(hasCustomFragShader); |
| |
| return programGenerator()->compileGeneratedShader(generatedShaderString, QSSGShaderCacheProgramFlags(), m_currentFeatureSet); |
| } |
| |
| QSSGRef<QSSGRenderShaderProgram> generateShader(const QSSGRenderGraphObject &inMaterial, |
| QSSGShaderDefaultMaterialKey inShaderDescription, |
| QSSGShaderStageGeneratorInterface &inVertexPipeline, |
| const ShaderFeatureSetList &inFeatureSet, |
| const QVector<QSSGRenderLight *> &inLights, |
| QSSGRenderableImage *inFirstImage, |
| bool inHasTransparency, |
| const QByteArray &inShaderPrefix, |
| const QByteArray &inCustomMaterialName) override |
| { |
| Q_ASSERT(inMaterial.type == QSSGRenderGraphObject::Type::CustomMaterial); |
| m_currentMaterial = static_cast<const QSSGRenderCustomMaterial *>(&inMaterial); |
| m_currentKey = &inShaderDescription; |
| m_currentPipeline = static_cast<QSSGDefaultMaterialVertexPipelineInterface *>(&inVertexPipeline); |
| m_currentFeatureSet = inFeatureSet; |
| m_lights = inLights; |
| m_firstImage = inFirstImage; |
| m_hasTransparency = inHasTransparency; |
| |
| return generateCustomMaterialShader(inShaderPrefix, inCustomMaterialName); |
| } |
| }; |
| } |
| |
| QSSGRef<QSSGMaterialShaderGeneratorInterface> QSSGMaterialShaderGeneratorInterface::createCustomMaterialShaderGenerator(QSSGRenderContextInterface *inRc) |
| { |
| return QSSGRef<QSSGMaterialShaderGeneratorInterface>(new QSSGShaderGenerator(inRc)); |
| } |
| |
| QT_END_NAMESPACE |