正确的glsl仿射纹理映射

时间:2012-09-13 20:59:42

标签: math opengl glsl texture-mapping

我正在尝试在GLSL中编码正确的2D仿射纹理映射。

说明:

...此图片中的任何一个都不符合我的目的。右(标记为正确)具有我不想要的透视校正。所以:Getting to know the Q texture coordinate解决方案(没有进一步改进)不是我想要的。

我想在四边形内部简单地“拉伸”纹理,如下所示:

enter image description here

但由两个三角形组成。请问任何建议(GLSL)?

6 个答案:

答案 0 :(得分:6)

只要你有一个梯形,并且它的平行边与一个局部轴对齐,这种效果很好。我建议玩我的Unity package

GLSL:

varying vec2 shiftedPosition, width_height;

#ifdef VERTEX
void main() {
    gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
    shiftedPosition = gl_MultiTexCoord0.xy; // left and bottom edges zeroed.
    width_height = gl_MultiTexCoord1.xy;
}
#endif

#ifdef FRAGMENT
uniform sampler2D _MainTex;
void main() {
    gl_FragColor = texture2D(_MainTex, shiftedPosition / width_height);
}
#endif

C#:

// Zero out the left and bottom edges, 
// leaving a right trapezoid with two sides on the axes and a vertex at the origin.
var shiftedPositions = new Vector2[] {
    Vector2.zero,
    new Vector2(0, vertices[1].y - vertices[0].y),
    new Vector2(vertices[2].x - vertices[1].x, vertices[2].y - vertices[3].y),
    new Vector2(vertices[3].x - vertices[0].x, 0)
};
mesh.uv = shiftedPositions;

var widths_heights = new Vector2[4];
widths_heights[0].x = widths_heights[3].x = shiftedPositions[3].x;
widths_heights[1].x = widths_heights[2].x = shiftedPositions[2].x;
widths_heights[0].y = widths_heights[1].y = shiftedPositions[1].y;
widths_heights[2].y = widths_heights[3].y = shiftedPositions[2].y;
mesh.uv2 = widths_heights;

答案 1 :(得分:2)

我最近设法为任何类型的四边形提出了这个问题的通用解决方案。计算和GLSL可能有所帮助。有一个java的工作演示(在Android上运行),但是紧凑且可读,应该可以轻松移植到统一或iOS:http://www.bitlush.com/posts/arbitrary-quadrilaterals-in-opengl-es-2-0

答案 2 :(得分:0)

Tessellation解决了这个问题。细分四边形顶点会添加提示以插入像素。

查看此链接。 https://www.youtube.com/watch?v=8TleepxIORU&feature=youtu.be

答案 3 :(得分:0)

如果仍然有人感兴趣,这是一个C#实现,它采用由顺时针屏幕版本(x0,y0)(x1,y1)...(x3,y3)定义的四边形,位于(x,y )并计算该像素的u和v。它最初是为CPU编写的,可将任意四边形渲染为纹理,但将算法轻松划分到CPU,Vertex和Pixel着色器上已经足够容易了。我在代码中做了相应的评论。

            float Ax, Bx, Cx, Dx, Ay, By, Cy, Dy, A, B, C;

            //These are all uniforms for a given quad. Calculate on CPU.
            Ax = (x3 - x0) - (x2 - x1);
            Bx = (x0 - x1);
            Cx = (x2 - x1);
            Dx = x1;

            Ay = (y3 - y0) - (y2 - y1);
            By = (y0 - y1);
            Cy = (y2 - y1);
            Dy = y1;

            float ByCx_plus_AyDx_minus_BxCy_minus_AxDy = (By * Cx) + (Ay * Dx) - (Bx * Cy) - (Ax * Dy);
            float ByDx_minus_BxDy = (By * Dx) - (Bx * Dy);

            A = (Ay*Cx)-(Ax*Cy);

            //These must be calculated per-vertex, and passed through as interpolated values to the pixel-shader 
            B = (Ax * y) + ByCx_plus_AyDx_minus_BxCy_minus_AxDy - (Ay * x);
            C = (Bx * y) + ByDx_minus_BxDy - (By * x);

            //These must be calculated per-pixel using the interpolated B, C and x from the vertex shader along with some of the other uniforms.
            u = ((-B) - Mathf.Sqrt((B*B-(4.0f*A*C))))/(A*2.0f);
            v = (x - (u * Cx) - Dx)/((u*Ax)+Bx);

