调整大小时移动了灯光着色器 window
Light shader moved while resizing window
我一直在努力制作一个小的光着色器。
它工作得很好,我的意思是,光线会像预期的那样消失,它是我的角色周围的一个圆圈,随着它移动。
只有当调整大小事件不存在时,它才可能是完美的。
当 SFML 调整 window 的大小时,它会放大所有内容,但方式很奇怪。它扩大了一切 除了着色器 。
我尝试调整我的大小 window(我喜欢调整像素图游戏的大小,我觉得它最漂亮。所以我不想阻止调整大小事件)。
这是我的着色器:
uniform vec3 light;
void main(void) {
float distance = sqrt(pow(gl_FragCoord.x - light.x, 2) + pow(gl_FragCoord.y - light.y, 2));
float alpha = 1.;
if (distance <= light.z) {
alpha = (1.0 / light.z) * distance;
}
gl_FragColor = vec4(0., 0., 0., alpha);
}
所以,问题是,我的 window 显示为 1280 x 736(以适应 32x32 纹理),而我有一个 1920 x 1080 显示器。当我将 window 放大以适合 1920 x 1080(包括标题栏)时,整个内容正确调整大小,一切正常,但着色器现在为 1920x1080(减去标题栏)。所以着色器需要不同的坐标(对于着色器来说,x = 32,y = 0 应该是 x = 48 y = 0)。
所以我想知道,是否可以将着色器与整个 window 一起放大?我应该使用事件或类似的东西吗?
感谢您的回答^^
编辑:这是一些照片:
所以这是调整大小之前的光着色器(它在播放器上到处都是黑暗的,就像它应该的那样)。
然后我调整 window 的大小,播放器没有移动,纹理适合整个 window,但光线移动了。
所以,为了正确解释,当我调整 window 的大小时,我希望一切都适合 window,所以它充满了纹理,但是当我这样做时,给我的坐标着色器是调整大小之前的着色器,如果我移动它,就好像我没有调整 window 大小一样移动,所以我的播放器再也不会亮了。
我不确定是否更清楚,但我尽力了。
EDIT2:这是我调用着色器的代码:
void Graphics::UpdateLight() {
short radius = 65; // 265 on the pictures
int x = m_game->GetPlayer()->GetSprite()->getPosition().x + CASE_LEN / 2; // Setting on the middle of the player sprite (CASE_LEN is a const which contains the size of a case (here 32))
int y = HEIGHT - (m_game->GetPlayer()->GetSprite()->getPosition().y + CASE_LEN / 2); // (the "HEIGHT -" part was set because it seems that y = 0 is on the bottom of the texture for GLSL)
sf::Vector3f shaderLight;
shaderLight.x = x;
shaderLight.y = y;
shaderLight.z = radius;
m_lightShader.setParameter("light", shaderLight);
}
您显示的代码片段实际上只更新了着色器坐标(快速浏览一下,它看起来不错)。该错误很可能发生在您实际绘制东西的地方。
我会使用完全不同的方法,因为一旦渲染多个事物、其他光源等,您的着色器方法可能会变得相当乏味。
因此,我建议您将光照贴图渲染为渲染纹理(本质上类似于 "black = no light, color = light of that color")。
我没有尝试用文字解释所有内容,而是编写了一个快速注释示例程序,它将在屏幕上绘制 window 并将一些光源移动到背景图像上(我使用了一个SFML 的着色器示例附带的):
除了启动路径中有一个名为 "background.jpg" 的文件外,没有其他要求。
请随意复制此代码或使用它来获取灵感。请记住,这没有经过优化,实际上只是一个快速编辑以显示总体思路。
#include <SFML/Graphics.hpp>
#include <vector>
#include <cmath>
const float PI = 3.1415f;
struct Light
{
sf::Vector2f position;
sf::Color color;
float radius;
};
int main()
{
// Let's setup a window
sf::RenderWindow window(sf::VideoMode(640, 480), "SFML Lights");
window.setVerticalSyncEnabled(false);
window.setFramerateLimit(60);
// Create something simple to draw
sf::Texture texture;
texture.loadFromFile("background.jpg");
sf::Sprite background(texture);
// Setup everything for the lightmap
sf::RenderTexture lightmapTex;
// We're using a 512x512 render texture for max. compatibility
// On modern hardware it could match the window resolution of course
lightmapTex.create(512, 512);
sf::Sprite lightmap(lightmapTex.getTexture());
// Scale the sprite to fill the window
lightmap.setScale(640 / 512.f, 480 / 512.f);
// Set the lightmap's view to the same as the window
lightmapTex.setView(window.getDefaultView());
// Drawable helper to draw lights
// We'll just have to adjust the first vertex's color to tint it
sf::VertexArray light(sf::PrimitiveType::TriangleFan);
light.append({sf::Vector2f(0, 0), sf::Color::White});
// This is inaccurate, but for demo purposes…
// This could be more elaborate to allow better graduation etc.
for (float i = 0; i <= 2 * PI; i += PI * .125f)
light.append({sf::Vector2f(std::sin(i), std::cos(i)), sf::Color::Transparent});
// Setup some lights
std::vector<Light> lights;
lights.push_back({sf::Vector2f(50.f, 50.f), sf::Color::White, 100.f });
lights.push_back({sf::Vector2f(350.f, 150.f), sf::Color::Red, 150.f });
lights.push_back({sf::Vector2f(150.f, 250.f), sf::Color::Yellow, 200.f });
lights.push_back({sf::Vector2f(250.f, 450.f), sf::Color::Cyan, 100.f });
// RenderStates helper to transform and draw lights
sf::RenderStates rs(sf::BlendAdd);
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
switch (event.type) {
case sf::Event::Closed:
window.close();
break;
}
}
bool flip = false; // simple toggle to animate differently
// Draw the light map
lightmapTex.clear(sf::Color::Black);
for(Light &l : lights)
{
// Apply all light attributes and render it
// Reset the transformation
rs.transform = sf::Transform::Identity;
// Move the light
rs.transform.translate(l.position);
// And scale it (this could be animated to create flicker)
rs.transform.scale(l.radius, l.radius);
// Adjust the light color (first vertex)
light[0].color = l.color;
// Draw the light
lightmapTex.draw(light, rs);
// To make things a bit more interesting
// We're moving the lights
l.position.x += flip ? 2 : -2;
flip = !flip;
if (l.position.x > 640)
l.position.x -= 640;
else if (l.position.x < 0)
l.position.x += 640;
}
lightmapTex.display();
window.clear(sf::Color::White);
// Draw the background / game
window.draw(background);
// Draw the lightmap
window.draw(lightmap, sf::BlendMultiply);
window.display();
}
}
我一直在努力制作一个小的光着色器。 它工作得很好,我的意思是,光线会像预期的那样消失,它是我的角色周围的一个圆圈,随着它移动。 只有当调整大小事件不存在时,它才可能是完美的。
当 SFML 调整 window 的大小时,它会放大所有内容,但方式很奇怪。它扩大了一切 除了着色器 。 我尝试调整我的大小 window(我喜欢调整像素图游戏的大小,我觉得它最漂亮。所以我不想阻止调整大小事件)。
这是我的着色器:
uniform vec3 light;
void main(void) {
float distance = sqrt(pow(gl_FragCoord.x - light.x, 2) + pow(gl_FragCoord.y - light.y, 2));
float alpha = 1.;
if (distance <= light.z) {
alpha = (1.0 / light.z) * distance;
}
gl_FragColor = vec4(0., 0., 0., alpha);
}
所以,问题是,我的 window 显示为 1280 x 736(以适应 32x32 纹理),而我有一个 1920 x 1080 显示器。当我将 window 放大以适合 1920 x 1080(包括标题栏)时,整个内容正确调整大小,一切正常,但着色器现在为 1920x1080(减去标题栏)。所以着色器需要不同的坐标(对于着色器来说,x = 32,y = 0 应该是 x = 48 y = 0)。
所以我想知道,是否可以将着色器与整个 window 一起放大?我应该使用事件或类似的东西吗?
感谢您的回答^^
编辑:这是一些照片:
所以,为了正确解释,当我调整 window 的大小时,我希望一切都适合 window,所以它充满了纹理,但是当我这样做时,给我的坐标着色器是调整大小之前的着色器,如果我移动它,就好像我没有调整 window 大小一样移动,所以我的播放器再也不会亮了。
我不确定是否更清楚,但我尽力了。
EDIT2:这是我调用着色器的代码:
void Graphics::UpdateLight() {
short radius = 65; // 265 on the pictures
int x = m_game->GetPlayer()->GetSprite()->getPosition().x + CASE_LEN / 2; // Setting on the middle of the player sprite (CASE_LEN is a const which contains the size of a case (here 32))
int y = HEIGHT - (m_game->GetPlayer()->GetSprite()->getPosition().y + CASE_LEN / 2); // (the "HEIGHT -" part was set because it seems that y = 0 is on the bottom of the texture for GLSL)
sf::Vector3f shaderLight;
shaderLight.x = x;
shaderLight.y = y;
shaderLight.z = radius;
m_lightShader.setParameter("light", shaderLight);
}
您显示的代码片段实际上只更新了着色器坐标(快速浏览一下,它看起来不错)。该错误很可能发生在您实际绘制东西的地方。
我会使用完全不同的方法,因为一旦渲染多个事物、其他光源等,您的着色器方法可能会变得相当乏味。
因此,我建议您将光照贴图渲染为渲染纹理(本质上类似于 "black = no light, color = light of that color")。
我没有尝试用文字解释所有内容,而是编写了一个快速注释示例程序,它将在屏幕上绘制 window 并将一些光源移动到背景图像上(我使用了一个SFML 的着色器示例附带的):
除了启动路径中有一个名为 "background.jpg" 的文件外,没有其他要求。
请随意复制此代码或使用它来获取灵感。请记住,这没有经过优化,实际上只是一个快速编辑以显示总体思路。
#include <SFML/Graphics.hpp>
#include <vector>
#include <cmath>
const float PI = 3.1415f;
struct Light
{
sf::Vector2f position;
sf::Color color;
float radius;
};
int main()
{
// Let's setup a window
sf::RenderWindow window(sf::VideoMode(640, 480), "SFML Lights");
window.setVerticalSyncEnabled(false);
window.setFramerateLimit(60);
// Create something simple to draw
sf::Texture texture;
texture.loadFromFile("background.jpg");
sf::Sprite background(texture);
// Setup everything for the lightmap
sf::RenderTexture lightmapTex;
// We're using a 512x512 render texture for max. compatibility
// On modern hardware it could match the window resolution of course
lightmapTex.create(512, 512);
sf::Sprite lightmap(lightmapTex.getTexture());
// Scale the sprite to fill the window
lightmap.setScale(640 / 512.f, 480 / 512.f);
// Set the lightmap's view to the same as the window
lightmapTex.setView(window.getDefaultView());
// Drawable helper to draw lights
// We'll just have to adjust the first vertex's color to tint it
sf::VertexArray light(sf::PrimitiveType::TriangleFan);
light.append({sf::Vector2f(0, 0), sf::Color::White});
// This is inaccurate, but for demo purposes…
// This could be more elaborate to allow better graduation etc.
for (float i = 0; i <= 2 * PI; i += PI * .125f)
light.append({sf::Vector2f(std::sin(i), std::cos(i)), sf::Color::Transparent});
// Setup some lights
std::vector<Light> lights;
lights.push_back({sf::Vector2f(50.f, 50.f), sf::Color::White, 100.f });
lights.push_back({sf::Vector2f(350.f, 150.f), sf::Color::Red, 150.f });
lights.push_back({sf::Vector2f(150.f, 250.f), sf::Color::Yellow, 200.f });
lights.push_back({sf::Vector2f(250.f, 450.f), sf::Color::Cyan, 100.f });
// RenderStates helper to transform and draw lights
sf::RenderStates rs(sf::BlendAdd);
while (window.isOpen()) {
sf::Event event;
while (window.pollEvent(event)) {
switch (event.type) {
case sf::Event::Closed:
window.close();
break;
}
}
bool flip = false; // simple toggle to animate differently
// Draw the light map
lightmapTex.clear(sf::Color::Black);
for(Light &l : lights)
{
// Apply all light attributes and render it
// Reset the transformation
rs.transform = sf::Transform::Identity;
// Move the light
rs.transform.translate(l.position);
// And scale it (this could be animated to create flicker)
rs.transform.scale(l.radius, l.radius);
// Adjust the light color (first vertex)
light[0].color = l.color;
// Draw the light
lightmapTex.draw(light, rs);
// To make things a bit more interesting
// We're moving the lights
l.position.x += flip ? 2 : -2;
flip = !flip;
if (l.position.x > 640)
l.position.x -= 640;
else if (l.position.x < 0)
l.position.x += 640;
}
lightmapTex.display();
window.clear(sf::Color::White);
// Draw the background / game
window.draw(background);
// Draw the lightmap
window.draw(lightmap, sf::BlendMultiply);
window.display();
}
}