3-step groundwork for AO in the pipeline

This commit is contained in:
Niraj Venkat 2015-07-17 16:42:03 -07:00
parent 022529f03f
commit 0580c8477e
9 changed files with 306 additions and 25 deletions

View file

@ -274,7 +274,6 @@ bool setupEssentials(int& argc, char** argv) {
auto audio = DependencyManager::set<AudioClient>(); auto audio = DependencyManager::set<AudioClient>();
auto audioScope = DependencyManager::set<AudioScope>(); auto audioScope = DependencyManager::set<AudioScope>();
auto deferredLightingEffect = DependencyManager::set<DeferredLightingEffect>(); auto deferredLightingEffect = DependencyManager::set<DeferredLightingEffect>();
auto ambientOcclusionEffect = DependencyManager::set<AmbientOcclusionEffect>();
auto textureCache = DependencyManager::set<TextureCache>(); auto textureCache = DependencyManager::set<TextureCache>();
auto animationCache = DependencyManager::set<AnimationCache>(); auto animationCache = DependencyManager::set<AnimationCache>();
auto ddeFaceTracker = DependencyManager::set<DdeFaceTracker>(); auto ddeFaceTracker = DependencyManager::set<DdeFaceTracker>();

View file

@ -144,6 +144,38 @@ public:
case gpu::RGB: case gpu::RGB:
case gpu::RGBA: case gpu::RGBA:
texel.internalFormat = GL_RED; texel.internalFormat = GL_RED;
/* switch (dstFormat.getType()) {
case gpu::UINT32:
case gpu::INT32:
case gpu::NUINT32:
case gpu::NINT32: {
texel.internalFormat = GL_DEPTH_COMPONENT32;
break;
}
case gpu::NFLOAT:
case gpu::FLOAT: {
texel.internalFormat = GL_DEPTH_COMPONENT32F;
break;
}
case gpu::UINT16:
case gpu::INT16:
case gpu::NUINT16:
case gpu::NINT16:
case gpu::HALF:
case gpu::NHALF: {
texel.internalFormat = GL_DEPTH_COMPONENT16;
break;
}
case gpu::UINT8:
case gpu::INT8:
case gpu::NUINT8:
case gpu::NINT8: {
texel.internalFormat = GL_DEPTH_COMPONENT24;
break;
}
case gpu::NUM_TYPES: { // quiet compiler
Q_UNREACHABLE();
}*/
break; break;
case gpu::DEPTH: case gpu::DEPTH:
texel.format = GL_DEPTH_COMPONENT; // It's depth component to load it texel.format = GL_DEPTH_COMPONENT; // It's depth component to load it

View file

@ -31,12 +31,16 @@
#include "ambient_occlusion_vert.h" #include "ambient_occlusion_vert.h"
#include "ambient_occlusion_frag.h" #include "ambient_occlusion_frag.h"
#include "gaussian_blur_vertical_vert.h"
#include "gaussian_blur_horizontal_vert.h"
#include "gaussian_blur_frag.h"
const int ROTATION_WIDTH = 4; const int ROTATION_WIDTH = 4;
const int ROTATION_HEIGHT = 4; const int ROTATION_HEIGHT = 4;
/*
void AmbientOcclusionEffect::init(AbstractViewStateInterface* viewState) { void AmbientOcclusionEffect::init(AbstractViewStateInterface* viewState) {
/*_viewState = viewState; // we will use this for view state services _viewState = viewState; // we will use this for view state services
_occlusionProgram = new ProgramObject(); _occlusionProgram = new ProgramObject();
_occlusionProgram->addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath() _occlusionProgram->addShaderFromSourceFile(QGLShader::Vertex, PathUtils::resourcesPath()
@ -99,11 +103,11 @@ void AmbientOcclusionEffect::init(AbstractViewStateInterface* viewState) {
_blurProgram->setUniformValue("originalTexture", 0); _blurProgram->setUniformValue("originalTexture", 0);
_blurProgram->release(); _blurProgram->release();
_blurScaleLocation = _blurProgram->uniformLocation("blurScale");*/ _blurScaleLocation = _blurProgram->uniformLocation("blurScale");
} }
void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext){ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext){
/*glDisable(GL_BLEND); glDisable(GL_BLEND);
glDisable(GL_DEPTH_TEST); glDisable(GL_DEPTH_TEST);
glDepthMask(GL_FALSE); glDepthMask(GL_FALSE);
@ -170,15 +174,15 @@ void AmbientOcclusionEffect::run(const render::SceneContextPointer& sceneContext
glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE);
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE);*/ glDepthMask(GL_TRUE)
} }
*/
AmbientOcclusion::AmbientOcclusion() { AmbientOcclusion::AmbientOcclusion() {
} }
const gpu::PipelinePointer& AmbientOcclusion::getAOPipeline() { const gpu::PipelinePointer& AmbientOcclusion::getOcclusionPipeline() {
if (!_AOPipeline) { if (!_occlusionPipeline) {
auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(ambient_occlusion_vert))); auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(ambient_occlusion_vert)));
auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(ambient_occlusion_frag))); auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(ambient_occlusion_frag)));
gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps)); gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps));
@ -198,10 +202,85 @@ const gpu::PipelinePointer& AmbientOcclusion::getAOPipeline() {
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA, gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO); gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO);
// Link the occlusion FBO to texture
_occlusionBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_RGBA_32,
DependencyManager::get<TextureCache>()->getFrameBufferSize().width(), DependencyManager::get<TextureCache>()->getFrameBufferSize().height()));
auto format = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
auto width = _occlusionBuffer->getWidth();
auto height = _occlusionBuffer->getHeight();
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);
_occlusionTexture = gpu::TexturePointer(gpu::Texture::create2D(format, width, height, defaultSampler));
// Good to go add the brand new pipeline // Good to go add the brand new pipeline
_AOPipeline.reset(gpu::Pipeline::create(program, state)); _occlusionPipeline.reset(gpu::Pipeline::create(program, state));
} }
return _AOPipeline; return _occlusionPipeline;
}
const gpu::PipelinePointer& AmbientOcclusion::getVBlurPipeline() {
if (!_vBlurPipeline) {
auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(gaussian_blur_vertical_vert)));
auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(gaussian_blur_frag)));
gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps));
gpu::Shader::BindingSet slotBindings;
gpu::Shader::makeProgram(*program, slotBindings);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setDepthTest(false, false, gpu::LESS_EQUAL);
// Blend on transparent
state->setBlendFunction(false,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO);
// Link the horizontal blur FBO to texture
_vBlurBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_RGBA_32,
DependencyManager::get<TextureCache>()->getFrameBufferSize().width(), DependencyManager::get<TextureCache>()->getFrameBufferSize().height()));
auto format = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
auto width = _vBlurBuffer->getWidth();
auto height = _vBlurBuffer->getHeight();
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);
_vBlurTexture = gpu::TexturePointer(gpu::Texture::create2D(format, width, height, defaultSampler));
// Good to go add the brand new pipeline
_vBlurPipeline.reset(gpu::Pipeline::create(program, state));
}
return _vBlurPipeline;
}
const gpu::PipelinePointer& AmbientOcclusion::getHBlurPipeline() {
if (!_hBlurPipeline) {
auto vs = gpu::ShaderPointer(gpu::Shader::createVertex(std::string(gaussian_blur_horizontal_vert)));
auto ps = gpu::ShaderPointer(gpu::Shader::createPixel(std::string(gaussian_blur_frag)));
gpu::ShaderPointer program = gpu::ShaderPointer(gpu::Shader::createProgram(vs, ps));
gpu::Shader::BindingSet slotBindings;
gpu::Shader::makeProgram(*program, slotBindings);
gpu::StatePointer state = gpu::StatePointer(new gpu::State());
state->setDepthTest(false, false, gpu::LESS_EQUAL);
// Blend on transparent
state->setBlendFunction(false,
gpu::State::SRC_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::INV_SRC_ALPHA,
gpu::State::DEST_ALPHA, gpu::State::BLEND_OP_ADD, gpu::State::ZERO);
// Link the horizontal blur FBO to texture
_hBlurBuffer = gpu::FramebufferPointer(gpu::Framebuffer::create(gpu::Element::COLOR_RGBA_32,
DependencyManager::get<TextureCache>()->getFrameBufferSize().width(), DependencyManager::get<TextureCache>()->getFrameBufferSize().height()));
auto format = gpu::Element(gpu::VEC4, gpu::NUINT8, gpu::RGBA);
auto width = _hBlurBuffer->getWidth();
auto height = _hBlurBuffer->getHeight();
auto defaultSampler = gpu::Sampler(gpu::Sampler::FILTER_MIN_MAG_POINT);
_hBlurTexture = gpu::TexturePointer(gpu::Texture::create2D(format, width, height, defaultSampler));
// Good to go add the brand new pipeline
_hBlurPipeline.reset(gpu::Pipeline::create(program, state));
}
return _hBlurPipeline;
} }
void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) { void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext) {
@ -226,18 +305,57 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons
batch.setProjectionTransform(projMat); batch.setProjectionTransform(projMat);
batch.setViewTransform(viewMat); batch.setViewTransform(viewMat);
batch.setModelTransform(Transform()); batch.setModelTransform(Transform());
batch.setResourceTexture(0, DependencyManager::get<TextureCache>()->getPrimaryDepthTexture());
// bind the one gpu::Pipeline we need // Occlusion step
batch.setPipeline(getAOPipeline()); getOcclusionPipeline();
batch.setResourceTexture(0, DependencyManager::get<TextureCache>()->getPrimaryDepthTexture());
_occlusionBuffer->setRenderBuffer(0, _occlusionTexture);
batch.setFramebuffer(_occlusionBuffer);
// bind the first gpu::Pipeline we need - for calculating occlusion buffer
batch.setPipeline(getOcclusionPipeline());
glm::vec4 color(0.0f, 0.0f, 0.0f, 1.0f); glm::vec4 color(0.0f, 0.0f, 0.0f, 1.0f);
glm::vec2 bottomLeft(0.5f, -1.0f); glm::vec2 bottomLeft(-1.0f, -1.0f);
glm::vec2 topRight(1.0f, -0.5f); glm::vec2 topRight(1.0f, 1.0f);
glm::vec2 texCoordTopLeft(0.0f, 0.0f); glm::vec2 texCoordTopLeft(0.0f, 0.0f);
glm::vec2 texCoordBottomRight(1.0f, 1.0f); glm::vec2 texCoordBottomRight(1.0f, 1.0f);
DependencyManager::get<GeometryCache>()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color); DependencyManager::get<GeometryCache>()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color);
// Vertical blur step
getVBlurPipeline();
batch.setResourceTexture(0, _occlusionTexture);
_vBlurBuffer->setRenderBuffer(0, _vBlurTexture);
batch.setFramebuffer(_vBlurBuffer);
// bind the second gpu::Pipeline we need - for calculating blur buffer
batch.setPipeline(getVBlurPipeline());
DependencyManager::get<GeometryCache>()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color);
// Horizontal blur step
getHBlurPipeline();
batch.setResourceTexture(0, _vBlurTexture);
_hBlurBuffer->setRenderBuffer(0, _hBlurTexture);
batch.setFramebuffer(_hBlurBuffer);
// bind the second gpu::Pipeline we need - for calculating blur buffer
batch.setPipeline(getHBlurPipeline());
DependencyManager::get<GeometryCache>()->renderQuad(batch, bottomLeft, topRight, texCoordTopLeft, texCoordBottomRight, color);
// "Blend" step
batch.setResourceTexture(0, _hBlurTexture);
batch.setFramebuffer(DependencyManager::get<TextureCache>()->getPrimaryFramebuffer());
// bind the second gpu::Pipeline we need
batch.setPipeline(getOcclusionPipeline());
glm::vec2 bottomLeftSmall(0.5f, -1.0f);
glm::vec2 topRightSmall(1.0f, -0.5f);
DependencyManager::get<GeometryCache>()->renderQuad(batch, bottomLeftSmall, topRightSmall, texCoordTopLeft, texCoordBottomRight, color);
// Ready to render
args->_context->syncCache(); args->_context->syncCache();
renderContext->args->_context->syncCache(); renderContext->args->_context->syncCache();
args->_context->render((batch)); args->_context->render((batch));
@ -247,4 +365,3 @@ void AmbientOcclusion::run(const render::SceneContextPointer& sceneContext, cons
} }

