blob: 87c70ab69aa95b10db9c9ea29908b17452d52661 [file] [log] [blame]
/****************************************************************************
**
** 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$
**
****************************************************************************/
/* clang-format off */
#include <QtQuick3DUtils/private/qssgutils_p.h>
#include <QtQuick3DRender/private/qssgrendercontext_p.h>
#include <QtQuick3DRender/private/qssgrendershaderprogram_p.h>
#include <QtQuick3DRender/private/qssgrendershaderprogram_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 <QtQuick3DRuntimeRender/private/qssgrenderlight_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendercamera_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendershadowmap_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendercustommaterial_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderdynamicobjectsystem_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderlightconstantproperties_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendershaderkeys_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendererimplshaders_p.h>
#include <QtCore/QByteArray>
QT_BEGIN_NAMESPACE
namespace {
/**
* Cached light property lookups, used one per light so a shader generator for N
* lights will have an array of N of these lookup objects.
*/
struct QSSGShaderLightProperties
{
// Color of the light
QVector3D lightColor;
QSSGLightSourceShader lightData;
};
struct QSSGShadowMapProperties
{
QSSGRenderCachedShaderProperty<QSSGRenderTexture2D *> m_shadowmapTexture; ///< shadow texture
QSSGRenderCachedShaderProperty<QSSGRenderTextureCube *> m_shadowCubeTexture; ///< shadow cubemap
QSSGRenderCachedShaderProperty<QMatrix4x4> m_shadowmapMatrix; ///< world to ligh space transform matrix
QSSGRenderCachedShaderProperty<QVector4D> m_shadowmapSettings; ///< shadow rendering settings
QSSGShadowMapProperties() = default;
QSSGShadowMapProperties(const QByteArray &shadowmapTextureName,
const QByteArray &shadowcubeTextureName,
const QByteArray &shadowmapMatrixName,
const QByteArray &shadowmapSettingsName,
const QSSGRef<QSSGRenderShaderProgram> &inShader)
: m_shadowmapTexture(shadowmapTextureName, inShader)
, m_shadowCubeTexture(shadowcubeTextureName, inShader)
, m_shadowmapMatrix(shadowmapMatrixName, inShader)
, m_shadowmapSettings(shadowmapSettingsName, inShader)
{
}
};
/**
* The results of generating a shader. Caches all possible variable names into
* typesafe objects.
*/
struct QSSGShaderGeneratorGeneratedShader
{
QAtomicInt ref;
QSSGRef<QSSGRenderShaderProgram> m_shader;
// Specific properties we know the shader has to have.
QSSGRenderCachedShaderProperty<QMatrix4x4> m_mvp;
QSSGRenderCachedShaderProperty<QMatrix3x3> m_normalMatrix;
QSSGRenderCachedShaderProperty<QMatrix4x4> m_globalTransform;
QSSGRenderCachedShaderProperty<QMatrix4x4> m_viewProj;
QSSGRenderCachedShaderProperty<QMatrix4x4> m_viewMatrix;
QSSGRenderCachedShaderProperty<QVector3D> m_materialDiffuse;
QSSGRenderCachedShaderProperty<QVector4D> m_materialProperties;
// tint, ior
QSSGRenderCachedShaderProperty<QVector4D> m_materialSpecular;
QSSGRenderCachedShaderProperty<float> m_bumpAmount;
QSSGRenderCachedShaderProperty<float> m_displaceAmount;
QSSGRenderCachedShaderProperty<float> m_translucentFalloff;
QSSGRenderCachedShaderProperty<float> m_diffuseLightWrap;
QSSGRenderCachedShaderProperty<float> m_fresnelPower;
QSSGRenderCachedShaderProperty<float> m_occlusionAmount;
QSSGRenderCachedShaderProperty<float> m_alphaCutoff;
QSSGRenderCachedShaderProperty<QVector4D> m_baseColor;
QSSGRenderCachedShaderProperty<QVector3D> m_cameraPosition;
QSSGRenderCachedShaderProperty<QVector3D> m_cameraDirection;
QVector3D m_lightAmbientTotal;
QSSGRenderCachedShaderProperty<QVector3D> m_materialDiffuseLightAmbientTotal;
QSSGRenderCachedShaderProperty<QVector2D> m_cameraProperties;
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<QVector2D> m_lightProbeSize;
QSSGRenderCachedShaderProperty<QSSGRenderTexture2D *> m_lightProbe2;
QSSGRenderCachedShaderProperty<QVector4D> m_lightProbe2Props;
QSSGRenderCachedShaderProperty<QVector2D> m_lightProbe2Size;
QSSGRenderCachedShaderBuffer<QSSGRenderShaderConstantBuffer> m_aoShadowParams;
QSSGRenderCachedShaderBuffer<QSSGRenderShaderConstantBuffer> m_lightsBuffer;
QSSGLightConstantProperties<QSSGShaderGeneratorGeneratedShader> *m_lightConstantProperties = nullptr;
// Cache the image property name lookups
QVector<QSSGShaderTextureProperties> m_images;
QVector<QSSGShaderLightProperties> m_lights;
// Cache shadow map properties
QVector<QSSGShadowMapProperties> m_shadowMaps;
QSSGShaderGeneratorGeneratedShader(const QSSGRef<QSSGRenderShaderProgram> &inShader,
const QSSGRef<QSSGRenderContext> &inContext)
: m_shader(inShader)
, m_mvp("modelViewProjection", inShader)
, m_normalMatrix("normalMatrix", inShader)
, m_globalTransform("modelMatrix", inShader)
, m_viewProj("viewProjectionMatrix", inShader)
, m_viewMatrix("viewMatrix", inShader)
, m_materialDiffuse("material_diffuse", inShader)
, m_materialProperties("material_properties", inShader)
, m_materialSpecular("material_specular", inShader)
, m_bumpAmount("bumpAmount", inShader)
, m_displaceAmount("displaceAmount", inShader)
, m_translucentFalloff("translucentFalloff", inShader)
, m_diffuseLightWrap("diffuseLightWrap", inShader)
, m_fresnelPower("fresnelPower", inShader)
, m_occlusionAmount("occlusionAmount", inShader)
, m_alphaCutoff("alphaCutoff", inShader)
, m_baseColor("base_color", inShader)
, m_cameraPosition("cameraPosition", inShader)
, m_cameraDirection("cameraDirection", inShader)
, m_materialDiffuseLightAmbientTotal("light_ambient_total", inShader)
, m_cameraProperties("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_lightProbeSize("lightProbeSize", inShader)
, m_lightProbe2("lightProbe2", inShader)
, m_lightProbe2Props("lightProbe2Properties", inShader)
, m_lightProbe2Size("lightProbe2Size", inShader)
, m_aoShadowParams("aoShadow", inShader)
, m_lightsBuffer("lightsBuffer", inShader)
{
Q_UNUSED(inContext)
}
~QSSGShaderGeneratorGeneratedShader() { delete m_lightConstantProperties; }
};
struct QSSGShaderGenerator : public QSSGDefaultMaterialShaderGeneratorInterface
{
const QSSGRenderDefaultMaterial *m_currentMaterial;
typedef QHash<QSSGRef<QSSGRenderShaderProgram>, QSSGRef<QSSGShaderGeneratorGeneratedShader>> ProgramToShaderMap;
ProgramToShaderMap m_programToShaderMap;
QSSGRef<QSSGRenderShadowMap> m_shadowMapManager;
bool m_lightsAsSeparateUniforms;
QByteArray m_imageSampler;
QByteArray m_imageFragCoords;
QByteArray m_imageOffsets;
QByteArray m_imageRotations;
QByteArray m_imageTemp;
QByteArray m_imageSamplerSize;
QByteArray m_lightColor;
QByteArray m_lightSpecularColor;
QByteArray m_lightAttenuation;
QByteArray m_lightConstantAttenuation;
QByteArray m_lightLinearAttenuation;
QByteArray m_lightQuadraticAttenuation;
QByteArray m_normalizedDirection;
QByteArray m_lightDirection;
QByteArray m_lightPos;
QByteArray m_lightUp;
QByteArray m_lightRt;
QByteArray m_relativeDistance;
QByteArray m_relativeDirection;
QByteArray m_shadowMapStem;
QByteArray m_shadowCubeStem;
QByteArray m_shadowMatrixStem;
QByteArray m_shadowCoordStem;
QByteArray m_shadowControlStem;
QSSGShaderGenerator(QSSGRenderContextInterface *inRc)
: QSSGDefaultMaterialShaderGeneratorInterface (inRc)
, m_shadowMapManager(nullptr)
, m_lightsAsSeparateUniforms(false)
{
}
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 QSSGRenderDefaultMaterial *material() { return m_currentMaterial; }
bool hasTransparency() { return m_hasTransparency; }
void addFunction(QSSGShaderStageGeneratorInterface &generator, const QByteArray &functionName)
{
generator.addFunction(functionName);
}
void setupImageVariableNames(size_t imageIdx)
{
QByteArray imageStem = "image";
char buf[16];
qsnprintf(buf, 16, "%d", int(imageIdx));
imageStem.append(buf);
imageStem.append("_");
m_imageSampler = imageStem;
m_imageSampler.append("sampler");
m_imageOffsets = imageStem;
m_imageOffsets.append("offsets");
m_imageRotations = imageStem;
m_imageRotations.append("rotations");
m_imageFragCoords = imageStem;
m_imageFragCoords.append("uv_coords");
m_imageSamplerSize = imageStem;
m_imageSamplerSize.append("size");
}
QByteArray textureCoordVariableName(size_t uvSet)
{
QByteArray texCoordTemp = "varTexCoord";
char buf[16];
qsnprintf(buf, 16, "%d", int(uvSet));
texCoordTemp.append(buf);
return texCoordTemp;
}
ImageVariableNames getImageVariableNames(quint32 inIdx) override
{
setupImageVariableNames(inIdx);
ImageVariableNames retval;
retval.m_imageSampler = m_imageSampler;
retval.m_imageFragCoords = m_imageFragCoords;
return retval;
}
void addLocalVariable(QSSGShaderStageGeneratorInterface &inGenerator, const QByteArray &inName, const QByteArray &inType)
{
inGenerator << " " << inType << " " << inName << ";\n";
}
QByteArray uvTransform()
{
QByteArray transform;
transform = " uTransform = vec3(" + m_imageRotations + ".x, " + m_imageRotations + ".y, " + m_imageOffsets + ".x);\n";
transform += " vTransform = vec3(" + m_imageRotations + ".z, " + m_imageRotations + ".w, " + m_imageOffsets + ".y);\n";
return transform;
}
void generateImageUVCoordinates(QSSGShaderStageGeneratorInterface &inVertexPipeline, quint32 idx, quint32 uvSet, QSSGRenderableImage &image) override
{
QSSGDefaultMaterialVertexPipelineInterface &vertexShader(
static_cast<QSSGDefaultMaterialVertexPipelineInterface &>(inVertexPipeline));
QSSGShaderStageGeneratorInterface &fragmentShader(fragmentGenerator());
setupImageVariableNames(idx);
QByteArray textureCoordName = textureCoordVariableName(uvSet);
fragmentShader.addUniform(m_imageSampler, "sampler2D");
vertexShader.addUniform(m_imageOffsets, "vec3");
vertexShader.addUniform(m_imageRotations, "vec4");
QByteArray uvTrans = uvTransform();
if (image.m_image.m_mappingMode == QSSGRenderImage::MappingModes::Normal) {
vertexShader << uvTrans;
vertexShader.addOutgoing(m_imageFragCoords, "vec2");
addFunction(vertexShader, "getTransformedUVCoords");
vertexShader.generateUVCoords(uvSet);
m_imageTemp = m_imageFragCoords;
m_imageTemp.append("temp");
vertexShader << " vec2 " << m_imageTemp << " = getTransformedUVCoords(vec3(" << textureCoordName << ", 1.0), uTransform, vTransform);\n";
if (image.m_image.m_textureData.m_textureFlags.isInvertUVCoords())
vertexShader << " " << m_imageTemp << ".y = 1.0 - " << m_imageTemp << ".y;\n";
vertexShader.assignOutput(m_imageFragCoords, m_imageTemp);
} else {
fragmentShader.addUniform(m_imageOffsets, "vec3");
fragmentShader.addUniform(m_imageRotations, "vec4");
fragmentShader << uvTrans;
vertexShader.generateEnvMapReflection();
addFunction(fragmentShader, "getTransformedUVCoords");
fragmentShader << " vec2 " << m_imageFragCoords << " = getTransformedUVCoords(environment_map_reflection, uTransform, vTransform);\n";
if (image.m_image.m_textureData.m_textureFlags.isInvertUVCoords())
fragmentShader << " " << m_imageFragCoords << ".y = 1.0 - " << m_imageFragCoords << ".y;\n";
}
}
void generateImageUVSampler(quint32 idx, quint32 uvSet = 0)
{
QSSGShaderStageGeneratorInterface &fragmentShader(fragmentGenerator());
setupImageVariableNames(idx);
fragmentShader.addUniform(m_imageSampler, "sampler2D");
m_imageFragCoords = textureCoordVariableName(uvSet);
vertexGenerator().generateUVCoords(uvSet);
}
void generateImageUVCoordinates(quint32 idx, QSSGRenderableImage &image, quint32 uvSet = 0)
{
generateImageUVCoordinates(vertexGenerator(), idx, uvSet, image);
}
void outputSpecularEquation(QSSGRenderDefaultMaterial::MaterialSpecularModel inSpecularModel,
QSSGShaderStageGeneratorInterface &fragmentShader,
const QByteArray &inLightDir,
const QByteArray &inLightSpecColor)
{
switch (inSpecularModel) {
case QSSGRenderDefaultMaterial::MaterialSpecularModel::KGGX: {
fragmentShader.addInclude("defaultMaterialPhysGlossyBSDF.glsllib");
fragmentShader.addUniform("material_specular", "vec4");
fragmentShader << " global_specular_light.rgb += lightAttenuation * specularAmount * specularColor"
" * kggxGlossyDefaultMtl(world_normal, tangent, -" << inLightDir << ".xyz, view_vector, " << inLightSpecColor << ".rgb, vec3(material_specular.rgb), roughnessAmount).rgb;\n";
} break;
case QSSGRenderDefaultMaterial::MaterialSpecularModel::KWard: {
fragmentShader.addInclude("defaultMaterialPhysGlossyBSDF.glsllib");
fragmentShader.addUniform("material_specular", "vec4");
fragmentShader << " global_specular_light.rgb += lightAttenuation * specularAmount * specularColor"
" * wardGlossyDefaultMtl(world_normal, tangent, -" << inLightDir << ".xyz, view_vector, " << inLightSpecColor << ".rgb, vec3(material_specular.rgb), roughnessAmount).rgb;\n";
} break;
default:
addFunction(fragmentShader, "specularBSDF");
fragmentShader << " global_specular_light.rgb += lightAttenuation * specularAmount * specularColor"
" * specularBSDF(world_normal, -" << inLightDir << ".xyz, view_vector, " << inLightSpecColor << ".rgb, 2.56 / (roughnessAmount + 0.01)).rgb;\n";
break;
}
}
void outputDiffuseAreaLighting(QSSGShaderStageGeneratorInterface &infragmentShader, const QByteArray &inPos, const QByteArray &inLightPrefix)
{
m_normalizedDirection = inLightPrefix + "_areaDir";
addLocalVariable(infragmentShader, m_normalizedDirection, "vec3");
infragmentShader << " lightAttenuation = calculateDiffuseAreaOld(" << m_lightDirection << ".xyz, " << m_lightPos << ".xyz, " << m_lightUp << ", " << m_lightRt << ", " << inPos << ", " << m_normalizedDirection << ");\n";
}
void outputSpecularAreaLighting(QSSGShaderStageGeneratorInterface &infragmentShader,
const QByteArray &inPos,
const QByteArray &inView,
const QByteArray &inLightSpecColor)
{
addFunction(infragmentShader, "sampleAreaGlossyDefault");
infragmentShader.addUniform("material_specular", "vec4");
infragmentShader << "global_specular_light.rgb += " << inLightSpecColor << ".rgb * lightAttenuation * shadowFac * material_specular.rgb * specularAmount"
" * sampleAreaGlossyDefault(tanFrame, " << inPos << ", " << m_normalizedDirection << ", " << m_lightPos << ".xyz, " << m_lightRt << ".w, " << m_lightUp << ".w, " << inView << ", roughnessAmount).rgb;\n";
}
void addTranslucencyIrradiance(QSSGShaderStageGeneratorInterface &infragmentShader,
QSSGRenderableImage *image,
bool areaLight)
{
if (image == nullptr)
return;
addFunction(infragmentShader, "diffuseReflectionWrapBSDF");
if (areaLight) {
infragmentShader << " global_diffuse_light.rgb += lightAttenuation * translucent_thickness_exp * diffuseReflectionWrapBSDF(-world_normal, " << m_normalizedDirection << ", " << m_lightColor << ".rgb, diffuseLightWrap).rgb;\n";
} else {
infragmentShader << " global_diffuse_light.rgb += lightAttenuation * translucent_thickness_exp * diffuseReflectionWrapBSDF(-world_normal, -" << m_normalizedDirection << ", " << m_lightColor << ".rgb, diffuseLightWrap).rgb;\n";
}
}
void setupShadowMapVariableNames(size_t lightIdx)
{
m_shadowMapStem = "shadowmap";
m_shadowCubeStem = "shadowcube";
char buf[16];
qsnprintf(buf, 16, "%d", int(lightIdx));
m_shadowMapStem.append(buf);
m_shadowCubeStem.append(buf);
m_shadowMatrixStem = m_shadowMapStem;
m_shadowMatrixStem.append("_matrix");
m_shadowCoordStem = m_shadowMapStem;
m_shadowCoordStem.append("_coord");
m_shadowControlStem = m_shadowMapStem;
m_shadowControlStem.append("_control");
}
void addShadowMapContribution(QSSGShaderStageGeneratorInterface &inLightShader, quint32 lightIndex, QSSGRenderLight::Type inType)
{
setupShadowMapVariableNames(lightIndex);
inLightShader.addInclude("shadowMapping.glsllib");
if (inType == QSSGRenderLight::Type::Directional) {
inLightShader.addUniform(m_shadowMapStem, "sampler2D");
} else {
inLightShader.addUniform(m_shadowCubeStem, "samplerCube");
}
inLightShader.addUniform(m_shadowControlStem, "vec4");
inLightShader.addUniform(m_shadowMatrixStem, "mat4");
/*
if ( inType == RenderLightTypes::Area )
{
inLightShader << "vec2 " << m_shadowCoordStem << ";" << "\n";
inLightShader << " shadow_map_occl = sampleParaboloid( " << m_shadowMapStem << ", "
<< m_shadowControlStem << ", "
<<
m_shadowMatrixStem << ", varWorldPos, vec2(1.0, " << m_shadowControlStem << ".z), "
<< m_shadowCoordStem
<< " );" << "\n";
}
else */
if (inType != QSSGRenderLight::Type::Directional) {
inLightShader << " shadow_map_occl = sampleCubemap(" << m_shadowCubeStem << ", " << m_shadowControlStem << ", " << m_shadowMatrixStem << ", " << m_lightPos << ".xyz, varWorldPos, vec2(1.0, " << m_shadowControlStem << ".z));\n";
} else {
inLightShader << " shadow_map_occl = sampleOrthographic(" << m_shadowMapStem << ", " << m_shadowControlStem << ", " << m_shadowMatrixStem << ", varWorldPos, vec2(1.0, " << m_shadowControlStem << ".z));\n";
}
}
void addDisplacementMappingForDepthPass(QSSGShaderStageGeneratorInterface &inShader) override
{
inShader.addIncoming("attr_uv0", "vec2");
inShader.addIncoming("attr_norm", "vec3");
inShader.addUniform("displacementSampler", "sampler2D");
inShader.addUniform("displaceAmount", "float");
inShader.addUniform("displacementMap_rot", "vec4");
inShader.addUniform("displacementMap_offset", "vec3");
inShader.addInclude("defaultMaterialFileDisplacementTexture.glsllib");
inShader << " vec3 uTransform = vec3(displacementMap_rot.x, displacementMap_rot.y, displacementMap_offset.x);\n"
" vec3 vTransform = vec3(displacementMap_rot.z, displacementMap_rot.w, displacementMap_offset.y);\n";
addFunction(inShader, "getTransformedUVCoords");
inShader << " vec2 uv_coords = attr_uv0;\n"
" uv_coords = getTransformedUVCoords(vec3(uv_coords, 1.0), uTransform, vTransform);\n"
" vec3 displacedPos = defaultMaterialFileDisplacementTexture(displacementSampler , displaceAmount, uv_coords , attr_norm, attr_pos);\n"
" gl_Position = modelViewProjection * vec4(displacedPos, 1.0);\n";
}
void addDisplacementImageUniforms(QSSGShaderStageGeneratorInterface &inGenerator,
quint32 displacementImageIdx,
QSSGRenderableImage *displacementImage) override
{
if (displacementImage) {
setupImageVariableNames(displacementImageIdx);
inGenerator.addInclude("defaultMaterialFileDisplacementTexture.glsllib");
inGenerator.addUniform("modelMatrix", "mat4");
inGenerator.addUniform("cameraPosition", "vec3");
inGenerator.addUniform("displaceAmount", "float");
inGenerator.addUniform(m_imageSampler, "sampler2D");
}
}
bool maybeAddMaterialFresnel(QSSGShaderStageGeneratorInterface &fragmentShader, QSSGDataView<quint32> inKey, bool inFragmentHasSpecularAmount)
{
if (m_defaultMaterialShaderKeyProperties.m_fresnelEnabled.getValue(inKey)) {
if (inFragmentHasSpecularAmount == false)
fragmentShader << " float specularAmount = 1.0;\n";
inFragmentHasSpecularAmount = true;
fragmentShader.addInclude("defaultMaterialFresnel.glsllib");
fragmentShader.addUniform("fresnelPower", "float");
fragmentShader.addUniform("material_specular", "vec4");
fragmentShader << " // Add fresnel ratio\n"
" specularAmount *= defaultMaterialSimpleFresnel(world_normal, view_vector, material_specular.w, fresnelPower);\n";
}
return inFragmentHasSpecularAmount;
}
void setupLightVariableNames(qint32 lightIdx, QSSGRenderLight &inLight)
{
Q_ASSERT(lightIdx > -1);
if (m_lightsAsSeparateUniforms) {
char buf[16];
qsnprintf(buf, 16, "light_%d", int(lightIdx));
QByteArray lightStem = buf;
m_lightColor = lightStem;
m_lightColor.append("_diffuse");
m_lightDirection = lightStem;
m_lightDirection.append("_direction");
m_lightSpecularColor = lightStem;
m_lightSpecularColor.append("_specular");
if (inLight.m_lightType == QSSGRenderLight::Type::Point) {
m_lightPos = lightStem;
m_lightPos.append("_position");
m_lightAttenuation = lightStem;
m_lightAttenuation.append("_attenuation");
} else if (inLight.m_lightType == QSSGRenderLight::Type::Area) {
m_lightPos = lightStem;
m_lightPos.append("_position");
m_lightUp = lightStem;
m_lightUp.append("_up");
m_lightRt = lightStem;
m_lightRt.append("_right");
}
} else {
QByteArray lightStem = "lights";
char buf[16];
qsnprintf(buf, 16, "[%d].", int(lightIdx));
lightStem.append(buf);
m_lightColor = lightStem;
m_lightColor.append("diffuse");
m_lightDirection = lightStem;
m_lightDirection.append("direction");
m_lightSpecularColor = lightStem;
m_lightSpecularColor.append("specular");
if (inLight.m_lightType == QSSGRenderLight::Type::Point) {
m_lightPos = lightStem;
m_lightPos.append("position");
m_lightConstantAttenuation = lightStem;
m_lightConstantAttenuation.append("constantAttenuation");
m_lightLinearAttenuation = lightStem;
m_lightLinearAttenuation.append("linearAttenuation");
m_lightQuadraticAttenuation = lightStem;
m_lightQuadraticAttenuation.append("quadraticAttenuation");
} else if (inLight.m_lightType == QSSGRenderLight::Type::Area) {
m_lightPos = lightStem;
m_lightPos.append("position");
m_lightUp = lightStem;
m_lightUp.append("up");
m_lightRt = lightStem;
m_lightRt.append("right");
}
}
}
void addDisplacementMapping(QSSGDefaultMaterialVertexPipelineInterface &inShader)
{
inShader.addIncoming("attr_uv0", "vec2");
inShader.addIncoming("attr_norm", "vec3");
inShader.addUniform("displacementSampler", "sampler2D");
inShader.addUniform("displaceAmount", "float");
inShader.addUniform("displacementMap_rot", "vec4");
inShader.addUniform("displacementMap_offset", "vec3");
inShader.addInclude("defaultMaterialFileDisplacementTexture.glsllib");
inShader << " vec3 uTransform = vec3(displacementMap_rot.x, displacementMap_rot.y, displacementMap_offset.x);\n"
" vec3 vTransform = vec3(displacementMap_rot.z, displacementMap_rot.w, displacementMap_offset.y);\n";
addFunction(inShader, "getTransformedUVCoords");
inShader.generateUVCoords();
inShader << " varTexCoord0 = getTransformedUVCoords(vec3(varTexCoord0, 1.0), uTransform, vTransform);\n"
" vec3 displacedPos = defaultMaterialFileDisplacementTexture(displacementSampler , displaceAmount, varTexCoord0 , attr_norm, attr_pos);\n"
" gl_Position = modelViewProjection * vec4(displacedPos, 1.0);\n";
}
void generateTextureSwizzle(QSSGRenderTextureSwizzleMode swizzleMode, QByteArray &texSwizzle, QByteArray &lookupSwizzle)
{
QSSGRenderContextTypes deprecatedContextFlags(QSSGRenderContextType::GL2 | QSSGRenderContextType::GLES2);
if (!(deprecatedContextFlags & m_renderContext->renderContext()->renderContextType())) {
switch (swizzleMode) {
case QSSGRenderTextureSwizzleMode::L8toR8:
case QSSGRenderTextureSwizzleMode::L16toR16:
texSwizzle.append(".rgb");
lookupSwizzle.append(".rrr");
break;
case QSSGRenderTextureSwizzleMode::L8A8toRG8:
texSwizzle.append(".rgba");
lookupSwizzle.append(".rrrg");
break;
case QSSGRenderTextureSwizzleMode::A8toR8:
texSwizzle.append(".a");
lookupSwizzle.append(".r");
break;
default:
break;
}
}
}
///< get the light constant buffer and generate if necessary
QSSGRef<QSSGRenderConstantBuffer> getLightConstantBuffer(qint32 inLightCount)
{
Q_ASSERT(inLightCount >= 0);
const QSSGRef<QSSGRenderContext> &theContext = m_renderContext->renderContext();
// we assume constant buffer support
Q_ASSERT(theContext->supportsConstantBuffer());
// we only create if if we have lights
if (!inLightCount || !theContext->supportsConstantBuffer())
return nullptr;
static const QByteArray theName = QByteArrayLiteral("lightsBuffer");
QSSGRef<QSSGRenderConstantBuffer> pCB = theContext->getConstantBuffer(theName);
if (pCB)
return pCB;
// create
const size_t size = sizeof(QSSGLightSourceShader) * QSSG_MAX_NUM_LIGHTS + (4 * sizeof(qint32));
quint8 stackData[size];
memset(stackData, 0, 4 * sizeof(qint32));
// QSSGLightSourceShader *s = new (stackData + 4*sizeof(qint32)) QSSGLightSourceShader[QSSG_MAX_NUM_LIGHTS];
QSSGByteView cBuffer(stackData, size);
pCB = *m_constantBuffers.insert(theName, new QSSGRenderConstantBuffer(theContext, theName, QSSGRenderBufferUsageType::Static, cBuffer));
if (Q_UNLIKELY(!pCB)) {
Q_ASSERT(false);
return nullptr;
}
return pCB;
}
void setImageShaderVariables(const QSSGRef<QSSGShaderGeneratorGeneratedShader> &inShader, QSSGRenderableImage &inImage, quint32 idx)
{
size_t numImageVariables = inShader->m_images.size();
for (size_t namesIdx = numImageVariables; namesIdx <= idx; ++namesIdx) {
setupImageVariableNames(idx);
inShader->m_images.push_back(
QSSGShaderTextureProperties(inShader->m_shader, m_imageSampler, m_imageOffsets, m_imageRotations, m_imageSamplerSize));
}
QSSGShaderTextureProperties &theShaderProps = inShader->m_images[idx];
const QMatrix4x4 &textureTransform = inImage.m_image.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.
QVector3D offsets(dataPtr[12], dataPtr[13], inImage.m_image.m_textureData.m_textureFlags.isPreMultiplied() ? 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]);
// 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.
const QSSGRef<QSSGRenderTexture2D> &imageTexture = inImage.m_image.m_textureData.m_texture;
imageTexture->setTextureWrapS(inImage.m_image.m_horizontalTilingMode);
imageTexture->setTextureWrapT(inImage.m_image.m_verticalTilingMode);
theShaderProps.sampler.set(imageTexture.data());
theShaderProps.offsets.set(offsets);
theShaderProps.rotations.set(rotations);
theShaderProps.size.set(QVector2D(imageTexture->textureDetails().width, imageTexture->textureDetails().height));
}
void generateShadowMapOcclusion(quint32 lightIdx, bool inShadowEnabled, QSSGRenderLight::Type inType)
{
if (inShadowEnabled) {
vertexGenerator().generateWorldPosition();
addShadowMapContribution(fragmentGenerator(), lightIdx, inType);
/*
VertexGenerator().AddUniform( m_ShadowMatrixStem, "mat4" );
VertexGenerator().AddOutgoing( m_ShadowCoordStem, "vec4" );
VertexGenerator() << " vec4 local_" << m_ShadowCoordStem << " = " << m_ShadowMatrixStem
<< " * vec4(local_model_world_position, 1.0);" << "\n";
m_TempStr.assign( "local_" );
m_TempStr.append( m_ShadowCoordStem );
VertexGenerator().AssignOutput( m_ShadowCoordStem, m_TempStr );
*/
} else {
fragmentGenerator() << " shadow_map_occl = 1.0;\n";
}
}
void generateVertexShader()
{
// 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);
}
void generateFragmentShader(QSSGShaderDefaultMaterialKey &inKey)
{
const bool metalnessEnabled = material()->isMetalnessEnabled();
const bool specularEnabled = material()->isSpecularEnabled();
bool vertexColorsEnabled = material()->isVertexColorsEnabled();
bool hasLighting = material()->hasLighting();
bool isDoubleSided = m_defaultMaterialShaderKeyProperties.m_isDoubleSided.getValue(inKey);
bool hasImage = m_firstImage != nullptr;
bool hasIblProbe = m_defaultMaterialShaderKeyProperties.m_hasIbl.getValue(inKey);
bool hasSpecMap = false;
bool hasMetalMap = false;
bool hasEnvMap = false;
bool hasEmissiveMap = false;
bool hasLightmaps = false;
bool hasBaseColorMap = false;
// Pull the bump out as
QSSGRenderableImage *bumpImage = nullptr;
quint32 imageIdx = 0;
quint32 bumpImageIdx = 0;
QSSGRenderableImage *specularAmountImage = nullptr;
quint32 specularAmountImageIdx = 0;
QSSGRenderableImage *roughnessImage = nullptr;
quint32 roughnessImageIdx = 0;
QSSGRenderableImage *metalnessImage = nullptr;
quint32 metalnessImageIdx = 0;
QSSGRenderableImage *occlusionImage = nullptr;
quint32 occlusionImageIdx = 0;
// normal mapping
QSSGRenderableImage *normalImage = nullptr;
quint32 normalImageIdx = 0;
// translucency map
QSSGRenderableImage *translucencyImage = nullptr;
quint32 translucencyImageIdx = 0;
// lightmaps
QSSGRenderableImage *lightmapIndirectImage = nullptr;
quint32 lightmapIndirectImageIdx = 0;
QSSGRenderableImage *lightmapRadiosityImage = nullptr;
quint32 lightmapRadiosityImageIdx = 0;
QSSGRenderableImage *lightmapShadowImage = nullptr;
quint32 lightmapShadowImageIdx = 0;
const bool supportStandardDerivatives = m_renderContext->renderContext()->supportsStandardDerivatives();
// Use shared texcoord when transforms are identity
QVector<QSSGRenderableImage *> identityImages;
Q_UNUSED(lightmapShadowImage)
Q_UNUSED(lightmapShadowImageIdx)
Q_UNUSED(supportStandardDerivatives)
for (QSSGRenderableImage *img = m_firstImage; img != nullptr; img = img->m_nextImage, ++imageIdx) {
if (img->m_image.isImageTransformIdentity())
identityImages.push_back(img);
if (img->m_mapType == QSSGImageMapTypes::Specular) {
hasSpecMap = true;
} else if (img->m_mapType == QSSGImageMapTypes::BaseColor) {
hasBaseColorMap = true;
} else if (img->m_mapType == QSSGImageMapTypes::Bump) {
bumpImage = img;
bumpImageIdx = imageIdx;
} else if (img->m_mapType == QSSGImageMapTypes::SpecularAmountMap) {
specularAmountImage = img;
specularAmountImageIdx = imageIdx;
} else if (img->m_mapType == QSSGImageMapTypes::Roughness) {
roughnessImage = img;
roughnessImageIdx = imageIdx;
} else if (img->m_mapType == QSSGImageMapTypes::Metalness) {
metalnessImage = img;
metalnessImageIdx = imageIdx;
hasMetalMap = true;
} else if (img->m_mapType == QSSGImageMapTypes::Occlusion) {
occlusionImage = img;
occlusionImageIdx = imageIdx;
} else if (img->m_mapType == QSSGImageMapTypes::Normal) {
normalImage = img;
normalImageIdx = imageIdx;
} else if (img->m_image.m_mappingMode == QSSGRenderImage::MappingModes::Environment) {
hasEnvMap = true;
} else if (img->m_mapType == QSSGImageMapTypes::Translucency) {
translucencyImage = img;
translucencyImageIdx = imageIdx;
} else if (img->m_mapType == QSSGImageMapTypes::Emissive) {
hasEmissiveMap = true;
} else if (img->m_mapType == QSSGImageMapTypes::LightmapIndirect) {
lightmapIndirectImage = img;
lightmapIndirectImageIdx = imageIdx;
hasLightmaps = true;
} else if (img->m_mapType == QSSGImageMapTypes::LightmapRadiosity) {
lightmapRadiosityImage = img;
lightmapRadiosityImageIdx = imageIdx;
hasLightmaps = true;
} else if (img->m_mapType == QSSGImageMapTypes::LightmapShadow) {
lightmapShadowImage = img;
lightmapShadowImageIdx = imageIdx;
hasLightmaps = true;
}
}
bool enableFresnel = m_defaultMaterialShaderKeyProperties.m_fresnelEnabled.getValue(inKey);
bool enableSSAO = false;
bool enableSSDO = false;
bool enableShadowMaps = false;
bool enableBumpNormal = normalImage || bumpImage;
for (qint32 idx = 0; idx < m_currentFeatureSet.size(); ++idx) {
const auto &name = m_currentFeatureSet.at(idx).name;
if (name == QSSGShaderDefines::asString(QSSGShaderDefines::Ssao))
enableSSAO = m_currentFeatureSet.at(idx).enabled;
else if (name == QSSGShaderDefines::asString(QSSGShaderDefines::Ssdo))
enableSSDO = m_currentFeatureSet.at(idx).enabled;
else if (name == QSSGShaderDefines::asString(QSSGShaderDefines::Ssm))
enableShadowMaps = m_currentFeatureSet.at(idx).enabled;
}
bool includeSSAOSSDOVars = enableSSAO || enableSSDO || enableShadowMaps;
vertexGenerator().beginFragmentGeneration();
QSSGShaderStageGeneratorInterface &fragmentShader(fragmentGenerator());
QSSGDefaultMaterialVertexPipelineInterface &vertexShader(vertexGenerator());
// The fragment or vertex shaders may not use the material_properties or diffuse
// uniforms in all cases but it is simpler to just add them and let the linker strip them.
fragmentShader.addUniform("material_diffuse", "vec3");
fragmentShader.addUniform("base_color", "vec4");
fragmentShader.addUniform("material_properties", "vec4");
// All these are needed for SSAO
if (includeSSAOSSDOVars) {
fragmentShader.addInclude("SSAOCustomMaterial.glsllib");
// fragmentShader.AddUniform( "aoTexture", "sampler2D" );
}
if (hasIblProbe && hasLighting) {
fragmentShader.addInclude("sampleProbe.glsllib");
}
if (hasLighting) {
if (!m_lightsAsSeparateUniforms)
addFunction(fragmentShader, "sampleLightVars");
addFunction(fragmentShader, "diffuseReflectionBSDF");
}
if (hasLighting && hasLightmaps) {
fragmentShader.addInclude("evalLightmaps.glsllib");
}
// view_vector, varWorldPos, world_normal are all used if there is a specular map
// in addition to if there is specular lighting. So they are lifted up here, always
// generated.
// we rely on the linker to strip out what isn't necessary instead of explicitly stripping
// it for code simplicity.
if (hasImage && hasLighting && hasLightmaps) {
fragmentShader.append(" vec3 uTransform;");
fragmentShader.append(" vec3 vTransform;");
}
if (includeSSAOSSDOVars || hasSpecMap || hasMetalMap || hasLighting || hasEnvMap || enableFresnel || hasIblProbe || enableBumpNormal) {
vertexShader.generateViewVector();
vertexShader.generateWorldNormal();
vertexShader.generateWorldPosition();
}
if (includeSSAOSSDOVars || specularEnabled || metalnessEnabled || hasIblProbe || enableBumpNormal)
vertexShader.generateVarTangentAndBinormal();
if (vertexColorsEnabled)
vertexShader.generateVertexColor();
else
fragmentShader.append(" vec3 vertColor = vec3(1.0);");
// You do bump or normal mapping but not both
if (bumpImage != nullptr) {
generateImageUVCoordinates(bumpImageIdx, *bumpImage);
fragmentShader.addUniform("bumpAmount", "float");
fragmentShader.addUniform(m_imageSamplerSize, "vec2");
fragmentShader.addInclude("defaultMaterialBumpNoLod.glsllib");
fragmentShader << " world_normal = defaultMaterialBumpNoLod(" << m_imageSampler << ", bumpAmount, " << m_imageFragCoords << ", tangent, binormal, world_normal, " << m_imageSamplerSize << ");\n";
// Do gram schmidt
fragmentShader << " binormal = normalize(cross(world_normal, tangent));\n";
fragmentShader << " tangent = normalize(cross(binormal, world_normal));\n";
} else if (normalImage != nullptr) {
generateImageUVCoordinates(normalImageIdx, *normalImage);
fragmentShader.addFunction("sampleNormalTexture");
fragmentShader.addUniform("bumpAmount", "float");
fragmentShader << " world_normal = sampleNormalTexture(" << m_imageSampler << ", bumpAmount, " << m_imageFragCoords << ", tangent, binormal, world_normal);\n";
}
if (hasLighting && isDoubleSided) {
fragmentShader.addInclude("doubleSided.glsllib");
fragmentShader.append(" world_normal = adjustNormalForFace(world_normal, varWorldPos);\n");
}
if (includeSSAOSSDOVars || specularEnabled || metalnessEnabled || hasIblProbe || enableBumpNormal)
fragmentShader << " mat3 tanFrame = mat3(tangent, binormal, world_normal);\n";
bool fragmentHasSpecularAmount = false;
if (hasEmissiveMap)
fragmentShader.append(" vec3 global_emission = material_diffuse.rgb;");
fragmentShader << " vec3 diffuseColor = base_color.rgb;\n";
if (hasLighting) {
fragmentShader.addUniform("light_ambient_total", "vec3");
fragmentShader.append(" vec4 global_diffuse_light = vec4(light_ambient_total.rgb, 1.0);");
fragmentShader.append(" vec3 global_specular_light = vec3(0.0, 0.0, 0.0);");
fragmentShader.append(" float shadow_map_occl = 1.0;");
if (specularEnabled || metalnessEnabled) {
vertexShader.generateViewVector();
fragmentShader.addUniform("material_properties", "vec4");
// Technically this should always be 0 + x or 0 + w
fragmentShader << " float specularAmount = clamp(material_properties.x + material_properties.z, 0.0, 1.0);\n";
fragmentHasSpecularAmount = true;
}
if (lightmapIndirectImage != nullptr) {
if (identityImages.contains(lightmapIndirectImage))
generateImageUVSampler(lightmapIndirectImageIdx, 1);
else
generateImageUVCoordinates(lightmapIndirectImageIdx, *lightmapIndirectImage, 1);
fragmentShader << " vec4 indirect_light = texture2D(" << m_imageSampler << ", " << m_imageFragCoords << ");\n";
fragmentShader << " global_diffuse_light += indirect_light;\n";
if (metalnessEnabled || specularEnabled)
fragmentShader << " global_specular_light += indirect_light.rgb * specularAmount;\n";
}
if (lightmapRadiosityImage != nullptr) {
if (identityImages.contains(lightmapRadiosityImage))
generateImageUVSampler(lightmapRadiosityImageIdx, 1);
else
generateImageUVCoordinates(lightmapRadiosityImageIdx, *lightmapRadiosityImage, 1);
fragmentShader << " vec4 direct_light = texture2D(" << m_imageSampler << ", " << m_imageFragCoords << ");\n";
fragmentShader << " global_diffuse_light += direct_light;\n";
if (metalnessEnabled || specularEnabled)
fragmentShader << " global_specular_light += direct_light.rgb * specularAmount;\n";
}
if (translucencyImage != nullptr) {
fragmentShader.addUniform("translucentFalloff", "float");
fragmentShader.addUniform("diffuseLightWrap", "float");
if (identityImages.contains(translucencyImage))
generateImageUVSampler(translucencyImageIdx);
else
generateImageUVCoordinates(translucencyImageIdx, *translucencyImage);
fragmentShader << " vec4 translucent_depth_range = texture2D(" << m_imageSampler << ", " << m_imageFragCoords << ");\n"
" float translucent_thickness = translucent_depth_range.r * translucent_depth_range.r;\n"
" float translucent_thickness_exp = exp(translucent_thickness * translucentFalloff);\n";
}
fragmentShader.append(" float lightAttenuation = 1.0;");
addLocalVariable(fragmentShader, "aoFactor", "float");
if (hasLighting && enableSSAO)
fragmentShader.append(" aoFactor = customMaterialAO();");
else
fragmentShader.append(" aoFactor = 1.0;");
addLocalVariable(fragmentShader, "shadowFac", "float");
// Fragment lighting means we can perhaps attenuate the specular amount by a texture
// lookup.
fragmentShader << " vec3 specularColor = vec3(1.0);\n";
if (specularAmountImage) {
if (!fragmentHasSpecularAmount) {
fragmentShader << " float specularAmount = 1.0;\n";
fragmentHasSpecularAmount = true;
}
if (identityImages.contains(specularAmountImage))
generateImageUVSampler(specularAmountImageIdx);
else
generateImageUVCoordinates(specularAmountImageIdx, *specularAmountImage);
fragmentShader << " specularColor = texture2D(" << m_imageSampler << ", " << m_imageFragCoords << ").rgb;\n";
}
fragmentShader << " float roughnessAmount = material_properties.y;\n";
if (roughnessImage) {
if (identityImages.contains(roughnessImage))
generateImageUVSampler(roughnessImageIdx);
else
generateImageUVCoordinates(roughnessImageIdx, *roughnessImage);
fragmentShader << " roughnessAmount *= texture2D(" << m_imageSampler << ", " << m_imageFragCoords << ").g;\n";
}
fragmentShader << " float metalnessAmount = material_properties.z;\n";
if (metalnessImage) {
if (identityImages.contains(metalnessImage))
generateImageUVSampler(metalnessImageIdx);
else
generateImageUVCoordinates(metalnessImageIdx, *metalnessImage);
fragmentShader << " float sampledMetalness = texture2D(" << m_imageSampler << ", " << m_imageFragCoords << ").b;\n"
<< " metalnessAmount = clamp(metalnessAmount * sampledMetalness, 0.0, 1.0);\n";
if (!fragmentHasSpecularAmount) {
fragmentShader << " float specularAmount = 1.0;\n";
fragmentHasSpecularAmount = true;
}
fragmentShader << " specularAmount *= metalnessAmount;\n";
}
fragmentShader << " diffuseColor *= (1.0 - metalnessAmount);\n";
if (!hasBaseColorMap && material()->type == QSSGRenderGraphObject::Type::PrincipledMaterial) {
fragmentShader << " float lum = dot(base_color.rgb, vec3(0.21, 0.72, 0.07));\n"
" specularColor += (lum > 0.0) ? (base_color.rgb) / lum : vec3(1.0);\n";
}
if (metalnessEnabled || specularEnabled)
fragmentHasSpecularAmount = maybeAddMaterialFresnel(fragmentShader, inKey, fragmentHasSpecularAmount);
// Iterate through all lights
Q_ASSERT(m_lights.size() < INT32_MAX);
for (qint32 lightIdx = 0; lightIdx < m_lights.size(); ++lightIdx) {
QSSGRenderLight *lightNode = m_lights[lightIdx];
setupLightVariableNames(lightIdx, *lightNode);
bool isDirectional = lightNode->m_lightType == QSSGRenderLight::Type::Directional;
bool isArea = lightNode->m_lightType == QSSGRenderLight::Type::Area;
bool isShadow = enableShadowMaps && lightNode->m_castShadow;
fragmentShader.append("");
char buf[11];
snprintf(buf, 11, "%d", lightIdx);
QByteArray tempStr = "light";
tempStr.append(buf);
fragmentShader << " //Light " << buf << "\n"
" lightAttenuation = 1.0;\n";
if (isDirectional) {
if (m_lightsAsSeparateUniforms) {
fragmentShader.addUniform(m_lightDirection, "vec4");
fragmentShader.addUniform(m_lightColor, "vec4");
}
if (enableSSDO)
fragmentShader << " shadowFac = customMaterialShadow(" << m_lightDirection << ".xyz, varWorldPos);\n";
else
fragmentShader << " shadowFac = 1.0;\n";
generateShadowMapOcclusion(lightIdx, enableShadowMaps && isShadow, lightNode->m_lightType);
if ((specularEnabled || metalnessEnabled) && enableShadowMaps && isShadow)
fragmentShader << " lightAttenuation *= shadow_map_occl;\n";
fragmentShader << " global_diffuse_light.rgb += shadowFac * shadow_map_occl * diffuseReflectionBSDF(world_normal, -" << m_lightDirection << ".xyz, " << m_lightColor << ".rgb).rgb;\n";
if (specularEnabled || metalnessEnabled) {
if (m_lightsAsSeparateUniforms)
fragmentShader.addUniform(m_lightSpecularColor, "vec4");
outputSpecularEquation(material()->specularModel, fragmentShader, m_lightDirection, m_lightSpecularColor);
}
} else if (isArea) {
if (m_lightsAsSeparateUniforms) {
fragmentShader.addUniform(m_lightColor, "vec4");
fragmentShader.addUniform(m_lightPos, "vec4");
fragmentShader.addUniform(m_lightDirection, "vec4");
fragmentShader.addUniform(m_lightUp, "vec4");
fragmentShader.addUniform(m_lightRt, "vec4");
} else {
addFunction(fragmentShader, "areaLightVars");
}
addFunction(fragmentShader, "calculateDiffuseAreaOld");
vertexShader.generateWorldPosition();
generateShadowMapOcclusion(lightIdx, enableShadowMaps && isShadow, lightNode->m_lightType);
// Debug measure to make sure paraboloid sampling was projecting to the right
// location
// fragmentShader << " global_diffuse_light.rg += " << m_ShadowCoordStem << ";"
// << "\n";
m_normalizedDirection = tempStr;
m_normalizedDirection.append("_Frame");
addLocalVariable(fragmentShader, m_normalizedDirection, "mat3");
fragmentShader << " " << m_normalizedDirection << " = mat3(" << m_lightRt << ".xyz, " << m_lightUp << ".xyz, -" << m_lightDirection << ".xyz);\n";
if (enableSSDO)
fragmentShader << " shadowFac = shadow_map_occl * customMaterialShadow(" << m_lightDirection << ".xyz, varWorldPos);\n";
else
fragmentShader << " shadowFac = shadow_map_occl;\n";
if (specularEnabled || metalnessEnabled) {
vertexShader.generateViewVector();
if (m_lightsAsSeparateUniforms)
fragmentShader.addUniform(m_lightSpecularColor, "vec4");
outputSpecularAreaLighting(fragmentShader, "varWorldPos", "view_vector", m_lightSpecularColor);
}
outputDiffuseAreaLighting(fragmentShader, "varWorldPos", tempStr);
fragmentShader << " lightAttenuation *= shadowFac;\n";
addTranslucencyIrradiance(fragmentShader, translucencyImage, true);
fragmentShader << " global_diffuse_light.rgb += lightAttenuation * diffuseReflectionBSDF(world_normal, " << m_normalizedDirection << ", " << m_lightColor << ".rgb).rgb;\n";
} else {
vertexShader.generateWorldPosition();
generateShadowMapOcclusion(lightIdx, enableShadowMaps && isShadow, lightNode->m_lightType);
if (m_lightsAsSeparateUniforms) {
fragmentShader.addUniform(m_lightColor, "vec4");
fragmentShader.addUniform(m_lightPos, "vec4");
}
m_relativeDirection = tempStr;
m_relativeDirection.append("_relativeDirection");
m_normalizedDirection = m_relativeDirection;
m_normalizedDirection.append("_normalized");
m_relativeDistance = tempStr;
m_relativeDistance.append("_distance");
fragmentShader << " vec3 " << m_relativeDirection << " = varWorldPos - " << m_lightPos << ".xyz;\n"
" float " << m_relativeDistance << " = length(" << m_relativeDirection << ");\n"
" vec3 " << m_normalizedDirection << " = " << m_relativeDirection << " / " << m_relativeDistance << ";\n";
if (enableSSDO) {
fragmentShader << " shadowFac = shadow_map_occl * customMaterialShadow(" << m_normalizedDirection << ", varWorldPos);\n";
} else {
fragmentShader << " shadowFac = shadow_map_occl;\n";
}
addFunction(fragmentShader, "calculatePointLightAttenuation");
if (m_lightsAsSeparateUniforms) {
fragmentShader.addUniform(m_lightAttenuation, "vec3");
fragmentShader << " lightAttenuation = shadowFac * calculatePointLightAttenuation(vec3(" << m_lightAttenuation << ".x, " << m_lightAttenuation << ".y, " << m_lightAttenuation << ".z), " << m_relativeDistance << ");\n";
} else {
fragmentShader << " lightAttenuation = shadowFac * calculatePointLightAttenuation(vec3(" << m_lightConstantAttenuation << ", " << m_lightLinearAttenuation << ", " << m_lightQuadraticAttenuation << "), " << m_relativeDistance << ");\n";
}
addTranslucencyIrradiance(fragmentShader, translucencyImage, false);
fragmentShader << " global_diffuse_light.rgb += lightAttenuation * diffuseReflectionBSDF(world_normal, -" << m_normalizedDirection << ", " << m_lightColor << ".rgb).rgb;\n";
if (specularEnabled || metalnessEnabled) {
if (m_lightsAsSeparateUniforms)
fragmentShader.addUniform(m_lightSpecularColor, "vec4");
outputSpecularEquation(material()->specularModel, fragmentShader, m_normalizedDirection, m_lightSpecularColor);
}
}
}
// This may be confusing but the light colors are already modulated by the base
// material color.
// Thus material color is the base material color * material emissive.
// Except material_color.a *is* the actual opacity factor.
// Furthermore objectOpacity is something that may come from the vertex pipeline or
// somewhere else.
// We leave it up to the vertex pipeline to figure it out.
fragmentShader << " global_diffuse_light = vec4(global_diffuse_light.rgb * aoFactor, objectOpacity * base_color.a);\n"
" global_specular_light = vec3(global_specular_light.rgb);\n";
} else { // no lighting.
fragmentShader << " vec4 global_diffuse_light = vec4(0.0, 0.0, 0.0, objectOpacity * base_color.a);\n"
" vec3 global_specular_light = vec3(0.0, 0.0, 0.0);\n";
// We still have specular maps and such that could potentially use the fresnel variable.
fragmentHasSpecularAmount = maybeAddMaterialFresnel(fragmentShader, inKey, fragmentHasSpecularAmount);
}
if (!hasEmissiveMap)
fragmentShader << " global_diffuse_light.rgb += diffuseColor.rgb * material_diffuse.rgb;\n";
// since we already modulate our material diffuse color
// into the light color we will miss it entirely if no IBL
// or light is used
if (hasLightmaps && !(m_lights.size() || hasIblProbe))
fragmentShader << " global_diffuse_light.rgb *= diffuseColor.rgb;\n";
if (hasLighting && hasIblProbe) {
vertexShader.generateWorldNormal();
fragmentShader << " global_diffuse_light.rgb += diffuseColor.rgb * aoFactor * sampleDiffuse(tanFrame).rgb;\n";
if (specularEnabled || metalnessEnabled) {
fragmentShader.addUniform("material_specular", "vec4");
fragmentShader << " global_specular_light.rgb += specularAmount * specularColor * vec3(material_specular.rgb)* sampleGlossy(tanFrame, view_vector, roughnessAmount).rgb;\n";
}
}
if (hasImage) {
fragmentShader.append(" vec4 texture_color;");
quint32 idx = 0;
for (QSSGRenderableImage *image = m_firstImage; image; image = image->m_nextImage, ++idx) {
// Various maps are handled on a different locations
if (image->m_mapType == QSSGImageMapTypes::Bump || image->m_mapType == QSSGImageMapTypes::Normal
|| image->m_mapType == QSSGImageMapTypes::Displacement || image->m_mapType == QSSGImageMapTypes::SpecularAmountMap
|| image->m_mapType == QSSGImageMapTypes::Roughness || image->m_mapType == QSSGImageMapTypes::Translucency
|| image->m_mapType == QSSGImageMapTypes::Metalness || image->m_mapType == QSSGImageMapTypes::Occlusion
|| image->m_mapType == QSSGImageMapTypes::LightmapIndirect
|| image->m_mapType == QSSGImageMapTypes::LightmapRadiosity) {
continue;
}
QByteArray texSwizzle;
QByteArray lookupSwizzle;
if (identityImages.contains(image))
generateImageUVSampler(idx);
else
generateImageUVCoordinates(idx, *image);
generateTextureSwizzle(image->m_image.m_textureData.m_texture->textureSwizzleMode(), texSwizzle, lookupSwizzle);
fragmentShader << " texture_color" << texSwizzle << " = texture2D(" << m_imageSampler << ", " << m_imageFragCoords << ")" << lookupSwizzle << ";\n";
if (image->m_image.m_textureData.m_textureFlags.isPreMultiplied() == true)
fragmentShader << " texture_color.rgb = texture_color.a > 0.0 ? texture_color.rgb / texture_color.a : vec3(0.0);\n";
// These mapping types honestly don't make a whole ton of sense to me.
switch (image->m_mapType) {
case QSSGImageMapTypes::BaseColor:
if (material()->alphaMode == QSSGRenderDefaultMaterial::MaterialAlphaMode::Opaque) {
// Opaque ignore alpha channel of base color
fragmentShader << " global_diffuse_light *= vec4(texture_color.rgb * diffuseColor.rgb, 1.0);\n";
} else if (material()->alphaMode == QSSGRenderDefaultMaterial::MaterialAlphaMode::Mask) {
// The rendered output is either fully opaque or fully transparent depending on the alpha
// value and the specified alpha cutoff value.
fragmentShader.addUniform("alphaCutoff", "float");
fragmentShader << " if ((texture_color.a * base_color.a) < alphaCutoff) {\n"
" fragOutput = vec4(0);\n"
" return;\n"
" }\n"
" global_diffuse_light *= vec4(texture_color.rgb * diffuseColor.rgb, 1.0);\n";
} else {
// Blend && Default
// Use the alpha channel of base color
fragmentShader << " global_diffuse_light *= vec4(texture_color.rgb * diffuseColor.rgb, texture_color.a * base_color.a);\n";
}
fragmentShader.addInclude("luminance.glsllib");
fragmentShader << " float lum = luminance(texture_color.rgb * base_color.rgb);\n"
" global_specular_light.rgb *= (lum > 0.0) ? (texture_color.rgb * base_color.rgb) / lum : vec3(1.0);\n";
break;
case QSSGImageMapTypes::Diffuse: // assume images are premultiplied.
Q_FALLTHROUGH();
case QSSGImageMapTypes::LightmapShadow:
// We use image offsets.z to switch between incoming premultiplied textures or
// not premultiplied textures.
// If Z is 1, then we assume the incoming texture is already premultiplied, else
// we just read the rgb value.
fragmentShader.append(" global_diffuse_light *= texture_color;");
break;
case QSSGImageMapTypes::Specular:
fragmentShader.addUniform("material_specular", "vec4");
if (fragmentHasSpecularAmount) {
fragmentShader.append(" global_specular_light.rgb += specularAmount * specularColor * texture_color.rgb * material_specular.rgb;");
} else {
fragmentShader.append(" global_specular_light.rgb += texture_color.rgb * material_specular.rgb;");
}
fragmentShader.append(" global_diffuse_light.a *= texture_color.a;");
break;
case QSSGImageMapTypes::Opacity:
fragmentShader.append(" global_diffuse_light.a *= texture_color.a;");
break;
case QSSGImageMapTypes::Emissive:
fragmentShader.append(" global_emission *= texture_color.rgb * texture_color.a;");
break;
default:
Q_ASSERT(false); // fallthrough intentional
}
}
}
// Occlusion Map
if (occlusionImage) {
fragmentShader.addUniform("occlusionAmount", "float");
if (identityImages.contains(occlusionImage))
generateImageUVSampler(occlusionImageIdx);
else
generateImageUVCoordinates(occlusionImageIdx, *occlusionImage);
fragmentShader << " float ao = texture2D(" << m_imageSampler << ", " << m_imageFragCoords << ").r;\n";
fragmentShader << " global_diffuse_light.rgb = mix(global_diffuse_light.rgb, global_diffuse_light.rgb * ao, occlusionAmount);\n";
}
if (hasEmissiveMap)
fragmentShader.append(" global_diffuse_light.rgb += global_emission.rgb;");
// Ensure the rgb colors are in range.
fragmentShader.append(" fragOutput = vec4(clamp(vertColor * global_diffuse_light.rgb + global_specular_light.rgb, 0.0, 65519.0), global_diffuse_light.a);");
if (vertexGenerator().hasActiveWireframe()) {
fragmentShader << " 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
" fragOutput = mix(vec4(0.0, 1.0, 0.0, 1.0), fragOutput, mixVal);\n";
}
}
QSSGRef<QSSGRenderShaderProgram> generateMaterialShader(const QByteArray &inShaderPrefix)
{
// 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;
QSSGShaderDefaultMaterialKey theKey(key());
theKey.toString(generatedShaderString, m_defaultMaterialShaderKeyProperties);
m_lightsAsSeparateUniforms = !m_renderContext->renderContext()->supportsConstantBuffer();
generateVertexShader();
generateFragmentShader(theKey);
vertexGenerator().endVertexGeneration(false);
vertexGenerator().endFragmentGeneration(false);
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 &inVertexPipelineName,
const QByteArray &) override
{
Q_ASSERT(inMaterial.type == QSSGRenderGraphObject::Type::DefaultMaterial || inMaterial.type == QSSGRenderGraphObject::Type::PrincipledMaterial);
m_currentMaterial = static_cast<const QSSGRenderDefaultMaterial *>(&inMaterial);
m_currentKey = &inShaderDescription;
m_currentPipeline = static_cast<QSSGDefaultMaterialVertexPipelineInterface *>(&inVertexPipeline);
m_currentFeatureSet = inFeatureSet;
m_lights = inLights;
m_firstImage = inFirstImage;
m_hasTransparency = inHasTransparency;
return generateMaterialShader(inVertexPipelineName);
}
const 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,
m_renderContext->renderContext())));
return inserter.value();
}
void setGlobalProperties(const QSSGRef<QSSGRenderShaderProgram> &inProgram,
const QSSGRenderLayer & /*inLayer*/,
QSSGRenderCamera &inCamera,
const QVector3D &inCameraDirection,
const QVector<QSSGRenderLight *> &inLights,
const QVector<QVector3D> &inLightDirections,
const QSSGRef<QSSGRenderShadowMap> &inShadowMapManager,
bool receivesShadows = true)
{
const QSSGRef<QSSGShaderGeneratorGeneratedShader> &shader(getShaderForProgram(inProgram));
m_renderContext->renderContext()->setActiveShader(inProgram);
m_shadowMapManager = inShadowMapManager;
QSSGRenderCamera &theCamera(inCamera);
shader->m_cameraPosition.set(theCamera.getGlobalPos());
shader->m_cameraDirection.set(inCameraDirection);
QMatrix4x4 viewProj;
if (shader->m_viewProj.isValid()) {
theCamera.calculateViewProjectionMatrix(viewProj);
shader->m_viewProj.set(viewProj);
}
if (shader->m_viewMatrix.isValid()) {
viewProj = theCamera.globalTransform.inverted();
shader->m_viewMatrix.set(viewProj);
}
// update the constant buffer
shader->m_aoShadowParams.set();
// We can't cache light properties because they can change per object.
QVector3D theLightAmbientTotal = QVector3D(0, 0, 0);
size_t numShaderLights = shader->m_lights.size();
size_t numShadowLights = shader->m_shadowMaps.size();
for (quint32 lightIdx = 0, shadowMapIdx = 0, lightEnd = inLights.size(); lightIdx < lightEnd && lightIdx < QSSG_MAX_NUM_LIGHTS;
++lightIdx) {
QSSGRenderLight *theLight(inLights[lightIdx]);
if (lightIdx >= numShaderLights) {
shader->m_lights.push_back(QSSGShaderLightProperties());
++numShaderLights;
}
if (shadowMapIdx >= numShadowLights && numShadowLights < QSSG_MAX_NUM_SHADOWS && receivesShadows) {
if (theLight->m_scope == nullptr && theLight->m_castShadow) {
// PKC TODO : Fix multiple shadow issues.
// Need to know when the list of lights changes order, and clear shadow maps
// when that happens.
setupShadowMapVariableNames(lightIdx);
shader->m_shadowMaps.push_back(
QSSGShadowMapProperties(m_shadowMapStem, m_shadowCubeStem, m_shadowMatrixStem, m_shadowControlStem, inProgram));
}
}
Q_ASSERT(lightIdx < numShaderLights);
QSSGShaderLightProperties &theLightProperties(shader->m_lights[lightIdx]);
float brightness = aux::translateBrightness(theLight->m_brightness);
// setup light data
theLightProperties.lightColor = theLight->m_diffuseColor * brightness;
theLightProperties.lightData.specular = QVector4D(theLight->m_specularColor * brightness, 1.0);
theLightProperties.lightData.direction = QVector4D(inLightDirections[lightIdx], 1.0);
// TODO : This does potentially mean that we can create more shadow map entries than
// we can actually use at once.
if ((theLight->m_scope == nullptr) && (theLight->m_castShadow && receivesShadows && inShadowMapManager)) {
QSSGShadowMapProperties &theShadowMapProperties(shader->m_shadowMaps[shadowMapIdx++]);
QSSGShadowMapEntry *pEntry = inShadowMapManager->getShadowMapEntry(lightIdx);
if (pEntry) {
// add fixed scale bias matrix
QMatrix4x4 bias = { 0.5, 0.0, 0.0, 0.5,
0.0, 0.5, 0.0, 0.5,
0.0, 0.0, 0.5, 0.5,
0.0, 0.0, 0.0, 1.0 };
if (theLight->m_lightType != QSSGRenderLight::Type::Directional) {
theShadowMapProperties.m_shadowCubeTexture.set(pEntry->m_depthCube.data());
theShadowMapProperties.m_shadowmapMatrix.set(pEntry->m_lightView);
} else {
theShadowMapProperties.m_shadowmapTexture.set(pEntry->m_depthMap.data());
theShadowMapProperties.m_shadowmapMatrix.set(bias * pEntry->m_lightVP);
}
theShadowMapProperties.m_shadowmapSettings.set(
QVector4D(theLight->m_shadowBias, theLight->m_shadowFactor, theLight->m_shadowMapFar, 0.0f));
} else {
// if we have a light casting shadow we should find an entry
Q_ASSERT(false);
}
}
if (theLight->m_lightType == QSSGRenderLight::Type::Point) {
theLightProperties.lightData.position = QVector4D(theLight->getGlobalPos(), 1.0);
theLightProperties.lightData.constantAttenuation = aux::translateConstantAttenuation(theLight->m_constantFade);
theLightProperties.lightData.linearAttenuation = aux::translateLinearAttenuation(theLight->m_linearFade);
theLightProperties.lightData.quadraticAttenuation = aux::translateQuadraticAttenuation(theLight->m_quadraticFade);
} else if (theLight->m_lightType == QSSGRenderLight::Type::Area) {
theLightProperties.lightData.position = QVector4D(theLight->getGlobalPos(), 1.0);
QVector3D upDir = mat33::transform(mat44::getUpper3x3(theLight->globalTransform), QVector3D(0, 1, 0));
QVector3D rtDir = mat33::transform(mat44::getUpper3x3(theLight->globalTransform), QVector3D(1, 0, 0));
theLightProperties.lightData.up = QVector4D(upDir, theLight->m_areaHeight);
theLightProperties.lightData.right = QVector4D(rtDir, theLight->m_areaWidth);
}
theLightAmbientTotal += theLight->m_ambientColor;
}
shader->m_lightAmbientTotal = theLightAmbientTotal;
}
// Also sets the blend function on the render context.
void setMaterialProperties(const QSSGRef<QSSGRenderShaderProgram> &inProgram,
const QSSGRenderDefaultMaterial &inMaterial,
const QVector2D &inCameraVec,
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<QSSGRenderContext> &context = m_renderContext->renderContext();
const QSSGRef<QSSGShaderGeneratorGeneratedShader> &shader = getShaderForProgram(inProgram);
shader->m_mvp.set(inModelViewProjection);
shader->m_normalMatrix.set(inNormalMatrix);
shader->m_globalTransform.set(inGlobalTransform);
shader->m_depthTexture.set(inDepthTexture.data());
shader->m_aoTexture.set(inSSaoTexture.data());
QSSGRenderImage *theLightProbe = inLightProbe;
QSSGRenderImage *theLightProbe2 = inLightProbe2;
// If the material has its own IBL Override, we should use that image instead.
const bool hasIblProbe = inMaterial.iblProbe != nullptr;
const bool useMaterialIbl = hasIblProbe ? (inMaterial.iblProbe->m_textureData.m_texture != nullptr) : false;
if (useMaterialIbl)
theLightProbe = inMaterial.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.
QVector4D offsets(dataPtr[12],
dataPtr[13],
theLightProbe->m_textureData.m_textureFlags.isPreMultiplied() ? 1.0f : 0.0f,
(float)theLightProbe->m_textureData.m_texture->numMipmaps());
// Grab just the upper 2x2 rotation matrix from the larger matrix.
QVector4D rotations(dataPtr[0], dataPtr[4], dataPtr[1], dataPtr[5]);
shader->m_lightProbeRot.set(rotations);
shader->m_lightProbeOfs.set(offsets);
if ((!inMaterial.iblProbe) && (inProbeFOV < 180.f)) {
shader->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);
shader->m_lightProbe2.set(theLightProbe2->m_textureData.m_texture.data());
shader->m_lightProbe2Props.set(QVector4D(inProbe2Window, inProbe2Pos, inProbe2Fade, 1.0f));
const QMatrix4x4 &xform2 = theLightProbe2->m_textureTransform;
const float *dataPtr(xform2.constData());
shader->m_lightProbeProps.set(QVector4D(dataPtr[12], dataPtr[13], inProbeHorizon, inProbeBright * 0.01f));
} else {
shader->m_lightProbe2Props.set(QVector4D(0.0f, 0.0f, 0.0f, 0.0f));
shader->m_lightProbeProps.set(QVector4D(0.0f, 0.0f, inProbeHorizon, inProbeBright * 0.01f));
}
QSSGRef<QSSGRenderTexture2D> textureImage = theLightProbe->m_textureData.m_texture;
shader->m_lightProbe.set(textureImage.data());
shader->m_lightProbeSize.set(
QVector2D(textureImage->textureDetails().width, textureImage->textureDetails().height));
} else {
shader->m_lightProbeProps.set(QVector4D(0.0f, 0.0f, -1.0f, 0.0f));
shader->m_lightProbe2Props.set(QVector4D(0.0f, 0.0f, 0.0f, 0.0f));
}
} else {
shader->m_lightProbeProps.set(QVector4D(0.0f, 0.0f, -1.0f, 0.0f));
shader->m_lightProbe2Props.set(QVector4D(0.0f, 0.0f, 0.0f, 0.0f));
}
const auto &color = inMaterial.color;
shader->m_materialDiffuse.set(inMaterial.emissiveColor);
const auto qMix = [](float x, float y, float a) {
return (x * (1.0f - a) + (y * a));
};
const auto qMix3 = [&qMix](const QVector3D &x, const QVector3D &y, float a) {
return QVector3D{qMix(x.x(), y.x(), a), qMix(x.y(), y.y(), a), qMix(x.z(), y.z(), a)};
};
const auto &specularTint = (inMaterial.type == QSSGRenderGraphObject::Type::PrincipledMaterial) ? qMix3(QVector3D(1.0f, 1.0f, 1.0f), color.toVector3D(), inMaterial.specularTint.x())
: inMaterial.specularTint;
shader->m_baseColor.set(color);
shader->m_materialSpecular.set(QVector4D(specularTint, inMaterial.ior));
shader->m_cameraProperties.set(inCameraVec);
shader->m_fresnelPower.set(inMaterial.fresnelPower);
const auto diffuse = color.toVector3D() * (1.0f - inMaterial.metalnessAmount);
const bool hasLighting = inMaterial.lighting != QSSGRenderDefaultMaterial::MaterialLighting::NoLighting;
if (hasLighting) {
if (context->supportsConstantBuffer()) {
const QSSGRef<QSSGRenderConstantBuffer> &pLightCb = getLightConstantBuffer(shader->m_lights.size());
// if we have lights we need a light buffer
Q_ASSERT(shader->m_lights.size() == 0 || pLightCb);
for (qint32 idx = 0, end = shader->m_lights.size(); idx < end && pLightCb; ++idx) {
auto &lightProp = shader->m_lights[idx];
lightProp.lightData.diffuse = QVector4D(lightProp.lightColor * diffuse, 1.0);
// this is our final change update memory
pLightCb->updateRaw(quint32(idx) * sizeof(QSSGLightSourceShader) + (4 * sizeof(qint32)), toByteView(lightProp.lightData));
}
// update light buffer to hardware
if (pLightCb) {
qint32 cgLights = shader->m_lights.size();
pLightCb->updateRaw(0, toByteView(cgLights));
shader->m_lightsBuffer.set();
}
} else {
QSSGLightConstantProperties<QSSGShaderGeneratorGeneratedShader> *pLightConstants = getLightConstantProperties(shader);
// if we have lights we need a light buffer
Q_ASSERT(shader->m_lights.size() == 0 || pLightConstants);
for (qint32 idx = 0, end = shader->m_lights.size(); idx < end && pLightConstants; ++idx) {
auto &lightProp = shader->m_lights[idx];
lightProp.lightData.diffuse = QVector4D(lightProp.lightColor * diffuse, 1.0);
}
// update light buffer to hardware
if (pLightConstants)
pLightConstants->updateLights(shader);
}
}
shader->m_materialDiffuseLightAmbientTotal.set(shader->m_lightAmbientTotal * diffuse);
shader->m_materialProperties.set(QVector4D(inMaterial.specularAmount, inMaterial.specularRoughness, inMaterial.metalnessAmount, inOpacity));
shader->m_bumpAmount.set(inMaterial.bumpAmount);
shader->m_displaceAmount.set(inMaterial.displaceAmount);
shader->m_translucentFalloff.set(inMaterial.translucentFalloff);
shader->m_diffuseLightWrap.set(inMaterial.diffuseLightWrap);
shader->m_occlusionAmount.set(inMaterial.occlusionAmount);
shader->m_alphaCutoff.set(inMaterial.alphaCutoff);
quint32 imageIdx = 0;
for (QSSGRenderableImage *theImage = inFirstImage; theImage; theImage = theImage->m_nextImage, ++imageIdx)
setImageShaderVariables(shader, *theImage, imageIdx);
QSSGRenderBlendFunctionArgument blendFunc;
QSSGRenderBlendEquationArgument blendEqua(QSSGRenderBlendEquation::Add, QSSGRenderBlendEquation::Add);
// The blend function goes:
// src op
// dst op
// src alpha op
// dst alpha op
// All of our shaders produce non-premultiplied values.
switch (inMaterial.blendMode) {
case QSSGRenderDefaultMaterial::MaterialBlendMode::Screen:
blendFunc = QSSGRenderBlendFunctionArgument(QSSGRenderSrcBlendFunc::SrcAlpha,
QSSGRenderDstBlendFunc::One,
QSSGRenderSrcBlendFunc::One,
QSSGRenderDstBlendFunc::One);
break;
case QSSGRenderDefaultMaterial::MaterialBlendMode::Multiply:
blendFunc = QSSGRenderBlendFunctionArgument(QSSGRenderSrcBlendFunc::DstColor,
QSSGRenderDstBlendFunc::Zero,
QSSGRenderSrcBlendFunc::One,
QSSGRenderDstBlendFunc::One);
break;
case QSSGRenderDefaultMaterial::MaterialBlendMode::Overlay:
// SW fallback is not using blend equation
// note blend func is not used here anymore
if (context->supportsAdvancedBlendHW() || context->supportsAdvancedBlendHwKHR())
blendEqua = QSSGRenderBlendEquationArgument(QSSGRenderBlendEquation::Overlay, QSSGRenderBlendEquation::Overlay);
break;
case QSSGRenderDefaultMaterial::MaterialBlendMode::ColorBurn:
// SW fallback is not using blend equation
// note blend func is not used here anymore
if (context->supportsAdvancedBlendHW() || context->supportsAdvancedBlendHwKHR())
blendEqua = QSSGRenderBlendEquationArgument(QSSGRenderBlendEquation::ColorBurn,
QSSGRenderBlendEquation::ColorBurn);
break;
case QSSGRenderDefaultMaterial::MaterialBlendMode::ColorDodge:
// SW fallback is not using blend equation
// note blend func is not used here anymore
if (context->supportsAdvancedBlendHW() || context->supportsAdvancedBlendHwKHR())
blendEqua = QSSGRenderBlendEquationArgument(QSSGRenderBlendEquation::ColorDodge,
QSSGRenderBlendEquation::ColorDodge);
break;
default:
blendFunc = QSSGRenderBlendFunctionArgument(QSSGRenderSrcBlendFunc::SrcAlpha,
QSSGRenderDstBlendFunc::OneMinusSrcAlpha,
QSSGRenderSrcBlendFunc::One,
QSSGRenderDstBlendFunc::OneMinusSrcAlpha);
break;
}
context->setBlendFunction(blendFunc);
context->setBlendEquation(blendEqua);
}
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 QSSGRenderDefaultMaterial &theMaterial(static_cast<const QSSGRenderDefaultMaterial &>(inMaterial));
Q_ASSERT(inMaterial.type == QSSGRenderGraphObject::Type::DefaultMaterial || inMaterial.type == QSSGRenderGraphObject::Type::PrincipledMaterial);
setGlobalProperties(inProgram,
inRenderProperties.layer,
inRenderProperties.camera,
inRenderProperties.cameraDirection,
inRenderProperties.lights,
inRenderProperties.lightDirections,
inRenderProperties.shadowMapManager,
receivesShadows);
setMaterialProperties(inProgram,
theMaterial,
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);
}
QSSGLightConstantProperties<QSSGShaderGeneratorGeneratedShader> *getLightConstantProperties(
const QSSGRef<QSSGShaderGeneratorGeneratedShader> &shader)
{
if (!shader->m_lightConstantProperties
|| int(shader->m_lights.size()) > shader->m_lightConstantProperties->m_constants.size()) {
if (shader->m_lightConstantProperties)
delete shader->m_lightConstantProperties;
shader->m_lightConstantProperties = new QSSGLightConstantProperties<QSSGShaderGeneratorGeneratedShader>(shader.data(), m_lightsAsSeparateUniforms);
}
return shader->m_lightConstantProperties;
}
};
}
QSSGRef<QSSGDefaultMaterialShaderGeneratorInterface> QSSGDefaultMaterialShaderGeneratorInterface::createDefaultMaterialShaderGenerator(
QSSGRenderContextInterface *inRc)
{
return QSSGRef<QSSGDefaultMaterialShaderGeneratorInterface>(new QSSGShaderGenerator(inRc));
}
QSSGDefaultMaterialVertexPipelineInterface::~QSSGDefaultMaterialVertexPipelineInterface() = default;
QT_END_NAMESPACE