有的2d地图要实现黑夜效果,在人物周围有光源实现部分可见。就需要借助glsl着色器语言。利用显卡绘制光源效果。
在片段着色器里,需要定义光源半径,中心位置,还有绘制的纹理。根据像素点偏离中心的距离,计算像素点的颜色亮度。
shader.txt
#version 330 core
// 定义常量
uniform float in_radius; // 半径
uniform vec2 in_lightcenter;//光亮中心位置
uniform sampler2D in_texture;//纹理
void main()
{
float distance = length( gl_TexCoord[0].xy -in_lightcenter);
//当前像素颜色
vec4 color = texture2D(in_texture, gl_TexCoord[0].xy);
// 根据距离判断是否在圆形区域内,并计算颜色
if(distance<in_radius){
//简单的计算偏离光源中心的位置,用于弱化亮度
float a = distance/in_radius;
//在圆形区域内,亮度是原来的1.0,但是随着偏离逐渐变暗
gl_FragColor = vec4(1.0-0.9*a,1.0-0.9*a,1.0-0.9*a, 1.0)*color;
}
else
{
// 在圆形区域外,亮度减少到原来的0.08
gl_FragColor = vec4(0.08,0.08,0.08, 1.0)*color;
}
}
在glsl里,
gl_TexCoord[0]就是片段的纹理坐标,gl_FragCoord.xy / in_resolution.xy,gl_FragCoord.xy是当前片段的窗口坐标,而 in_resolution.xy是外部输入的变量表示当前窗口的分辨率,通过除以窗口的分辨率,将当前片段坐标规范化到0到1的范围内,方便对纹理或屏幕空间进行采样或计算。
使用sfml实现程序,首先就定义了白色背景和两个三角形。从shader.txt加载片段着色器,设置输入的变量。
在主循环中进行绘制。不断更新光源位置,将图像绘制在sf::RenderTexture里,然后使用片段着色器绘制到窗口。
#include <SFML/Graphics.hpp>
#include <iostream>
int main()
{
const int WIDTH = 800;
const int HEIGHT = 600;
sf::RenderWindow window(sf::VideoMode(WIDTH, HEIGHT), "a");
window.setFramerateLimit(30);
//时钟
sf::Clock clock;
//白色背景
sf::VertexArray bg(sf::Quads,4);
bg[0].position = sf::Vector2f(0.f,0.f);
bg[1].position = sf::Vector2f(0.f,600.f);
bg[2].position = sf::Vector2f(800.f,600.f);
bg[3].position = sf::Vector2f(800.f,0.f);
bg[0].color = sf::Color::White;
bg[1].color = sf::Color::White;
bg[2].color = sf::Color::White;
bg[3].color = sf::Color::White;
// 创建两个三角形
sf::VertexArray target(sf::Triangles, 6);
target[0].position = sf::Vector2f(100.f,100.f);
target[1].position = sf::Vector2f(200.f,500.f);
target[2].position = sf::Vector2f(300.f,150.f);
target[3].position = sf::Vector2f(400.f,300.f);
target[4].position = sf::Vector2f(500.f,200.f);
target[5].position = sf::Vector2f(600.f,400.f);
target[0].color = sf::Color::Red;
target[1].color = sf::Color::Blue;
target[2].color = sf::Color::Yellow;
target[3].color = sf::Color::Red;
target[4].color = sf::Color::Green;
target[5].color = sf::Color::Blue;
//创建RenderTexture
sf::RenderTexture renderTexture;
renderTexture.create(WIDTH, HEIGHT);
// 创建一个着色器
sf::Shader shader;
if (!shader.loadFromFile("shader.txt", sf::Shader::Fragment))
{
std::cout << "Failed to load shader!" << std::endl;
return -1;
}
//设置着色器输入变量
shader.setUniform("in_texture", sf::Shader::CurrentTexture);//纹理
shader.setUniform("in_radius", 0.15f);//半径
// 主循环
while (window.isOpen())
{
sf::Event event;
while (window.pollEvent(event))
{
if (event.type == sf::Event::Closed)
window.close();
}
//控制光亮位置 简单的在水平方向移动
if(clock.getElapsedTime().asSeconds()>10){
clock.restart();
shader.setUniform("in_lightcenter", sf::Vector2f(clock.getElapsedTime().asSeconds()/10,0.5));
}else{
shader.setUniform("in_lightcenter", sf::Vector2f(clock.getElapsedTime().asSeconds()/10,0.5));
}
//离屏绘制
renderTexture.clear(sf::Color::Transparent);
renderTexture.draw(bg);
renderTexture.draw(target);
//绘制到窗体
window.clear();
window.draw(sf::Sprite(renderTexture.getTexture()), &shader);
window.display();
}
return 0;
}
其中的
是使用了一个特殊的函数重载,这样不需要自己设置纹理,而是它会在绘制时自动设置绘制实体的纹理给目标变量。
效果