View file

@ -22,6 +22,7 @@ class ProgramObject;
/// A screen space ambient occlusion effect. See John Chapman's tutorial at /// A screen space ambient occlusion effect. See John Chapman's tutorial at
/// http://john-chapman-graphics.blogspot.co.uk/2013/01/ssao-tutorial.html for reference. /// http://john-chapman-graphics.blogspot.co.uk/2013/01/ssao-tutorial.html for reference.
/*
class AmbientOcclusionEffect : public Dependency { class AmbientOcclusionEffect : public Dependency {
SINGLETON_DEPENDENCY SINGLETON_DEPENDENCY
@ -53,6 +54,7 @@ private:
GLuint _rotationTextureID; GLuint _rotationTextureID;
AbstractViewStateInterface* _viewState; AbstractViewStateInterface* _viewState;
}; };
*/
class AmbientOcclusion { class AmbientOcclusion {
public: public:
@ -62,11 +64,23 @@ public:
void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext); void run(const render::SceneContextPointer& sceneContext, const render::RenderContextPointer& renderContext);
typedef render::Job::Model<AmbientOcclusion> JobModel; typedef render::Job::Model<AmbientOcclusion> JobModel;
const gpu::PipelinePointer& AmbientOcclusion::getAOPipeline(); const gpu::PipelinePointer& AmbientOcclusion::getOcclusionPipeline();
const gpu::PipelinePointer& AmbientOcclusion::getHBlurPipeline();
const gpu::PipelinePointer& AmbientOcclusion::getVBlurPipeline();
private: private:
gpu::PipelinePointer _AOPipeline; gpu::PipelinePointer _occlusionPipeline;
gpu::PipelinePointer _hBlurPipeline;
gpu::PipelinePointer _vBlurPipeline;
gpu::FramebufferPointer _occlusionBuffer;
gpu::FramebufferPointer _hBlurBuffer;
gpu::FramebufferPointer _vBlurBuffer;
gpu::TexturePointer _occlusionTexture;
gpu::TexturePointer _hBlurTexture;
gpu::TexturePointer _vBlurTexture;
}; };
#endif // hifi_AmbientOcclusionEffect_h #endif // hifi_AmbientOcclusionEffect_h

