feat: Implement proper self illumination (blur-based)

This commit is contained in:
Vsevolod Kremianskii 2020-09-30 20:19:11 +07:00
parent 17ab14d99d
commit d04096ed58
7 changed files with 197 additions and 60 deletions

View file

@ -546,11 +546,11 @@ void Control::render3D(const glm::ivec2 &offset) const {
shaders.activate(ShaderProgram::GUIGUI, locals);
}
glActiveTexture(GL_TEXTURE0);
framebuffer->bindTexture();
framebuffer->bindColorBuffer(0);
DefaultQuad.render(GL_TRIANGLES);
framebuffer->unbindTexture();
framebuffer->unbindColorBuffer();
}
void Control::stretch(float x, float y) {

View file

@ -27,7 +27,7 @@ namespace reone {
namespace render {
Framebuffer::Framebuffer(int w, int h) : _width(w), _height(h) {
Framebuffer::Framebuffer(int w, int h, int colorBufferCount) : _width(w), _height(h), _colorBufferCount(colorBufferCount) {
}
Framebuffer::~Framebuffer() {
@ -37,24 +37,30 @@ Framebuffer::~Framebuffer() {
void Framebuffer::init() {
if (_inited) return;
glGenTextures(1, &_texture);
glBindTexture(GL_TEXTURE_2D, _texture);
_colorBuffers.resize(_colorBufferCount);
glGenTextures(_colorBufferCount, &_colorBuffers[0]);
for (int i = 0; i < _colorBufferCount; ++i) {
glBindTexture(GL_TEXTURE_2D, _colorBuffers[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, _width, _height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
glBindTexture(GL_TEXTURE_2D, 0);
glGenRenderbuffers(1, &_renderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _renderbuffer);
}
glGenRenderbuffers(1, &_depthBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, _depthBuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, _width, _height);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glGenFramebuffers(1, &_framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, _framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, _texture, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _renderbuffer);
for (int i = 0; i < _colorBufferCount; ++i) {
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0 + i, GL_TEXTURE_2D, _colorBuffers[i], 0);
}
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, _depthBuffer);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
throw runtime_error("Control: framebuffer is not complete");
@ -68,8 +74,10 @@ void Framebuffer::deinit() {
if (!_inited) return;
glDeleteFramebuffers(1, &_framebuffer);
glDeleteTextures(1, &_texture);
glDeleteRenderbuffers(1, &_renderbuffer);
glDeleteTextures(_colorBufferCount, &_colorBuffers[0]);
glDeleteRenderbuffers(1, &_depthBuffer);
_inited = false;
}
void Framebuffer::bind() const {
@ -80,12 +88,12 @@ void Framebuffer::unbind() const {
glBindFramebuffer(GL_FRAMEBUFFER, 0);
}
void Framebuffer::bindTexture() const {
glBindTexture(GL_TEXTURE_2D, _texture);
void Framebuffer::bindColorBuffer(int n) const {
glBindTexture(GL_TEXTURE_2D, _colorBuffers[n]);
}
void Framebuffer::unbindTexture() const {
glBindTexture(GL_TEXTURE_2D, _texture);
void Framebuffer::unbindColorBuffer() const {
glBindTexture(GL_TEXTURE_2D, 0);
}
int Framebuffer::width() const {

View file

@ -18,6 +18,7 @@
#pragma once
#include <cstdint>
#include <vector>
namespace reone {
@ -25,15 +26,15 @@ namespace render {
class Framebuffer {
public:
Framebuffer(int w, int h);
Framebuffer(int w, int h, int colorBufferCount = 1);
~Framebuffer();
void init();
void deinit();
void bind() const;
void unbind() const;
void bindTexture() const;
void unbindTexture() const;
void bindColorBuffer(int n) const;
void unbindColorBuffer() const;
int width() const;
int height() const;
@ -41,10 +42,11 @@ public:
private:
int _width { 0 };
int _height { 0 };
int _colorBufferCount { 0 };
bool _inited { false };
uint32_t _framebuffer { 0 };
uint32_t _texture { 0 };
uint32_t _renderbuffer { 0 };
std::vector<uint32_t> _colorBuffers;
uint32_t _depthBuffer { 0 };
};
} // namespace render

View file

@ -33,12 +33,19 @@ namespace reone {
namespace render {
static const int kMaxLightCount = 8;
static const int kBlurPassCount = 1;
SceneGraph::SceneGraph(const GraphicsOptions &opts) : _opts(opts), _framebuffer(opts.width, opts.height) {
SceneGraph::SceneGraph(const GraphicsOptions &opts) :
_opts(opts),
_geometryFramebuffer(opts.width, opts.height, 2),
_vBlurFramebuffer(opts.width, opts.height),
_hBlurFramebuffer(opts.width, opts.height) {
}
void SceneGraph::init() {
_framebuffer.init();
_geometryFramebuffer.init();
_vBlurFramebuffer.init();
_hBlurFramebuffer.init();
}
void SceneGraph::clear() {
@ -95,7 +102,11 @@ void SceneGraph::render() const {
ShaderManager &shaders = Shaders;
{
_framebuffer.bind();
// Render geometry
_geometryFramebuffer.bind();
static const GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1 };
glDrawBuffers(2, buffers);
glEnable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
@ -117,28 +128,98 @@ void SceneGraph::render() const {
mesh->render();
}
_framebuffer.unbind();
_geometryFramebuffer.unbind();
}
{
float w = static_cast<float>(_opts.width);
float h = static_cast<float>(_opts.height);
GlobalUniforms globals;
globals.projection = glm::ortho(0.0f, static_cast<float>(_opts.width), static_cast<float>(_opts.height), 0.0f);
globals.projection = glm::ortho(0.0f, w, h, 0.0f);
shaders.setGlobalUniforms(globals);
for (int i = 0; i < kBlurPassCount; ++i) {
{
// Apply horizontal blur
_hBlurFramebuffer.bind();
glEnable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glm::mat4 transform(1.0f);
transform = glm::scale(transform, glm::vec3(_framebuffer.width(), _framebuffer.height(), 1.0f));
transform = glm::scale(transform, glm::vec3(w, h, 1.0f));
LocalUniforms locals;
locals.model = move(transform);
locals.features.blurEnabled = true;
locals.blur.resolution = glm::vec2(w, h);
locals.blur.direction = glm::vec2(1.0f, 0.0f);
shaders.activate(ShaderProgram::GUIGUI, locals);
shaders.activate(ShaderProgram::GUIBlur, locals);
glActiveTexture(GL_TEXTURE0);
_framebuffer.bindTexture();
if (i == 0) {
_geometryFramebuffer.bindColorBuffer(1);
} else {
_vBlurFramebuffer.bindColorBuffer(0);
}
DefaultQuad.render(GL_TRIANGLES);
_framebuffer.unbindTexture();
_geometryFramebuffer.unbindColorBuffer();
_hBlurFramebuffer.unbind();
}
{
// Apply vertical blur
_vBlurFramebuffer.bind();
glEnable(GL_DEPTH_TEST);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glm::mat4 transform(1.0f);
transform = glm::scale(transform, glm::vec3(w, h, 1.0f));
LocalUniforms locals;
locals.model = move(transform);
locals.features.blurEnabled = true;
locals.blur.resolution = glm::vec2(_opts.width, _opts.height);
locals.blur.direction = glm::vec2(0.0f, 1.0f);
shaders.activate(ShaderProgram::GUIBlur, locals);
glActiveTexture(GL_TEXTURE0);
_hBlurFramebuffer.bindColorBuffer(0);
DefaultQuad.render(GL_TRIANGLES);
_hBlurFramebuffer.unbindColorBuffer();
_vBlurFramebuffer.unbind();
}
}
{
glm::mat4 transform(1.0f);
transform = glm::scale(transform, glm::vec3(w, h, 1.0f));
LocalUniforms locals;
locals.model = move(transform);
locals.features.bloomEnabled = true;
locals.textures.bloom = 1;
shaders.activate(ShaderProgram::GUIBloom, locals);
glActiveTexture(GL_TEXTURE0);
_geometryFramebuffer.bindColorBuffer(0);
glActiveTexture(GL_TEXTURE1);
_vBlurFramebuffer.bindColorBuffer(0);
DefaultQuad.render(GL_TRIANGLES);
glActiveTexture(GL_TEXTURE1);
_vBlurFramebuffer.unbindColorBuffer();
glActiveTexture(GL_TEXTURE0);
_geometryFramebuffer.unbindColorBuffer();
}
}

View file

@ -61,7 +61,9 @@ private:
std::shared_ptr<CameraSceneNode> _activeCamera;
glm::vec3 _ambientLightColor { 1.0f };
uint32_t _textureId { 0 };
Framebuffer _framebuffer;
Framebuffer _geometryFramebuffer;
Framebuffer _vBlurFramebuffer;
Framebuffer _hBlurFramebuffer;
SceneGraph(const SceneGraph &) = delete;
SceneGraph &operator=(const SceneGraph &) = delete;

View file

@ -116,10 +116,12 @@ static const GLchar kWhiteFragmentShader[] = R"END(
uniform float uAlpha;
out vec4 fragColor;
layout(location = 0) out vec4 fragColor;
layout(location = 1) out vec4 fragColorBright;
void main() {
fragColor = vec4(1.0, 1.0, 1.0, uAlpha);
fragColorBright = vec4(0.0, 0.0, 0.0, 1.0);
}
)END";
@ -132,11 +134,13 @@ uniform float uAlpha;
in vec2 fragTexCoords;
out vec4 fragColor;
layout(location = 0) out vec4 fragColor;
layout(location = 1) out vec4 fragColorBright;
void main() {
vec4 textureSample = texture(uTexture, fragTexCoords);
fragColor = vec4(uColor * textureSample.rgb, uAlpha * textureSample.a);
fragColorBright = vec4(0.0, 0.0, 0.0, 1.0);
}
)END";
@ -179,7 +183,8 @@ in vec3 fragNormal;
in vec2 fragTexCoords;
in vec2 fragLightmapCoords;
out vec4 fragColor;
layout(location = 0) out vec4 fragColor;
layout(location = 1) out vec4 fragColorBright;
void applyLightmap(inout vec3 color) {
vec4 lightmapSample = texture(uLightmap, fragLightmapCoords);
@ -219,13 +224,6 @@ void applyLighting(vec3 normal, inout vec3 color) {
}
}
void applySelfIllum(inout vec3 color) {
vec4 diffuseSample = texture(uDiffuse, fragTexCoords);
float luminosity = dot(diffuseSample.rgb, RGB_TO_LUMINOSITY);
color += 0.5 * smoothstep(0.5, 1.0, luminosity) * uSelfIllumColor;
}
void main() {
vec4 diffuseSample = texture(uDiffuse, fragTexCoords);
vec3 surfaceColor = diffuseSample.rgb;
@ -246,15 +244,19 @@ void main() {
} else {
lightColor = vec3(1.0);
}
if (uSelfIllumEnabled) {
applySelfIllum(lightColor);
}
float finalAlpha = uAlpha;
if (!uEnvmapEnabled && !uBumpyShinyEnabled) {
finalAlpha *= diffuseSample.a;
}
fragColor = vec4(lightColor * surfaceColor, finalAlpha);
if (uSelfIllumEnabled) {
vec3 color = uSelfIllumColor * diffuseSample.rgb;
fragColorBright = vec4(smoothstep(0.75, 1.0, color), 1.0);
} else {
fragColorBright = vec4(0.0, 0.0, 0.0, 1.0);
}
}
)END";
@ -269,17 +271,37 @@ out vec4 fragColor;
void main() {
vec2 uv = vec2(gl_FragCoord.xy / uResolution);
vec2 off1 = vec2(1.3333333333333333) * uDirection;
vec4 color = vec4(0.0);
color += texture2D(uTexture, uv) * 0.29411764705882354;
color += texture2D(uTexture, uv + (off1 / uResolution)) * 0.35294117647058826;
color += texture2D(uTexture, uv - (off1 / uResolution)) * 0.35294117647058826;
vec2 off1 = vec2(1.3846153846) * uDirection;
vec2 off2 = vec2(3.2307692308) * uDirection;
color += texture2D(uTexture, uv) * 0.2270270270;
color += texture2D(uTexture, uv + (off1 / uResolution)) * 0.3162162162;
color += texture2D(uTexture, uv - (off1 / uResolution)) * 0.3162162162;
color += texture2D(uTexture, uv + (off2 / uResolution)) * 0.0702702703;
color += texture2D(uTexture, uv - (off2 / uResolution)) * 0.0702702703;
fragColor = color;
}
)END";
static const GLchar kBloomFragmentShader[] = R"END(
#version 330
uniform sampler2D uGeometry;
uniform sampler2D uBloom;
in vec2 fragTexCoords;
out vec4 fragColor;
void main() {
vec3 geometryColor = texture(uGeometry, fragTexCoords).rgb;
vec3 bloomColor = texture(uBloom, fragTexCoords).rgb;
fragColor = vec4(geometryColor + bloomColor, 1.0);
}
)END";
ShaderManager &ShaderManager::instance() {
static ShaderManager instance;
return instance;
@ -291,9 +313,12 @@ void ShaderManager::initGL() {
initShader(ShaderName::FragmentWhite, GL_FRAGMENT_SHADER, kWhiteFragmentShader);
initShader(ShaderName::FragmentGUI, GL_FRAGMENT_SHADER, kGUIFragmentShader);
initShader(ShaderName::FragmentModel, GL_FRAGMENT_SHADER, kModelFragmentShader);
initShader(ShaderName::FragmentGaussianBlur, GL_FRAGMENT_SHADER, kGaussianBlurFragmentShader);
initShader(ShaderName::FragmentBlur, GL_FRAGMENT_SHADER, kGaussianBlurFragmentShader);
initShader(ShaderName::FragmentBloom, GL_FRAGMENT_SHADER, kBloomFragmentShader);
initProgram(ShaderProgram::GUIGUI, ShaderName::VertexGUI, ShaderName::FragmentGUI);
initProgram(ShaderProgram::GUIBlur, ShaderName::VertexGUI, ShaderName::FragmentBlur);
initProgram(ShaderProgram::GUIBloom, ShaderName::VertexGUI, ShaderName::FragmentBloom);
initProgram(ShaderProgram::ModelWhite, ShaderName::VertexModel, ShaderName::FragmentWhite);
initProgram(ShaderProgram::ModelModel, ShaderName::VertexModel, ShaderName::FragmentModel);
}
@ -432,6 +457,13 @@ void ShaderManager::setLocalUniforms(const LocalUniforms &locals) {
if (locals.features.selfIllumEnabled) {
setUniform("uSelfIllumColor", locals.selfIllumColor);
}
if (locals.features.blurEnabled) {
setUniform("uResolution", locals.blur.resolution);
setUniform("uDirection", locals.blur.direction);
}
if (locals.features.bloomEnabled) {
setUniform("uBloom", locals.textures.bloom);
}
}
void ShaderManager::setUniform(const string &name, const glm::mat4 &m) {

View file

@ -31,6 +31,8 @@ namespace render {
enum class ShaderProgram {
None,
GUIGUI,
GUIBlur,
GUIBloom,
ModelWhite,
ModelModel
};
@ -49,6 +51,8 @@ struct FeatureUniforms {
bool skeletalEnabled { false };
bool lightingEnabled { false };
bool selfIllumEnabled { false };
bool blurEnabled { false };
bool bloomEnabled { false };
};
struct TextureUniforms {
@ -56,6 +60,7 @@ struct TextureUniforms {
int envmap { 0 };
int bumpyShiny { 0 };
int bumpmap { 0 };
int bloom { 0 };
};
struct SkeletalUniforms {
@ -75,11 +80,17 @@ struct LightingUniforms {
std::vector<ShaderLight> lights;
};
struct GaussianBlurUniforms {
glm::vec2 resolution { 0.0f };
glm::vec2 direction { 0.0f };
};
struct LocalUniforms {
FeatureUniforms features;
TextureUniforms textures;
SkeletalUniforms skeletal;
LightingUniforms lighting;
GaussianBlurUniforms blur;
glm::mat4 model { 1.0f };
glm::vec3 color { 1.0f };
@ -105,7 +116,8 @@ private:
FragmentWhite,
FragmentGUI,
FragmentModel,
FragmentGaussianBlur
FragmentBlur,
FragmentBloom
};
std::unordered_map<ShaderName, uint32_t> _shaders;