OpenGL:关于纹理映射时任意四边形中的插值问题

发布时间:2024年01月18日

有一张图片如下,想将其贴图到窗口中。

正常的效果应该就是原图的效果。

但是,我纹理贴图时的纹理顶点有四个,由两个三角形组成,当两个三角形组成矩形时,没什么问题,当其中的某个顶点移动时,就变成下图右的情况,此时就出现了问题。

在两个三角形的公共边处图像变得不连续,有明显的折痕。

这是因为渲染的时候单独对每个三角形进行插值,所以导致折痕。

问题就是这么个问题。

怎么解决呢。

参考一下这里:纹理四边形插值2--逆双线性插值(InvBilinear Interpolation)-腾讯游戏学堂

可以使用投影插值和逆双线性插值。

这里使用逆双线性插值。

关于双线性插值,就是给定一个四边形的四个顶点坐标和某个uv坐标,然后通过uv计算出UV坐标对应的顶点坐标插值坐标。

逆双线性插值就是已知四边形的四个顶点坐标,和四边形内部某点的坐标,求出该点对应的UV坐标。

双线性插值相对简单,逆双线性就有点麻烦了。

关于逆双线性插值的推导,参考

逆双线性插值(任意四边形中的插值)-CSDN博客

纹理四边形插值2--逆双线性插值(InvBilinear Interpolation)-腾讯游戏学堂

推导就不重写了。

下面验证一下。

void main()
{
	Point2d P(0.5, 0.5);
	Point2d p1 = Point2d(-0.9, 1);
	Point2d p2 = Point2d(1, 1);
	Point2d p3 = Point2d(1, -1);
	Point2d p4 = Point2d(-1, -1);

	Point2d p43 = p3 - p4;
	Point2d p42 = p2 - p4;
	Point2d p41 = p1 - p4;

	double d43 = norm(p43);
	double d42 = norm(p42);
	double d41 = norm(p41);

	Point2d b1 = p3 - p4;
	Point2d b2 = p1 - p4;
	Point2d b3 = p2 - (p4 + b1 + b2);
	Point2d q = P - p4;

	double A = b2.cross(b3);
	double B = b3.cross(q) - b1.cross(b2);
	double C = b1.cross(q);

	double discrim = B * B - 4 * A * C;
	double v = 0.5 * (-B - sqrt(discrim))/A;
	double u;
	Point2d denom = b1 + b3 * v;
	Point2d denom2 = q - b2 * v;

	if (abs(denom.x) > abs(denom.y))
	{
		u = denom2.x / denom.x;
	}
	else
	{
		u = denom2.y / denom.y;
	}

	cout << "u = " << u << endl;
	cout << "v = " << v << endl;
}

输出结果:

u = 0.74026
v = 0.75

基本差不多。

着色器中再验证一下。

#version 330 core
in vec2 TexCoord;
in vec2 aPos_out;//这个是当前处理的坐标

out vec4 FragColor;

uniform sampler2D ourTexture;
uniform mat3 transformMatrix;

uniform vec2 point1;//窗口顶点坐标
uniform vec2 point2;
uniform vec2 point3;
uniform vec2 point4;

vec2 v_q;
vec2 v_b1;
vec2 v_b2;
vec2 v_b3;

float cross2D(vec2 v, vec2 w)
{
	return v.x*w.y - v.y*w.x;
}

void main()
{
    v_q = aPos_out - point4;
    v_b1 = point3 - point4;
    v_b2 = point1 - point4;
    v_b3 = point4 - point3 - point1 + point2;


    float A = cross2D(v_b2, v_b3);
    float B = cross2D(v_b3, v_q) - cross2D(v_b1, v_b2);
    float C = cross2D(v_b1, v_q);
    //float discrim = 1;
    // Solve for v
    vec2 uv;
    if (abs(A) < 0.001)
    {
	    // Linear form
	    uv.y = -C/B;
    }
    else
    {
	    // Quadratic form. Take positive root for CCW winding with V-up
	    float discrim = B*B - 4*A*C;
	    //uv.y = 0.5 * (-B - sqrt(discrim)) / A;//CCW
	    uv.y = 0.5 * (-B - sqrt(discrim)) / A;//CW
    }
    // Solve for u, using largest-magnitude component
    vec2 denom = v_b1 + uv.y * v_b3;
    if (abs(denom.x) > abs(denom.y))
	    uv.x = (v_q.x - v_b2.x * uv.y) / denom.x;
    else
	    uv.x = (v_q.y - v_b2.y * uv.y) / denom.y;

    FragColor = texture2D(ourTexture, uv);
}

效果可以,符合预期。

但是上面还有点缺点,上面这种方式相当于没有使用OpenGL内部的插值,而是自己做的插值,就是找到uv坐标,直接去纹理中取对应像素值。会造成一定的锯齿。

在计算得到uv坐标后,可以接着进行一次双线性插值,这个插值,是在纹理中取值的这个过程中做的。当然也可以进行双三次插值。目的是减少锯齿。

这个后面再码吧。

文章来源:https://blog.csdn.net/cd_yourheart/article/details/135656369
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。