View file

@ -14,9 +14,6 @@
<@include DeferredBufferWrite.slh@> <@include DeferredBufferWrite.slh@>
// the interpolated normal
//varying vec4 interpolatedNormal;
varying vec2 varTexcoord; varying vec2 varTexcoord;
uniform sampler2D depthTexture; uniform sampler2D depthTexture;

View file

@ -16,11 +16,9 @@
<$declareStandardTransform()$> <$declareStandardTransform()$>
// the interpolated normal
//varying vec4 interpolatedNormal;
varying vec2 varTexcoord; varying vec2 varTexcoord;
void main(void) { void main(void) {
varTexcoord = gl_MultiTexCoord0.xy; varTexcoord = gl_MultiTexCoord0.xy;
gl_Position = gl_Vertex; gl_Position = gl_Vertex;
} }

View file

@ -0,0 +1,42 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// gaussian_blur.frag
// fragment shader
//
// Created by Niraj Venkat on 7/17/15.
// Copyright 2015 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@>
// the interpolated normal
//varying vec4 interpolatedNormal;
varying vec2 varTexcoord;
varying vec2 varBlurTexcoords[14];
uniform sampler2D occlusionTexture;
void main(void) {
gl_FragColor = vec4(0.0);
gl_FragColor += texture2D(occlusionTexture, varBlurTexcoords[ 0])*0.0044299121055113265;
gl_FragColor += texture2D(occlusionTexture, varBlurTexcoords[ 1])*0.00895781211794;
gl_FragColor += texture2D(occlusionTexture, varBlurTexcoords[ 2])*0.0215963866053;
gl_FragColor += texture2D(occlusionTexture, varBlurTexcoords[ 3])*0.0443683338718;
gl_FragColor += texture2D(occlusionTexture, varBlurTexcoords[ 4])*0.0776744219933;
gl_FragColor += texture2D(occlusionTexture, varBlurTexcoords[ 5])*0.115876621105;
gl_FragColor += texture2D(occlusionTexture, varBlurTexcoords[ 6])*0.147308056121;
gl_FragColor += texture2D(occlusionTexture, varTexcoord )*0.159576912161;
gl_FragColor += texture2D(occlusionTexture, varBlurTexcoords[ 7])*0.147308056121;
gl_FragColor += texture2D(occlusionTexture, varBlurTexcoords[ 8])*0.115876621105;
gl_FragColor += texture2D(occlusionTexture, varBlurTexcoords[ 9])*0.0776744219933;
gl_FragColor += texture2D(occlusionTexture, varBlurTexcoords[10])*0.0443683338718;
gl_FragColor += texture2D(occlusionTexture, varBlurTexcoords[11])*0.0215963866053;
gl_FragColor += texture2D(occlusionTexture, varBlurTexcoords[12])*0.00895781211794;
gl_FragColor += texture2D(occlusionTexture, varBlurTexcoords[13])*0.0044299121055113265;
}

