diff --git a/interface/resources/shaders/ambient_occlusion.frag b/interface/resources/shaders/ambient_occlusion.frag index b01f7f2db7..b59c87d797 100644 --- a/interface/resources/shaders/ambient_occlusion.frag +++ b/interface/resources/shaders/ambient_occlusion.frag @@ -11,6 +11,13 @@ // the depth texture uniform sampler2D depthTexture; +// the random rotation texture +uniform sampler2D rotationTexture; + +// the sample kernel containing the unit offset vectors +const int SAMPLE_KERNEL_SIZE = 16; +uniform vec3 sampleKernel[SAMPLE_KERNEL_SIZE]; + // the distance to the near clip plane uniform float near; @@ -23,20 +30,37 @@ uniform vec2 leftBottom; // the right and top edges of the view window uniform vec2 rightTop; +// the radius of the effect +uniform float radius; + +// the scale for the noise texture +uniform vec2 noiseScale; + +// given a texture coordinate, returns the 3D view space z coordinate +float texCoordToViewSpaceZ(vec2 texCoord) { + return (far * near) / (texture2D(depthTexture, texCoord).r * (far - near) - far); +} + // given a texture coordinate, returns the 3D view space coordinate vec3 texCoordToViewSpace(vec2 texCoord) { - float z = (far * near) / (texture2D(depthTexture, texCoord).r * (far - near) - far); + float z = texCoordToViewSpaceZ(texCoord); return vec3(((texCoord * 2.0 - vec2(1.0, 1.0)) * (rightTop - leftBottom) + rightTop + leftBottom) * z / (-2.0 * near), z); } void main(void) { - float ds = dFdx(gl_TexCoord[0].s); - float dt = dFdy(gl_TexCoord[0].t); + vec3 rotationX = texture2D(rotationTexture, gl_TexCoord[0].st * noiseScale).rgb; + vec3 rotationY = normalize(cross(rotationX, vec3(0.0, 0.0, 1.0))); + mat3 rotation = mat3(rotationX, rotationY, cross(rotationX, rotationY)); + vec3 center = texCoordToViewSpace(gl_TexCoord[0].st); - vec3 left = texCoordToViewSpace(gl_TexCoord[0].st - vec2(-ds, 0.0)) - center; - vec3 right = texCoordToViewSpace(gl_TexCoord[0].st - vec2(ds, 0.0)) - center; - vec3 up = texCoordToViewSpace(gl_TexCoord[0].st - vec2(0.0, dt)) - center; - vec3 down = texCoordToViewSpace(gl_TexCoord[0].st - vec2(0.0, -dt)) - center; - float occlusion = 0.5 - (left.z / length(left) + right.z / length(right) + up.z / length(up) + down.z / length(down)) / 8.0; - gl_FragColor = vec4(occlusion, occlusion, occlusion, 0.0); + + float occlusion = 4.0; + for (int i = 0; i < SAMPLE_KERNEL_SIZE; i++) { + vec3 offset = center + rotation * (radius * sampleKernel[i]); + vec4 projected = gl_ProjectionMatrix * vec4(offset, 1.0); + float depth = texCoordToViewSpaceZ(projected.xy * 0.5 / projected.w + vec2(0.5, 0.5)); + occlusion += 1.0 - step(offset.z, depth); // * step(abs(center.z - depth), radius); + } + + gl_FragColor = vec4(occlusion, occlusion, occlusion, 0.0) / 16.0; } diff --git a/interface/resources/shaders/ambient_occlusion.vert b/interface/resources/shaders/ambient_occlusion.vert new file mode 100644 index 0000000000..ca6af718bd --- /dev/null +++ b/interface/resources/shaders/ambient_occlusion.vert @@ -0,0 +1,14 @@ +#version 120 + +// +// ambient_occlusion.vert +// vertex shader +// +// Created by Andrzej Kapolka on 8/16/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +void main(void) { + gl_TexCoord[0] = gl_MultiTexCoord0; + gl_Position = gl_Vertex; +} diff --git a/interface/resources/shaders/occlusion_blur.frag b/interface/resources/shaders/occlusion_blur.frag new file mode 100644 index 0000000000..2ee5eaf2cb --- /dev/null +++ b/interface/resources/shaders/occlusion_blur.frag @@ -0,0 +1,25 @@ +#version 120 + +// +// occlusion_blur.frag +// fragment shader +// +// Created by Andrzej Kapolka on 8/16/13. +// Copyright (c) 2013 High Fidelity, Inc. All rights reserved. +// + +// the original texture +uniform sampler2D originalTexture; + +void main(void) { + float ds = dFdx(gl_TexCoord[0].s); + float dt = dFdy(gl_TexCoord[0].t); + vec4 sum = vec4(0.0, 0.0, 0.0, 0.0); + for (int i = 0; i < 4; i++) { + for (int j = 0; j < 4; j++) { + sum += texture2D(originalTexture, gl_TexCoord[0].st + + vec2(ds, dt) * vec2(-2.0 + float(i), -2.0 + float(j))); + } + } + gl_FragColor = sum / 16.0; +} diff --git a/interface/src/renderer/AmbientOcclusionEffect.cpp b/interface/src/renderer/AmbientOcclusionEffect.cpp index 8719350ec8..83ed2fdbec 100644 --- a/interface/src/renderer/AmbientOcclusionEffect.cpp +++ b/interface/src/renderer/AmbientOcclusionEffect.cpp @@ -6,6 +6,13 @@ // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. // +// include this before QOpenGLFramebufferObject, which includes an earlier version of OpenGL +#include "InterfaceConfig.h" + +#include + +#include + #include #include "AmbientOcclusionEffect.h" @@ -14,61 +21,120 @@ #include "ProgramObject.h" #include "RenderUtil.h" +const int ROTATION_WIDTH = 4; +const int ROTATION_HEIGHT = 4; + void AmbientOcclusionEffect::init() { switchToResourcesParentIfRequired(); - _program = new ProgramObject(); - _program->addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/ambient_occlusion.frag"); - _program->link(); + _occlusionProgram = new ProgramObject(); + _occlusionProgram->addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/ambient_occlusion.vert"); + _occlusionProgram->addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/ambient_occlusion.frag"); + _occlusionProgram->link(); - _nearLocation = _program->uniformLocation("near"); - _farLocation = _program->uniformLocation("far"); - _leftBottomLocation = _program->uniformLocation("leftBottom"); - _rightTopLocation = _program->uniformLocation("rightTop"); + _blurProgram = new ProgramObject(); + _blurProgram->addShaderFromSourceFile(QGLShader::Vertex, "resources/shaders/ambient_occlusion.vert"); + _blurProgram->addShaderFromSourceFile(QGLShader::Fragment, "resources/shaders/occlusion_blur.frag"); + _blurProgram->link(); - _program->bind(); - _program->setUniformValue("depthTexture", 0); - _program->release(); + _blurProgram->bind(); + _blurProgram->setUniformValue("originalTexture", 0); + _blurProgram->release(); + + // create the sample kernel: an array of spherically distributed offset vectors + const int SAMPLE_KERNEL_SIZE = 16; + QVector3D sampleKernel[SAMPLE_KERNEL_SIZE]; + for (int i = 0; i < SAMPLE_KERNEL_SIZE; i++) { + glm::vec3 vector = glm::ballRand(1.0f); + sampleKernel[i] = QVector3D(vector.x, vector.y, vector.z); + } + + _occlusionProgram->bind(); + _occlusionProgram->setUniformValue("depthTexture", 0); + _occlusionProgram->setUniformValue("rotationTexture", 1); + _occlusionProgram->setUniformValueArray("sampleKernel", sampleKernel, SAMPLE_KERNEL_SIZE); + _occlusionProgram->setUniformValue("radius", 0.1f); + _occlusionProgram->release(); + + _nearLocation = _occlusionProgram->uniformLocation("near"); + _farLocation = _occlusionProgram->uniformLocation("far"); + _leftBottomLocation = _occlusionProgram->uniformLocation("leftBottom"); + _rightTopLocation = _occlusionProgram->uniformLocation("rightTop"); + _noiseScaleLocation = _occlusionProgram->uniformLocation("noiseScale"); + + // generate the random rotation texture + glGenTextures(1, &_rotationTextureID); + glBindTexture(GL_TEXTURE_2D, _rotationTextureID); + const int ELEMENTS_PER_PIXEL = 3; + unsigned char rotationData[ROTATION_WIDTH * ROTATION_HEIGHT * ELEMENTS_PER_PIXEL]; + unsigned char* rotation = rotationData; + for (int i = 0; i < ROTATION_WIDTH * ROTATION_HEIGHT; i++) { + glm::vec3 randvec = glm::sphericalRand(1.0f); + *rotation++ = ((randvec.x + 1.0f) / 2.0f) * 255.0f; + *rotation++ = ((randvec.y + 1.0f) / 2.0f) * 255.0f; + *rotation++ = ((randvec.z + 1.0f) / 2.0f) * 255.0f; + } + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, ROTATION_WIDTH, ROTATION_HEIGHT, 0, GL_RGB, GL_UNSIGNED_BYTE, rotationData); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glBindTexture(GL_TEXTURE_2D, 0); } void AmbientOcclusionEffect::render() { - glPushMatrix(); - glLoadIdentity(); - - glMatrixMode(GL_PROJECTION); - glPushMatrix(); - glLoadIdentity(); - glDisable(GL_BLEND); - //glBlendFuncSeparate(GL_ZERO, GL_SRC_COLOR, GL_ZERO, GL_ONE); glDisable(GL_DEPTH_TEST); glDepthMask(GL_FALSE); glBindTexture(GL_TEXTURE_2D, Application::getInstance()->getTextureCache()->getPrimaryDepthTextureID()); + glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, _rotationTextureID); + + // render with the occlusion shader to the secondary buffer + QOpenGLFramebufferObject* secondaryFBO = Application::getInstance()->getTextureCache()->getSecondaryFramebufferObject(); + secondaryFBO->bind(); + float left, right, bottom, top, nearVal, farVal; glm::vec4 nearClipPlane, farClipPlane; Application::getInstance()->getViewFrustum()->computeOffAxisFrustum( left, right, bottom, top, nearVal, farVal, nearClipPlane, farClipPlane); - _program->bind(); - _program->setUniformValue(_nearLocation, nearVal); - _program->setUniformValue(_farLocation, farVal); - _program->setUniformValue(_leftBottomLocation, left, bottom); - _program->setUniformValue(_rightTopLocation, right, top); + _occlusionProgram->bind(); + _occlusionProgram->setUniformValue(_nearLocation, nearVal); + _occlusionProgram->setUniformValue(_farLocation, farVal); + _occlusionProgram->setUniformValue(_leftBottomLocation, left, bottom); + _occlusionProgram->setUniformValue(_rightTopLocation, right, top); + QSize size = Application::getInstance()->getGLWidget()->size(); + _occlusionProgram->setUniformValue(_noiseScaleLocation, size.width() / (float)ROTATION_WIDTH, + size.height() / (float)ROTATION_HEIGHT); renderFullscreenQuad(); - _program->release(); + _occlusionProgram->release(); - glPopMatrix(); + secondaryFBO->release(); + + glBindTexture(GL_TEXTURE_2D, 0); + glActiveTexture(GL_TEXTURE0); + + // now render secondary to primary with 4x4 blur + Application::getInstance()->getTextureCache()->getPrimaryFramebufferObject()->bind(); - glMatrixMode(GL_MODELVIEW); - glPopMatrix(); - glEnable(GL_BLEND); - //glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); + glBlendFuncSeparate(GL_ZERO, GL_SRC_COLOR, GL_ZERO, GL_ONE); + + glBindTexture(GL_TEXTURE_2D, secondaryFBO->texture()); + + _blurProgram->bind(); + + renderFullscreenQuad(); + + _blurProgram->release(); + + glBindTexture(GL_TEXTURE_2D, 0); + + //glEnable(GL_BLEND); + glBlendFuncSeparate(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_CONSTANT_ALPHA, GL_ONE); glEnable(GL_DEPTH_TEST); glDepthMask(GL_TRUE); - glBindTexture(GL_TEXTURE_2D, 0); } diff --git a/interface/src/renderer/AmbientOcclusionEffect.h b/interface/src/renderer/AmbientOcclusionEffect.h index 8fba351827..4891a6eb2c 100644 --- a/interface/src/renderer/AmbientOcclusionEffect.h +++ b/interface/src/renderer/AmbientOcclusionEffect.h @@ -9,6 +9,8 @@ #ifndef __interface__AmbientOcclusionEffect__ #define __interface__AmbientOcclusionEffect__ +#include "InterfaceConfig.h" + class ProgramObject; /// A screen space ambient occlusion effect. @@ -21,11 +23,16 @@ public: private: - ProgramObject* _program; + ProgramObject* _occlusionProgram; int _nearLocation; int _farLocation; int _leftBottomLocation; int _rightTopLocation; + int _noiseScaleLocation; + + ProgramObject* _blurProgram; + + GLuint _rotationTextureID; }; #endif /* defined(__interface__AmbientOcclusionEffect__) */ diff --git a/interface/src/renderer/GlowEffect.cpp b/interface/src/renderer/GlowEffect.cpp index 35d1777be3..85c4eb27b2 100644 --- a/interface/src/renderer/GlowEffect.cpp +++ b/interface/src/renderer/GlowEffect.cpp @@ -5,7 +5,7 @@ // Created by Andrzej Kapolka on 8/7/13. // Copyright (c) 2013 High Fidelity, Inc. All rights reserved. -// include this before QGLWidget, which includes an earlier version of OpenGL +// include this before QOpenGLFramebufferObject, which includes an earlier version of OpenGL #include "InterfaceConfig.h" #include