两个图像的多重混合动画

Animation of multiply blending of two images

我有两张图片:
bg Image1 和 overlay Image2(高度大于 bg image1)

现在我需要使用混合模式将 [Image2] 放在 [Image1] 上。 但在那之后我需要为 Image2 的 Y 位置设置动画但保持混合模式以获得这样的动画: https://www.youtube.com/watch?v=D4Gqm9_Eo68

那么我该如何实现呢?
也许我应该使用 OpenGL?如果是,那么具体如何?

您需要的是:

- drawInRect:blendMode:alpha:

我没有为 IOS 编写代码,也不知道您正在编写代码的平台(内存、速度、gfx 功能)。从你的问题中我得到的印象是你对渲染和动画部分一无所知......

  1. 最简单的方法是自己编码

    如果您获得像素访问权限,那么只需要 2 个嵌套的 for 循环 copying/combining 纹理像素到目标颜色即可。我通常为此使用 GDIBitmap->ScanLine[] 但如前所述,我不知道你是否在 IOS.

    [=119= 上有类似的东西]
  2. OpenGL

    因为您没有 OpenGL 经验,所以我不建议使用 GLSL,因为从一开始它可能太多了。像设置 OpenGL 环境和解释 OpenGL 程序如何工作这样的事情即使对我来说也太长了(而且我已经习惯了冗长的回答)。所以我会跳过那个(你需要 google 一些教程)无论如何看:

    • Setting up OpenGL in Borland C++

    • simple rendering in OpenGL

    由于这需要 MultiTexturing,因此您还需要该扩展(至少我认为这些不在 OpenGL 1.0 中)。我建议像 GLEW 或其他任何东西一样使用 lib。

    想法是同时渲染具有 2 个纹理的四边形(矩形),如下所示:

    四边形顶点坐标是固定的(取决于屏幕和图像 resolution/aspect 比例)。 Texture0 也是固定的而 Texture1 固定了一个轴,第二个轴正在变化...其中:

    • t是动画参数t=<0,1>
    • T是使用梯度块大小T<1.0

    动画是通过改变 t 每帧的一些小步骤来完成的,例如递增和环绕或使用正弦...

    您需要记住,标准 OpenGL 只知道 2 纹理的幂,因此您需要 resize/crop/resample 您的纹理符合此标准。我这样做是这样的:

    您还需要处理应用程序 OpenGL 屏幕和图像之间的 宽高比 差异...我使用了快捷方式,所以OpenGL 视图是方形的,图像也被调整为方形。

    现在,当使用 MultiTexturing 时,您需要正确设置纹理组合器以满足您的需要。我已经用了很多年了所以我根本不记得它而且懒得再研究它因为我现在使用 GLSL ...

    幸运的是,它的默认 OpenGL 设置看起来正是您想要的。所以这里有一些基于 C++/VCL 的代码(没有依赖于平台的 OpenGL 环境设置):

    //---------------------------------------------------------------------------
    const int _txrs=2;              // max number of textures
    GLuint  txrid[_txrs],txrids=0;  // texture ids
    GLfloat t=0.0,T=0.1,dt=0.1;     // animation texture coordinates
    
    void init()
        {
        glGenTextures(_txrs,txrid);
    
        // textures
        Byte q;
        unsigned int *pp;
        int xs,ys,x,y,adr,*txr;
        union { unsigned int c32; Byte db[4]; } c;
        Graphics::TBitmap *bmp=new Graphics::TBitmap;   // new bmp
    
        // image texture
        bmp->LoadFromFile("effect_image.bmp");  // load from file
        bmp->HandleType=bmDIB;      // allow direct access to pixels
        bmp->PixelFormat=pf32bit;   // set pixel to 32bit so int is the same size as pixel
        xs=bmp->Width;              // resolution should be power of 2
        ys=bmp->Height;
        txr=new int[xs*ys];         // create linear framebuffer
        for(adr=0,y=0;y<ys;y++)
            {
            pp=(unsigned int*)bmp->ScanLine[y];
            for(x=0;x<xs;x++,adr++)
                {
                // rgb2bgr and copy bmp -> txr[]
                c.c32=pp[x];
                q      =c.db[2];
                c.db[2]=c.db[0];
                c.db[0]=q;
                txr[adr]=c.c32;
                }
            }
        glEnable(GL_TEXTURE_2D);    // copy it to gfx card
        glBindTexture(GL_TEXTURE_2D,txrid[txrids]); txrids++;
        glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR);
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_MODULATE);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, xs, ys, 0, GL_RGBA, GL_UNSIGNED_BYTE, txr);
        glDisable(GL_TEXTURE_2D);
        delete[] txr;
    
        // gradient texture
        bmp->LoadFromFile("effect_mask.bmp");   // load from file
        bmp->HandleType=bmDIB;      // allow direct access to pixels
        bmp->PixelFormat=pf32bit;   // set pixel to 32bit so int is the same size as pixel
        xs=bmp->Width;              // resolution should be power of 2
        ys=bmp->Height;
        txr=new int[xs*ys];         // create linear framebuffer
        for(adr=0,y=0;y<ys;y++)
            {
            pp=(unsigned int*)bmp->ScanLine[y];
            for(x=0;x<xs;x++,adr++)
                {
                // rgb2bgr and copy bmp -> txr[]
                c.c32=pp[x];
                q      =c.db[2];
                c.db[2]=c.db[0];
                c.db[0]=q;
                txr[adr]=c.c32;
                }
            }
        glEnable(GL_TEXTURE_2D);    // copy it to gfx card
        glBindTexture(GL_TEXTURE_2D,txrid[txrids]); txrids++;
        glPixelStorei(GL_UNPACK_ALIGNMENT, 4);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,GL_CLAMP);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,GL_LINEAR);
        glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE,GL_MODULATE);
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, xs, ys, 0, GL_RGBA, GL_UNSIGNED_BYTE, txr);
        glDisable(GL_TEXTURE_2D);
        delete[] txr;
        T=0.4;                      // 40% of gradient height cover the whole image
        dt=0.015*T;                 // animation step 1.5% of image
    
        delete bmp;
        }
    //---------------------------------------------------------------------------
    void TForm1::ogl_draw()
        {
        // clear buffers
        glClearColor(0.0,0.0,0.0,0.0);
        glClear(GL_COLOR_BUFFER_BIT);
    
        // unit matrices ... no projections ... so view is just <-1,+1>
        glMatrixMode(GL_PROJECTION);
        glLoadIdentity();
        glMatrixMode(GL_MODELVIEW);
        glLoadIdentity();
        glMatrixMode(GL_TEXTURE);
        glLoadIdentity();
    
        glDisable(GL_DEPTH_TEST);   // no Z-buffer for 2D
        glDisable(GL_CULL_FACE);    // no strict polygon winding
        glDisable(GL_TEXTURE_2D);
        // bind textures
        glActiveTexture(GL_TEXTURE1); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,txrid[1]);
        glActiveTexture(GL_TEXTURE0); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,txrid[0]);
        glColor3f(1.0,0.0,1.0);
        // render QUAD
        glColor3f(1.0,1.0,1.0);
        GLfloat t0=t,t1=t+T;
    
        glBegin(GL_QUADS);
        glMultiTexCoord2f(GL_TEXTURE0,0.0,0.0);
        glMultiTexCoord2f(GL_TEXTURE1,0.0,t0);
        glVertex3f(-1.0,+1.0,0.0);
        glMultiTexCoord2f(GL_TEXTURE0,0.0,1.0);
        glMultiTexCoord2f(GL_TEXTURE1,0.0,t1);
        glVertex2f(-1.0,-1.0);
        glMultiTexCoord2f(GL_TEXTURE0,1.0,1.0);
        glMultiTexCoord2f(GL_TEXTURE1,1.0,t1);
        glVertex2f(+1.0,-1.0);
        glMultiTexCoord2f(GL_TEXTURE0,1.0,0.0);
        glMultiTexCoord2f(GL_TEXTURE1,1.0,t0);
        glVertex2f(+1.0,+1.0);
        glEnd();
    
        // unbind textures so it does not mess any rendering after this (texture unit 0 at the end !!!)
        glActiveTexture(GL_TEXTURE1); glDisable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,0);
        glActiveTexture(GL_TEXTURE0); glDisable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,0);
    
        // force to render qued OpenGL rendering and swap double buffers
        glFlush();
        SwapBuffers(hdc);   // this is platform dependend !!!
        }
    //---------------------------------------------------------------------------
    void __fastcall TForm1::Timer1Timer(TObject *Sender)
        {
        ogl_draw();
        t+=dt;  // step the animation
        if ((dt>0.0)&&(t+T>1.0)) { t=1.0-T; dt=-dt; } // handle if hit top
        if ((dt<0.0)&&(t  <0.0)) { t=0.0; dt=-dt; }  // handle if hit bottom
        }
    //---------------------------------------------------------------------------
    

    如您所见,代码量很大。如果你使用一些 lib 来加载纹理和处理东西,大部分都会消失(我为此使用我的引擎,所以我花了 mi 时间将这些部分重新组合在一起,所以它可以在没有 lib 的情况下独立使用)。不要忘记初始化 GLEW 或其他任何东西来访问 MultiTexturing ...

这是一个简单的 VCL 单窗体应用程序,其中包含一个计时器 (interval=20ms)。

  • init() 只是为 gfx 卡中的纹理分配 space 并将图像加载到它。
  • ogl_draw()渲染你的效果...
  • Timer1Timer(TObject *Sender) 在每个计时器事件上调用,并强制渲染帧和更新动画...它通过渐变上升然后下降...

    这是结果:

    当然是动画但是我懒得拍视频了...

[edit1]

在这里link下载整个BDS2006项目download