如何在OpenGL中更好地制作2D照明

时间:2015-11-28 14:56:45

标签: c++ opengl glsl draw

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

我正在尝试添加灯光,但我认为它并不好,而且我已经看到了一些比我好得多的2D照明图片。

问题:我已经成为一个聚光灯,但是我希望它变得更暗,因为它的光线范围变得更低并且更像是自然光,但是我无法找到解决方案。

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

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

2d Light

enter image description here

这是顶点和片段着色器

Vetex着色器

#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);
}

}

Drawer.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(){

        }

}
}

1 个答案:

答案 0 :(得分:2)

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);

这会让你对这个效果有很多控制权,但可能有点过分。首先尝试其他方法。