View file

@ -0,0 +1,41 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// guassian_blur_horizontal.vert
// vertex shader
//
// Created by Niraj Venkat on 7/17/15.
// Copyright 2015 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/Transform.slh@>
<$declareStandardTransform()$>
varying vec2 varTexcoord;
varying vec2 varBlurTexcoords[14];
void main(void) {
varTexcoord = gl_MultiTexCoord0.xy;
gl_Position = gl_Vertex;
varBlurTexcoords[ 0] = varTexcoord + vec2(-0.028, 0.0);
varBlurTexcoords[ 1] = varTexcoord + vec2(-0.024, 0.0);
varBlurTexcoords[ 2] = varTexcoord + vec2(-0.020, 0.0);
varBlurTexcoords[ 3] = varTexcoord + vec2(-0.016, 0.0);
varBlurTexcoords[ 4] = varTexcoord + vec2(-0.012, 0.0);
varBlurTexcoords[ 5] = varTexcoord + vec2(-0.008, 0.0);
varBlurTexcoords[ 6] = varTexcoord + vec2(-0.004, 0.0);
varBlurTexcoords[ 7] = varTexcoord + vec2( 0.004, 0.0);
varBlurTexcoords[ 8] = varTexcoord + vec2( 0.008, 0.0);
varBlurTexcoords[ 9] = varTexcoord + vec2( 0.012, 0.0);
varBlurTexcoords[10] = varTexcoord + vec2( 0.016, 0.0);
varBlurTexcoords[11] = varTexcoord + vec2( 0.020, 0.0);
varBlurTexcoords[12] = varTexcoord + vec2( 0.024, 0.0);
varBlurTexcoords[13] = varTexcoord + vec2( 0.028, 0.0);
}

