From 8071c04e98d1837ae2f2bef4c28b0c6c1f121e47 Mon Sep 17 00:00:00 2001 From: Olivier Prat Date: Thu, 27 Apr 2017 10:31:06 +0200 Subject: [PATCH] Added fade effect prototype version --- interface/resources/images/fadeMask.png | Bin 0 -> 68932 bytes libraries/render-utils/src/Fade.slh | 59 +++++++++++++++ .../render-utils/src/MeshPartPayload.cpp | 68 +++++++++++------ libraries/render-utils/src/MeshPartPayload.h | 7 +- .../render-utils/src/RenderDeferredTask.cpp | 25 ++++++- .../render-utils/src/RenderDeferredTask.h | 8 +- .../render-utils/src/RenderPipelines.cpp | 22 ++++++ libraries/render-utils/src/model_fade.slf | 65 ++++++++++++++++ libraries/render-utils/src/model_fade.slv | 44 +++++++++++ .../src/model_normal_map_fade.slf | 70 ++++++++++++++++++ .../src/model_normal_map_fade.slv | 48 ++++++++++++ libraries/render/src/render/ShapePipeline.cpp | 4 + libraries/render/src/render/ShapePipeline.h | 11 +++ libraries/shared/src/RenderArgs.h | 3 +- 14 files changed, 407 insertions(+), 27 deletions(-) create mode 100644 interface/resources/images/fadeMask.png create mode 100644 libraries/render-utils/src/Fade.slh create mode 100644 libraries/render-utils/src/model_fade.slf create mode 100644 libraries/render-utils/src/model_fade.slv create mode 100644 libraries/render-utils/src/model_normal_map_fade.slf create mode 100644 libraries/render-utils/src/model_normal_map_fade.slv diff --git a/interface/resources/images/fadeMask.png b/interface/resources/images/fadeMask.png new file mode 100644 index 0000000000000000000000000000000000000000..9c342ba7888ab0d66a59dc448e30d9bdfd8204d7 GIT binary patch literal 68932 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 literal 0 HcmV?d00001 diff --git a/libraries/render-utils/src/Fade.slh b/libraries/render-utils/src/Fade.slh new file mode 100644 index 0000000000..a40f1048a8 --- /dev/null +++ b/libraries/render-utils/src/Fade.slh @@ -0,0 +1,59 @@ +// Generated on <$_SCRIBE_DATE$> +// +// Created by Olivier Prat on 04/12/17. +// Copyright 2017 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 +// +<@if not FADE_SLH@> +<@def FADE_SLH@> + +<@func declareFade()@> + +struct Fade { + vec3 _Offset; + float _Percent; +}; + +uniform fadeBuffer { + Fade fade; +}; + +uniform sampler2D fadeMaskMap; + +vec2 hash2D(vec3 position) { + return position.xy* vec2(0.1677, 0.221765) + position.z*0.561; +} + +float evalFadeMask(vec3 position, vec3 normal) { + const float FADE_MASK_INV_SCALE = 3.0; + + // Do tri-linear interpolation + vec3 noisePosition = position * FADE_MASK_INV_SCALE; + vec3 noisePositionFloored = floor(noisePosition) + fade._Offset; + vec3 noisePositionFraction = fract(noisePosition); + float noiseLowXLowYLowZ = textureLod(fadeMaskMap, hash2D(noisePositionFloored), 0).r; + float noiseLowXHighYLowZ = textureLod(fadeMaskMap, hash2D(noisePositionFloored+vec3(0,1,0)), 0).r; + float noiseHighXLowYLowZ = textureLod(fadeMaskMap, hash2D(noisePositionFloored+vec3(1,0,0)), 0).r; + float noiseHighXHighYLowZ = textureLod(fadeMaskMap, hash2D(noisePositionFloored+vec3(1,1,0)), 0).r; + float noiseLowXLowYHighZ = textureLod(fadeMaskMap, hash2D(noisePositionFloored+vec3(0,0,1)), 0).r; + float noiseLowXHighYHighZ = textureLod(fadeMaskMap, hash2D(noisePositionFloored+vec3(0,1,1)), 0).r; + float noiseHighXLowYHighZ = textureLod(fadeMaskMap, hash2D(noisePositionFloored+vec3(1,0,1)), 0).r; + float noiseHighXHighYHighZ = textureLod(fadeMaskMap, hash2D(noisePositionFloored+vec3(1,1,1)), 0).r; + vec4 maskLowZ = vec4(noiseLowXLowYLowZ, noiseLowXHighYLowZ, noiseHighXLowYLowZ, noiseHighXHighYLowZ); + vec4 maskHighZ = vec4(noiseLowXLowYHighZ, noiseLowXHighYHighZ, noiseHighXLowYHighZ, noiseHighXHighYHighZ); + vec4 maskXY = mix(maskLowZ, maskHighZ, noisePositionFraction.z); + vec2 maskY = mix(maskXY.xy, maskXY.zw, noisePositionFraction.x); + + return mix(maskY.x, maskY.y, noisePositionFraction.y); +} + +void applyFade(vec3 position, vec3 normal) { + if (evalFadeMask(position, normal) < fade._Percent) { + discard; + } +} +<@endfunc@> + +<@endif@> \ No newline at end of file diff --git a/libraries/render-utils/src/MeshPartPayload.cpp b/libraries/render-utils/src/MeshPartPayload.cpp index 51ce0fffa7..57070479b6 100644 --- a/libraries/render-utils/src/MeshPartPayload.cpp +++ b/libraries/render-utils/src/MeshPartPayload.cpp @@ -321,11 +321,20 @@ template <> void payloadRender(const ModelMeshPartPayload::Pointer& payload, Ren } } +struct ModelMeshPartPayload::Fade +{ + glm::vec3 _offset; // The noise offset + float _percent; // The fade percent +}; + ModelMeshPartPayload::ModelMeshPartPayload(Model* model, int _meshIndex, int partIndex, int shapeIndex, const Transform& transform, const Transform& offsetTransform) : _model(model), _meshIndex(_meshIndex), _shapeID(shapeIndex) { + Fade fade; + _fadeBuffer = gpu::BufferView(std::make_shared(sizeof(Fade), (const gpu::Byte*) &fade)); + assert(_model && _model->isLoaded()); auto& modelMesh = _model->getGeometry()->getMeshes().at(_meshIndex); updateMeshPart(modelMesh, partIndex); @@ -461,7 +470,7 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { ShapeKey::Builder builder; builder.withMaterial(); - if (isTranslucent || _fadeState != FADE_COMPLETE) { + if (isTranslucent) { builder.withTranslucent(); } if (hasTangents) { @@ -482,6 +491,9 @@ ShapeKey ModelMeshPartPayload::getShapeKey() const { if (wireframe) { builder.withWireframe(); } + if (_fadeState != FADE_COMPLETE) { + builder.withFade(); + } return builder.build(); } @@ -501,12 +513,6 @@ void ModelMeshPartPayload::bindMesh(gpu::Batch& batch) const { batch.setInputBuffer(1, _model->_blendedVertexBuffers[_meshIndex], _drawMesh->getNumVertices() * sizeof(glm::vec3), sizeof(glm::vec3)); batch.setInputStream(2, _drawMesh->getVertexStream().makeRangedStream(2)); } - - if (_fadeState != FADE_COMPLETE) { - batch._glColor4f(1.0f, 1.0f, 1.0f, computeFadeAlpha()); - } else if (!_hasColorAttrib) { - batch._glColor4f(1.0f, 1.0f, 1.0f, 1.0f); - } } void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const { @@ -517,23 +523,40 @@ void ModelMeshPartPayload::bindTransform(gpu::Batch& batch, const ShapePipeline: batch.setModelTransform(_transform); } -float ModelMeshPartPayload::computeFadeAlpha() const { - if (_fadeState == FADE_WAITING_TO_START) { - return 0.0f; +float ModelMeshPartPayload::computeFadePercent(bool isDebugEnabled) const { + if (!isDebugEnabled) { + if (_fadeState == FADE_WAITING_TO_START) { + return 0.0f; + } + float fadeAlpha = 1.0f; + const float INV_FADE_PERIOD = 1.0f / (float)(1 * USECS_PER_SECOND); + float fraction = (float)(usecTimestampNow() - _fadeStartTime) * INV_FADE_PERIOD; + if (fraction < 1.0f) { + fadeAlpha = Interpolate::simpleNonLinearBlend(fraction); + } + if (fadeAlpha >= 1.0f) { + _fadeState = FADE_COMPLETE; + // when fade-in completes we flag model for one last "render item update" + _model->setRenderItemsNeedUpdate(); + return 1.0f; + } + return Interpolate::simpleNonLinearBlend(fadeAlpha); } - float fadeAlpha = 1.0f; - const float INV_FADE_PERIOD = 1.0f / (float)(1 * USECS_PER_SECOND); - float fraction = (float)(usecTimestampNow() - _fadeStartTime) * INV_FADE_PERIOD; - if (fraction < 1.0f) { - fadeAlpha = Interpolate::simpleNonLinearBlend(fraction); + else { + // Animate fade for debugging purposes during repeated 3 second cycles + return (usecTimestampNow() % (3 * USECS_PER_SECOND)) / (float)(3 * USECS_PER_SECOND); } - if (fadeAlpha >= 1.0f) { - _fadeState = FADE_COMPLETE; - // when fade-in completes we flag model for one last "render item update" - _model->setRenderItemsNeedUpdate(); - return 1.0f; +} + +void ModelMeshPartPayload::bindFade(gpu::Batch& batch, bool isDebugEnabled) const { + if (_fadeState != FADE_COMPLETE || isDebugEnabled) { + auto& fade = _fadeBuffer.edit(); + glm::vec3 offset = _transform.getTranslation(); + + fade._percent = computeFadePercent(isDebugEnabled); + fade._offset = offset; + batch.setUniformBuffer(ShapePipeline::Slot::BUFFER::FADE, _fadeBuffer); } - return Interpolate::simpleNonLinearBlend(fadeAlpha); } void ModelMeshPartPayload::render(RenderArgs* args) const { @@ -576,6 +599,9 @@ void ModelMeshPartPayload::render(RenderArgs* args) const { // apply material properties bindMaterial(batch, locations, args->_enableTexturing); + // Apply fade effect + bindFade(batch, (args->_debugFlags & RenderArgs::RENDER_DEBUG_FADE) != 0); + args->_details._materialSwitches++; // Draw! diff --git a/libraries/render-utils/src/MeshPartPayload.h b/libraries/render-utils/src/MeshPartPayload.h index ef74011c40..ffffb9ec88 100644 --- a/libraries/render-utils/src/MeshPartPayload.h +++ b/libraries/render-utils/src/MeshPartPayload.h @@ -93,7 +93,7 @@ public: const Transform& boundTransform, const gpu::BufferPointer& buffer); - float computeFadeAlpha() const; + float computeFadePercent(bool isDebugEnabled) const; // Render Item interface render::ItemKey getKey() const override; @@ -104,6 +104,7 @@ public: // ModelMeshPartPayload functions to perform render void bindMesh(gpu::Batch& batch) const override; void bindTransform(gpu::Batch& batch, const render::ShapePipeline::LocationsPointer locations, RenderArgs::RenderMode renderMode) const override; + void bindFade(gpu::Batch& batch, bool isDebugEnabled) const; void initCache(); @@ -119,6 +120,10 @@ public: bool _isBlendShaped{ false }; private: + + struct Fade; + + mutable gpu::BufferView _fadeBuffer; mutable quint64 _fadeStartTime { 0 }; mutable uint8_t _fadeState { FADE_WAITING_TO_START }; }; diff --git a/libraries/render-utils/src/RenderDeferredTask.cpp b/libraries/render-utils/src/RenderDeferredTask.cpp index 313b176f19..1dd212fea3 100644 --- a/libraries/render-utils/src/RenderDeferredTask.cpp +++ b/libraries/render-utils/src/RenderDeferredTask.cpp @@ -86,9 +86,13 @@ void RenderDeferredTask::build(JobModel& task, const render::Varying& input, ren const auto deferredFramebuffer = prepareDeferredOutputs.getN(0); const auto lightingFramebuffer = prepareDeferredOutputs.getN(1); + // Fade texture mask + auto texturePath = PathUtils::resourcesPath() + "images/fadeMask.png"; + auto fadeMaskMap = DependencyManager::get()->getImageTexture(texturePath, image::TextureUsage::STRICT_TEXTURE); + // Render opaque objects in DeferredBuffer const auto opaqueInputs = DrawStateSortDeferred::Inputs(opaques, lightingModel).hasVarying(); - task.addJob("DrawOpaqueDeferred", opaqueInputs, shapePlumber); + task.addJob("DrawOpaqueDeferred", opaqueInputs, shapePlumber, fadeMaskMap); // Once opaque is all rendered create stencil background task.addJob("DrawOpaqueStencil", deferredFramebuffer); @@ -310,11 +314,21 @@ void DrawStateSortDeferred::run(const RenderContextPointer& renderContext, const // Setup lighting model for all items; batch.setUniformBuffer(render::ShapePipeline::Slot::LIGHTING_MODEL, lightingModel->getParametersBuffer()); - // From the lighting model define a global shapKey ORED with individiual keys + // From the lighting model define a global shapeKey ORED with individiual keys ShapeKey::Builder keyBuilder; if (lightingModel->isWireframeEnabled()) { keyBuilder.withWireframe(); } + + // Prepare fade effect + batch.setResourceTexture(ShapePipeline::Slot::MAP::FADE_MASK, _fadeMaskMap); + if (_debugFade) { + args->_debugFlags = static_cast(args->_debugFlags | + static_cast(RenderArgs::RENDER_DEBUG_FADE)); + // Force fade for everyone + keyBuilder.withFade(); + } + ShapeKey globalKey = keyBuilder.build(); args->_globalShapeKey = globalKey._flags.to_ulong(); @@ -325,6 +339,13 @@ void DrawStateSortDeferred::run(const RenderContextPointer& renderContext, const } args->_batch = nullptr; args->_globalShapeKey = 0; + + // Not sure this is really needed... + if (_debugFade) { + // Turn off fade debug + args->_debugFlags = static_cast(args->_debugFlags & + ~static_cast(RenderArgs::RENDER_DEBUG_FADE)); + } }); config->setNumDrawn((int)inItems.size()); diff --git a/libraries/render-utils/src/RenderDeferredTask.h b/libraries/render-utils/src/RenderDeferredTask.h index 12ecd5ecaf..c8f3326486 100644 --- a/libraries/render-utils/src/RenderDeferredTask.h +++ b/libraries/render-utils/src/RenderDeferredTask.h @@ -86,6 +86,7 @@ class DrawStateSortConfig : public render::Job::Config { Q_PROPERTY(int numDrawn READ getNumDrawn NOTIFY numDrawnChanged) Q_PROPERTY(int maxDrawn MEMBER maxDrawn NOTIFY dirty) Q_PROPERTY(bool stateSort MEMBER stateSort NOTIFY dirty) + Q_PROPERTY(bool debugFade MEMBER debugFade NOTIFY dirty) public: int getNumDrawn() { return numDrawn; } @@ -93,6 +94,7 @@ public: int maxDrawn{ -1 }; bool stateSort{ true }; + bool debugFade{ false }; signals: void numDrawnChanged(); @@ -109,15 +111,17 @@ public: using Config = DrawStateSortConfig; using JobModel = render::Job::ModelI; - DrawStateSortDeferred(render::ShapePlumberPointer shapePlumber) : _shapePlumber{ shapePlumber } {} + DrawStateSortDeferred(render::ShapePlumberPointer shapePlumber, gpu::TexturePointer fadeMaskMap) : _shapePlumber{ shapePlumber }, _fadeMaskMap{ fadeMaskMap } {} - void configure(const Config& config) { _maxDrawn = config.maxDrawn; _stateSort = config.stateSort; } + void configure(const Config& config) { _maxDrawn = config.maxDrawn; _stateSort = config.stateSort; _debugFade = config.debugFade; } void run(const render::RenderContextPointer& renderContext, const Inputs& inputs); protected: render::ShapePlumberPointer _shapePlumber; + gpu::TexturePointer _fadeMaskMap; int _maxDrawn; // initialized by Config bool _stateSort; + bool _debugFade; }; class DeferredFramebuffer; diff --git a/libraries/render-utils/src/RenderPipelines.cpp b/libraries/render-utils/src/RenderPipelines.cpp index da264cbf7d..763fca04ed 100644 --- a/libraries/render-utils/src/RenderPipelines.cpp +++ b/libraries/render-utils/src/RenderPipelines.cpp @@ -41,6 +41,12 @@ #include "model_normal_specular_map_frag.h" #include "model_specular_map_frag.h" +#include "model_fade_vert.h" +#include "model_normal_map_fade_vert.h" + +#include "model_fade_frag.h" +#include "model_normal_map_fade_frag.h" + #include "forward_model_frag.h" #include "forward_model_unlit_frag.h" #include "forward_model_normal_map_frag.h" @@ -151,6 +157,10 @@ void initDeferredPipelines(render::ShapePlumber& plumber) { auto skinModelNormalMapVertex = gpu::Shader::createVertex(std::string(skin_model_normal_map_vert)); auto skinModelShadowVertex = gpu::Shader::createVertex(std::string(skin_model_shadow_vert)); + // Only models can have fade applied to them (?) + auto modelFadeVertex = gpu::Shader::createVertex(std::string(model_fade_vert)); + auto modelNormalMapFadeVertex = gpu::Shader::createVertex(std::string(model_normal_map_fade_vert)); + // Pixel shaders auto simplePixel = gpu::Shader::createPixel(std::string(simple_textured_frag)); auto simpleUnlitPixel = gpu::Shader::createPixel(std::string(simple_textured_unlit_frag)); @@ -169,6 +179,10 @@ void initDeferredPipelines(render::ShapePlumber& plumber) { auto modelLightmapSpecularMapPixel = gpu::Shader::createPixel(std::string(model_lightmap_specular_map_frag)); auto modelLightmapNormalSpecularMapPixel = gpu::Shader::createPixel(std::string(model_lightmap_normal_specular_map_frag)); + // Only models can have fade applied to them (?) + auto modelFadePixel = gpu::Shader::createPixel(std::string(model_fade_frag)); + auto modelNormalMapFadePixel = gpu::Shader::createPixel(std::string(model_normal_map_fade_frag)); + using Key = render::ShapeKey; auto addPipeline = std::bind(&addPlumberPipeline, std::ref(plumber), _1, _2, _3); // TODO: Refactor this to use a filter @@ -194,6 +208,14 @@ void initDeferredPipelines(render::ShapePlumber& plumber) { addPipeline( Key::Builder().withMaterial().withTangents().withSpecular(), modelNormalMapVertex, modelNormalSpecularMapPixel); + // Same thing but with Fade on + addPipeline( + Key::Builder().withMaterial().withFade(), + modelFadeVertex, modelFadePixel); + addPipeline( + Key::Builder().withMaterial().withTangents().withFade(), + modelNormalMapFadeVertex, modelNormalMapFadePixel); + // Translucents addPipeline( Key::Builder().withMaterial().withTranslucent(), diff --git a/libraries/render-utils/src/model_fade.slf b/libraries/render-utils/src/model_fade.slf new file mode 100644 index 0000000000..06b2695d2b --- /dev/null +++ b/libraries/render-utils/src/model_fade.slf @@ -0,0 +1,65 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// model_fade.frag +// fragment shader +// +// Created by Olivier Prat on 04/19/17. +// Copyright 2017 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 +// + +<@include DeferredBufferWrite.slh@> + +<@include model/Material.slh@> + +<@include MaterialTextures.slh@> +<$declareMaterialTextures(ALBEDO, ROUGHNESS, _SCRIBE_NULL, _SCRIBE_NULL, EMISSIVE, OCCLUSION)$> + +<@include Fade.slh@> +<$declareFade()$> + +in vec4 _position; +in vec4 _worldPosition; +in vec3 _normal; +in vec3 _color; +in vec2 _texCoord0; +in vec2 _texCoord1; + + +void main(void) { + applyFade(_worldPosition.xyz, _normal); + + Material mat = getMaterial(); + int matKey = getMaterialKey(mat); + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, _SCRIBE_NULL, _SCRIBE_NULL, emissiveTex)$> + <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> + + float opacity = 1.0; + <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; + <$discardTransparent(opacity)$>; + + vec3 albedo = getMaterialAlbedo(mat); + <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; + albedo *= _color; + + float roughness = getMaterialRoughness(mat); + <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; + + vec3 emissive = getMaterialEmissive(mat); + <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; + + float scattering = getMaterialScattering(mat); + + packDeferredFragment( + normalize(_normal.xyz), + opacity, + albedo, + roughness, + getMaterialMetallic(mat), + emissive, + occlusionTex, + scattering); +} diff --git a/libraries/render-utils/src/model_fade.slv b/libraries/render-utils/src/model_fade.slv new file mode 100644 index 0000000000..4e6e02c1ad --- /dev/null +++ b/libraries/render-utils/src/model_fade.slv @@ -0,0 +1,44 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// model_fade.slv +// vertex shader +// +// Created by Olivier Prat on 04/24/17. +// Copyright 2017 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 +// + +<@include gpu/Inputs.slh@> +<@include gpu/Color.slh@> +<@include gpu/Transform.slh@> +<$declareStandardTransform()$> + +<@include MaterialTextures.slh@> +<$declareMaterialTexMapArrayBuffer()$> + +out vec3 _color; +out float _alpha; +out vec2 _texCoord0; +out vec2 _texCoord1; +out vec4 _position; +out vec4 _worldPosition; +out vec3 _normal; + +void main(void) { + _color = colorToLinearRGB(inColor.xyz); + _alpha = inColor.w; + + TexMapArray texMapArray = getTexMapArray(); + <$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _texCoord0)$> + <$evalTexMapArrayTexcoord1(texMapArray, inTexCoord0, _texCoord1)$> + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$> + <$transformModelToWorldPos(obj, inPosition, _worldPosition)$> + <$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$> +} diff --git a/libraries/render-utils/src/model_normal_map_fade.slf b/libraries/render-utils/src/model_normal_map_fade.slf new file mode 100644 index 0000000000..4586fe555a --- /dev/null +++ b/libraries/render-utils/src/model_normal_map_fade.slf @@ -0,0 +1,70 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// model_normal_map_fade.frag +// fragment shader +// +// Created by Olivier Prat on 04/19/17. +// Copyright 2017 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 +// + +<@include DeferredBufferWrite.slh@> + +<@include model/Material.slh@> + +<@include MaterialTextures.slh@> +<$declareMaterialTextures(ALBEDO, ROUGHNESS, NORMAL, _SCRIBE_NULL, EMISSIVE, OCCLUSION, SCATTERING)$> + +<@include Fade.slh@> +<$declareFade()$> + +in vec4 _position; +in vec4 _worldPosition; +in vec2 _texCoord0; +in vec2 _texCoord1; +in vec3 _normal; +in vec3 _tangent; +in vec3 _color; + +void main(void) { + applyFade(_worldPosition.xyz, _normal); + + Material mat = getMaterial(); + int matKey = getMaterialKey(mat); + <$fetchMaterialTexturesCoord0(matKey, _texCoord0, albedoTex, roughnessTex, normalTex, _SCRIBE_NULL, emissiveTex, scatteringTex)$> + <$fetchMaterialTexturesCoord1(matKey, _texCoord1, occlusionTex)$> + + float opacity = 1.0; + <$evalMaterialOpacity(albedoTex.a, opacity, matKey, opacity)$>; + <$discardTransparent(opacity)$>; + + vec3 albedo = getMaterialAlbedo(mat); + <$evalMaterialAlbedo(albedoTex, albedo, matKey, albedo)$>; + albedo *= _color; + + float roughness = getMaterialRoughness(mat); + <$evalMaterialRoughness(roughnessTex, roughness, matKey, roughness)$>; + + vec3 emissive = getMaterialEmissive(mat); + <$evalMaterialEmissive(emissiveTex, emissive, matKey, emissive)$>; + + vec3 viewNormal; + <$tangentToViewSpaceLOD(_position, normalTex, _normal, _tangent, viewNormal)$> + + float scattering = getMaterialScattering(mat); + <$evalMaterialScattering(scatteringTex, scattering, matKey, scattering)$>; + + packDeferredFragment( + viewNormal, + opacity, + albedo, + roughness, + getMaterialMetallic(mat), + emissive, + occlusionTex, + scattering); +} diff --git a/libraries/render-utils/src/model_normal_map_fade.slv b/libraries/render-utils/src/model_normal_map_fade.slv new file mode 100644 index 0000000000..a71900d5c3 --- /dev/null +++ b/libraries/render-utils/src/model_normal_map_fade.slv @@ -0,0 +1,48 @@ +<@include gpu/Config.slh@> +<$VERSION_HEADER$> +// Generated on <$_SCRIBE_DATE$> +// +// model_normal_map_fade.vert +// vertex shader +// +// Created by Olivier Prat on 04/24/17. +// Copyright 2017 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 +// + +<@include gpu/Inputs.slh@> +<@include gpu/Color.slh@> +<@include gpu/Transform.slh@> +<$declareStandardTransform()$> + +<@include MaterialTextures.slh@> +<$declareMaterialTexMapArrayBuffer()$> + +out vec4 _position; +out vec4 _worldPosition; +out vec2 _texCoord0; +out vec2 _texCoord1; +out vec3 _normal; +out vec3 _tangent; +out vec3 _color; +out float _alpha; + +void main(void) { + // pass along the color + _color = colorToLinearRGB(inColor.rgb); + _alpha = inColor.a; + + TexMapArray texMapArray = getTexMapArray(); + <$evalTexMapArrayTexcoord0(texMapArray, inTexCoord0, _texCoord0)$> + <$evalTexMapArrayTexcoord1(texMapArray, inTexCoord0, _texCoord1)$> + + // standard transform + TransformCamera cam = getTransformCamera(); + TransformObject obj = getTransformObject(); + <$transformModelToEyeAndClipPos(cam, obj, inPosition, _position, gl_Position)$> + <$transformModelToWorldPos(obj, inPosition, _worldPosition)$> + <$transformModelToWorldDir(cam, obj, inNormal.xyz, _normal)$> + <$transformModelToWorldDir(cam, obj, inTangent.xyz, _tangent)$> +} diff --git a/libraries/render/src/render/ShapePipeline.cpp b/libraries/render/src/render/ShapePipeline.cpp index d51d7f8cb6..77f7f61801 100644 --- a/libraries/render/src/render/ShapePipeline.cpp +++ b/libraries/render/src/render/ShapePipeline.cpp @@ -69,6 +69,8 @@ void ShapePlumber::addPipeline(const Filter& filter, const gpu::ShaderPointer& p slotBindings.insert(gpu::Shader::Binding(std::string("lightBuffer"), Slot::BUFFER::LIGHT)); slotBindings.insert(gpu::Shader::Binding(std::string("lightAmbientBuffer"), Slot::BUFFER::LIGHT_AMBIENT_BUFFER)); slotBindings.insert(gpu::Shader::Binding(std::string("skyboxMap"), Slot::MAP::LIGHT_AMBIENT)); + slotBindings.insert(gpu::Shader::Binding(std::string("fadeMaskMap"), Slot::MAP::FADE_MASK)); + slotBindings.insert(gpu::Shader::Binding(std::string("fadeBuffer"), Slot::BUFFER::FADE)); gpu::Shader::makeProgram(*program, slotBindings); @@ -87,6 +89,8 @@ void ShapePlumber::addPipeline(const Filter& filter, const gpu::ShaderPointer& p locations->lightBufferUnit = program->getUniformBuffers().findLocation("lightBuffer"); locations->lightAmbientBufferUnit = program->getUniformBuffers().findLocation("lightAmbientBuffer"); locations->lightAmbientMapUnit = program->getTextures().findLocation("skyboxMap"); + locations->fadeMaskTextureUnit = program->getTextures().findLocation("fadeMaskMap"); + locations->fadeBufferUnit = program->getUniformBuffers().findLocation("fadeBuffer"); ShapeKey key{filter._flags}; auto gpuPipeline = gpu::Pipeline::create(program, state); diff --git a/libraries/render/src/render/ShapePipeline.h b/libraries/render/src/render/ShapePipeline.h index 73e8f82f24..b8045dfbcc 100644 --- a/libraries/render/src/render/ShapePipeline.h +++ b/libraries/render/src/render/ShapePipeline.h @@ -33,6 +33,7 @@ public: DEPTH_BIAS, WIREFRAME, NO_CULL_FACE, + FADE, OWN_PIPELINE, INVALID, @@ -68,6 +69,7 @@ public: Builder& withDepthBias() { _flags.set(DEPTH_BIAS); return (*this); } Builder& withWireframe() { _flags.set(WIREFRAME); return (*this); } Builder& withoutCullFace() { _flags.set(NO_CULL_FACE); return (*this); } + Builder& withFade() { _flags.set(FADE); return (*this); } Builder& withOwnPipeline() { _flags.set(OWN_PIPELINE); return (*this); } Builder& invalidate() { _flags.set(INVALID); return (*this); } @@ -126,6 +128,9 @@ public: Builder& withCullFace() { _flags.reset(NO_CULL_FACE); _mask.set(NO_CULL_FACE); return (*this); } Builder& withoutCullFace() { _flags.set(NO_CULL_FACE); _mask.set(NO_CULL_FACE); return (*this); } + Builder& withFade() { _flags.reset(FADE); _mask.set(FADE); return (*this); } + Builder& withoutFade() { _flags.set(FADE); _mask.set(FADE); return (*this); } + protected: friend class Filter; Flags _flags{0}; @@ -150,6 +155,7 @@ public: bool isDepthBiased() const { return _flags[DEPTH_BIAS]; } bool isWireframe() const { return _flags[WIREFRAME]; } bool isCullFace() const { return !_flags[NO_CULL_FACE]; } + bool isFaded() const { return _flags[FADE]; } bool hasOwnPipeline() const { return _flags[OWN_PIPELINE]; } bool isValid() const { return !_flags[INVALID]; } @@ -186,6 +192,7 @@ inline QDebug operator<<(QDebug debug, const ShapeKey& key) { << "isDepthBiased:" << key.isDepthBiased() << "isWireframe:" << key.isWireframe() << "isCullFace:" << key.isCullFace() + << "isFaded:" << key.isFaded() << "]"; } } else { @@ -207,6 +214,7 @@ public: LIGHTING_MODEL, LIGHT, LIGHT_AMBIENT_BUFFER, + FADE, }; enum MAP { @@ -218,6 +226,7 @@ public: OCCLUSION, SCATTERING, LIGHT_AMBIENT, + FADE_MASK, }; }; @@ -236,6 +245,8 @@ public: int lightBufferUnit; int lightAmbientBufferUnit; int lightAmbientMapUnit; + int fadeBufferUnit; + int fadeMaskTextureUnit; }; using LocationsPointer = std::shared_ptr; diff --git a/libraries/shared/src/RenderArgs.h b/libraries/shared/src/RenderArgs.h index 10a9a20287..8fe65fb5a6 100644 --- a/libraries/shared/src/RenderArgs.h +++ b/libraries/shared/src/RenderArgs.h @@ -79,7 +79,8 @@ public: enum RenderSide { MONO, STEREO_LEFT, STEREO_RIGHT }; enum DebugFlags { RENDER_DEBUG_NONE = 0, - RENDER_DEBUG_HULLS = 1 + RENDER_DEBUG_HULLS = 1, + RENDER_DEBUG_FADE = 2, }; RenderArgs(std::shared_ptr context = nullptr,