[OpenGL] 太阳镜头光晕效果

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/ZJU_fish1996/article/details/86761765

        太阳镜头光晕效果展示。光晕的贴图是随便画的多边形,如果替换成比较好的贴图效果会更真实,本文主要介绍原理。(框架:OpenGL + Qt)

      基本原理

        在屏幕上用一张面片绘制太阳和光晕,使其始终朝向镜头,且大小不随深度变化而变化。

        光晕分布在太阳位置和镜头(屏幕)中心的连线上,且仅在太阳出现在镜头中时,绘制光晕。

        接下来介绍几个要点的实现。

      太阳和光晕的绘制

        如前文所提,太阳和光晕需要始终朝向镜头,且大小不随深度变化而变化。

        为了实现前者,在物体从本地空间到世界空间的变换中,我们对物体做了一个矫正旋转,使其旋转到朝向镜头的位置。

        也就是说,对于太阳和光晕,我们的模型变换矩阵如下构造:

    modelMatrix.setToIdentity();
    modelMatrix.translate(position);
    modelMatrix *= Camera::Inst()->GetRotation();
    modelMatrix.scale(scale,scale,1);

       其中,Camera的GetRotation是取得相机旋转的逆矩阵,它的实现如下:

QMatrix4x4 Camera::GetRotation()
{
    QMatrix4x4 rotation;
    rotation.rotate(rotateX, QVector3D(1,0,0));
    rotation.rotate(rotateY, QVector3D(0,1,0));
    return rotation.inverted();
}

        为了实现后者,我们需要保证物体到相机的距离是恒定的,为了实现这一点,最简单的方法是,我们可以直接在视图变换后,把z坐标写死,此处写在了顶点着色器中:

uniform mat4 ModelMatrix;
uniform mat4 ViewMatrix;
uniform mat4 ProjectMatrix;
in vec4 a_position;
in vec2 a_texcoord;
out vec2 v_texcoord;
void main(void)
{
    gl_Position = ModelMatrix * a_position;
    gl_Position = ViewMatrix * gl_Position;
    gl_Position.z = -30;
    gl_Position = ProjectMatrix * gl_Position;
    v_texcoord = a_texcoord;
}

      判断物体是否出现在镜头中

        我们知道,对于OpenGL而言,透视变换之后,会转换到一个裁剪空间(Clipping Space) ,空间内x,y,z取值范围都在[-1,1]之间,在此范围之外的将被裁剪。由此我们可以得到一个结论,如果不考虑深度上的遮挡,对于一个物体而言,如果透视变换后,x,y,z都落在[-1,1]之间,那么它处在镜头中。因此,我们只需判断太阳位置经过视图、透视变换后的坐标即可,又因为我们将其深度限制了,所以,可以无需考虑z的取值。

        我们的判定代码如下:


bool Icon::IsOnScreen()
{
    QVector4D pos = RenderCommon::Inst()->GetProjectMatrix() 
           *(Camera::Inst()->GetViewMatrix() * (QVector4D(position,1)));
    float x = pos.x()/pos.w();
    float y = pos.y()/pos.w();
    return (x >= -1 && x <= 1 && y >= -1 && y <= 1 );
}

       透明混合开启

         为了表现多个光晕重叠在一起的效果,我们需要开启透明混合效果,开启方式如下,记得绘制结束后关闭效果。

    glEnable(GL_BLEND);
    glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

         我们尽可能将透明物体放到较后绘制,此例中我们的透明物体情况没有复杂的遮挡关系,我们只需要从靠近太阳的光晕开始绘制,最后绘制靠近镜头的光晕,保证渲染结果。

        还有一个特别需要注意的问题,我们需要在此期间关闭深度测试,不然会出现被透明物体遮挡的现象。关闭方式为:

glDepthMask(GL_FALSE);

        如果没有关闭,由于此处太阳深度更靠近相机,我们会发现,太阳的位置附近的光晕被遮挡住了,有一个明显的折痕:

猜你喜欢

转载自blog.csdn.net/ZJU_fish1996/article/details/86761765
今日推荐