You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

OpenGL仿射变换后纹理采样对齐失效及透视校正实现问询

问题背景

我需要在OpenGL画布上对齐两张分辨率相同的图像,为此准备了如下顶点数据:

![两张图像共用的顶点]

float vertices[] = {
        // 位置
        0.0f,  0.0f,     // 左下角
        width,  0.0f,    // 右下角
        0.0f, height,    // 左上角
        width,  0.0f,    // 右下角
        0.0f, height,    // 左上角
        width, height,   // 右上角
};

通过这种方式,我可以用同一个着色器在画布上叠加绘制任意数量的图像,只需为每张对齐的图像传入纹理和变换矩阵即可。

使用以下顶点着色器和片段着色器时,效果符合预期:

顶点着色器

#version 330 core
layout (location = 0) in vec3 vertex_in;

out vec2 TexCoord;

uniform mat3 cvAffine; // OpenCV 仿射变换矩阵

const vec2 texture_coord[] = {
  vec2( 0.0, 0.0),
  vec2( 1.0, 0.0),
  vec2( 0.0,  1.0),
  vec2( 1.0,  0.0),
  vec2( 0.0,  1.0),
  vec2( 1.0,  1.0)
};


void main(){
    vec3 cvCoord = vec3(vertex_in.xy,1.0f); // 创建齐次2D向量用于3x3矩阵乘法
    vec3 transformedCoord = cvAffine*cvCoord; // 对图像进行变换
    gl_Position = vec4((transformedCoord.xy*0.0002), 0.0, transformedCoord.z); // 缩小以便查看
    TexCoord = texture_coord[gl_VertexID]; // 向片段着色器传递纹理坐标
};

片段着色器

#version 330 core
in vec2 TexCoord;
out vec4 FragColor;

uniform sampler2D texture;
void main(){
    FragColor = texture(texture, TexCoord);
}

![效果展示]

核心问题

当我把顶点着色器中的这一行:

gl_Position = vec4((transformedCoord.xy*0.0002), 0.0, transformedCoord.z);

替换为理论上等价的代码:

gl_Position = vec4(((transformedCoord.xy/transformedCoord.z)*0.0002), 0.0, 1.0);

图像对齐失效:

![对齐失效效果]

这涉及一类常见问题,有多种称呼:

  • Affine Texture mapping(仿射纹理映射)
  • Perspective correct Texture mapping(透视校正纹理映射)
  • Projective Texture mapping(投影纹理映射)
  • Trapezoid texture mapping(梯形纹理映射)

为何向量(x*W,y*W,z*W,W)与向量(x,y,z,1)在片段着色器中表现不一致?我需要将gl_Position的w分量设为1,以便在画布上进一步变换图像时仍能保持对齐。我尝试过修改UV纹理空间的w分量并进行投影,但未成功。请问如何正确变换纹理坐标,才能在扭曲的双三角矩形上实现均匀的纹理采样?(注:修改前后几何形状未发生变化,仅纹理采样方式改变)


问题根源

OpenGL的透视校正插值是核心原因:

  • 当gl_Position.w = transformedCoord.z时,GPU会自动对TexCoord做透视校正插值——它会基于顶点的w分量,在光栅化时按透视空间比例重新计算每个片段的纹理坐标,保证透视变换后纹理采样正确。
  • 手动将w设为1时,GPU判定这是正交空间顶点,只会做线性插值。此时纹理坐标的插值逻辑和顶点的透视变换脱节,扭曲区域的纹理采样就会出错,最终导致图像对齐失效。

解决方案:手动实现透视校正纹理采样

要在gl_Position.w=1的前提下保持正确纹理采样,需要自行传递透视校正参数,在片段着色器中完成校正:

修改后的顶点着色器

#version 330 core
layout (location = 0) in vec3 vertex_in;

out vec3 TexCoord; // 改为传递带w分量的纹理坐标

uniform mat3 cvAffine; // OpenCV 仿射变换矩阵

const vec3 texture_coord[] = {
  vec3(0.0, 0.0, 1.0),
  vec3(1.0, 0.0, 1.0),
  vec3(0.0, 1.0, 1.0),
  vec3(1.0, 0.0, 1.0),
  vec3(0.0, 1.0, 1.0),
  vec3(1.0, 1.0, 1.0)
};


void main(){
    vec3 cvCoord = vec3(vertex_in.xy, 1.0f);
    vec3 transformedCoord = cvAffine * cvCoord;
    
    float w = transformedCoord.z;
    gl_Position = vec4((transformedCoord.xy / w) * 0.0002, 0.0, 1.0);
    
    // 将纹理坐标乘以w,后续在片段着色器中再除以插值后的w
    TexCoord = texture_coord[gl_VertexID] * w;
};

修改后的片段着色器

#version 330 core
in vec3 TexCoord; // 接收带w分量的纹理坐标
out vec4 FragColor;

uniform sampler2D texture;
void main(){
    // 用插值后的w分量校正纹理坐标
    vec2 uv = TexCoord.xy / TexCoord.z;
    FragColor = texture(texture, uv);
}

原理说明

  1. 顶点着色器中,把纹理坐标和顶点的w分量(即transformedCoord.z)相乘,得到带透视权重的纹理坐标。
  2. GPU对TexCoord做线性插值时,会连带权重一起插值。
  3. 片段着色器中,将插值后的纹理坐标除以其w分量,就能得到经过透视校正的正确UV坐标,效果和gl_Position.w保留原始值时的自动校正完全一致。

这样既满足了gl_Position.w=1的需求,又保证了纹理采样的正确性,图像对齐不会失效。


内容的提问来源于stack exchange,提问作者Patrik Staron

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 14:35:57