【发布时间】:2019-06-09 19:17:07
【问题描述】:
我想为我的游戏创建一个发光效果。为了保持这种简约,假设我想为图像发光。 :)
从这个开始:
这是一个三步法。
- 保存场景中的所有明亮像素(= 发光)
- 对这些像素应用模糊效果(= 模糊)
- 在顶部绘制原始图片和模糊纹理(= 组装)
第 1 步和第 3 步没问题。模糊部分只是不想正常工作。
在我进一步解释之前,这是我的发光结果: (阈值 = 0.67f)
现在,当我模糊这一点时,我得到了一些不幸的结果:
这个黑边来自这样一个事实,即任何透明颜色都是黑色vec4(0.0, 0.0, 0.0, 0.0)。这不是 SFML/GLSL 中的未知问题,建议为此使用 SFML 的 sf::BlendMode 并将片段着色器中最终像素颜色的 .rgb 值与其 alpha 值相乘。所以我做到了,现在这是我的结果:
更好,但绝对不好。模糊着色器现在还平均化发光蒙版的周围像素。组装后只是一张模糊的图片:
.. 我尝试通过检查像素的 alpha 是否为零来在着色器文件中“修复”这个问题。这样我在平均时就不重视它们。但是由于 sf::BlendMode 被激活,我不知道 alpha 现在的行为如何 - 所以我停用了混合模式,但我仍然有奇怪的结果。 (在这个问题的最开始,我提供了代码和这次尝试的结果)
我没有尝试修复这项工作。我真的可以在这里使用一些帮助。也许我在着色器中做了一些根本错误的事情......这是完整的代码 - 如果要编译它,请使用 2 个片段着色器和 background.jpg(在 1280x720 中)创建一个文件夹资源。
luminescence.frag
#version 120
uniform sampler2D texture;
uniform float threshold;
void main(void){
vec3 current_color = texture2D(texture, gl_TexCoord[0].xy).rgb;
vec4 pixel = vec4(current_color.rgb, 0.0);
float brightness = dot(current_color.rgb, vec3(0.2126, 0.7152, 0.0722));
if (brightness >= threshold){
pixel = texture2D(texture, gl_TexCoord[0].xy);
}
gl_FragColor = pixel;
}
boxblur.frag
#version 120
uniform sampler2D texture;
uniform float texture_inverse;
uniform int blur_radius;
uniform vec2 blur_direction;
void main(void){
vec4 sum = texture2D(texture, gl_TexCoord[0].xy);
for (int i = 0; i < blur_radius; ++i){
sum += texture2D(texture, gl_TexCoord[0].xy + (i * texture_inverse) * blur_direction);
sum += texture2D(texture, gl_TexCoord[0].xy - (i * texture_inverse) * blur_direction);
}
vec4 pixel = vec4(sum / (blur_radius * 2 + 1));
pixel.rgb *= pixel.a;
gl_FragColor = pixel;
}
main.cpp
#include <SFML/Graphics.hpp>
#include <iostream>
#include <exception>
void run() {
const sf::Vector2f SIZE(1280, 720);
sf::Texture background_tex;
background_tex.loadFromFile("resources/background.jpg");
sf::Sprite background(background_tex);
sf::Shader luminescence;
luminescence.loadFromFile("resources/luminescence.frag", sf::Shader::Fragment);
luminescence.setUniform("texture", sf::Shader::CurrentTexture);
luminescence.setUniform("threshold", 0.67f);
sf::Shader blur;
blur.loadFromFile("resources/boxblur.frag", sf::Shader::Fragment);
blur.setUniform("texture", sf::Shader::CurrentTexture);
blur.setUniform("texture_inverse", 1.0f / SIZE.x);
sf::RenderStates shader_states;
shader_states.blendMode = sf::BlendMode(sf::BlendMode::One, sf::BlendMode::OneMinusSrcAlpha);
sf::ContextSettings context_settings;
context_settings.antialiasingLevel = 12;
//draws background
sf::RenderTexture scene_render;
scene_render.create(SIZE.x, SIZE.y, context_settings);
//draws luminescence and blur
sf::RenderTexture shader_render;
shader_render.create(SIZE.x, SIZE.y, context_settings);
sf::RenderWindow window(sf::VideoMode(SIZE.x, SIZE.y), "glsl fun", sf::Style::Default, context_settings);
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
}
}
scene_render.clear();
scene_render.draw(background);
scene_render.display();
//apply luminescence
shader_states.shader = &luminescence;
shader_render.clear(sf::Color::Transparent);
shader_render.draw(sf::Sprite(scene_render.getTexture()), shader_states);
shader_render.display();
//apply two pass gaussian blur 3 times to simulate gaussian blur.
shader_states.shader = &blur;
float blur_radius = 30.0f;
for (int i = 0; i < 3; ++i) {
blur.setUniform("blur_radius", static_cast<int>(blur_radius));
//vertical blur
blur.setUniform("blur_direction", sf::Glsl::Vec2(1.0, 0.0));
shader_render.draw(sf::Sprite(shader_render.getTexture()), shader_states);
shader_render.display();
//horizontal blur
blur.setUniform("blur_direction", sf::Glsl::Vec2(0.0, 1.0));
shader_render.draw(sf::Sprite(shader_render.getTexture()), shader_states);
shader_render.display();
//decrease blur_radius to simulate a gaussian blur
blur_radius *= 0.45f;
}
//assembly
window.clear();
window.draw(sf::Sprite(scene_render.getTexture()));
window.draw(sf::Sprite(shader_render.getTexture()));
window.display();
}
}
int main() {
try {
run();
}
catch (std::exception e) {
std::cerr << "caught exception - - - " << e.what() << '\n';
return 1;
}
return 0;
}
这是我试图排除零 alpha 值的 boxblur.frag:(当然,我在 main.cpp 的第 29 行删除了 shader_states.blendMode = sf::BlendMode(sf::BlendMode::One, sf::BlendMode::OneMinusSrcAlpha);):
#version 120
uniform sampler2D texture;
uniform float texture_inverse;
uniform int blur_radius;
uniform vec2 blur_direction;
void main(void){
float div = 0.0;
vec4 sum = vec4(0.0, 0.0, 0.0, 0.0);
vec4 temp_color = texture2D(texture, gl_TexCoord[0].xy);
if (temp_color.a > 0.0){
sum += temp_color;
div += 1.0;
}
for (int i = 0; i < blur_radius; ++i){
temp_color = texture2D(texture, gl_TexCoord[0].xy + (i * texture_inverse) * blur_direction);
if (temp_color.a > 0.0){
sum += temp_color;
div += 1.0;
}
temp_color = texture2D(texture, gl_TexCoord[0].xy - (i * texture_inverse) * blur_direction);
if (temp_color.a > 0.0){
sum += temp_color;
div += 1.0;
}
}
vec4 pixel;
if (div == 0.0){
pixel = vec4(texture2D(texture, gl_TexCoord[0].xy).rgb, 0.0);
}
else{
pixel = vec4(sum / div);
}
gl_FragColor = pixel;
}
[我正在使用 Visual Studio 2017 社区] - 感谢您的帮助!
【问题讨论】:
-
不检查 alpha>0,您必须将每种颜色乘以它的 alpha,然后除以在本地找到的总 alpha。大量计算...我有使用 alpha 通道进行卷积效果的代码。在这里发布了好几次,但没有人理解它.... :-/
-
@DanByström 嘿,这听起来很有趣,你能给我一个你的代码的链接吗?
-
哦,是的,我以前见过这个,是的,我不明白。
T= 红色,M= 绿色,B= 蓝色,D= 阿尔法?什么是后缀L、M和R?我也不明白 alpha 偏移量。 -
这是因为这是实时优化的代码,旨在快速而不是可读的 T/M/R 代表顶部、中间和底部扫描线,因为这是一个 3x3 卷积。我已经尝试过回答。