Sample output of the algorithm CPU-rendered to a texture

答案 4 :(得分:0)

我有类似的问题(https://gamedev.stackexchange.com/questions/174857/mapping-a-texture-to-a-2d-quadrilateral/174871),在gamedev中,他们建议使用虚构的Z坐标,我使用以下C代码进行计算,该C代码似乎在一般情况下适用(不仅是梯形):

//usual euclidean distance
float distance(int ax, int ay, int bx, int by) {
  int x = ax-bx;
  int y = ay-by;
  return sqrtf((float)(x*x + y*y));
}

void gfx_quad(gfx_t *dst //destination texture, we are rendering into
             ,gfx_t *src //source texture
             ,int *quad  // quadrilateral vertices
             )
{
  int *v = quad; //quad vertices
  float z = 20.0;
  float top = distance(v[0],v[1],v[2],v[3]); //top
  float bot = distance(v[4],v[5],v[6],v[7]); //bottom
  float lft = distance(v[0],v[1],v[4],v[5]); //left
  float rgt = distance(v[2],v[3],v[6],v[7]); //right

  // By default all vertices lie on the screen plane
  float az = 1.0;
  float bz = 1.0;
  float cz = 1.0;
  float dz = 1.0;

  // Move Z from screen, if based on distance ratios.
  if (top<bot) {
    az *= top/bot;
    bz *= top/bot;
  } else {
    cz *= bot/top;
    dz *= bot/top;
  }

  if (lft<rgt) {
    az *= lft/rgt;
    cz *= lft/rgt;
  } else {
    bz *= rgt/lft;
    dz *= rgt/lft;
  }

  // draw our quad as two textured triangles
  gfx_textured(dst, src
              , v[0],v[1],az, v[2],v[3],bz, v[4],v[5],cz
              , 0.0,0.0,      1.0,0.0,      0.0,1.0);
  gfx_textured(dst, src
              , v[2],v[3],bz, v[4],v[5],cz, v[6],v[7],dz
              , 1.0,0.0,      0.0,1.0,      1.0,1.0);
}

我正在用软件缩放和旋转2d精灵,对于OpenGL 3d应用,您将需要在像素/片段着色器中进行此操作,除非您能够映射这些虚构的az,bz,cz,dz进入实际的3D空间并使用通常的管道。 DMGregory给出了OpenGL着色器的确切代码:https://gamedev.stackexchange.com/questions/148082/how-can-i-fix-zig-zagging-uv-mapping-artifacts-on-a-generated-mesh-that-tapers

答案 5 :(得分:-2)

感谢您的回答,但经过实验,我找到了解决方案。

左边的两个三角形有this的uv(strq),右边的两个三角形是这个透视校正的修改版本。

数字和着色器:

tri1 = [Vec2(-0.5, -1), Vec2(0.5, -1), Vec2(1, 1)]
tri2 = [Vec2(-0.5, -1), Vec2(1, 1), Vec2(-1, 1)]

d1 = length of top edge = 2
d2 = length of bottom edge = 1

tri1_uv = [Vec4(0, 0, 0, d2 / d1), Vec4(d2 / d1, 0, 0, d2 / d1), Vec4(1, 1, 0, 1)]
tri2_uv = [Vec4(0, 0, 0, d2 / d1), Vec4(1, 1, 0, 1), Vec4(0, 1, 0, 1)]

只使用此glsl着色器渲染直角三角形(左侧是固定管道):

void main()
{
    gl_FragColor = texture2D(colormap, vec2(gl_TexCoord[0].x / glTexCoord[0].w, gl_TexCoord[0].y);
}

所以..只有U是透视,V是线性的。