View file

@ -0,0 +1,41 @@
<@include gpu/Config.slh@>
<$VERSION_HEADER$>
// Generated on <$_SCRIBE_DATE$>
//
// guassian_blur_vertical.vert
// vertex shader
//
// Created by Niraj Venkat on 7/17/15.
// Copyright 2015 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/Transform.slh@>
<$declareStandardTransform()$>
varying vec2 varTexcoord;
varying vec2 varBlurTexcoords[14];
void main(void) {
varTexcoord = gl_MultiTexCoord0.xy;
gl_Position = gl_Vertex;
varBlurTexcoords[ 0] = varTexcoord + vec2(0.0, -0.028);
varBlurTexcoords[ 1] = varTexcoord + vec2(0.0, -0.024);
varBlurTexcoords[ 2] = varTexcoord + vec2(0.0, -0.020);
varBlurTexcoords[ 3] = varTexcoord + vec2(0.0, -0.016);
varBlurTexcoords[ 4] = varTexcoord + vec2(0.0, -0.012);
varBlurTexcoords[ 5] = varTexcoord + vec2(0.0, -0.008);
varBlurTexcoords[ 6] = varTexcoord + vec2(0.0, -0.004);
varBlurTexcoords[ 7] = varTexcoord + vec2(0.0, 0.004);
varBlurTexcoords[ 8] = varTexcoord + vec2(0.0, 0.008);
varBlurTexcoords[ 9] = varTexcoord + vec2(0.0, 0.012);
varBlurTexcoords[10] = varTexcoord + vec2(0.0, 0.016);
varBlurTexcoords[11] = varTexcoord + vec2(0.0, 0.020);
varBlurTexcoords[12] = varTexcoord + vec2(0.0, 0.024);
varBlurTexcoords[13] = varTexcoord + vec2(0.0, 0.028);
}