c++ - 如何在 OpenGL 中更好地制作 2D 光照

标签 c++ opengl glsl draw

我想问一个关于我在 OpenGL 中的光照效果的问题。

我正在尝试添加照明,但我认为这不是很好,而且我看到一些 2D 照明图片比我的好得多。

问题:我做了一个聚光灯,但我希望它能随着光照范围变小而变暗,让它更像自然光,但我想不出解决办法。

我正在使用窗口大小为 (800, 600) 的正交矩阵,并使用真实的 x、y 坐标制作网格。我将 lightPos 和 PlayerPos 发送到片段着色器,并使用顶点作为网格的宽度和高度,以便为每个像素生成光照。

灯光只是一个基本的圆形,我不知道如何让它看起来更好。这里有一些图片。在片段着色器中,我使用毕达哥拉斯定理来计算两点之间的距离。

2d Light

enter image description here

这里是顶点和片元Shader

顶点着色器

#version 330

layout (location = 0) in vec3 pos;
layout (location = 1) in vec2 tCoord;

uniform mat4 mat;
out vec2 tCoord0;
out vec2 vPos;


void main(){
tCoord0 = vec2(tCoord.x, 1 - tCoord.y);
gl_Position = mat * vec4(pos, 1.0);
vPos = vec2(pos.x, pos.y);
}

片段着色器

    #version 330

out vec4 color;
uniform sampler2D sampler;
in vec2 tCoord0;
uniform vec3 objColor;
uniform vec2 lightPos;
uniform vec2 xyPos;

in vec2 vPos;

void main(){
vec4 textureColor = texture2D(sampler, tCoord0);

vec3 ambientLight = vec3(0.3f, 0.3f, 0.3f);

float dx = lightPos.x - (xyPos.x + vPos.x);
float dy = lightPos.y - (xyPos.y + vPos.y);

float dist = sqrt(dx * dx + dy * dy);

if(dist > 0 && dist < 50){
ambientLight = vec3(0.7f, 0.7f, 0.7f) * 0.6f;
}
else if(dist > 50 && dist < 70){
ambientLight = vec3(0.4f, 0.4f, 0.4f) * 0.6f;
}
else{
discard;
}

if((textureColor.x == 0 && textureColor.y == 0 && textureColor.z == 0) || textureColor.a <= 0){
color = vec4(objColor, 1.0) * vec4(ambientLight, 1.0);
}
else{
color = textureColor * vec4(ambientLight, 1.0) * vec4(objColor, 1.0);
}

}

抽屉.cpp

#include <graphics\shader.h>
#include <graphics\texture.h>
#include <graphics\shape.h>
#include <GL\glew.h>
#include <graphics\light.h>
#include <core\TSAContainer.h>
#include <core\drawer.h>

namespace GE{
    namespace core{

        std::vector<graphics::GraphicComponent*> Drawer::drawables;
        GLuint Drawer::buffer;

        void Drawer::init(){
            glGenFramebuffers(1, &buffer);
        }

        std::vector<graphics::GraphicComponent*>& Drawer::getAllGraphicComponents(){
            return drawables;
        }

        void Drawer::addDrawable(graphics::GraphicComponent* drawable){
            drawables.push_back(drawable);
        }

        void Drawer::destroy(){
            for (unsigned int i = 0; i < drawables.size(); i++)
                delete drawables[i];

            drawables.clear();

        }

        void Drawer::render(){
            for (std::vector<graphics::GraphicComponent*>::iterator it = drawables.begin(); it != drawables.end(); it++){
                if ((*it)->isDraw()){
                    (*it)->getShader().bind();

                    int color = getColor(static_cast<graphics::Shape*>(*it)->getColor());
                    int r = (color >> 16) & 0xff;
                    int g = (color >> 8) & 0xff;
                    int b = (color)& 0xff;

                    (*it)->getShader().setUniform("mat", (*it)->getTransformation().getTransformationMatrix());
                    (*it)->getShader().setUniform("objColor", r, g, b);
                    (*it)->getShader().setUniform("xyPos", (*it)->getTransformation().getPosition());
                    (*it)->getShader().setUniform("sampler", 1);

                    if (static_cast<graphics::Shape*>(*it)->getLight() != NULL){
                        static_cast<graphics::Shape*>(*it)->getLight()->update();
                    }
                    //(*it)->getShader().setUniform("ambientLight", static_cast<graphics::Shape*>(*it)->getAmbientLight());


                    glActiveTexture(GL_TEXTURE1);

                    if ((*it)->getTexture() != NULL)
                    (*it)->getTexture()->bind();

                    (*it)->getMesh().draw();


                    if ((*it)->getTexture() != NULL)
                    (*it)->getTexture()->unbind();

                    (*it)->getShader().unbind();
                }
            }
        }

