如何在顶点/片段着色器中使用3×3 2D变换(金属)

我有一个简单的任务,但显然我还是不明白在着色器中的投影是如何工作的。 我需要在纹理四边形(2个三angular形)上进行2D透视变换,但在视觉上它看起来不正确(例如,梯形比CPU版本中的梯形略高或更拉伸)。

我有这个结构:

struct VertexInOut { float4 position [[position]]; float3 warp0; float3 warp1; float3 warp2; float3 warp3; }; 

在顶点着色器中,我做了类似的事情( texCoords是四angular的像素坐标,单应texCoords是以像素坐标计算的):

 v.warp0 = texCoords[vid] * homographies[0]; 

然后在像这样的片段着色器中:

 return intensity.sample(s, inFrag.warp0.xy / inFrag.warp0.z); 

结果不是我所期望的。 我花了几个小时,但我无法弄清楚。 通风

更新:

这些是CPU的代码和结果(又名预期结果 ):

 // _image contains the original image cv::Matx33d h(1.03140473, 0.0778113901, 0.000169219566, 0.0342947133, 1.06025684, 0.000459250761, -0.0364957005, -38.3375587, 0.818259298); cv::Mat dest(_image.size(), CV_8UC4); // h is transposed because OpenCV is col major and using backwarping because it is what is used on the GPU, so better for comparison cv::warpPerspective(_image, dest, ht(), _image.size(), cv::WARP_INVERSE_MAP | cv::INTER_LINEAR); 

由CPU扭曲

这些是GPU的代码和结果(又名错误结果 ):

 // constants passed in buffers, image size 320x240 const simd::float4 quadVertices[4] = { { -1.0f, -1.0f, 0.0f, 1.0f }, { +1.0f, -1.0f, 0.0f, 1.0f }, { -1.0f, +1.0f, 0.0f, 1.0f }, { +1.0f, +1.0f, 0.0f, 1.0f }, }; const simd::float3 textureCoords[4] = { { 0, IMAGE_HEIGHT, 1.0f }, { IMAGE_WIDTH, IMAGE_HEIGHT, 1.0f }, { 0, 0, 1.0f }, { IMAGE_WIDTH, 0, 1.0f }, }; // vertex shader vertex VertexInOut homographyVertex(uint vid [[ vertex_id ]], constant float4 *positions [[ buffer(0) ]], constant float3 *texCoords [[ buffer(1) ]], constant simd::float3x3 *homographies [[ buffer(2) ]]) { VertexInOut v; v.position = positions[vid]; // example homography simd::float3x3 h = { {1.03140473, 0.0778113901, 0.000169219566}, {0.0342947133, 1.06025684, 0.000459250761}, {-0.0364957005, -38.3375587, 0.818259298} }; v.warp = h * texCoords[vid]; return v; } // fragment shader fragment int4 homographyFragment(VertexInOut inFrag [[stage_in]], texture2d<uint, access::sample> intensity [[ texture(1) ]]) { constexpr sampler s(coord::pixel, filter::linear, address::clamp_to_zero); float4 targetIntensity = intensityRight.sample(s, inFrag.warp.xy / inFrag.warp.z); return targetIntensity; } 

在这里输入图像说明

原始图像:

原始图像

更新2:

与普遍认为在片段着色器中应该完成视angular分割的观点相反,如果我在顶点着色器中划分(并且三angular形之间没有变形或缝隙),我会得到更加相似的结果,但是为什么?

在这里输入图像说明

更新3:

我得到相同(错误)的结果,如果:

  • 我将视angular划分移至片段着色器
  • 我只是从代码中删除了鸿沟

很奇怪,看起来分歧并没有发生。

好的,这个解决scheme当然是一个非常小的细节: simd::float3分裂行为绝对是simd::float3 。 实际上,如果我在片段着色器中做这样的视angular分割:

 float4 targetIntensity = intensityRight.sample(s, inFrag.warp.xy * (1.0 / inFrag.warp.z)); 

有用!

这导致我发现乘以预先分开的浮动不同于除以浮动。 如果有人知道为什么我们能解开这个谜团,那么这个原因现在还不得而知。