提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
前言
之前实现了一系列不同形状的放大镜,参考了网上很多文章以及资料,完成后发上来分享一下。
提示:以下是本篇文章正文内容,下面案例可供参考
一、圆形放大镜
作为最基础的放大镜样式,实现起来也比较简单,在指定圆形区域的纹理乘以一个放大系数(小于零),直接上代码,在代码里注释。
//int magnifying_Center_x; //镜头中心x坐标(-100到100)
//int magnifying_Center_y; //镜头中心y坐标(-100到100)
//int magnifying_Radius; //(0到100)放大镜半径
//int magnifyingl_Times; //(0到10)放大倍率
//v2TexSize为窗口大小 例如(1920,1080)
float2 center = float2(0.5 + magnifying_Center_x / 200, 0.5-magnifying_Center_y / 200) * v2TexSize;//计算出放大镜中心点
float radius = magnifying_Radius / 100 * v2TexSize.y;//一般情况下窗口都是呈现长方形的,选取短边作为半径扩展系数
float Times = (1 - magnifyingl_Times / 10);//计算出放大的倍率,Times 越小,放大程度越大
tex *= v2TexSize;//将范围(0-1)的纹理恢复至窗口大小
float dist = distance(tex, center);//当前纹理点与放大镜中心的距离
//圆内
if (dist < radius)//当前纹理点在放大镜中时
{
tex -= center;//将纹理坐标轴移动到以放大镜中心为原心的纹理坐标空间
tex = tex * Times;//放大,即让当前纹理显示更靠进圆心的纹理区域
tex += center;//恢复成原来的纹理坐标空间
}
float4 outp = g_Tex.Sample(g_SamLinear, tex / v2TexSize); //拿到当前放大镜处理后的纹理
return outp;//输出
效果:
三、矩形放大镜
根据圆心以及半径划定一个矩形区域
关键区域的代码如下:
//int magnifying_Center_x; //镜头中心x坐标(-100到100)
//int magnifying_Center_y; //镜头中心y坐标(-100到100)
//int magnifying_Radius; //(0到100)放大镜半径
//int magnifyingl_Times; //(0到10)放大倍率
//v2TexSize为窗口大小 例如(1920,1080)
float2 center = float2(0.5 + magnifying_Center_x / 200, 0.5-magnifying_Center_y / 200) * v2TexSize;//计算出放大镜中心点
float radius = magnifying_Radius / 100 * v2TexSize.y;//一般情况下窗口都是呈现长方形的,选取短边作为半径扩展系数
float Times = (1 - magnifyingl_Times / 10);//计算出放大的倍率,Times 越小,放大程度越大
tex *= v2TexSize;//将范围(0-1)的纹理恢复至窗口大小
//当前纹理在矩形区域内,矩形边长为radius
if ((tex.x < center.x + radius / 2) && (tex.x > center.x - radius / 2) && (tex.y < center.y + radius / 2) && (tex.y > center.y - radius / 2))
{
tex -= center;
tex = tex * Times;
tex += center;
}
float4 outp = g_Tex.Sample(g_SamLinear, tex / v2TexSize); //拿到当前放大镜处理后的纹理
return outp;//输出
效果如下:
四、三角形放大镜
关键区域的代码如下:
//int magnifying_Center_x; //镜头中心x坐标(-100到100)
//int magnifying_Center_y; //镜头中心y坐标(-100到100)
//int magnifying_Radius; //(0到100)放大镜半径
//int magnifyingl_Times; //(0到10)放大倍率
//v2TexSize为窗口大小 例如(1920,1080)
float2 center = float2(0.5 + magnifying_Center_x / 200, 0.5-magnifying_Center_y / 200) * v2TexSize;//计算出放大镜中心点
float radius = magnifying_Radius / 100 * v2TexSize.y;//一般情况下窗口都是呈现长方形的,选取短边作为半径扩展系数
float Times = (1 - magnifyingl_Times / 10);//计算出放大的倍率,Times 越小,放大程度越大
tex *= v2TexSize;//将范围(0-1)的纹理恢复至窗口大小
float g = sin(PI/3)*2;
center.y += (g / 4) * radius;
tex -= center;
//a、b、c为三角形的三个顶点
float2 a = float2(-radius / 2, 0);
float2 b = float2(0, -radius * g/2);
float2 c = float2(radius / 2, 0);
if (inTriangle(a, b, c, tex))//tex是否在abc构成的三角形内
{
tex = tex * Times;
}
tex += center;
float4 outp = g_Tex.Sample(g_SamLinear, tex / v2TexSize); //拿到当前放大镜处理后的纹理
return outp;//输出
inTriangle函数用以判断当前点tex是否在abc三点构成的三角形内,也就是说可以通过更改abc三个点的位置来更改三角形的形状,后续的五角星放大镜也用到了这个函数。其中用到了伪叉积的概念,伪叉积就不展开说了,可以百度,inTriangle函数如下所示:
参考文章链接:链接
bool inTriangle(float2 a, float2 b, float2 c, float2 tex)
{
float2 AB = float2(b - a);
float2 BC = float2(c - b);
float2 CA = float2(a - c);
float2 AP = float2(tex - a);
float2 BP = float2(tex - b);
float2 CP = float2(tex - c);
bool Out_In1 = AB.x * AP.y - AB.y * AP.x > 0
&& BC.x * BP.y - BC.y * BP.x > 0
&& CA.x * CP.y - CA.y * CP.x > 0;
bool Out_In2 = AB.x * AP.y - AB.y * AP.x < 0
&& BC.x * BP.y - BC.y * BP.x < 0
&& CA.x * CP.y - CA.y * CP.x < 0;
return Out_In1 || Out_In2;
}
三角形马赛克效果如下:
五、梯形放大镜
跟之前的操作类似,求出梯形的区域,在区域内的纹理进行放大操作。
//int magnifying_Center_x; //镜头中心x坐标(-100到100)
//int magnifying_Center_y; //镜头中心y坐标(-100到100)
//int magnifying_Radius; //(0到100)放大镜半径
//int magnifyingl_Times; //(0到10)放大倍率
//v2TexSize为窗口大小 例如(1920,1080)
float2 center = float2(0.5 + magnifying_Center_x / 200, 0.5-magnifying_Center_y / 200) * v2TexSize;//计算出放大镜中心点
float radius = magnifying_Radius / 100 * v2TexSize.y;//一般情况下窗口都是呈现长方形的,选取短边作为半径扩展系数
float Times = (1 - magnifyingl_Times / 10);//计算出放大的倍率,Times 越小,放大程度越大
tex *= v2TexSize;//将范围(0-1)的纹理恢复至窗口大小
//以梯形底边中心为原点,求出另外两条直线的公式
float a = 1.732; //根号三 另外两条边的公式 1:y=-ax-(a/2)*R 2: y=ax-(a/2)*R
center.y += (a / 4) * radius;
if (tex.y < center.y && tex.y > center.y - (a / 2) * radius)//将范围限定在梯形上下两边中
{
tex -= center;
//梯形内
if ((tex.y > (-1 * a * tex.x - a * radius)) && (tex.y > ( a * tex.x - a * radius)))
{
tex = tex * Times;
}
}
tex += center;
float4 outp = g_Tex.Sample(g_SamLinear, tex / v2TexSize); //拿到当前放大镜处理后的纹理
return outp;//输出
六、六边形放大镜
六边形区域可由上下两边+剩下四个斜边构成:
//int magnifying_Center_x; //镜头中心x坐标(-100到100)
//int magnifying_Center_y; //镜头中心y坐标(-100到100)
//int magnifying_Radius; //(0到100)放大镜半径
//int magnifyingl_Times; //(0到10)放大倍率
//v2TexSize为窗口大小 例如(1920,1080)
float2 center = float2(0.5 + magnifying_Center_x / 200, 0.5-magnifying_Center_y / 200) * v2TexSize;//计算出放大镜中心点
float radius = magnifying_Radius / 100 * v2TexSize.y;//一般情况下窗口都是呈现长方形的,选取短边作为半径扩展系数
float Times = (1 - magnifyingl_Times / 10);//计算出放大的倍率,Times 越小,放大程度越大
tex *= v2TexSize;//将范围(0-1)的纹理恢复至窗口大小
//以六边形中点为原点,求出另外四条直线的公式
//1.732=根号三 另外四条边的公式 1:y=-ax-a*R 2: y=ax-2*R 3:y=a*x+a*R ,4:y=-a*x+a*R
float a = sin(PI/3)*2;
if ((tex.y < center.y + radius / 2) && (tex.y > center.y - radius / 2))//将范围限定上下
{
tex -= center;
radius *=0.6;
if ((tex.y > (-1. * a * tex.x - a * radius)) && (tex.y > (a * tex.x - a * radius)) && (tex.y < (a * tex.x + a * radius)) && (tex.y < (-1.*a * tex.x + a * radius)))
{
tex = tex * Times;
}
tex += center;
}
float4 outp = g_Tex.Sample(g_SamLinear, tex / v2TexSize); //拿到当前放大镜处理后的纹理
return outp;//输出
当然,也可以按照之前画三角形的方法实现,六边形就是由六个三角形构成的。
七、心形放大镜
心形放大镜的核心在于构建出一个心形区域。
//int magnifying_Center_x; //镜头中心x坐标(-100到100)
//int magnifying_Center_y; //镜头中心y坐标(-100到100)
//int magnifying_Radius; //(0到100)放大镜半径
//int magnifyingl_Times; //(0到10)放大倍率
//v2TexSize为窗口大小 例如(1920,1080)
float2 center = float2(0.5 + magnifying_Center_x / 200, 0.5-magnifying_Center_y / 200) * v2TexSize;//计算出放大镜中心点
float radius = magnifying_Radius / 100 * v2TexSize.y;//一般情况下窗口都是呈现长方形的,选取短边作为半径扩展系数
float Times = (1 - magnifyingl_Times / 10);//计算出放大的倍率,Times 越小,放大程度越大
tex *= v2TexSize;//将范围(0-1)的纹理恢复至窗口大小
//爱心公式((x*x+y*y-1)^3)=x^2*y^3
tex -= center; //以中心为原点
if (inHeart(tex, radius) != 0)
{
tex *= Times;
}
tex += center;
float4 outp = g_Tex.Sample(g_SamLinear, tex / v2TexSize);
float inHeart(float2 tex, float size)//用于判断当前点是否在心性内部
{
if (size == 0.)
{
return 0;
}
tex /= size;
float s = tex.x * tex.x - tex.y * -tex.y - 1;
//爱心公式((x*x+y*y-1)^3)=x^2*y^3
float f1 = s * s * s;
float f2 = tex.x * tex.x * tex.y * tex.y * tex.y * -1;
return step(f1, f2);
}
效果:
八、五角星形放大镜
五角星是由十个三角形构成的,首先求出这十个三角形的各点的坐标,再判断当前点是否在这十个三角形内。
参考的文章:链接
代码:
//int magnifying_Center_x; //镜头中心x坐标(-100到100)
//int magnifying_Center_y; //镜头中心y坐标(-100到100)
//int magnifying_Radius; //(0到100)放大镜半径
//int magnifyingl_Times; //(0到10)放大倍率
//v2TexSize为窗口大小 例如(1920,1080)
float2 center = float2(0.5 + magnifying_Center_x / 200, 0.5-magnifying_Center_y / 200) * v2TexSize;//计算出放大镜中心点
float radius = magnifying_Radius / 100 * v2TexSize.y;//一般情况下窗口都是呈现长方形的,选取短边作为半径扩展系数
float Times = (1 - magnifyingl_Times / 10);//计算出放大的倍率,Times 越小,放大程度越大
tex *= v2TexSize;//将范围(0-1)的纹理恢复至窗口大小
float R = radius;//外五角半径
float r = radius * sin(PI/10.) / cos(PI/5.);//内五角半径
float2 PointO[5];//外五角
float2 PointI[5];//内五角
tex -= center;
for (int i = 0; i < 5; i++)
{
PointO[i] = float2(R * cos(0.5 * PI + i * 0.4 * PI), -R * sin(0.5 * PI + i * 0.4 * PI));
PointI[i] = float2(r * cos(0.7 * PI + i * 0.4 * PI), -r * sin(0.7 * PI + i * 0.4 * PI));
if (inTriangle(float2(0, 0), PointO[i], PointI[i], tex))
{
tex *= Times;
}
}
if (inTriangle(float2(0, 0), PointO[0], PointI[4], tex))
{
tex *= Times;
}
for (int j = 1;j < 5; j++)
{
if (inTriangle(float2(0, 0), PointO[j], PointI[j-1], tex))
{
tex *= Times;
}
}
tex += center;
float4 outp = g_Tex.Sample(g_SamLinear, tex / v2TexSize); //拿到当前放大镜处理后的纹理
return outp;//输出
效果:
总结
分享了使用HLSL实现七种不同形状放大镜的实现方法,其中有些方法肯定存在不足之处,欢迎指出,谢谢。