        int Drawer::getColor(colorType color){
            int col = 0;
            if (color == GE_COLOR_BLUE){
                col = 0 << 16 | 0 << 8 | 1;
            }
            else if (GE_COLOR_GREEN == color){
                col = 0 << 16 | 1 << 8 | 0;
            }
            else if (GE_COLOR_RED == color){
                col = 1 << 16 | 0 << 8 | 0;
            }
            else{
                col = 1 << 16 | 1 << 8 | 1;
            }

            return col;
        }

        Drawer::Drawer(){

        }

        Drawer::~Drawer(){

        }

}
}

最佳答案

float dx = lightPos.x - (xyPos.x + vPos.x);
float dy = lightPos.y - (xyPos.y + vPos.y);
float dist = sqrt(dx * dx + dy * dy);
if(dist > 0 && dist < 50)
{
    ambientLight = vec3(0.7f, 0.7f, 0.7f) * 0.6f;
}
else if(dist > 50 && dist < 70)
{
    ambientLight = vec3(0.4f, 0.4f, 0.4f) * 0.6f;
}

此处您使用的是一种基于距离的恒定衰减。这将产生明亮的内圈和暗淡的外圈之间具有不自然的硬边的那种效果。

如果你想要一种柔和的渐变效果,你要避免这里的分支和常量。我们可以从线性衰减开始:

float dx = lightPos.x - (xyPos.x + vPos.x);
float dy = lightPos.y - (xyPos.y + vPos.y);
float dist = sqrt(dx * dx + dy * dy);
float max_dist = 70.0f;
float percent = clamp(1.0f - dist / max_dist, 0.0, 1.0f);

ambientLight = vec3(percent, percent, percent);

但是,如果中心周围有一个尖点,您可能会觉得有点难看。我们可以改用指数曲线,如下所示:

...
percent *= percent;
ambientLight = vec3(percent, percent, percent);

为了使它有点“圆润”,你可以再次相乘:

...
percent *= percent * percent;
ambientLight = vec3(percent, percent, percent);

如果这与您想要的视觉效果相反,您可以尝试 sqrt:

float percent = clamp(1.0f - dist / max_dist, 0.0, 1.0f);
percent = sqrt(percent);

由于我不确切地知道您在视觉上想要什么,所以这些是最初可以尝试的一些事情。和这两个一起玩,看看你是否喜欢你得到的东西。

如果你真的想最大限度地控制效果,三次贝塞尔曲线插值可能会派上用场:

float bezier4(float p1, float p2, float p3, float p4, float t)
{
    const float mum1 = 1.0f - t;
    const float mum13 = mum1 * mum1 * mum1;
    const float mu3 = t * t * t;
    return mum13 * p1 + 3 * t * mum1 * mum1 * p2 + 3 * t * t * mum1 * p3 + mu3 * p4;
}
...
float percent = clamp(1.0f - dist / max_dist, 0.0, 1.0f);

// Can play with the first four arguments to achieve the desired effect.
percent = bezier4(0.0f, 0.25f, 0.75f, 1.0f, percent);
ambientLight = vec3(percent, percent, percent);

这会让您对效果有很大的控制权,但可能有点矫枉过正。先尝试其他方法。

关于c++ - 如何在 OpenGL 中更好地制作 2D 光照,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33972699/

相关文章:

c++ - 如何在不违反 C++ 核心准则的情况下将整数转换为 void*?

glsl - 当 memoryBarrier 不同步时,为什么屏障同步共享内存?

c++:在网格上找到离查询点最近的点

c++ - MS Windows 中的 QT 和 native OpenGL 支持

javascript - WebGL:使用两个着色器,第一个着色器的输出作为第二个着色器的输入,只能看到第二个着色器的输出

c++ - 在纹理中存储 float 数组并使用纹理坐标从着色器访问 float

c++ - (C++) 使用大括号和右值初始化时出现内部编译器错误

c++ - “下一个”不是 'std' 的成员

c++ - 在什么情况下我应该更喜欢 io.h 而不是 windows.h?

c++ - 阻止通过 ifstream 对象从 FIFO 读取