Opengl ES之转场动画

转场

什么是转场效果?一般来说,就是两个视频画面之间的过渡衔接效果。在opengl中,图片的转场,其实就是两个纹理的过渡切换,一般会有两个纹理作为输入,一个是逐渐消失的纹理,一个是逐渐完全可见的纹理。

在这里推荐一个开源项目,该项目主要用来收集各种GL转场特效及其 GLSL 实现代码,开发者可以很方便地移植到自己的项目中,而且该这个项目网站甚至还支持GLSL的在线编译运行,真是学习GLSL的不二之选。 这个项目网站就是GLTransitions:https://gl-transitions.com/gallery

这个项目有一百多张转场特效,这些特效对于想要学习opengl转场的童鞋们来说确是首选推荐。

比如在这里必须按随便选一个从上往下出现的转场特效点进进去,即可查阅看到相关的着色器代码以及参数说明:

Opengl ES之转场动画

上图中的transition就是封装的转场函数,其中参数uv表示的是纹理坐标,getFromColor(uv) 表示对较要消失的纹理进行采样,getToColor(uv)表示对将要进场的目标纹理进行采样。 然后使用mix进行纹理融合输出。

我的天,开源就是这么简单…

转场移植

本着将开源包装一下就是自主研发的学习精神,这里我们将这个从上往下的转场效果移植到Opengl ES中去。

首先我们在GLTransitions中找到该转场特效,然后点击选中Editor菜单,看到它的着色器转场函数如下:

// Author: Gaëtan Renaudeau
// License: MIT

uniform vec2 direction; // = vec2(0.0, 1.0)

vec4 transition (vec2 uv) {
  vec2 p = uv + progress * sign(direction);
  vec2 f = fract(p);
  return mix(
    getToColor(f),
    getFromColor(f),
    step(0.0, p.y) * step(p.y, 1.0) * step(0.0, p.x) * step(p.x, 1.0)
  );
}

可以看到该特效比较简单,只有两个控制参数,分别是directionprogress,文如其义,direction表示方向,通过这个变量可以控制特效的方向,例如从上往下,从左往右,甚至从右上角到左下角等,progress则表示转场的进度,这个值应该在0到1之间,比如该转场持续30帧画面,当第15帧时progress的值应该是0.5,progress=15/30=0.5。

按照以上意思,那么在Opengl ES中该特效移植后完整的着色器代码如下:

#version 300 es
precision mediump float;
uniform vec2 direction;
uniform float progress;
uniform sampler2D u_texture_01;
uniform sampler2D u_texture_02;
in vec2 TexCoord;
out vec4 FragColor;
vec4 transition (vec2 uv) {
    vec2 p = uv + progress * sign(direction);
    vec2 f = fract(p);
    return mix(
        texture(u_texture_01, f),
        texture(u_texture_02, f),
        step(0.0, p.y) * step(p.y, 1.0) * step(0.0, p.x) * step(p.x, 1.0)
    );
}

void main(){
    FragColor = transition(TexCoord);
}

结合我们之前Opengl ES系列教程封装好的开发环境,完整的转场渲染代码如下:

TransitionOpengl.cpp


#include "TransitionOpengl.h"
#include "../utils/Log.h"

// 顶点着色器
static const char *ver = "#version 300 es\n"
                         "in vec4 aPosition;\n"
                         "in vec2 aTexCoord;\n"
                         "out vec2 TexCoord;\n"
                         "void main() {\n"
                         "  TexCoord = aTexCoord;\n"
                         "  gl_Position = aPosition;\n"
                         "}";

// 片元着色器
static const char *fragment = "#version 300 es\n"
                              "precision mediump float;\n"
                              "uniform vec2 direction;\n"
                              "uniform float progress;\n"
                              "uniform sampler2D u_texture_01;\n"
                              "uniform sampler2D u_texture_02;\n"
                              "in vec2 TexCoord;\n"
                              "out vec4 FragColor;\n"
                              "vec4 transition (vec2 uv) {\n"
                              "    vec2 p = uv + progress * sign(direction);\n"
                              "    vec2 f = fract(p);\n"
                              "    return mix(\n"
                              "        texture(u_texture_01, f),\n"
                              "        texture(u_texture_02, f),\n"
                              "        step(0.0, p.y) * step(p.y, 1.0) * step(0.0, p.x) * step(p.x, 1.0)\n"
                              "    );\n"
                              "}\n"
                              "\n"
                              "void main(){\n"
                              "    FragColor = transition(TexCoord);\n"
                              "}";

// 动画总帧数
static const long ALL_FRAME = 60;

// 使用绘制两个三角形组成一个矩形的形式(三角形带)
// 第一第二第三个点组成一个三角形,第二第三第四个点组成一个三角形
const static GLfloat VERTICES[] = {
        0.5f,-0.5f, // 右下
        0.5f,0.5f, // 右上
        -0.5f,-0.5f, // 左下
        -0.5f,0.5f // 左上
};

