倒影(reflection)特效的实现
作者: 刘鹏
日期: 2010-06-09
本文详细介绍了如何实现 reflection 特效。

简介

倒影可以为 UI 界面带来立体感,在 UI 中用的很多,如 homescreen、coverflow 等,如下图所示。

from 参考资料1
from 参考资料1

实现 reflection 特效可以使用 3D 方法,也可以使用 2D 方法,下面将分别介绍。

2D 方法

用 2D 方法实现倒影(mirror)需要从两个方面考虑:

  1. 倒影(mirror)是将原来的图像上、下翻转过来;
  2. 从上到下半透明度的渐变效果。

QT 为例,生成一幅图像的 mirror 的伪代码如下所示:


... ...

QImage mirrorImage (const QImage &img,
                    MirrorStyle mirrorStyle=MirrorOverX,
                    FadeStyle fadeStyle=FadeDown)
{
    /* Reverse image */
    QImage tmpImage = img;
    if (mirrorStyle != NoMirror)
        tmpImage = tmpImage.mirrored(mirrorStyle == MirrorOverY,
                                     mirrorStyle == MirrorOverX);

    /*Add gradient to mirrored image.*/
    if (fadeStyle != NoFade) {
        QPoint p1, p2;

        if (fadeStyle == FadeDown)
            p2.setY(tmpImage.height());
        else if (fadeStyle == FadeUp)
            p1.setY(tmpImage.height());
        else if (fadeStyle == FadeRight)
            p2.setX(tmpImage.width());
        else if (fadeStyle == FadeLeft)
            p1.setX(tmpImage.width());

        QLinearGradient gradient(p1, p2);
        gradient.setColorAt(0, QColor(0, 0, 0, 100));
        gradient.setColorAt(1, Qt::transparent);

        QPainter p(&tmpImage);
        p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
        p.fillRect(0, 0, tmpImage.width(), tmpImage.height(), gradient);
        p.end();
    }

    return tmpImage;


}

在上面的代码中用到了 QImage 的一个方法 mirrored,它的功能是将 image 的 数据按行上下对换,比如一幅 MxN 的图像,第 0 行与第 m-1 行对换,第 1 行 与第 m-2 行对换,依此类推,得到的结果是图像翻转过来,也就是 mirror。以 24 位色的图像为例,算法实现如下所示:


QImage QImage::mirrored(bool horizontal, bool vertical) const
{
    ... ...

    // Create result image, copy colormap
    QImage result(d->width, d->height, d->format);
    result.d->colortable = d->colortable;
    result.d->has_alpha_clut = d->has_alpha_clut;

    int dxi = horizontal ? -1 : 1;
    int dxs = horizontal ? w-1 : 0;
    int dyi = vertical ? -1 : 1;
    int dy = vertical ? h-1: 0;

    ... ...

    // 24 bit
    if (d->depth == 24) {
        for (int sy = 0; sy < h; sy++, dy += dyi) {
            quint24* ssl = (quint24*)(d->data + sy*d->bytes_per_line);
            quint24* dsl = (quint24*)(result.d->data + dy*result.d->bytes_per_line);
            int dx = dxs;
            for (int sx = 0; sx < w; sx++, dx += dxi)
                dsl[dx] = ssl[sx];
        }
    }
    ... ...

   return result;

}

把 mirror 显示在 image 的下方就产生了 reflection 效果,伪代码如下所示:


void drawItemAt (QPainter *p, const QImage &img, ...)
{
    p->save ();

    const QImage mirror = mirrored (image);

    QPointF pt(-img.height()/2, -img.height()/2);
    ... ...

    QPointF pt2(pt.x(), img.height()/2);

    p->save();
    p->setCompositionMode(QPainter::CompositionMode_Source);
    p->drawImage(pt2, mirror);
    p->restore();

    p->drawImage(pt, img);

    p->restore();


}

3D 方法

效果图如下所示:

from 参考资料3
from 参考资料3

OpenGL 中实现 mirror 效果使用如下技术:

  1. blend
  2. 纹理坐标自动生成
  3. 球体纹理

伪代码如下所示3


void DrawObject()
{

    glColor3f(1.0f, 1.0f, 1.0f);	        // Set Color To White
    glBindTexture(GL_TEXTURE_2D, texture[1]);	// Select Texture 2 (1)
    gluSphere(q, 0.35f, 32, 16);		// Draw First Sphere


    glBindTexture(GL_TEXTURE_2D, texture[2]);	// Select Texture 3 (2)
    glColor4f(1.0f, 1.0f, 1.0f, 0.4f);		// Set Color To White With 40% Alpha
    glEnable(GL_BLEND);				// Enable Blending
    glBlendFunc(GL_SRC_ALPHA, GL_ONE);		// Set Blending Mode To Mix Based On SRC Alpha
    glEnable(GL_TEXTURE_GEN_S);			// Enable Sphere Mapping
    glEnable(GL_TEXTURE_GEN_T);			// Enable Sphere Mapping

    gluSphere(q, 0.35f, 32, 16);		// Draw Another Sphere Using New Texture
						// Textures Will Mix Creating A MultiTexture Effect (Reflection)
    glDisable(GL_TEXTURE_GEN_S);		// Disable Sphere Mapping
    glDisable(GL_TEXTURE_GEN_T);		// Disable Sphere Mapping
    glDisable(GL_BLEND);			// Disable Blending

}


Reference

  1. reflections
  2. 使用 2D 方法实现倒影特效
  3. Nehe Lesson 26