如何在Metal中固定采样纹理三角区域绘制到目标画布?
解决方案:将纹理固定三角区域映射到任意目标三角
要实现把纹理的同一固定三角区域绘制到所有目标三角中,核心是用重心坐标(Barycentric Coordinates)建立目标三角与纹理固定三角的相对位置映射,而不是直接用顶点坐标对应纹理坐标。以下是具体修改步骤和代码:
1. 定义必要的结构体(共享着色器头文件)
首先补充结构体,用于传递纹理固定三角的坐标和顶点重心坐标:
// 存储纹理上固定三角的三个顶点纹理坐标 struct FixedTriangleTexCoords { float2 p0; float2 p1; float2 p2; }; struct Uniforms { float4 color; bool drawWithTexture; FixedTriangleTexCoords fixedTriangle; // 新增:纹理固定三角的坐标 }; // 修改顶点输出结构体,加入重心坐标 struct VertexOut { float4 position [[position]]; float2 barycentric; // 重心坐标,用于插值计算相对位置 };
2. 修改顶点着色器:传递重心坐标
给每个目标三角的顶点分配对应的重心坐标,硬件会自动在三角内部插值这些坐标:
vertex VertexOut vertex_main(const device float2* position [[buffer(0)]], uint vid [[vertex_id]]) { VertexOut out; float2 pos = position[vid]; out.position = float4(pos, 0, 1); // 按顶点顺序分配重心坐标,顺序需与传入的顶点数据对应 switch(vid) { case 0: out.barycentric = float2(1.0, 0.0); break; case 1: out.barycentric = float2(0.0, 1.0); break; case 2: out.barycentric = float2(0.0, 0.0); break; default: out.barycentric = float2(0.0, 0.0); } return out; }
3. 修改片段着色器:基于重心坐标采样固定纹理区域
利用插值后的重心坐标,计算当前像素在纹理固定三角中的对应位置,再进行采样:
fragment float4 fragment_main(VertexOut in [[stage_in]], texture2d<float> tex [[texture(0)]], constant Uniforms& uniforms [[buffer(1)]]) { constexpr sampler s(address::repeat, filter::linear); if (uniforms.drawWithTexture) { // 用重心坐标插值计算纹理固定三角的采样坐标 float w = 1.0 - in.barycentric.x - in.barycentric.y; float2 texCoord = w * uniforms.fixedTriangle.p0 + in.barycentric.x * uniforms.fixedTriangle.p1 + in.barycentric.y * uniforms.fixedTriangle.p2; // 处理Y轴翻转(根据你的纹理实际存储方向调整) texCoord[1] = 1.0 - texCoord[1]; return tex.sample(s, texCoord); } else { return uniforms.color; } }
关键说明
- 重心坐标作用:硬件会自动在三角内插值重心坐标,每个像素的重心坐标代表它在目标三角内的相对位置,用这个位置去映射纹理固定三角,就能实现所有目标三角复用同一纹理区域。
- CPU端配置:需要在CPU端设置
fixedTriangle的三个坐标,比如要采样纹理左上角的三角,可设p0=(0,0), p1=(1,0), p2=(0,1)(根据你的纹理坐标系调整)。 - 顶点顺序对应:顶点着色器中重心坐标的分配顺序,必须与你传入的顶点数据顺序一致,否则会出现映射错位。
内容的提问来源于stack exchange,提问作者Duncan C
相关产品推荐
相关产品推荐