const static GLfloat TEXTURE_COORD[] = {
        1.0f,1.0f, // 右下
        1.0f,0.0f, // 右上
        0.0f,1.0f, // 左下
        0.0f,0.0f // 左上
};

TransitionOpengl::TransitionOpengl() {
    initGlProgram(ver,fragment);
    positionHandle = glGetAttribLocation(program,"aPosition");
    textureHandle = glGetAttribLocation(program,"aTexCoord");
    textureSampler_01 = glGetUniformLocation(program,"u_texture_01");
    textureSampler_02 = glGetUniformLocation(program,"u_texture_02");
    directionHandle = glGetUniformLocation(program,"direction");
    progressHandle = glGetUniformLocation(program,"progress");
    LOGD("program:%d",program);
    LOGD("positionHandle:%d",positionHandle);
    LOGD("textureHandle:%d",textureHandle);
}

TransitionOpengl::~TransitionOpengl() noexcept {
    LOGD("TransitionOpengl析构函数");
    glDeleteTextures(1,&textureId_01);
    glDeleteTextures(1,&textureId_02);
}

void TransitionOpengl::setPixel(void *data, int width, int height, int length) {
    LOGD("texture setPixel");
    glGenTextures(1, &textureId_01);

    // 绑定纹理
    glBindTexture(GL_TEXTURE_2D, textureId_01);
    // 为当前绑定的纹理对象设置环绕、过滤方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
    // 生成mip贴图
    glGenerateMipmap(GL_TEXTURE_2D);
    // 解绑定
    glBindTexture(GL_TEXTURE_2D, 0);
}

void TransitionOpengl::setPixel2(void *data, int width, int height, int length) {
    LOGD("texture setPixel2");
    LOGD("texture setPixel");
    glGenTextures(1, &textureId_02);

    // 绑定纹理
    glBindTexture(GL_TEXTURE_2D, textureId_02);
    // 为当前绑定的纹理对象设置环绕、过滤方式
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
    // 生成mip贴图
    glGenerateMipmap(GL_TEXTURE_2D);
    // 解绑定
    glBindTexture(GL_TEXTURE_2D, 0);
}


void TransitionOpengl::onDraw() {
    glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    glUseProgram(program);

    // 控制方向 下往上
//    glUniform2f(directionHandle,0 ,1);

    // 控制方向 上往下
    glUniform2f(directionHandle,0 ,-1);

    // 控制进度 0到1之间
    float progress = (currentFrame % ALL_FRAME) * 1.0f / ALL_FRAME;
    currentFrame++;
    glUniform1f(progressHandle,progress);

    // 激活纹理
    glActiveTexture(GL_TEXTURE1);
    glUniform1i(textureSampler_01, 1);

    // 绑定纹理
    glBindTexture(GL_TEXTURE_2D, textureId_01);

    // 激活纹理
    glActiveTexture(GL_TEXTURE2);
    glUniform1i(textureSampler_02, 2);

    // 绑定纹理
    glBindTexture(GL_TEXTURE_2D, textureId_02);

    /**
     * size 几个数字表示一个点,显示是两个数字表示一个点
     * normalized 是否需要归一化,不用,这里已经归一化了
     * stride 步长,连续顶点之间的间隔,如果顶点直接是连续的,也可填0
     */
    // 启用顶点数据
    glEnableVertexAttribArray(positionHandle);
    glVertexAttribPointer(positionHandle,2,GL_FLOAT,GL_FALSE,0,VERTICES);

    // 纹理坐标
    glEnableVertexAttribArray(textureHandle);
    glVertexAttribPointer(textureHandle,2,GL_FLOAT,GL_FALSE,0,TEXTURE_COORD);

    // 4个顶点绘制两个三角形组成矩形
    glDrawArrays(GL_TRIANGLE_STRIP,0,4);

    glUseProgram(0);

    // 禁用顶点
    glDisableVertexAttribArray(positionHandle);
    if(nullptr != eglHelper){
        eglHelper->swapBuffers();
    }

    glBindTexture(GL_TEXTURE_2D, 0);
}

思考

以上是单个转场的特效效果,如今的短视频编辑软件对于转场的处理都是特别的炫酷的,细细研究分解不难发现其实它们都是多种转场的融合,而不仅仅是使用单一的转场特效,那么针对GLTransitions中的转场特效,作为开发者的你怎么将多个转场特效融合成一个,打造出一个数据自己的炫酷特效转场呢?

关注我,一起进步,人生不止coding!!!

思想觉悟

本文来自作者投稿,版权归原作者所有。如需转载,请注明出处:https://www.nxrte.com/jishu/28215.html

(0)

相关推荐

发表回复

登录后才能评论