diff options
Diffstat (limited to 'src/Runtime/Source/runtimerender/Qt3DSRenderDefaultMaterialShaderGenerator.cpp')
-rw-r--r-- | src/Runtime/Source/runtimerender/Qt3DSRenderDefaultMaterialShaderGenerator.cpp | 1930 |
1 files changed, 1930 insertions, 0 deletions
diff --git a/src/Runtime/Source/runtimerender/Qt3DSRenderDefaultMaterialShaderGenerator.cpp b/src/Runtime/Source/runtimerender/Qt3DSRenderDefaultMaterialShaderGenerator.cpp new file mode 100644 index 00000000..4dceaca8 --- /dev/null +++ b/src/Runtime/Source/runtimerender/Qt3DSRenderDefaultMaterialShaderGenerator.cpp @@ -0,0 +1,1930 @@ +/**************************************************************************** +** +** Copyright (C) 2008-2012 NVIDIA Corporation. +** Copyright (C) 2017 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt 3D Studio. +** +** $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 "Qt3DSRenderDefaultMaterialShaderGenerator.h" +#include "foundation/Qt3DSAtomic.h" +#include "Qt3DSRenderContextCore.h" +#include "Qt3DSRenderShaderCodeGeneratorV2.h" +#include "Qt3DSRenderableImage.h" +#include "Qt3DSRenderImage.h" +#include "render/Qt3DSRenderContext.h" +#include "Qt3DSRenderLight.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "Qt3DSRenderCamera.h" +#include "Qt3DSRenderShadowMap.h" +#include "Qt3DSRenderCustomMaterial.h" +#include "Qt3DSRenderDynamicObjectSystem.h" +#include "render/Qt3DSRenderShaderProgram.h" +#include "Qt3DSRenderLightConstantProperties.h" + +using namespace qt3ds::render; +using qt3ds::render::NVRenderCachedShaderProperty; +using qt3ds::render::NVRenderCachedShaderBuffer; + +namespace { + +const QT3DSF32 MINATTENUATION = 0; +const QT3DSF32 MAXATTENUATION = 1000; + +QT3DSF32 ClampFloat(QT3DSF32 value, QT3DSF32 min, QT3DSF32 max) +{ + return value < min ? min : ((value > max) ? max : value); +} + +QT3DSF32 TranslateConstantAttenuation(QT3DSF32 attenuation) +{ + return attenuation * .01f; +} + +QT3DSF32 TranslateLinearAttenuation(QT3DSF32 attenuation) +{ + attenuation = ClampFloat(attenuation, MINATTENUATION, MAXATTENUATION); + return attenuation * 0.0001f; +} + +QT3DSF32 TranslateQuadraticAttenuation(QT3DSF32 attenuation) +{ + attenuation = ClampFloat(attenuation, MINATTENUATION, MAXATTENUATION); + return attenuation * 0.0000001f; +} + +/** + * Cached texture property lookups, used one per texture so a shader generator for N + * textures will have an array of N of these lookup objects. + */ +struct SShaderTextureProperties +{ + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_Sampler; + NVRenderCachedShaderProperty<QT3DSVec3> m_Offsets; + NVRenderCachedShaderProperty<QT3DSVec4> m_Rotations; + NVRenderCachedShaderProperty<QT3DSVec2> m_Size; + SShaderTextureProperties(const char *sampName, const char *offName, const char *rotName, + const char *sizeName, + NVRenderShaderProgram &inShader) + : m_Sampler(sampName, inShader) + , m_Offsets(offName, inShader) + , m_Rotations(rotName, inShader) + , m_Size(sizeName, inShader) + { + } + SShaderTextureProperties() {} +}; + +/** + * 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 SShaderLightProperties +{ + // Color of the light + QT3DSVec3 m_LightColor; + SLightSourceShader m_LightData; + + SShaderLightProperties() {} +}; + +struct SShadowMapProperties +{ + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_ShadowmapTexture; ///< shadow texture + NVRenderCachedShaderProperty<NVRenderTextureCube *> m_ShadowCubeTexture; ///< shadow cubemap + NVRenderCachedShaderProperty<QT3DSMat44> + m_ShadowmapMatrix; ///< world to ligh space transform matrix + NVRenderCachedShaderProperty<QT3DSVec4> m_ShadowmapSettings; ///< shadow rendering settings + + SShadowMapProperties() {} + SShadowMapProperties(const char *shadowmapTextureName, const char *shadowcubeTextureName, + const char *shadowmapMatrixName, const char *shadowmapSettingsName, + NVRenderShaderProgram &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 SShaderGeneratorGeneratedShader +{ + NVAllocatorCallback &m_Allocator; + NVRenderShaderProgram &m_Shader; + // Specific properties we know the shader has to have. + NVRenderCachedShaderProperty<QT3DSMat44> m_MVP; + NVRenderCachedShaderProperty<QT3DSMat33> m_NormalMatrix; + NVRenderCachedShaderProperty<QT3DSMat44> m_GlobalTransform; + NVRenderCachedShaderProperty<QT3DSMat44> m_ViewProj; + NVRenderCachedShaderProperty<QT3DSMat44> m_ViewMatrix; + NVRenderCachedShaderProperty<QT3DSVec4> m_MaterialDiffuse; + NVRenderCachedShaderProperty<QT3DSVec4> m_MaterialProperties; + // tint, ior + NVRenderCachedShaderProperty<QT3DSVec4> m_MaterialSpecular; + NVRenderCachedShaderProperty<QT3DSF32> m_BumpAmount; + NVRenderCachedShaderProperty<QT3DSF32> m_DisplaceAmount; + NVRenderCachedShaderProperty<QT3DSF32> m_TranslucentFalloff; + NVRenderCachedShaderProperty<QT3DSF32> m_DiffuseLightWrap; + NVRenderCachedShaderProperty<QT3DSF32> m_FresnelPower; + NVRenderCachedShaderProperty<QT3DSVec3> m_DiffuseColor; + NVRenderCachedShaderProperty<QT3DSVec3> m_CameraPosition; + NVRenderCachedShaderProperty<QT3DSVec3> m_CameraDirection; + QT3DSVec3 m_LightAmbientTotal; + NVRenderCachedShaderProperty<QT3DSVec3> m_MaterialDiffuseLightAmbientTotal; + NVRenderCachedShaderProperty<QT3DSVec2> m_CameraProperties; + + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_DepthTexture; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_AOTexture; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_LightProbe; + NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbeProps; + NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbeOpts; + NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbeRot; + NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbeOfs; + NVRenderCachedShaderProperty<QT3DSVec2> m_LightProbeSize; + NVRenderCachedShaderProperty<NVRenderTexture2D *> m_LightProbe2; + NVRenderCachedShaderProperty<QT3DSVec4> m_LightProbe2Props; + NVRenderCachedShaderProperty<QT3DSVec2> m_LightProbe2Size; + + NVRenderCachedShaderBuffer<qt3ds::render::NVRenderShaderConstantBuffer *> m_AoShadowParams; + NVRenderCachedShaderBuffer<qt3ds::render::NVRenderShaderConstantBuffer *> m_LightsBuffer; + + SLightConstantProperties<SShaderGeneratorGeneratedShader> *m_lightConstantProperties; + + // Cache the image property name lookups + nvvector<SShaderTextureProperties> m_Images; + nvvector<SShaderLightProperties> m_Lights; + // Cache shadow map properties + nvvector<SShadowMapProperties> m_ShadowMaps; + + QT3DSI32 m_RefCount; + + SShaderGeneratorGeneratedShader(NVRenderShaderProgram &inShader, NVRenderContext &inContext) + : m_Allocator(inContext.GetAllocator()) + , m_Shader(inShader) + , m_MVP("model_view_projection", inShader) + , m_NormalMatrix("normal_matrix", inShader) + , m_GlobalTransform("model_matrix", inShader) + , m_ViewProj("view_projection_matrix", inShader) + , m_ViewMatrix("view_matrix", 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_DiffuseColor("diffuse_color", inShader) + , m_CameraPosition("camera_position", inShader) + , m_CameraDirection("camera_direction", inShader) + , m_MaterialDiffuseLightAmbientTotal("light_ambient_total", inShader) + , m_CameraProperties("camera_properties", inShader) + , m_DepthTexture("depth_sampler", inShader) + , m_AOTexture("ao_sampler", inShader) + , m_LightProbe("light_probe", inShader) + , m_LightProbeProps("light_probe_props", inShader) + , m_LightProbeOpts("light_probe_opts", inShader) + , m_LightProbeRot("light_probe_rotation", inShader) + , m_LightProbeOfs("light_probe_offset", inShader) + , m_LightProbeSize("light_probe_size", inShader) + , m_LightProbe2("light_probe2", inShader) + , m_LightProbe2Props("light_probe2_props", inShader) + , m_LightProbe2Size("light_probe2_size", inShader) + , m_AoShadowParams("cbAoShadow", inShader) + , m_LightsBuffer("cbBufferLights", inShader) + , m_lightConstantProperties(NULL) + , m_Images(inContext.GetAllocator(), "SShaderGeneratorGeneratedShader::m_Images") + , m_Lights(inContext.GetAllocator(), "SShaderGeneratorGeneratedShader::m_Lights") + , m_ShadowMaps(inContext.GetAllocator(), "SShaderGeneratorGeneratedShader::m_ShadowMaps") + , m_RefCount(0) + { + m_Shader.addRef(); + } + ~SShaderGeneratorGeneratedShader() + { + if (m_lightConstantProperties) + delete m_lightConstantProperties; + m_Shader.release(); + } + + void addRef() { ++m_RefCount; } + void release() + { + --m_RefCount; + if (m_RefCount <= 0) { + NVAllocatorCallback &alloc(m_Allocator); + NVDelete(alloc, this); + } + } +}; + + +#ifndef EA_PLATFORM_WINDOWS +#define _snprintf snprintf +#endif + +struct SShaderGenerator : public IDefaultMaterialShaderGenerator +{ + typedef CRenderString TStrType; + typedef nvhash_map<NVRenderShaderProgram *, NVScopedRefCounted<SShaderGeneratorGeneratedShader>> + TProgramToShaderMap; + typedef qt3ds::foundation::nvhash_map<CRegisteredString, + NVScopedRefCounted<qt3ds::render::NVRenderConstantBuffer>> + TStrConstanBufMap; + + IQt3DSRenderContext &m_RenderContext; + IShaderProgramGenerator &m_ProgramGenerator; + + const SDefaultMaterial *m_CurrentMaterial; + SShaderDefaultMaterialKey *m_CurrentKey; + Qt3DSShadowMap *m_ShadowMapManager; + IDefaultMaterialVertexPipeline *m_CurrentPipeline; + TShaderFeatureSet m_CurrentFeatureSet; + NVDataRef<SLight *> m_Lights; + SRenderableImage *m_FirstImage; + bool m_HasTransparency; + bool m_LightsAsSeparateUniforms; + + TStrType m_ImageStem; + TStrType m_ImageSampler; + TStrType m_ImageOffsets; + TStrType m_ImageRotations; + TStrType m_ImageFragCoords; + TStrType m_ImageTemp; + TStrType m_ImageSamplerSize; + + TStrType m_TexCoordTemp; + + TStrType m_LightStem; + TStrType m_LightColor; + TStrType m_LightSpecularColor; + TStrType m_LightAttenuation; + TStrType m_LightConstantAttenuation; + TStrType m_LightLinearAttenuation; + TStrType m_LightQuadraticAttenuation; + TStrType m_NormalizedDirection; + TStrType m_LightDirection; + TStrType m_LightPos; + TStrType m_LightUp; + TStrType m_LightRt; + TStrType m_RelativeDistance; + TStrType m_RelativeDirection; + + TStrType m_ShadowMapStem; + TStrType m_ShadowCubeStem; + TStrType m_ShadowMatrixStem; + TStrType m_ShadowCoordStem; + TStrType m_ShadowControlStem; + + TStrType m_TempStr; + + eastl::string m_GeneratedShaderString; + + SShaderDefaultMaterialKeyProperties m_DefaultMaterialShaderKeyProperties; + TProgramToShaderMap m_ProgramToShaderMap; + + TStrConstanBufMap m_ConstantBuffers; ///< store all constants buffers + + QT3DSI32 m_RefCount; + + SShaderGenerator(IQt3DSRenderContext &inRc) + : m_RenderContext(inRc) + , m_ProgramGenerator(m_RenderContext.GetShaderProgramGenerator()) + , m_CurrentMaterial(NULL) + , m_CurrentKey(NULL) + , m_ShadowMapManager(NULL) + , m_CurrentPipeline(NULL) + , m_FirstImage(NULL) + , m_LightsAsSeparateUniforms(false) + , m_ProgramToShaderMap(inRc.GetAllocator(), "m_ProgramToShaderMap") + , m_ConstantBuffers(inRc.GetAllocator(), "m_ConstantBuffers") + , m_RefCount(0) + { + } + + void addRef() override { atomicIncrement(&m_RefCount); } + void release() override + { + atomicDecrement(&m_RefCount); + if (m_RefCount <= 0) { + m_ConstantBuffers.clear(); + NVDelete(m_RenderContext.GetAllocator(), this); + } + } + IShaderProgramGenerator &ProgramGenerator() { return m_ProgramGenerator; } + IDefaultMaterialVertexPipeline &VertexGenerator() { return *m_CurrentPipeline; } + IShaderStageGenerator &FragmentGenerator() + { + return *m_ProgramGenerator.GetStage(ShaderGeneratorStages::Fragment); + } + SShaderDefaultMaterialKey &Key() { return *m_CurrentKey; } + const SDefaultMaterial &Material() { return *m_CurrentMaterial; } + TShaderFeatureSet FeatureSet() { return m_CurrentFeatureSet; } + bool HasTransparency() { return m_HasTransparency; } + + void addFunction(IShaderStageGenerator &generator, QString functionName) + { + generator.AddFunction(functionName); + } + + void SetupImageVariableNames(size_t imageIdx) + { + m_ImageStem = "image"; + char buf[16]; + _snprintf(buf, 16, "%d", int(imageIdx)); + m_ImageStem.append(buf); + m_ImageStem.append("_"); + + m_ImageSampler = m_ImageStem; + m_ImageSampler.append("sampler"); + m_ImageOffsets = m_ImageStem; + m_ImageOffsets.append("offsets"); + m_ImageRotations = m_ImageStem; + m_ImageRotations.append("rotations"); + m_ImageFragCoords = m_ImageStem; + m_ImageFragCoords.append("uv_coords"); + m_ImageSamplerSize = m_ImageStem; + m_ImageSamplerSize.append("size"); + } + + void SetupTexCoordVariableName(size_t uvSet) + { + m_TexCoordTemp = "varTexCoord"; + char buf[16]; + _snprintf(buf, 16, "%d", int(uvSet)); + m_TexCoordTemp.append(buf); + } + + SImageVariableNames GetImageVariableNames(QT3DSU32 inIdx) override + { + SetupImageVariableNames(inIdx); + SImageVariableNames retval; + retval.m_ImageSampler = m_ImageSampler.c_str(); + retval.m_ImageFragCoords = m_ImageFragCoords.c_str(); + return retval; + } + + void AddLocalVariable(IShaderStageGenerator &inGenerator, const char8_t *inName, + const char8_t *inType) + { + inGenerator << "\t" << inType << " " << inName << ";" << Endl; + } + + void AddLocalVariable(IShaderStageGenerator &inGenerator, const TStrType &inName, + const char8_t *inType) + { + AddLocalVariable(inGenerator, inName.c_str(), inType); + } + + void GenerateImageUVCoordinates(IShaderStageGenerator &inVertexPipeline, QT3DSU32 idx, QT3DSU32 uvSet, + SRenderableImage &image) override + { + IDefaultMaterialVertexPipeline &vertexShader( + static_cast<IDefaultMaterialVertexPipeline &>(inVertexPipeline)); + IShaderStageGenerator &fragmentShader(FragmentGenerator()); + SetupImageVariableNames(idx); + SetupTexCoordVariableName(uvSet); + fragmentShader.AddUniform(m_ImageSampler, "sampler2D"); + vertexShader.AddUniform(m_ImageOffsets, "vec3"); + fragmentShader.AddUniform(m_ImageOffsets, "vec3"); + vertexShader.AddUniform(m_ImageRotations, "vec4"); + fragmentShader.AddUniform(m_ImageRotations, "vec4"); + + if (image.m_Image.m_MappingMode == ImageMappingModes::Normal) { + vertexShader << "\tuTransform = vec3( " << m_ImageRotations << ".x, " + << m_ImageRotations << ".y, " << m_ImageOffsets << ".x );" << Endl; + vertexShader << "\tvTransform = vec3( " << m_ImageRotations << ".z, " + << m_ImageRotations << ".w, " << m_ImageOffsets << ".y );" << Endl; + vertexShader.AddOutgoing(m_ImageFragCoords, "vec2"); + addFunction(vertexShader, "getTransformedUVCoords"); + vertexShader.GenerateUVCoords(uvSet); + m_ImageTemp = m_ImageFragCoords; + m_ImageTemp.append("temp"); + vertexShader << "\tvec2 " << m_ImageTemp << " = getTransformedUVCoords( vec3( " + << m_TexCoordTemp << ", 1.0), uTransform, vTransform );" << Endl; + if (image.m_Image.m_TextureData.m_TextureFlags.IsInvertUVCoords()) + vertexShader << "\t" << m_ImageTemp << ".y = 1.0 - " << m_ImageFragCoords << ".y;" + << Endl; + + vertexShader.AssignOutput(m_ImageFragCoords.c_str(), m_ImageTemp.c_str()); + } else { + fragmentShader << "\tuTransform = vec3( " << m_ImageRotations << ".x, " + << m_ImageRotations << ".y, " << m_ImageOffsets << ".x );" << Endl; + fragmentShader << "\tvTransform = vec3( " << m_ImageRotations << ".z, " + << m_ImageRotations << ".w, " << m_ImageOffsets << ".y );" << Endl; + vertexShader.GenerateEnvMapReflection(); + addFunction(fragmentShader, "getTransformedUVCoords"); + fragmentShader << "\tvec2 " << m_ImageFragCoords + << " = getTransformedUVCoords( environment_map_reflection, uTransform, " + "vTransform );" + << Endl; + if (image.m_Image.m_TextureData.m_TextureFlags.IsInvertUVCoords()) + fragmentShader << "\t" << m_ImageFragCoords << ".y = 1.0 - " << m_ImageFragCoords + << ".y;" << Endl; + } + } + + void GenerateImageUVCoordinates(QT3DSU32 idx, SRenderableImage &image, QT3DSU32 uvSet = 0) + { + GenerateImageUVCoordinates(VertexGenerator(), idx, uvSet, image); + } + + void GenerateImageUVCoordinates(QT3DSU32 idx, SRenderableImage &image, + IDefaultMaterialVertexPipeline &inShader) + { + if (image.m_Image.m_MappingMode == ImageMappingModes::Normal) { + SetupImageVariableNames(idx); + inShader.AddUniform(m_ImageSampler, "sampler2D"); + inShader.AddUniform(m_ImageOffsets, "vec3"); + inShader.AddUniform(m_ImageRotations, "vec4"); + + inShader << "\tuTransform = vec3( " << m_ImageRotations << ".x, " << m_ImageRotations + << ".y, " << m_ImageOffsets << ".x );" << Endl; + inShader << "\tvTransform = vec3( " << m_ImageRotations << ".z, " << m_ImageRotations + << ".w, " << m_ImageOffsets << ".y );" << Endl; + inShader << "\tvec2 " << m_ImageFragCoords << ";" << Endl; + addFunction(inShader, "getTransformedUVCoords"); + inShader.GenerateUVCoords(); + inShader + << "\t" << m_ImageFragCoords + << " = getTransformedUVCoords( vec3( varTexCoord0, 1.0), uTransform, vTransform );" + << Endl; + if (image.m_Image.m_TextureData.m_TextureFlags.IsInvertUVCoords()) + inShader << "\t" << m_ImageFragCoords << ".y = 1.0 - " << m_ImageFragCoords << ".y;" + << Endl; + } + } + + void OutputSpecularEquation(DefaultMaterialSpecularModel::Enum inSpecularModel, + IShaderStageGenerator &fragmentShader, const char *inLightDir, + const char *inLightSpecColor) + { + switch (inSpecularModel) { + case DefaultMaterialSpecularModel::KGGX: { + fragmentShader.AddInclude("defaultMaterialPhysGlossyBSDF.glsllib"); + fragmentShader.AddUniform("material_specular", "vec4"); + fragmentShader << "\tglobal_specular_light.rgb += lightAttenuation * specularAmount * " + "specularColor * kggxGlossyDefaultMtl( " + << "world_normal, tangent, -" << inLightDir << ".xyz, view_vector, " + << inLightSpecColor + << ".rgb, vec3(material_specular.xyz), roughnessAmount, " + "roughnessAmount ).rgb;" + << Endl; + } break; + case DefaultMaterialSpecularModel::KWard: { + fragmentShader.AddInclude("defaultMaterialPhysGlossyBSDF.glsllib"); + fragmentShader.AddUniform("material_specular", "vec4"); + fragmentShader << "\tglobal_specular_light.rgb += lightAttenuation * specularAmount * " + "specularColor * wardGlossyDefaultMtl( " + << "world_normal, tangent, -" << inLightDir << ".xyz, view_vector, " + << inLightSpecColor + << ".rgb, vec3(material_specular.xyz), roughnessAmount, " + "roughnessAmount ).rgb;" + << Endl; + } break; + default: + addFunction(fragmentShader, "specularBSDF"); + fragmentShader << "\tglobal_specular_light.rgb += lightAttenuation * specularAmount * " + "specularColor * specularBSDF( " + << "world_normal, -" << inLightDir << ".xyz, view_vector, " + << inLightSpecColor << ".rgb, 1.0, 2.56 / (roughnessAmount + " + "0.01), vec3(1.0), scatter_reflect ).rgb;" + << Endl; + break; + } + } + + void OutputDiffuseAreaLighting(IShaderStageGenerator &infragmentShader, const char *inPos, + TStrType inLightPrefix) + { + m_NormalizedDirection = inLightPrefix + "_areaDir"; + AddLocalVariable(infragmentShader, m_NormalizedDirection, "vec3"); + infragmentShader << "\tlightAttenuation = calculateDiffuseAreaOld( " << m_LightDirection + << ".xyz, " << m_LightPos << ".xyz, " << m_LightUp << ", " << m_LightRt + << ", " << inPos << ", " << m_NormalizedDirection << " );" << Endl; + } + + void OutputSpecularAreaLighting(IShaderStageGenerator &infragmentShader, const char *inPos, + const char *inView, const char *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, roughnessAmount ).rgb;" << Endl; + } + + void AddTranslucencyIrradiance(IShaderStageGenerator &infragmentShader, SRenderableImage *image, + TStrType inLightPrefix, bool areaLight) + { + if (image == NULL) + return; + + addFunction(infragmentShader, "diffuseReflectionWrapBSDF"); + if (areaLight) { + infragmentShader << "\tglobal_diffuse_light.rgb += lightAttenuation * " + "translucent_thickness_exp * diffuseReflectionWrapBSDF( " + "-world_normal, " + << m_NormalizedDirection << ", " << m_LightColor + << ".rgb, diffuseLightWrap ).rgb;" << Endl; + } else { + infragmentShader << "\tglobal_diffuse_light.rgb += lightAttenuation * " + "translucent_thickness_exp * diffuseReflectionWrapBSDF( " + "-world_normal, " + << "-" << m_NormalizedDirection << ", " << m_LightColor + << ".rgb, diffuseLightWrap ).rgb;" << Endl; + } + } + + void SetupShadowMapVariableNames(size_t lightIdx) + { + m_ShadowMapStem = "shadowmap"; + m_ShadowCubeStem = "shadowcube"; + char buf[16]; + _snprintf(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(IShaderStageGenerator &inLightShader, QT3DSU32 lightIndex, + RenderLightTypes::Enum inType) + { + SetupShadowMapVariableNames(lightIndex); + + inLightShader.AddInclude("shadowMapping.glsllib"); + if (inType == RenderLightTypes::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 << ";" << Endl; + inLightShader << "\tshadow_map_occl = sampleParaboloid( " << m_ShadowMapStem << ", " + << m_ShadowControlStem << ", " + << + m_ShadowMatrixStem << ", varWorldPos, vec2(1.0, " << m_ShadowControlStem << ".z), " + << m_ShadowCoordStem + << " );" << Endl; + } + else */ + if (inType != RenderLightTypes::Directional) { + inLightShader << "\tshadow_map_occl = sampleCubemap( " << m_ShadowCubeStem << ", " + << m_ShadowControlStem << ", " << m_ShadowMatrixStem << ", " << m_LightPos + << ".xyz, varWorldPos, vec2(1.0, " << m_ShadowControlStem << ".z) );" + << Endl; + } else + inLightShader << "\tshadow_map_occl = sampleOrthographic( " << m_ShadowMapStem << ", " + << m_ShadowControlStem << ", " << m_ShadowMatrixStem + << ", varWorldPos, vec2(1.0, " << m_ShadowControlStem << ".z) );" << Endl; + } + + void AddDisplacementMappingForDepthPass(IShaderStageGenerator &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.Append("\tvec3 uTransform = vec3( displacementMap_rot.x, displacementMap_rot.y, " + "displacementMap_offset.x );"); + inShader.Append("\tvec3 vTransform = vec3( displacementMap_rot.z, displacementMap_rot.w, " + "displacementMap_offset.y );"); + addFunction(inShader, "getTransformedUVCoords"); + inShader.Append("\tvec2 uv_coords = attr_uv0;"); + inShader << "\tuv_coords = getTransformedUVCoords( vec3( uv_coords, 1.0), uTransform, " + "vTransform );\n"; + inShader << "\tvec3 displacedPos = defaultMaterialFileDisplacementTexture( " + "displacementSampler , displaceAmount, uv_coords , attr_norm, attr_pos );" + << Endl; + inShader.Append("\tgl_Position = model_view_projection * vec4(displacedPos, 1.0);"); + } + + void AddDisplacementImageUniforms(IShaderStageGenerator &inGenerator, + QT3DSU32 displacementImageIdx, + SRenderableImage *displacementImage) override + { + if (displacementImage) { + SetupImageVariableNames(displacementImageIdx); + inGenerator.AddInclude("defaultMaterialFileDisplacementTexture.glsllib"); + inGenerator.AddUniform("model_matrix", "mat4"); + inGenerator.AddUniform("camera_position", "vec3"); + inGenerator.AddUniform("displaceAmount", "float"); + inGenerator.AddUniform(m_ImageSampler, "sampler2D"); + } + } + + bool MaybeAddMaterialFresnel(IShaderStageGenerator &fragmentShader, NVConstDataRef<QT3DSU32> inKey, + bool inFragmentHasSpecularAmount) + { + if (m_DefaultMaterialShaderKeyProperties.m_FresnelEnabled.GetValue(inKey)) { + if (inFragmentHasSpecularAmount == false) + fragmentShader << "\tfloat specularAmount = 1.0;" << Endl; + inFragmentHasSpecularAmount = true; + fragmentShader.AddInclude("defaultMaterialFresnel.glsllib"); + fragmentShader.AddUniform("fresnelPower", "float"); + fragmentShader.AddUniform("material_specular", "vec4"); + fragmentShader << "\tfloat fresnelRatio = defaultMaterialSimpleFresnel( world_normal, " + "view_vector, material_specular.w, fresnelPower );" + << Endl; + fragmentShader << "\tspecularAmount *= fresnelRatio;" << Endl; + } + return inFragmentHasSpecularAmount; + } + void SetupLightVariableNames(size_t lightIdx, SLight &inLight) + { + if (m_LightsAsSeparateUniforms) { + char buf[16]; + _snprintf(buf, 16, "light_%d", int(lightIdx)); + m_LightStem = buf; + m_LightColor = m_LightStem; + m_LightColor.append("_diffuse"); + m_LightDirection = m_LightStem; + m_LightDirection.append("_direction"); + m_LightSpecularColor = m_LightStem; + m_LightSpecularColor.append("_specular"); + if (inLight.m_LightType == RenderLightTypes::Point) { + m_LightPos = m_LightStem; + m_LightPos.append("_position"); + m_LightAttenuation = m_LightStem; + m_LightAttenuation.append("_attenuation"); + } else if (inLight.m_LightType == RenderLightTypes::Area) { + m_LightPos = m_LightStem; + m_LightPos.append("_position"); + m_LightUp = m_LightStem; + m_LightUp.append("_up"); + m_LightRt = m_LightStem; + m_LightRt.append("_right"); + } + } else { + m_LightStem = "lights"; + char buf[16]; + _snprintf(buf, 16, "[%d].", int(lightIdx)); + m_LightStem.append(buf); + + m_LightColor = m_LightStem; + m_LightColor.append("diffuse"); + m_LightDirection = m_LightStem; + m_LightDirection.append("direction"); + m_LightSpecularColor = m_LightStem; + m_LightSpecularColor.append("specular"); + if (inLight.m_LightType == RenderLightTypes::Point) { + m_LightPos = m_LightStem; + m_LightPos.append("position"); + m_LightConstantAttenuation = m_LightStem; + m_LightConstantAttenuation.append("constantAttenuation"); + m_LightLinearAttenuation = m_LightStem; + m_LightLinearAttenuation.append("linearAttenuation"); + m_LightQuadraticAttenuation = m_LightStem; + m_LightQuadraticAttenuation.append("quadraticAttenuation"); + } else if (inLight.m_LightType == RenderLightTypes::Area) { + m_LightPos = m_LightStem; + m_LightPos.append("position"); + m_LightUp = m_LightStem; + m_LightUp.append("up"); + m_LightRt = m_LightStem; + m_LightRt.append("right"); + } + } + } + + void addDisplacementMapping(IDefaultMaterialVertexPipeline &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.Append("\tvec3 uTransform = vec3( displacementMap_rot.x, displacementMap_rot.y, " + "displacementMap_offset.x );"); + inShader.Append("\tvec3 vTransform = vec3( displacementMap_rot.z, displacementMap_rot.w, " + "displacementMap_offset.y );"); + addFunction(inShader, "getTransformedUVCoords"); + inShader.GenerateUVCoords(); + inShader << "\tvarTexCoord0 = getTransformedUVCoords( vec3( varTexCoord0, 1.0), " + "uTransform, vTransform );\n"; + inShader << "\tvec3 displacedPos = defaultMaterialFileDisplacementTexture( " + "displacementSampler , displaceAmount, varTexCoord0 , attr_norm, attr_pos );" + << Endl; + inShader.Append("\tgl_Position = model_view_projection * vec4(displacedPos, 1.0);"); + } + + void GenerateTextureSwizzle(NVRenderTextureSwizzleMode::Enum swizzleMode, + eastl::basic_string<char8_t> &texSwizzle, + eastl::basic_string<char8_t> &lookupSwizzle) + { + qt3ds::render::NVRenderContextType deprecatedContextFlags(NVRenderContextValues::GL2 + | NVRenderContextValues::GLES2); + + if (!(m_RenderContext.GetRenderContext().GetRenderContextType() & deprecatedContextFlags)) { + switch (swizzleMode) { + case NVRenderTextureSwizzleMode::L8toR8: + case NVRenderTextureSwizzleMode::L16toR16: + texSwizzle.append(".rgb"); + lookupSwizzle.append(".rrr"); + break; + case NVRenderTextureSwizzleMode::L8A8toRG8: + texSwizzle.append(".rgba"); + lookupSwizzle.append(".rrrg"); + break; + case NVRenderTextureSwizzleMode::A8toR8: + texSwizzle.append(".a"); + lookupSwizzle.append(".r"); + break; + default: + break; + } + } + } + + ///< get the light constant buffer and generate if necessary + NVRenderConstantBuffer *GetLightConstantBuffer(QT3DSU32 inLightCount) + { + NVRenderContext &theContext(m_RenderContext.GetRenderContext()); + + // we assume constant buffer support + QT3DS_ASSERT(theContext.GetConstantBufferSupport()); + + // we only create if if we have lights + if (!inLightCount || !theContext.GetConstantBufferSupport()) + return NULL; + + CRegisteredString theName = theContext.GetStringTable().RegisterStr("cbBufferLights"); + NVRenderConstantBuffer *pCB = theContext.GetConstantBuffer(theName); + + if (!pCB) { + // create + SLightSourceShader s[QT3DS_MAX_NUM_LIGHTS]; + NVDataRef<QT3DSU8> cBuffer((QT3DSU8 *)&s, (sizeof(SLightSourceShader) * QT3DS_MAX_NUM_LIGHTS) + + (4 * sizeof(QT3DSI32))); + pCB = theContext.CreateConstantBuffer( + theName, qt3ds::render::NVRenderBufferUsageType::Static, + (sizeof(SLightSourceShader) * QT3DS_MAX_NUM_LIGHTS) + (4 * sizeof(QT3DSI32)), cBuffer); + if (!pCB) { + QT3DS_ASSERT(false); + return NULL; + } + // init first set + memset(&s[0], 0x0, sizeof(SLightSourceShader)); + QT3DSI32 cgLights = 0; + pCB->UpdateRaw(0, NVDataRef<QT3DSU8>((QT3DSU8 *)&cgLights, sizeof(QT3DSI32))); + pCB->UpdateRaw(4 * sizeof(QT3DSI32), + NVDataRef<QT3DSU8>((QT3DSU8 *)&s[0], sizeof(SLightSourceShader))); + pCB->Update(); // update to hardware + + m_ConstantBuffers.insert(eastl::make_pair(theName, pCB)); + } + + return pCB; + } + + void SetImageShaderVariables(SShaderGeneratorGeneratedShader &inShader, + SRenderableImage &inImage, QT3DSU32 idx) + { + size_t numImageVariables = inShader.m_Images.size(); + for (size_t namesIdx = numImageVariables; namesIdx <= idx; ++namesIdx) { + SetupImageVariableNames(idx); + inShader.m_Images.push_back( + SShaderTextureProperties(m_ImageSampler.c_str(), m_ImageOffsets.c_str(), + m_ImageRotations.c_str(), m_ImageSamplerSize.c_str(), + inShader.m_Shader)); + } + SShaderTextureProperties &theShaderProps = inShader.m_Images[idx]; + const QT3DSMat44 &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 QT3DSF32 *dataPtr(textureTransform.front()); + // 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. + QT3DSVec3 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. + QT3DSVec4 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. + NVRenderTexture2D *imageTexture = inImage.m_Image.m_TextureData.m_Texture; + 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.m_Sampler.Set(imageTexture); + theShaderProps.m_Offsets.Set(offsets); + theShaderProps.m_Rotations.Set(rotations); + theShaderProps.m_Size.Set(QT3DSVec2(imageTexture->GetTextureDetails().m_Width, + imageTexture->GetTextureDetails().m_Height)); + } + + void GenerateShadowMapOcclusion(QT3DSU32 lightIdx, bool inShadowEnabled, + RenderLightTypes::Enum inType) + { + if (inShadowEnabled) { + VertexGenerator().GenerateWorldPosition(); + AddShadowMapContribution(FragmentGenerator(), lightIdx, inType); + /* + VertexGenerator().AddUniform( m_ShadowMatrixStem, "mat4" ); + VertexGenerator().AddOutgoing( m_ShadowCoordStem, "vec4" ); + VertexGenerator() << "\tvec4 local_" << m_ShadowCoordStem << " = " << m_ShadowMatrixStem + << " * vec4(local_model_world_position, 1.0);" << Endl; + m_TempStr.assign( "local_" ); + m_TempStr.append( m_ShadowCoordStem ); + VertexGenerator().AssignOutput( m_ShadowCoordStem.c_str(), m_TempStr.c_str() ); + */ + } else { + FragmentGenerator() << "\tshadow_map_occl = 1.0;" << Endl; + } + } + + void GenerateVertexShader() + { + // vertex displacement + QT3DSU32 imageIdx = 0; + SRenderableImage *displacementImage = NULL; + QT3DSU32 displacementImageIdx = 0; + + for (SRenderableImage *img = m_FirstImage; img != NULL; + img = img->m_NextImage, ++imageIdx) { + if (img->m_MapType == ImageMapTypes::Displacement) { + displacementImage = img; + displacementImageIdx = imageIdx; + break; + } + } + + // the pipeline opens/closes up the shaders stages + VertexGenerator().BeginVertexGeneration(displacementImageIdx, displacementImage); + } + + void GenerateFragmentShader(SShaderDefaultMaterialKey &inKey) + { + bool specularEnabled = Material().IsSpecularEnabled(); + bool vertexColorsEnabled = Material().IsVertexColorsEnabled(); + + bool hasLighting = Material().HasLighting(); + bool hasImage = m_FirstImage != NULL; + + bool hasIblProbe = m_DefaultMaterialShaderKeyProperties.m_HasIbl.GetValue(inKey); + bool hasSpecMap = false; + bool hasEnvMap = false; + bool hasEmissiveMap = false; + bool hasLightmaps = false; + // Pull the bump out as + SRenderableImage *bumpImage = NULL; + QT3DSU32 imageIdx = 0; + QT3DSU32 bumpImageIdx = 0; + SRenderableImage *specularAmountImage = NULL; + QT3DSU32 specularAmountImageIdx = 0; + SRenderableImage *roughnessImage = NULL; + QT3DSU32 roughnessImageIdx = 0; + // normal mapping + SRenderableImage *normalImage = NULL; + QT3DSU32 normalImageIdx = 0; + // translucency map + SRenderableImage *translucencyImage = NULL; + QT3DSU32 translucencyImageIdx = 0; + // lightmaps + SRenderableImage *lightmapIndirectImage = NULL; + QT3DSU32 lightmapIndirectImageIdx = 0; + SRenderableImage *lightmapRadiosityImage = NULL; + QT3DSU32 lightmapRadiosityImageIdx = 0; + SRenderableImage *lightmapShadowImage = NULL; + QT3DSU32 lightmapShadowImageIdx = 0; + const bool supportStandardDerivatives + = m_RenderContext.GetRenderContext().IsStandardDerivativesSupported(); + + for (SRenderableImage *img = m_FirstImage; img != NULL; + img = img->m_NextImage, ++imageIdx) { + hasSpecMap = img->m_MapType == ImageMapTypes::Specular; + if (img->m_MapType == ImageMapTypes::Bump) { + bumpImage = img; + bumpImageIdx = imageIdx; + } else if (img->m_MapType == ImageMapTypes::SpecularAmountMap) { + specularAmountImage = img; + specularAmountImageIdx = imageIdx; + } else if (img->m_MapType == ImageMapTypes::Roughness) { + roughnessImage = img; + roughnessImageIdx = imageIdx; + } else if (img->m_MapType == ImageMapTypes::Normal) { + normalImage = img; + normalImageIdx = imageIdx; + } else if (img->m_Image.m_MappingMode == ImageMappingModes::Environment) { + hasEnvMap = true; + } else if (img->m_MapType == ImageMapTypes::Translucency) { + translucencyImage = img; + translucencyImageIdx = imageIdx; + } else if (img->m_MapType == ImageMapTypes::Emissive) { + hasEmissiveMap = true; + } else if (img->m_MapType == ImageMapTypes::LightmapIndirect) { + lightmapIndirectImage = img; + lightmapIndirectImageIdx = imageIdx; + hasLightmaps = true; + } else if (img->m_MapType == ImageMapTypes::LightmapRadiosity) { + lightmapRadiosityImage = img; + lightmapRadiosityImageIdx = imageIdx; + hasLightmaps = true; + } else if (img->m_MapType == ImageMapTypes::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 (QT3DSU32 idx = 0; idx < FeatureSet().size(); ++idx) { + eastl::string name(FeatureSet()[idx].m_Name.c_str()); + if (name == "QT3DS_ENABLE_SSAO") + enableSSAO = FeatureSet()[idx].m_Enabled; + else if (name == "QT3DS_ENABLE_SSDO") + enableSSDO = FeatureSet()[idx].m_Enabled; + else if (name == "QT3DS_ENABLE_SSM") + enableShadowMaps = FeatureSet()[idx].m_Enabled; + } + + bool includeSSAOSSDOVars = enableSSAO || enableSSDO || enableShadowMaps; + + VertexGenerator().BeginFragmentGeneration(); + IShaderStageGenerator &fragmentShader(FragmentGenerator()); + IDefaultMaterialVertexPipeline &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", "vec4"); + fragmentShader.AddUniform("diffuse_color", "vec3"); + fragmentShader.AddUniform("material_properties", "vec4"); + + // All these are needed for SSAO + if (includeSSAOSSDOVars) { + fragmentShader.AddInclude("SSAOCustomMaterial.glsllib"); + // fragmentShader.AddUniform( "ao_sampler", "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) { + fragmentShader.Append("\tvec3 uTransform;"); + fragmentShader.Append("\tvec3 vTransform;"); + } + + if (includeSSAOSSDOVars || hasSpecMap || hasLighting || hasEnvMap || enableFresnel + || hasIblProbe || enableBumpNormal) { + vertexShader.GenerateViewVector(); + vertexShader.GenerateWorldNormal(); + vertexShader.GenerateWorldPosition(); + } + if (includeSSAOSSDOVars || specularEnabled || hasIblProbe || enableBumpNormal) + vertexShader.GenerateVarTangentAndBinormal(); + + if (vertexColorsEnabled) + vertexShader.GenerateVertexColor(); + else + fragmentShader.Append("\tvec3 vertColor = vec3(1.0);"); + + // You do bump or normal mapping but not both + if (bumpImage != NULL) { + GenerateImageUVCoordinates(bumpImageIdx, *bumpImage); + fragmentShader.AddUniform("bumpAmount", "float"); + + fragmentShader.AddUniform(m_ImageSamplerSize, "vec2"); + fragmentShader.AddInclude("defaultMaterialBumpNoLod.glsllib"); + fragmentShader << "\tworld_normal = defaultMaterialBumpNoLod( " << m_ImageSampler + << ", bumpAmount, " << m_ImageFragCoords + << ", tangent, binormal, world_normal, " + << m_ImageSamplerSize << ");" << Endl; + // Do gram schmidt + fragmentShader << "\tbinormal = normalize(cross(world_normal, tangent) );\n"; + fragmentShader << "\ttangent = normalize(cross(binormal, world_normal) );\n"; + + } else if (normalImage != NULL) { + GenerateImageUVCoordinates(normalImageIdx, *normalImage); + + fragmentShader.AddInclude("defaultMaterialFileNormalTexture.glsllib"); + fragmentShader.AddUniform("bumpAmount", "float"); + + fragmentShader << "\tworld_normal = defaultMaterialFileNormalTexture( " + << m_ImageSampler << ", bumpAmount, " << m_ImageFragCoords + << ", tangent, binormal );" << Endl; + } + + if (includeSSAOSSDOVars || specularEnabled || hasIblProbe || enableBumpNormal) + fragmentShader << "\tmat3 tanFrame = mat3(tangent, binormal, world_normal);" << Endl; + + bool fragmentHasSpecularAmount = false; + + if (hasEmissiveMap) { + fragmentShader.Append("\tvec3 global_emission = material_diffuse.rgb;"); + } + + if (hasLighting) { + fragmentShader.AddUniform("light_ambient_total", "vec3"); + + fragmentShader.Append( + "\tvec4 global_diffuse_light = vec4(light_ambient_total.xyz, 1.0);"); + fragmentShader.Append("\tvec3 global_specular_light = vec3(0.0, 0.0, 0.0);"); + fragmentShader.Append("\tfloat shadow_map_occl = 1.0;"); + + if (specularEnabled) { + vertexShader.GenerateViewVector(); + fragmentShader.AddUniform("material_properties", "vec4"); + } + + if (lightmapIndirectImage != NULL) { + GenerateImageUVCoordinates(lightmapIndirectImageIdx, *lightmapIndirectImage, 1); + fragmentShader << "\tvec4 indirect_light = texture2D( " << m_ImageSampler << ", " + << m_ImageFragCoords << ");" << Endl; + fragmentShader << "\tglobal_diffuse_light += indirect_light;" << Endl; + if (specularEnabled) { + fragmentShader + << "\tglobal_specular_light += indirect_light.rgb * material_properties.x;" + << Endl; + } + } + + if (lightmapRadiosityImage != NULL) { + GenerateImageUVCoordinates(lightmapRadiosityImageIdx, *lightmapRadiosityImage, 1); + fragmentShader << "\tvec4 direct_light = texture2D( " << m_ImageSampler << ", " + << m_ImageFragCoords << ");" << Endl; + fragmentShader << "\tglobal_diffuse_light += direct_light;" << Endl; + if (specularEnabled) { + fragmentShader + << "\tglobal_specular_light += direct_light.rgb * material_properties.x;" + << Endl; + } + } + + if (translucencyImage != NULL) { + fragmentShader.AddUniform("translucentFalloff", "float"); + fragmentShader.AddUniform("diffuseLightWrap", "float"); + + GenerateImageUVCoordinates(translucencyImageIdx, *translucencyImage); + + fragmentShader << "\tvec4 translucent_depth_range = texture2D( " << m_ImageSampler + << ", " << m_ImageFragCoords << ");" << Endl; + fragmentShader << "\tfloat translucent_thickness = translucent_depth_range.r * " + "translucent_depth_range.r;" + << Endl; + fragmentShader << "\tfloat translucent_thickness_exp = exp( translucent_thickness " + "* translucentFalloff);" + << Endl; + } + + fragmentShader.Append("\tfloat lightAttenuation = 1.0;"); + + AddLocalVariable(fragmentShader, "aoFactor", "float"); + + if (hasLighting && enableSSAO) + fragmentShader.Append("\taoFactor = customMaterialAO();"); + else + fragmentShader.Append("\taoFactor = 1.0;"); + + AddLocalVariable(fragmentShader, "shadowFac", "float"); + + if (specularEnabled) { + fragmentShader << "\tfloat specularAmount = material_properties.x;" << Endl; + fragmentHasSpecularAmount = true; + } + // Fragment lighting means we can perhaps attenuate the specular amount by a texture + // lookup. + + fragmentShader << "\tvec3 specularColor = vec3(1.0);" << Endl; + if (specularAmountImage) { + if (!specularEnabled) + fragmentShader << "\tfloat specularAmount = 1.0;" << Endl; + GenerateImageUVCoordinates(specularAmountImageIdx, *specularAmountImage); + fragmentShader << "\tspecularColor = texture2D( " + << m_ImageSampler << ", " << m_ImageFragCoords << " ).xyz;" << Endl; + fragmentHasSpecularAmount = true; + } + + fragmentShader << "\tfloat roughnessAmount = material_properties.y;" << Endl; + if (roughnessImage) { + GenerateImageUVCoordinates(roughnessImageIdx, *roughnessImage); + fragmentShader << "\tfloat sampledRoughness = texture2D( " + << m_ImageSampler << ", " << m_ImageFragCoords << " ).x;" << Endl; + //The roughness sampled from roughness textures is Disney roughness + //which has to be squared to get the proper value + fragmentShader << "\troughnessAmount = roughnessAmount * " + << "sampledRoughness * sampledRoughness;" << Endl; + } + + fragmentHasSpecularAmount = + MaybeAddMaterialFresnel(fragmentShader, inKey, fragmentHasSpecularAmount); + + // Iterate through all lights + for (QT3DSU32 lightIdx = 0; lightIdx < m_Lights.size(); ++lightIdx) { + SLight *lightNode = m_Lights[lightIdx]; + SetupLightVariableNames(lightIdx, *lightNode); + bool isDirectional = lightNode->m_LightType == RenderLightTypes::Directional; + bool isArea = lightNode->m_LightType == RenderLightTypes::Area; + bool isShadow = enableShadowMaps && lightNode->m_CastShadow; + + fragmentShader.Append(""); + char buf[10]; + sprintf(buf, "%d", lightIdx); + + m_TempStr.assign("light"); + m_TempStr.append(buf); + + fragmentShader << "\t//Light " << buf << Endl; + fragmentShader << "\tlightAttenuation = 1.0;" << Endl; + if (isDirectional) { + + if (m_LightsAsSeparateUniforms) { + fragmentShader.AddUniform(m_LightDirection, "vec4"); + fragmentShader.AddUniform(m_LightColor, "vec4"); + } + + if (enableSSDO) { + fragmentShader << "\tshadowFac = customMaterialShadow( " << m_LightDirection + << ".xyz, varWorldPos );" << Endl; + } else { + fragmentShader << "\tshadowFac = 1.0;" << Endl; + } + + GenerateShadowMapOcclusion(lightIdx, enableShadowMaps && isShadow, + lightNode->m_LightType); + + if (specularEnabled && enableShadowMaps && isShadow) + fragmentShader << "\tlightAttenuation *= shadow_map_occl;" << Endl; + + fragmentShader << "\tglobal_diffuse_light.rgb += shadowFac * shadow_map_occl * " + "diffuseReflectionBSDF( world_normal, " + << "-" << m_LightDirection << ".xyz, view_vector, " + << m_LightColor << ".rgb, 0.0 ).rgb;" << Endl; + + if (specularEnabled) { + if (m_LightsAsSeparateUniforms) + fragmentShader.AddUniform(m_LightSpecularColor, "vec4"); + OutputSpecularEquation(Material().m_SpecularModel, fragmentShader, + m_LightDirection.c_str(), + m_LightSpecularColor.c_str()); + } + } 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 << "\tglobal_diffuse_light.rg += " << m_ShadowCoordStem << ";" + // << Endl; + m_NormalizedDirection = m_TempStr; + m_NormalizedDirection.append("_Frame"); + + AddLocalVariable(fragmentShader, m_NormalizedDirection, "mat3"); + fragmentShader << m_NormalizedDirection << " = mat3( " << m_LightRt << ".xyz, " + << m_LightUp << ".xyz, -" << m_LightDirection << ".xyz );" + << Endl; + + if (enableSSDO) { + fragmentShader << "\tshadowFac = shadow_map_occl * customMaterialShadow( " + << m_LightDirection << ".xyz, varWorldPos );" << Endl; + } else { + fragmentShader << "\tshadowFac = shadow_map_occl;" << Endl; + } + + if (specularEnabled) { + vertexShader.GenerateViewVector(); + if (m_LightsAsSeparateUniforms) + fragmentShader.AddUniform(m_LightSpecularColor, "vec4"); + OutputSpecularAreaLighting(fragmentShader, "varWorldPos", "view_vector", + m_LightSpecularColor.c_str()); + } + + OutputDiffuseAreaLighting(fragmentShader, "varWorldPos", m_TempStr); + fragmentShader << "\tlightAttenuation *= shadowFac;" << Endl; + + AddTranslucencyIrradiance(fragmentShader, translucencyImage, m_TempStr, true); + + fragmentShader << "\tglobal_diffuse_light.rgb += lightAttenuation * " + "diffuseReflectionBSDF( world_normal, " + << m_NormalizedDirection << ", view_vector, " << m_LightColor + << ".rgb, 0.0 ).rgb;" << Endl; + } 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 = m_TempStr; + m_RelativeDirection.append("_relativeDirection"); + + m_NormalizedDirection = m_RelativeDirection; + m_NormalizedDirection.append("_normalized"); + + m_RelativeDistance = m_TempStr; + m_RelativeDistance.append("_distance"); + + fragmentShader << "\tvec3 " << m_RelativeDirection << " = varWorldPos - " + << m_LightPos << ".xyz;" << Endl; + fragmentShader << "\tfloat " << m_RelativeDistance << " = length( " + << m_RelativeDirection << " );" << Endl; + fragmentShader << "\tvec3 " << m_NormalizedDirection << " = " + << m_RelativeDirection << " / " << m_RelativeDistance << ";" + << Endl; + + if (enableSSDO) { + fragmentShader << "\tshadowFac = shadow_map_occl * customMaterialShadow( " + << m_NormalizedDirection << ", varWorldPos );" << Endl; + } else { + fragmentShader << "\tshadowFac = shadow_map_occl;" << Endl; + } + + addFunction(fragmentShader, "calculatePointLightAttenuation"); + + if (m_LightsAsSeparateUniforms) { + fragmentShader.AddUniform(m_LightAttenuation, "vec3"); + fragmentShader + << "\tlightAttenuation = shadowFac * calculatePointLightAttenuation(" + << "vec3( " << m_LightAttenuation << ".x, " << m_LightAttenuation + << ".y, " << m_LightAttenuation << ".z), " << m_RelativeDistance + << ");" << Endl; + } else { + fragmentShader + << "\tlightAttenuation = shadowFac * calculatePointLightAttenuation(" + << "vec3( " << m_LightConstantAttenuation << ", " + << m_LightLinearAttenuation << ", " << m_LightQuadraticAttenuation + << "), " << m_RelativeDistance << ");" + << Endl; + } + + + + AddTranslucencyIrradiance(fragmentShader, translucencyImage, m_TempStr, false); + + fragmentShader << "\tglobal_diffuse_light.rgb += lightAttenuation * " + "diffuseReflectionBSDF( world_normal, " + << "-" << m_NormalizedDirection << ", view_vector, " + << m_LightColor << ".rgb, 0.0 ).rgb;" << Endl; + + if (specularEnabled) { + if (m_LightsAsSeparateUniforms) + fragmentShader.AddUniform(m_LightSpecularColor, "vec4"); + OutputSpecularEquation(Material().m_SpecularModel, fragmentShader, + m_NormalizedDirection.c_str(), + m_LightSpecularColor.c_str()); + } + } + } + + // 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 object_opacity 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 << "\tglobal_diffuse_light = vec4(global_diffuse_light.xyz * aoFactor, " + "object_opacity);" + << Endl << "\tglobal_specular_light = vec3(global_specular_light.xyz);" + << Endl; + } else // no lighting. + { + fragmentShader << "\tvec4 global_diffuse_light = vec4(0.0, 0.0, 0.0, object_opacity);" + << Endl << "\tvec3 global_specular_light = vec3(0.0, 0.0, 0.0);" << Endl; + + // We still have specular maps and such that could potentially use the fresnel variable. + fragmentHasSpecularAmount = + MaybeAddMaterialFresnel(fragmentShader, inKey, fragmentHasSpecularAmount); + } + + if (!hasEmissiveMap) + fragmentShader + << "\tglobal_diffuse_light.rgb += diffuse_color.rgb * material_diffuse.rgb;" + << Endl; + + // 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 << "\tglobal_diffuse_light.rgb *= diffuse_color.rgb;" << Endl; + + if (hasLighting && hasIblProbe) { + vertexShader.GenerateWorldNormal(); + + fragmentShader << "\tglobal_diffuse_light.rgb += diffuse_color.rgb * aoFactor * " + "sampleDiffuse( tanFrame ).xyz;" + << Endl; + + if (specularEnabled) { + + fragmentShader.AddUniform("material_specular", "vec4"); + + fragmentShader << "\tglobal_specular_light.xyz += specularAmount * specularColor * " + "vec3(material_specular.xyz) * sampleGlossy( tanFrame, " + "view_vector, roughnessAmount ).xyz;" + << Endl; + } + } + + if (hasImage) { + fragmentShader.Append("\tvec4 texture_color;"); + QT3DSU32 idx = 0; + for (SRenderableImage *image = m_FirstImage; image; image = image->m_NextImage, ++idx) { + // Various maps are handled on a different locations + if (image->m_MapType == ImageMapTypes::Bump + || image->m_MapType == ImageMapTypes::Normal + || image->m_MapType == ImageMapTypes::Displacement + || image->m_MapType == ImageMapTypes::SpecularAmountMap + || image->m_MapType == ImageMapTypes::Roughness + || image->m_MapType == ImageMapTypes::Translucency + || image->m_MapType == ImageMapTypes::LightmapIndirect + || image->m_MapType == ImageMapTypes::LightmapRadiosity) { + continue; + } + + eastl::basic_string<char8_t> texSwizzle, lookupSwizzle, texLodStr; + + GenerateImageUVCoordinates(idx, *image, 0); + + GenerateTextureSwizzle( + image->m_Image.m_TextureData.m_Texture->GetTextureSwizzleMode(), texSwizzle, + lookupSwizzle); + + if (texLodStr.empty()) { + fragmentShader << "\ttexture_color" << texSwizzle.c_str() << " = texture2D( " + << m_ImageSampler << ", " << m_ImageFragCoords << ")" + << lookupSwizzle.c_str() << ";" << Endl; + } else { + fragmentShader << "\ttexture_color" << texSwizzle.c_str() << "= textureLod( " + << m_ImageSampler << ", " << m_ImageFragCoords << ", " + << texLodStr.c_str() << " )" << lookupSwizzle.c_str() << ";" + << Endl; + } + + if (image->m_Image.m_TextureData.m_TextureFlags.IsPreMultiplied() == true) + fragmentShader << "\ttexture_color.rgb = texture_color.a > 0.0 ? " + "texture_color.rgb / texture_color.a : vec3( 0, 0, 0 );" + << Endl; + + // These mapping types honestly don't make a whole ton of sense to me. + switch (image->m_MapType) { + case ImageMapTypes::Diffuse: // assume images are premultiplied. + case ImageMapTypes::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("\tglobal_diffuse_light *= texture_color;"); + break; + case ImageMapTypes::Specular: + + fragmentShader.AddUniform("material_specular", "vec4"); + if (fragmentHasSpecularAmount) { + fragmentShader.Append("\tglobal_specular_light.xyz += specularAmount * " + "specularColor * texture_color.xyz * " + "material_specular.xyz;"); + } else { + fragmentShader.Append("\tglobal_specular_light.xyz += texture_color.xyz * " + "material_specular.xyz;"); + } + fragmentShader.Append("\tglobal_diffuse_light.a *= texture_color.a;"); + break; + case ImageMapTypes::Opacity: + fragmentShader.Append("\tglobal_diffuse_light.a *= texture_color.a;"); + break; + case ImageMapTypes::Emissive: + fragmentShader.Append( + "\tglobal_emission *= texture_color.xyz * texture_color.a;"); + break; + default: + QT3DS_ASSERT(false); // fallthrough intentional + } + } + } + + if (hasEmissiveMap) { + fragmentShader.Append("\tglobal_diffuse_light.rgb += global_emission.rgb;"); + } + + // Ensure the rgb colors are in range. + fragmentShader.Append("\tfragOutput = vec4( clamp( vertColor * global_diffuse_light.xyz + " + "global_specular_light.xyz, 0.0, 65519.0 ), global_diffuse_light.a " + ");"); + + if (VertexGenerator().HasActiveWireframe()) { + fragmentShader.Append("vec3 edgeDistance = varEdgeDistance * gl_FragCoord.w;"); + fragmentShader.Append( + "\tfloat d = min(min(edgeDistance.x, edgeDistance.y), edgeDistance.z);"); + fragmentShader.Append("\tfloat mixVal = smoothstep(0.0, 1.0, d);"); // line width 1.0 + + fragmentShader.Append( + "\tfragOutput = mix( vec4(0.0, 1.0, 0.0, 1.0), fragOutput, mixVal);"); + } + } + + NVRenderShaderProgram *GenerateMaterialShader(const char8_t *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. + + m_GeneratedShaderString.clear(); + m_GeneratedShaderString.assign(nonNull(inShaderPrefix)); + + SShaderDefaultMaterialKey theKey(Key()); + theKey.ToString(m_GeneratedShaderString, m_DefaultMaterialShaderKeyProperties); + + m_LightsAsSeparateUniforms = !m_RenderContext.GetRenderContext().GetConstantBufferSupport(); + + GenerateVertexShader(); + GenerateFragmentShader(theKey); + + VertexGenerator().EndVertexGeneration(); + VertexGenerator().EndFragmentGeneration(); + + return ProgramGenerator().CompileGeneratedShader(m_GeneratedShaderString.c_str(), + SShaderCacheProgramFlags(), FeatureSet()); + } + + virtual NVRenderShaderProgram * + GenerateShader(const SGraphObject &inMaterial, SShaderDefaultMaterialKey inShaderDescription, + IShaderStageGenerator &inVertexPipeline, TShaderFeatureSet inFeatureSet, + NVDataRef<SLight *> inLights, SRenderableImage *inFirstImage, + bool inHasTransparency, const char8_t *inVertexPipelineName, const char8_t *) override + { + QT3DS_ASSERT(inMaterial.m_Type == GraphObjectTypes::DefaultMaterial); + m_CurrentMaterial = static_cast<const SDefaultMaterial *>(&inMaterial); + m_CurrentKey = &inShaderDescription; + m_CurrentPipeline = static_cast<IDefaultMaterialVertexPipeline *>(&inVertexPipeline); + m_CurrentFeatureSet = inFeatureSet; + m_Lights = inLights; + m_FirstImage = inFirstImage; + m_HasTransparency = inHasTransparency; + + return GenerateMaterialShader(inVertexPipelineName); + } + + SShaderGeneratorGeneratedShader &GetShaderForProgram(NVRenderShaderProgram &inProgram) + { + eastl::pair<TProgramToShaderMap::iterator, bool> inserter = + m_ProgramToShaderMap.insert(eastl::make_pair( + &inProgram, NVScopedRefCounted<SShaderGeneratorGeneratedShader>(NULL))); + if (inserter.second) { + NVAllocatorCallback &alloc(m_RenderContext.GetRenderContext().GetAllocator()); + inserter.first->second = QT3DS_NEW(alloc, SShaderGeneratorGeneratedShader)( + inProgram, m_RenderContext.GetRenderContext()); + } + return *inserter.first->second; + } + + void SetGlobalProperties(NVRenderShaderProgram &inProgram, const SLayer & /*inLayer*/ + , + SCamera &inCamera, QT3DSVec3 inCameraDirection, + NVDataRef<SLight *> inLights, NVDataRef<QT3DSVec3> inLightDirections, + Qt3DSShadowMap *inShadowMapManager) + { + SShaderGeneratorGeneratedShader &shader(GetShaderForProgram(inProgram)); + m_RenderContext.GetRenderContext().SetActiveShader(&inProgram); + + m_ShadowMapManager = inShadowMapManager; + + SCamera &theCamera(inCamera); + shader.m_CameraPosition.Set(theCamera.GetGlobalPos()); + shader.m_CameraDirection.Set(inCameraDirection); + + QT3DSMat44 viewProj; + if (shader.m_ViewProj.IsValid()) { + theCamera.CalculateViewProjectionMatrix(viewProj); + shader.m_ViewProj.Set(viewProj); + } + + if (shader.m_ViewMatrix.IsValid()) { + viewProj = theCamera.m_GlobalTransform.getInverse(); + 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. + QT3DSVec3 theLightAmbientTotal = QT3DSVec3(0, 0, 0); + size_t numShaderLights = shader.m_Lights.size(); + size_t numShadowLights = shader.m_ShadowMaps.size(); + for (QT3DSU32 lightIdx = 0, shadowMapIdx = 0, lightEnd = inLights.size(); + lightIdx < lightEnd && lightIdx < QT3DS_MAX_NUM_LIGHTS; ++lightIdx) { + SLight *theLight(inLights[lightIdx]); + if (lightIdx >= numShaderLights) { + shader.m_Lights.push_back(SShaderLightProperties()); + ++numShaderLights; + } + if (shadowMapIdx >= numShadowLights && numShadowLights < QT3DS_MAX_NUM_SHADOWS) { + if (theLight->m_Scope == NULL && 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(SShadowMapProperties( + m_ShadowMapStem.c_str(), m_ShadowCubeStem.c_str(), + m_ShadowMatrixStem.c_str(), m_ShadowControlStem.c_str(), inProgram)); + } + } + QT3DS_ASSERT(lightIdx < numShaderLights); + SShaderLightProperties &theLightProperties(shader.m_Lights[lightIdx]); + float brightness = TranslateConstantAttenuation(theLight->m_Brightness); + + // setup light data + theLightProperties.m_LightColor = theLight->m_DiffuseColor * brightness; + theLightProperties.m_LightData.m_specular = + QT3DSVec4(theLight->m_SpecularColor * brightness, 1.0); + theLightProperties.m_LightData.m_direction = QT3DSVec4(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 == NULL) && (theLight->m_CastShadow && inShadowMapManager)) { + SShadowMapProperties &theShadowMapProperties(shader.m_ShadowMaps[shadowMapIdx++]); + SShadowMapEntry *pEntry = inShadowMapManager->GetShadowMapEntry(lightIdx); + if (pEntry) { + // add fixed scale bias matrix + QT3DSMat44 bias(QT3DSVec4(0.5, 0.0, 0.0, 0.0), QT3DSVec4(0.0, 0.5, 0.0, 0.0), + QT3DSVec4(0.0, 0.0, 0.5, 0.0), QT3DSVec4(0.5, 0.5, 0.5, 1.0)); + + if (theLight->m_LightType != RenderLightTypes::Directional) { + theShadowMapProperties.m_ShadowCubeTexture.Set(pEntry->m_DepthCube); + theShadowMapProperties.m_ShadowmapMatrix.Set(pEntry->m_LightView); + } else { + theShadowMapProperties.m_ShadowmapTexture.Set(pEntry->m_DepthMap); + theShadowMapProperties.m_ShadowmapMatrix.Set(bias * pEntry->m_LightVP); + } + + theShadowMapProperties.m_ShadowmapSettings.Set( + QT3DSVec4(theLight->m_ShadowBias, theLight->m_ShadowFactor, + theLight->m_ShadowMapFar, 0.0f)); + } else { + // if we have a light casting shadow we should find an entry + QT3DS_ASSERT(false); + } + } + + if (theLight->m_LightType == RenderLightTypes::Point) { + theLightProperties.m_LightData.m_position = QT3DSVec4(theLight->GetGlobalPos(), 1.0); + theLightProperties.m_LightData.m_constantAttenuation = 1.0; + theLightProperties.m_LightData.m_linearAttenuation = + TranslateLinearAttenuation(theLight->m_LinearFade); + theLightProperties.m_LightData.m_quadraticAttenuation = + TranslateQuadraticAttenuation(theLight->m_ExponentialFade); + } else if (theLight->m_LightType == RenderLightTypes::Area) { + theLightProperties.m_LightData.m_position = QT3DSVec4(theLight->GetGlobalPos(), 1.0); + + QT3DSVec3 upDir = theLight->m_GlobalTransform.getUpper3x3().transform(QT3DSVec3(0, 1, 0)); + QT3DSVec3 rtDir = theLight->m_GlobalTransform.getUpper3x3().transform(QT3DSVec3(1, 0, 0)); + + theLightProperties.m_LightData.m_up = QT3DSVec4(upDir, theLight->m_AreaHeight); + theLightProperties.m_LightData.m_right = QT3DSVec4(rtDir, theLight->m_AreaWidth); + } + theLightAmbientTotal += theLight->m_AmbientColor; + } + shader.m_LightAmbientTotal = theLightAmbientTotal; + } + + // Also sets the blend function on the render context. + void SetMaterialProperties(NVRenderShaderProgram &inProgram, const SDefaultMaterial &inMaterial, + const QT3DSVec2 &inCameraVec, const QT3DSMat44 &inModelViewProjection, + const QT3DSMat33 &inNormalMatrix, const QT3DSMat44 &inGlobalTransform, + SRenderableImage *inFirstImage, QT3DSF32 inOpacity, + NVRenderTexture2D *inDepthTexture, NVRenderTexture2D *inSSaoTexture, + SImage *inLightProbe, SImage *inLightProbe2, QT3DSF32 inProbeHorizon, + QT3DSF32 inProbeBright, QT3DSF32 inProbe2Window, QT3DSF32 inProbe2Pos, + QT3DSF32 inProbe2Fade, QT3DSF32 inProbeFOV) + { + + NVRenderContext &context(m_RenderContext.GetRenderContext()); + SShaderGeneratorGeneratedShader &shader(GetShaderForProgram(inProgram)); + shader.m_MVP.Set(inModelViewProjection); + shader.m_NormalMatrix.Set(inNormalMatrix); + shader.m_GlobalTransform.Set(inGlobalTransform); + shader.m_DepthTexture.Set(inDepthTexture); + + shader.m_AOTexture.Set(inSSaoTexture); + + qt3ds::render::SImage *theLightProbe = inLightProbe; + qt3ds::render::SImage *theLightProbe2 = inLightProbe2; + + // If the material has its own IBL Override, we should use that image instead. + if ((inMaterial.m_IblProbe) && (inMaterial.m_IblProbe->m_TextureData.m_Texture)) { + theLightProbe = inMaterial.m_IblProbe; + } + + if (theLightProbe) { + if (theLightProbe->m_TextureData.m_Texture) { + NVRenderTextureCoordOp::Enum theHorzLightProbeTilingMode = + theLightProbe->m_HorizontalTilingMode; + NVRenderTextureCoordOp::Enum theVertLightProbeTilingMode = + theLightProbe->m_VerticalTilingMode; + theLightProbe->m_TextureData.m_Texture->SetTextureWrapS( + theHorzLightProbeTilingMode); + theLightProbe->m_TextureData.m_Texture->SetTextureWrapT( + theVertLightProbeTilingMode); + const QT3DSMat44 &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 QT3DSF32 *dataPtr(textureTransform.front()); + // 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. + QT3DSVec4 offsets(dataPtr[12], dataPtr[13], + theLightProbe->m_TextureData.m_TextureFlags.IsPreMultiplied() ? 1.0f + : 0.0f, + (float)theLightProbe->m_TextureData.m_Texture->GetNumMipmaps()); + + // Grab just the upper 2x2 rotation matrix from the larger matrix. + QT3DSVec4 rotations(dataPtr[0], dataPtr[4], dataPtr[1], dataPtr[5]); + + shader.m_LightProbeRot.Set(rotations); + shader.m_LightProbeOfs.Set(offsets); + + if ((!inMaterial.m_IblProbe) && (inProbeFOV < 180.f)) { + shader.m_LightProbeOpts.Set( + QT3DSVec4(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); + shader.m_LightProbe2Props.Set( + QT3DSVec4(inProbe2Window, inProbe2Pos, inProbe2Fade, 1.0f)); + + const QT3DSMat44 &xform2 = theLightProbe2->m_TextureTransform; + const QT3DSF32 *dataPtr(xform2.front()); + shader.m_LightProbeProps.Set( + QT3DSVec4(dataPtr[12], dataPtr[13], inProbeHorizon, inProbeBright * 0.01f)); + } else { + shader.m_LightProbe2Props.Set(QT3DSVec4(0.0f, 0.0f, 0.0f, 0.0f)); + shader.m_LightProbeProps.Set( + QT3DSVec4(0.0f, 0.0f, inProbeHorizon, inProbeBright * 0.01f)); + } + NVRenderTexture2D *textureImage = theLightProbe->m_TextureData.m_Texture; + shader.m_LightProbe.Set(textureImage); + shader.m_LightProbeSize.Set(QT3DSVec2(textureImage->GetTextureDetails().m_Width, + textureImage->GetTextureDetails().m_Height)); + } else { + shader.m_LightProbeProps.Set(QT3DSVec4(0.0f, 0.0f, -1.0f, 0.0f)); + shader.m_LightProbe2Props.Set(QT3DSVec4(0.0f, 0.0f, 0.0f, 0.0f)); + } + } else { + shader.m_LightProbeProps.Set(QT3DSVec4(0.0f, 0.0f, -1.0f, 0.0f)); + shader.m_LightProbe2Props.Set(QT3DSVec4(0.0f, 0.0f, 0.0f, 0.0f)); + } + + QT3DSF32 emissivePower = 1.0; + + QT3DSU32 hasLighting = inMaterial.m_Lighting != DefaultMaterialLighting::NoLighting; + if (hasLighting) + emissivePower = inMaterial.m_EmissivePower / 100.0f; + + QT3DSVec4 material_diffuse = QT3DSVec4(inMaterial.m_EmissiveColor[0] * emissivePower, + inMaterial.m_EmissiveColor[1] * emissivePower, + inMaterial.m_EmissiveColor[2] * emissivePower, inOpacity); + shader.m_MaterialDiffuse.Set(material_diffuse); + shader.m_DiffuseColor.Set(inMaterial.m_DiffuseColor); + QT3DSVec4 material_specular = + QT3DSVec4(inMaterial.m_SpecularTint[0], inMaterial.m_SpecularTint[1], + inMaterial.m_SpecularTint[2], inMaterial.m_IOR); + shader.m_MaterialSpecular.Set(material_specular); + shader.m_CameraProperties.Set(inCameraVec); + shader.m_FresnelPower.Set(inMaterial.m_FresnelPower); + + if (context.GetConstantBufferSupport()) { + NVRenderConstantBuffer *pLightCb = GetLightConstantBuffer(shader.m_Lights.size()); + // if we have lights we need a light buffer + QT3DS_ASSERT(shader.m_Lights.size() == 0 || pLightCb); + + for (QT3DSU32 idx = 0, end = shader.m_Lights.size(); idx < end && pLightCb; ++idx) { + shader.m_Lights[idx].m_LightData.m_diffuse = + QT3DSVec4(shader.m_Lights[idx].m_LightColor.x * inMaterial.m_DiffuseColor.x, + shader.m_Lights[idx].m_LightColor.y * inMaterial.m_DiffuseColor.y, + shader.m_Lights[idx].m_LightColor.z * inMaterial.m_DiffuseColor.z, 1.0); + + // this is our final change update memory + pLightCb->UpdateRaw(idx * sizeof(SLightSourceShader) + (4 * sizeof(QT3DSI32)), + NVDataRef<QT3DSU8>((QT3DSU8 *)&shader.m_Lights[idx].m_LightData, + sizeof(SLightSourceShader))); + } + // update light buffer to hardware + if (pLightCb) { + QT3DSI32 cgLights = shader.m_Lights.size(); + pLightCb->UpdateRaw(0, NVDataRef<QT3DSU8>((QT3DSU8 *)&cgLights, sizeof(QT3DSI32))); + shader.m_LightsBuffer.Set(); + } + } else { + SLightConstantProperties<SShaderGeneratorGeneratedShader> *pLightConstants + = GetLightConstantProperties(shader); + + // if we have lights we need a light buffer + QT3DS_ASSERT(shader.m_Lights.size() == 0 || pLightConstants); + + for (QT3DSU32 idx = 0, end = shader.m_Lights.size(); + idx < end && pLightConstants; ++idx) { + shader.m_Lights[idx].m_LightData.m_diffuse = + QT3DSVec4(shader.m_Lights[idx].m_LightColor.x * inMaterial.m_DiffuseColor.x, + shader.m_Lights[idx].m_LightColor.y * inMaterial.m_DiffuseColor.y, + shader.m_Lights[idx].m_LightColor.z * inMaterial.m_DiffuseColor.z, 1.0); + } + // update light buffer to hardware + if (pLightConstants) + pLightConstants->updateLights(shader); + } + + shader.m_MaterialDiffuseLightAmbientTotal.Set( + QT3DSVec3(shader.m_LightAmbientTotal.x * inMaterial.m_DiffuseColor[0], + shader.m_LightAmbientTotal.y * inMaterial.m_DiffuseColor[1], + shader.m_LightAmbientTotal.z * inMaterial.m_DiffuseColor[2])); + + shader.m_MaterialProperties.Set(QT3DSVec4( + inMaterial.m_SpecularAmount, inMaterial.m_SpecularRoughness, emissivePower, 0.0f)); + shader.m_BumpAmount.Set(inMaterial.m_BumpAmount); + shader.m_DisplaceAmount.Set(inMaterial.m_DisplaceAmount); + shader.m_TranslucentFalloff.Set(inMaterial.m_TranslucentFalloff); + shader.m_DiffuseLightWrap.Set(inMaterial.m_DiffuseLightWrap); + QT3DSU32 imageIdx = 0; + for (SRenderableImage *theImage = inFirstImage; theImage; + theImage = theImage->m_NextImage, ++imageIdx) + SetImageShaderVariables(shader, *theImage, imageIdx); + + qt3ds::render::NVRenderBlendFunctionArgument blendFunc; + qt3ds::render::NVRenderBlendEquationArgument blendEqua(NVRenderBlendEquation::Add, + NVRenderBlendEquation::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.m_BlendMode) { + case DefaultMaterialBlendMode::Screen: + blendFunc = qt3ds::render::NVRenderBlendFunctionArgument( + NVRenderSrcBlendFunc::SrcAlpha, NVRenderDstBlendFunc::One, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::One); + break; + case DefaultMaterialBlendMode::Multiply: + blendFunc = qt3ds::render::NVRenderBlendFunctionArgument( + NVRenderSrcBlendFunc::DstColor, NVRenderDstBlendFunc::Zero, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::One); + break; + case DefaultMaterialBlendMode::Overlay: + // SW fallback is not using blend equation + // note blend func is not used here anymore + if (context.IsAdvancedBlendHwSupported() || context.IsAdvancedBlendHwSupportedKHR()) + blendEqua = qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::Overlay, NVRenderBlendEquation::Overlay); + break; + case DefaultMaterialBlendMode::ColorBurn: + // SW fallback is not using blend equation + // note blend func is not used here anymore + if (context.IsAdvancedBlendHwSupported() || context.IsAdvancedBlendHwSupportedKHR()) + blendEqua = qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::ColorBurn, NVRenderBlendEquation::ColorBurn); + break; + case DefaultMaterialBlendMode::ColorDodge: + // SW fallback is not using blend equation + // note blend func is not used here anymore + if (context.IsAdvancedBlendHwSupported() || context.IsAdvancedBlendHwSupportedKHR()) + blendEqua = qt3ds::render::NVRenderBlendEquationArgument( + NVRenderBlendEquation::ColorDodge, NVRenderBlendEquation::ColorDodge); + break; + default: + blendFunc = qt3ds::render::NVRenderBlendFunctionArgument( + NVRenderSrcBlendFunc::SrcAlpha, NVRenderDstBlendFunc::OneMinusSrcAlpha, + NVRenderSrcBlendFunc::One, NVRenderDstBlendFunc::OneMinusSrcAlpha); + break; + } + context.SetBlendFunction(blendFunc); + context.SetBlendEquation(blendEqua); + } + void SetMaterialProperties(NVRenderShaderProgram &inProgram, + const SGraphObject &inMaterial, const QT3DSVec2 &inCameraVec, + const QT3DSMat44 &inModelViewProjection, + const QT3DSMat33 &inNormalMatrix, + const QT3DSMat44 &inGlobalTransform, + SRenderableImage *inFirstImage, QT3DSF32 inOpacity, + SLayerGlobalRenderProperties inRenderProperties) override + { + const SDefaultMaterial &theMaterial(static_cast<const SDefaultMaterial &>(inMaterial)); + QT3DS_ASSERT(inMaterial.m_Type == GraphObjectTypes::DefaultMaterial); + + SetGlobalProperties(inProgram, inRenderProperties.m_Layer, inRenderProperties.m_Camera, + inRenderProperties.m_CameraDirection, inRenderProperties.m_Lights, + inRenderProperties.m_LightDirections, + inRenderProperties.m_ShadowMapManager); + SetMaterialProperties(inProgram, theMaterial, inCameraVec, inModelViewProjection, + inNormalMatrix, inGlobalTransform, inFirstImage, inOpacity, + inRenderProperties.m_DepthTexture, inRenderProperties.m_SSaoTexture, + inRenderProperties.m_LightProbe, inRenderProperties.m_LightProbe2, + inRenderProperties.m_ProbeHorizon, inRenderProperties.m_ProbeBright, + inRenderProperties.m_Probe2Window, inRenderProperties.m_Probe2Pos, + inRenderProperties.m_Probe2Fade, inRenderProperties.m_ProbeFOV); + } + + SLightConstantProperties<SShaderGeneratorGeneratedShader> *GetLightConstantProperties(SShaderGeneratorGeneratedShader &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 SLightConstantProperties<SShaderGeneratorGeneratedShader>( + shader, m_LightsAsSeparateUniforms); + } + return shader.m_lightConstantProperties; + } +}; +} + +IDefaultMaterialShaderGenerator & +IDefaultMaterialShaderGenerator::CreateDefaultMaterialShaderGenerator(IQt3DSRenderContext &inRc) +{ + return *QT3DS_NEW(inRc.GetAllocator(), SShaderGenerator)(inRc); +} |