From d0537e7ead264c855f0bf948a924d2d59c732298 Mon Sep 17 00:00:00 2001 From: Andrzej Kapolka Date: Tue, 6 May 2014 17:32:10 -0700 Subject: [PATCH] Specular map support. --- .../shaders/model_normal_specular_map.frag | 47 ++++++++ .../resources/shaders/model_specular_map.frag | 35 ++++++ interface/src/ModelUploader.cpp | 8 ++ interface/src/renderer/GeometryCache.cpp | 17 ++- interface/src/renderer/GeometryCache.h | 1 + interface/src/renderer/Model.cpp | 114 +++++++++++++++--- interface/src/renderer/Model.h | 9 +- libraries/fbx/src/FBXReader.cpp | 22 ++++ libraries/fbx/src/FBXReader.h | 3 + 9 files changed, 235 insertions(+), 21 deletions(-) create mode 100644 interface/resources/shaders/model_normal_specular_map.frag create mode 100644 interface/resources/shaders/model_specular_map.frag diff --git a/interface/resources/shaders/model_normal_specular_map.frag b/interface/resources/shaders/model_normal_specular_map.frag new file mode 100644 index 0000000000..79761446b1 --- /dev/null +++ b/interface/resources/shaders/model_normal_specular_map.frag @@ -0,0 +1,47 @@ +#version 120 + +// +// model_normal_specular_map.frag +// fragment shader +// +// Created by Andrzej Kapolka on 5/6/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// the diffuse texture +uniform sampler2D diffuseMap; + +// the normal map texture +uniform sampler2D normalMap; + +// the specular map texture +uniform sampler2D specularMap; + +// the interpolated normal +varying vec4 interpolatedNormal; + +// the interpolated tangent +varying vec4 interpolatedTangent; + +void main(void) { + vec3 normalizedNormal = normalize(vec3(interpolatedNormal)); + vec3 normalizedTangent = normalize(vec3(interpolatedTangent)); + vec3 normalizedBitangent = normalize(cross(normalizedNormal, normalizedTangent)); + vec3 localNormal = vec3(texture2D(normalMap, gl_TexCoord[0].st)) * 2.0 - vec3(1.0, 1.0, 1.0); + + // compute the base color based on OpenGL lighting model + vec4 viewNormal = vec4(normalizedTangent * localNormal.x + + normalizedBitangent * localNormal.y + normalizedNormal * localNormal.z, 0.0); + vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + + gl_FrontLightProduct[0].diffuse * max(0.0, dot(viewNormal, gl_LightSource[0].position))); + + // compute the specular component (sans exponent) + float specular = max(0.0, dot(normalize(gl_LightSource[0].position + vec4(0.0, 0.0, 1.0, 0.0)), viewNormal)); + + // modulate texture by base color and add specular contribution + gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) + vec4(pow(specular, gl_FrontMaterial.shininess) * + gl_FrontLightProduct[0].specular.rgb * texture2D(specularMap, gl_TexCoord[0].st).rgb, 0.0); +} diff --git a/interface/resources/shaders/model_specular_map.frag b/interface/resources/shaders/model_specular_map.frag new file mode 100644 index 0000000000..972a8e2de6 --- /dev/null +++ b/interface/resources/shaders/model_specular_map.frag @@ -0,0 +1,35 @@ +#version 120 + +// +// model_specular_map.frag +// fragment shader +// +// Created by Andrzej Kapolka on 5/6/14. +// Copyright 2014 High Fidelity, Inc. +// +// Distributed under the Apache License, Version 2.0. +// See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html +// + +// the diffuse texture +uniform sampler2D diffuseMap; + +// the specular texture +uniform sampler2D specularMap; + +// the interpolated normal +varying vec4 normal; + +void main(void) { + // compute the base color based on OpenGL lighting model + vec4 normalizedNormal = normalize(normal); + vec4 base = gl_Color * (gl_FrontLightModelProduct.sceneColor + gl_FrontLightProduct[0].ambient + + gl_FrontLightProduct[0].diffuse * max(0.0, dot(normalizedNormal, gl_LightSource[0].position))); + + // compute the specular component (sans exponent) + float specular = max(0.0, dot(normalize(gl_LightSource[0].position + vec4(0.0, 0.0, 1.0, 0.0)), normalizedNormal)); + + // modulate texture by base color and add specular contribution + gl_FragColor = base * texture2D(diffuseMap, gl_TexCoord[0].st) + vec4(pow(specular, gl_FrontMaterial.shininess) * + gl_FrontLightProduct[0].specular.rgb * texture2D(specularMap, gl_TexCoord[0].st).rgb, 0.0); +} diff --git a/interface/src/ModelUploader.cpp b/interface/src/ModelUploader.cpp index bf6a868368..08719f0f25 100644 --- a/interface/src/ModelUploader.cpp +++ b/interface/src/ModelUploader.cpp @@ -443,6 +443,14 @@ bool ModelUploader::addTextures(const QString& texdir, const FBXGeometry& geomet } added.insert(part.normalTexture.filename); } + if (!part.specularTexture.filename.isEmpty() && part.specularTexture.content.isEmpty() && + !added.contains(part.specularTexture.filename)) { + if (!addPart(texdir + "/" + part.specularTexture.filename, + QString("texture%1").arg(++_texturesCount), true)) { + return false; + } + added.insert(part.specularTexture.filename); + } } } diff --git a/interface/src/renderer/GeometryCache.cpp b/interface/src/renderer/GeometryCache.cpp index 6e93fc77af..8d31cdce1d 100644 --- a/interface/src/renderer/GeometryCache.cpp +++ b/interface/src/renderer/GeometryCache.cpp @@ -342,7 +342,8 @@ bool NetworkGeometry::isLoadedWithTextures() const { foreach (const NetworkMesh& mesh, _meshes) { foreach (const NetworkMeshPart& part, mesh.parts) { if ((part.diffuseTexture && !part.diffuseTexture->isLoaded()) || - (part.normalTexture && !part.normalTexture->isLoaded())) { + (part.normalTexture && !part.normalTexture->isLoaded()) || + (part.specularTexture && !part.specularTexture->isLoaded())) { return false; } } @@ -416,6 +417,9 @@ void NetworkGeometry::setLoadPriority(const QPointer& owner, float prio if (part.normalTexture) { part.normalTexture->setLoadPriority(owner, priority); } + if (part.specularTexture) { + part.specularTexture->setLoadPriority(owner, priority); + } } } } @@ -433,6 +437,9 @@ void NetworkGeometry::setLoadPriorities(const QHash, float>& p if (part.normalTexture) { part.normalTexture->setLoadPriorities(priorities); } + if (part.specularTexture) { + part.specularTexture->setLoadPriorities(priorities); + } } } } @@ -450,6 +457,9 @@ void NetworkGeometry::clearLoadPriority(const QPointer& owner) { if (part.normalTexture) { part.normalTexture->clearLoadPriority(owner); } + if (part.specularTexture) { + part.specularTexture->clearLoadPriority(owner); + } } } } @@ -566,6 +576,11 @@ void NetworkGeometry::setGeometry(const FBXGeometry& geometry) { _textureBase.resolved(QUrl(part.normalTexture.filename)), true, false, part.normalTexture.content); networkPart.normalTexture->setLoadPriorities(_loadPriorities); } + if (!part.specularTexture.filename.isEmpty()) { + networkPart.specularTexture = Application::getInstance()->getTextureCache()->getTexture( + _textureBase.resolved(QUrl(part.specularTexture.filename)), true, false, part.specularTexture.content); + networkPart.specularTexture->setLoadPriorities(_loadPriorities); + } networkMesh.parts.append(networkPart); totalIndices += (part.quadIndices.size() + part.triangleIndices.size()); diff --git a/interface/src/renderer/GeometryCache.h b/interface/src/renderer/GeometryCache.h index 0ad4f73904..a9b274fedc 100644 --- a/interface/src/renderer/GeometryCache.h +++ b/interface/src/renderer/GeometryCache.h @@ -124,6 +124,7 @@ public: QSharedPointer diffuseTexture; QSharedPointer normalTexture; + QSharedPointer specularTexture; bool isTranslucent() const; }; diff --git a/interface/src/renderer/Model.cpp b/interface/src/renderer/Model.cpp index b8b4f1f2a0..2a9a55abf0 100644 --- a/interface/src/renderer/Model.cpp +++ b/interface/src/renderer/Model.cpp @@ -56,13 +56,20 @@ Model::~Model() { ProgramObject Model::_program; ProgramObject Model::_normalMapProgram; +ProgramObject Model::_specularMapProgram; +ProgramObject Model::_normalSpecularMapProgram; ProgramObject Model::_shadowProgram; ProgramObject Model::_skinProgram; ProgramObject Model::_skinNormalMapProgram; +ProgramObject Model::_skinSpecularMapProgram; +ProgramObject Model::_skinNormalSpecularMapProgram; ProgramObject Model::_skinShadowProgram; int Model::_normalMapTangentLocation; +int Model::_normalSpecularMapTangentLocation; Model::SkinLocations Model::_skinLocations; Model::SkinLocations Model::_skinNormalMapLocations; +Model::SkinLocations Model::_skinSpecularMapLocations; +Model::SkinLocations Model::_skinNormalSpecularMapLocations; Model::SkinLocations Model::_skinShadowLocations; void Model::setScale(const glm::vec3& scale) { @@ -92,7 +99,7 @@ void Model::setOffset(const glm::vec3& offset) { } -void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locations) { +void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locations, int specularTextureUnit) { program.bind(); locations.clusterMatrices = program.uniformLocation("clusterMatrices"); locations.clusterIndices = program.attributeLocation("clusterIndices"); @@ -100,6 +107,7 @@ void Model::initSkinProgram(ProgramObject& program, Model::SkinLocations& locati locations.tangent = program.attributeLocation("tangent"); program.setUniformValue("diffuseMap", 0); program.setUniformValue("normalMap", 1); + program.setUniformValue("specularMap", specularTextureUnit); program.release(); } @@ -162,10 +170,10 @@ void Model::init() { _program.setUniformValue("texture", 0); _program.release(); - _normalMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() - + "shaders/model_normal_map.vert"); - _normalMapProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() - + "shaders/model_normal_map.frag"); + _normalMapProgram.addShaderFromSourceFile(QGLShader::Vertex, + Application::resourcesPath() + "shaders/model_normal_map.vert"); + _normalMapProgram.addShaderFromSourceFile(QGLShader::Fragment, + Application::resourcesPath() + "shaders/model_normal_map.frag"); _normalMapProgram.link(); _normalMapProgram.bind(); @@ -174,27 +182,65 @@ void Model::init() { _normalMapTangentLocation = _normalMapProgram.attributeLocation("tangent"); _normalMapProgram.release(); + _specularMapProgram.addShaderFromSourceFile(QGLShader::Vertex, + Application::resourcesPath() + "shaders/model.vert"); + _specularMapProgram.addShaderFromSourceFile(QGLShader::Fragment, + Application::resourcesPath() + "shaders/model_specular_map.frag"); + _specularMapProgram.link(); + + _specularMapProgram.bind(); + _specularMapProgram.setUniformValue("diffuseMap", 0); + _specularMapProgram.setUniformValue("specularMap", 1); + _specularMapProgram.release(); + + _normalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex, + Application::resourcesPath() + "shaders/model_normal_map.vert"); + _normalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment, + Application::resourcesPath() + "shaders/model_normal_specular_map.frag"); + _normalSpecularMapProgram.link(); + + _normalSpecularMapProgram.bind(); + _normalSpecularMapProgram.setUniformValue("diffuseMap", 0); + _normalSpecularMapProgram.setUniformValue("normalMap", 1); + _normalSpecularMapProgram.setUniformValue("specularMap", 2); + _normalSpecularMapTangentLocation = _normalMapProgram.attributeLocation("tangent"); + _normalSpecularMapProgram.release(); + _shadowProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/model_shadow.vert"); - _shadowProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + - "shaders/model_shadow.frag"); + _shadowProgram.addShaderFromSourceFile(QGLShader::Fragment, + Application::resourcesPath() + "shaders/model_shadow.frag"); _shadowProgram.link(); - _skinProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() - + "shaders/skin_model.vert"); - _skinProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() - + "shaders/model.frag"); + _skinProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/skin_model.vert"); + _skinProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() + "shaders/model.frag"); _skinProgram.link(); initSkinProgram(_skinProgram, _skinLocations); - _skinNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() - + "shaders/skin_model_normal_map.vert"); - _skinNormalMapProgram.addShaderFromSourceFile(QGLShader::Fragment, Application::resourcesPath() - + "shaders/model_normal_map.frag"); + _skinNormalMapProgram.addShaderFromSourceFile(QGLShader::Vertex, + Application::resourcesPath() + "shaders/skin_model_normal_map.vert"); + _skinNormalMapProgram.addShaderFromSourceFile(QGLShader::Fragment, + Application::resourcesPath() + "shaders/model_normal_map.frag"); _skinNormalMapProgram.link(); initSkinProgram(_skinNormalMapProgram, _skinNormalMapLocations); + _skinSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex, + Application::resourcesPath() + "shaders/skin_model.vert"); + _skinSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment, + Application::resourcesPath() + "shaders/model_specular_map.frag"); + _skinSpecularMapProgram.link(); + + initSkinProgram(_skinSpecularMapProgram, _skinSpecularMapLocations); + + _skinNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Vertex, + Application::resourcesPath() + "shaders/skin_model_normal_map.vert"); + _skinNormalSpecularMapProgram.addShaderFromSourceFile(QGLShader::Fragment, + Application::resourcesPath() + "shaders/model_normal_specular_map.frag"); + _skinNormalSpecularMapProgram.link(); + + initSkinProgram(_skinNormalSpecularMapProgram, _skinNormalSpecularMapLocations, 2); + _skinShadowProgram.addShaderFromSourceFile(QGLShader::Vertex, Application::resourcesPath() + "shaders/skin_model_shadow.vert"); _skinShadowProgram.addShaderFromSourceFile(QGLShader::Fragment, @@ -1331,15 +1377,29 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) { ProgramObject* program = &_program; ProgramObject* skinProgram = &_skinProgram; SkinLocations* skinLocations = &_skinLocations; + GLenum specularTextureUnit = 0; if (mode == SHADOW_RENDER_MODE) { program = &_shadowProgram; skinProgram = &_skinShadowProgram; skinLocations = &_skinShadowLocations; } else if (!mesh.tangents.isEmpty()) { - program = &_normalMapProgram; - skinProgram = &_skinNormalMapProgram; - skinLocations = &_skinNormalMapLocations; + if (mesh.hasSpecularTexture()) { + program = &_normalSpecularMapProgram; + skinProgram = &_skinNormalSpecularMapProgram; + skinLocations = &_skinNormalSpecularMapLocations; + specularTextureUnit = GL_TEXTURE2; + + } else { + program = &_normalMapProgram; + skinProgram = &_skinNormalMapProgram; + skinLocations = &_skinNormalMapLocations; + } + } else if (mesh.hasSpecularTexture()) { + program = &_specularMapProgram; + skinProgram = &_skinSpecularMapProgram; + skinLocations = &_skinSpecularMapLocations; + specularTextureUnit = GL_TEXTURE1; } const MeshState& state = _meshStates.at(i); @@ -1427,13 +1487,23 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) { glBindTexture(GL_TEXTURE_2D, !diffuseMap ? Application::getInstance()->getTextureCache()->getWhiteTextureID() : diffuseMap->getID()); + if (!mesh.tangents.isEmpty()) { + specularTextureUnit = GL_TEXTURE2; glActiveTexture(GL_TEXTURE1); Texture* normalMap = networkPart.normalTexture.data(); glBindTexture(GL_TEXTURE_2D, !normalMap ? Application::getInstance()->getTextureCache()->getBlueTextureID() : normalMap->getID()); glActiveTexture(GL_TEXTURE0); } + + if (specularTextureUnit) { + glActiveTexture(specularTextureUnit); + Texture* specularMap = networkPart.specularTexture.data(); + glBindTexture(GL_TEXTURE_2D, !specularMap ? + Application::getInstance()->getTextureCache()->getWhiteTextureID() : specularMap->getID()); + glActiveTexture(GL_TEXTURE0); + } } glDrawRangeElementsEXT(GL_QUADS, 0, vertexCount - 1, part.quadIndices.size(), GL_UNSIGNED_INT, (void*)offset); offset += part.quadIndices.size() * sizeof(int); @@ -1456,7 +1526,13 @@ void Model::renderMeshes(float alpha, RenderMode mode, bool translucent) { activeProgram->disableAttributeArray(tangentLocation); } - + + if (specularTextureUnit) { + glActiveTexture(specularTextureUnit); + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE0); + } + if (state.clusterMatrices.size() > 1) { skinProgram->disableAttributeArray(skinLocations->clusterIndices); skinProgram->disableAttributeArray(skinLocations->clusterWeights); diff --git a/interface/src/renderer/Model.h b/interface/src/renderer/Model.h index 6a79772ca7..1a469c8122 100644 --- a/interface/src/renderer/Model.h +++ b/interface/src/renderer/Model.h @@ -318,12 +318,17 @@ private: static ProgramObject _program; static ProgramObject _normalMapProgram; + static ProgramObject _specularMapProgram; + static ProgramObject _normalSpecularMapProgram; static ProgramObject _shadowProgram; static ProgramObject _skinProgram; static ProgramObject _skinNormalMapProgram; + static ProgramObject _skinSpecularMapProgram; + static ProgramObject _skinNormalSpecularMapProgram; static ProgramObject _skinShadowProgram; static int _normalMapTangentLocation; + static int _normalSpecularMapTangentLocation; class SkinLocations { public: @@ -335,9 +340,11 @@ private: static SkinLocations _skinLocations; static SkinLocations _skinNormalMapLocations; + static SkinLocations _skinSpecularMapLocations; + static SkinLocations _skinNormalSpecularMapLocations; static SkinLocations _skinShadowLocations; - static void initSkinProgram(ProgramObject& program, SkinLocations& locations); + static void initSkinProgram(ProgramObject& program, SkinLocations& locations, int specularTextureUnit = 1); }; Q_DECLARE_METATYPE(QPointer) diff --git a/libraries/fbx/src/FBXReader.cpp b/libraries/fbx/src/FBXReader.cpp index 40a7d56c0d..d637526067 100644 --- a/libraries/fbx/src/FBXReader.cpp +++ b/libraries/fbx/src/FBXReader.cpp @@ -54,6 +54,15 @@ void Extents::addPoint(const glm::vec3& point) { maximum = glm::max(maximum, point); } +bool FBXMesh::hasSpecularTexture() const { + foreach (const FBXMeshPart& part, parts) { + if (!part.specularTexture.filename.isEmpty()) { + return true; + } + } + return false; +} + QStringList FBXGeometry::getJointNames() const { QStringList names; foreach (const FBXJoint& joint, joints) { @@ -976,6 +985,7 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) QHash materials; QHash diffuseTextures; QHash bumpTextures; + QHash specularTextures; QHash localRotations; QHash xComponents; QHash yComponents; @@ -1330,6 +1340,9 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) } else if (type.contains("bump") || type.contains("normal")) { bumpTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); + } else if (type.contains("specular")) { + specularTextures.insert(getID(connection.properties, 2), getID(connection.properties, 1)); + } else if (type == "lcl rotation") { localRotations.insert(getID(connection.properties, 2), getID(connection.properties, 1)); @@ -1546,6 +1559,12 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) generateTangents = true; } + FBXTexture specularTexture; + QString specularTextureID = specularTextures.value(childID); + if (!specularTextureID.isNull()) { + specularTexture = getTexture(specularTextureID, textureFilenames, textureContent); + } + for (int j = 0; j < extracted.partMaterialTextures.size(); j++) { if (extracted.partMaterialTextures.at(j).first == materialIndex) { FBXMeshPart& part = extracted.mesh.parts[j]; @@ -1558,6 +1577,9 @@ FBXGeometry extractFBXGeometry(const FBXNode& node, const QVariantHash& mapping) if (!normalTexture.filename.isNull()) { part.normalTexture = normalTexture; } + if (!specularTexture.filename.isNull()) { + part.specularTexture = specularTexture; + } } } materialIndex++; diff --git a/libraries/fbx/src/FBXReader.h b/libraries/fbx/src/FBXReader.h index a4b04825ef..51e7380181 100644 --- a/libraries/fbx/src/FBXReader.h +++ b/libraries/fbx/src/FBXReader.h @@ -129,6 +129,7 @@ public: FBXTexture diffuseTexture; FBXTexture normalTexture; + FBXTexture specularTexture; }; /// A single mesh (with optional blendshapes) extracted from an FBX document. @@ -150,6 +151,8 @@ public: bool isEye; QVector blendshapes; + + bool hasSpecularTexture() const; }; /// A single animation frame extracted from an FBX document.