实现Mac窗口最小化动画效果

这篇博客分享了如何使用OpenGL实现Mac窗口最小化的动画效果。通过在片元着色器中修改UV坐标,模拟图片变形过程,达到平滑收缩的视觉效果。核心思路是对UV坐标进行变换,通过时间t和像素点坐标进行线性插值和二次函数运算,创造出曲线收缩动画。

前言

在学习opengl的过程中,经一前辈的教导,做出了一个比较有趣的东西:仿制Mac中窗口最小化的动画效果。因为是在我学习opengl的过程中制作的一个东西,所以实现的代码是使用是opengl来实现的,个人感觉思路也适用于其他的框架。实现的核心都在片元着色器中:对片元的uv坐标进行修改,从而模拟实现一个图片变形的整个过程。核心代码只有几句,刚做出来的时候自己也觉得太神奇了,所以希望把这个方法分享出来。

方法介绍

效果展示

首先给上一下最终做出来的一个效果吧~
在这里插入图片描述
上面实例用到的只是一个简单的矩形加一张贴图,这些大家就自己渲染好吧。本文主要是为了分享实现Mac窗口最小化的效果,所以有关opengl的一些渲染过程就不在本文多余的进行介绍了(默认大家都了解了,如果有需要源码的话后面可以在评论区留一下言,到时候我看到了再给大家分享出来把~)。可以给大家贴一下顶点坐标:

float vertices[] = {
	// 顶点坐标        uv坐标
	1.0f, 1.0f, 0.0f, 1.0f,1.0f,  // 右上角
	1.0f, -1.0f, 0.0f, 1.0f,0.0f, // 右下角
	-1.0f, 1.0f, 0.0f, 0.0f,1.0f, // 左上角
	1.0f, -1.0f, 0.0f, 1.0f,0.0f, // 右下角
	-1.0f, -1.0f, 0.0f, 0.0f,0.0f,// 左下角
	-1.0f, 1.0f, 0.0f, 0.0f, 1.0f // 左上角
};

核心思路

由效果图可以看到,这整个动画的变化过程实际上就是一个矩形顶点在x方向上的变化和y方向上的变化, 大家看到这可能有不同的一个想法,比如对顶点坐标进行移动,但是这需要注意的是因为顶点着色器中拿到的只是上面的六个顶点坐标,如果需要做出一个曲线的伸缩效果,光对这些坐标进行移动是不足够的,因为光栅化过程中使用的是一个线性插值,所以如果需要通过顶点着色器中对这些顶点的位置进行移动,就需要加入更多的顶点才可以,那整个过程就变得有点繁琐了。
这里选择在片元着色器中进行变换,因为片元着色器可以获得uv坐标,通过改变uv的坐标可以改变取样的值(对眼睛来说就是显示出来的图片发现了改变),同时在渲染管线中到片元着色器中的时候已经生成了一个个的片元(类似像素一样),所以我们得到了更多的数据,这样不需要人为的加入顶点数据便可以生成平滑曲线形状。这里变换的原理实际上就是对uv坐标进行变换,拿u坐标(水平方向)来说,把水平方向的取样坐标像两边拉伸,让本来取样坐标应该为0和1的地方往中间去靠拢,然后取样坐标超出0~1的地方都让他不取样或者取样为透明的颜色(这样看起来就像是图片没有了),这样整体看起来就像是图片往中间收缩了。

在这里插入图片描述

因为uv坐标取样值范围是0 ~ 1,为了方便计算这里假设像素点的x坐标和y坐标的取样范围也是0 ~ 1,因为无论缩放前还是缩放后图片都应该被完整的显示出来的,所以变换前和变换后的像素点坐标都应该是成比例的,这样就可以知道收缩后的坐标(pos)的uv对应的是收缩前的哪个点(target)的uv,拿x坐标来计算,即target.x:
在这里插入图片描述

因为uv坐标的范围是0 ~ 1,像素点的坐标xy的范围也是0 ~ 1,而且是一 一对应的,所以算出来的target.x的值实际上也等于target点的u坐标,所以我们就知道了目标点的 u 坐标,然后根据该点当前的u坐标和target点的 u 坐标根据时间 t 进行线性插值,就可以得到x方向上的一个变化效果:

	float targetX = (TexCoord.x - Left.x) / (Right.x - Left.x);
	vec2 targetUV = vec2 (lerp(TexCoord.x,targetX , pow(1 - TexCoord.y,2) * time),TexCoord.y);

在这里插入图片描述
到了这一步图片虽然看起来收缩了,但是只是竖直的去搜索,这样得出来的效果跟预期有点差距,我们想要搜索边缘呈现一个曲线的效果,越往下搜索的幅度越大,所以我们需要对时间 t 乘以 y 坐标的一个的二次函数,因为y坐标和v坐标是一一对应的,所以实际上等同于乘以v坐标的一个二次函数:

	float targetX = (TexCoord.x - Left.x) / (Right.x - Left.x);
	vec2 targetUV = vec2 (lerp(TexCoord.x,targetX , pow(1 - TexCoord.y,2) * time),TexCoord.y);

这样一来水平方向上就得到了我们想要的一个收缩效果
在这里插入图片描述
接着只需要按相同思路去对y方向上进行搜索就可以了,这里就不在重复去讲了,后面直接我会直接把着色器的代码贴出来给大家,最后的效果图:
在这里插入图片描述

代码展示

因为实现动画的方法全部在着色器中,所以这里只展示着色器的代码,opengl的一些渲染流程的代码我这里就不贴出来了,后面大家需要的话可以在评论区留一下言,有人要的话我再贴出来的吧!~
需要提醒的是这个方法需要基于条件超出0~1范围的uv坐标为用户指定的边缘颜色。 所以在渲染之前需要把纹理的环绕方式设置一下,不同的渲染库有不同的方法,opengl的代码我这里贴一下:

		float borderColor[] = { 0.2f, 0.3f, 0.3f, 1.0f }; //指定边缘颜色,设置成跟背景同样的颜色就可以了
		glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); //指定环绕方式为"超出的坐标为用户指定的边缘颜色"
		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);

顶点着色器(vertexShader)的代码:

layout (location = 0) in vec3 aPos;
layout (location = 1) in vec2 aTexCoord;

out vec2 TexCoord;

void main()
{
    gl_Position = vec4(aPos, 1.0);
	TexCoord = aTexCoord;
}

片元着色器(fragementShader)的代码:

out vec4 FragColor; 

in vec2 TexCoord;
uniform sampler2D ourTexture;
uniform float time;
uniform vec2 Left;
uniform vec2 Right;

float lerp(float curentVal,float targetVal, float t)
{
	return targetVal * t + curentVal * (1-t);
}

vec2 calTexCoord(vec2 texCoord){
	float targetX = (TexCoord.x - Left.x) / (Right.x - Left.x);
	float targetY = TexCoord.y * ( 1 / pow(1- max(2 * time -1,0),2));
	vec2 targetUV = vec2 (lerp(TexCoord.x,targetX , pow(1 - TexCoord.y,2) * time),targetY );
	return targetUV;
}

void main()
{
    FragColor = texture(ourTexture,calTexCoord(TexCoord));
}
``
评论 6
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值