Unity3D中Shader与SetPixel制作网格孔洞的坐标适配问题
嘿,我刚好碰到过类似的情况,你这个问题核心就是UV坐标转像素索引时的精度丢失——直接把float类型的UV强转成int会截断小数部分,导致绘制的黑色矩形和射线命中的精确点错位,就出现了你说的和粉色射线点对不上的情况。下面给你几个针对性的解决方案:
1. 用精确的像素映射替代直接强转
别直接(int)uv.x * textureWidth,这种强转会把UV的小数部分直接砍掉,比如0.6*1024=614.4,强转成int就是614,但实际应该映射到614或615像素(取决于纹理的采样方式)。正确的做法是用Mathf.RoundToInt来取最近的像素:
// 假设你的目标纹理是texture2D,射线命中的UV是hitUV int texWidth = texture2D.width; int texHeight = texture2D.height; // 计算精确的像素位置,用RoundToInt对齐到最近像素 int pixelX = Mathf.RoundToInt(hitUV.x * texWidth); int pixelY = Mathf.RoundToInt(hitUV.y * texHeight);
这样能最大程度减小UV到像素的转换误差,让你的黑色矩形边缘更贴近射线命中点。
2. 对齐Shader裁剪逻辑和纹理绘制范围
你的Shader里的SliceAmount是范围值,要确保它和纹理上的黑色区域严格匹配。比如如果Shader是通过纹理灰度值裁剪(黑色区域被裁掉),那SliceAmount要设置成刚好低于黑色的灰度值(比如黑色是0,就把SliceAmount设为0.1),这样Shader才能准确识别出你绘制的黑色区域作为孔洞:
// Shader片段着色器的裁剪逻辑示例 fixed4 frag(v2f i) : SV_Target { fixed4 texColor = tex2D(_MainTex, i.uv); // 只有纹理灰度值大于SliceAmount的区域才保留 if (texColor.r < _SliceAmount) discard; return texColor; }
如果SliceAmount设置不对,哪怕矩形位置准了,Shader也可能裁剪出和预期不符的孔洞。
3. 检查UV空间的原点一致性
Unity里网格的UV原点默认是左下角,但有些情况下(比如用了UI纹理的导入设置),纹理的UV原点会变成左上角,这会直接导致你的绘制位置上下颠倒。要确认射线检测拿到的hit.textureCoord和纹理的UV空间一致:
- 检查网格的UV映射是否是标准的左下角原点
- 如果纹理是UI用的,记得翻转Y坐标:
// 翻转UV的Y轴,适配左上角原点的纹理 hitUV.y = 1 - hitUV.y;
4. 用SetPixels批量绘制提升精度
单个SetPixel调用容易出现边缘的微小误差,建议用SetPixels批量绘制矩形区域,这样整个矩形的像素位置会更统一:
// 射线检测得到的起始和结束UV角点 Vector2 startUV = ...; Vector2 endUV = ...; int startX = Mathf.RoundToInt(startUV.x * texWidth); int startY = Mathf.RoundToInt(startUV.y * texHeight); int endX = Mathf.RoundToInt(endUV.x * texWidth); int endY = Mathf.RoundToInt(endUV.y * texHeight); // 计算矩形的宽高(取绝对值避免顺序问题) int rectWidth = Mathf.Abs(endX - startX); int rectHeight = Mathf.Abs(endY - startY); // 创建全黑的像素数组 Color[] blackPixels = new Color[rectWidth * rectHeight]; System.Array.Fill(blackPixels, Color.black); // 批量设置纹理像素并应用 texture2D.SetPixels(startX, startY, rectWidth, rectHeight, blackPixels); texture2D.Apply();
批量绘制能避免逐个像素设置的累积误差,让孔洞边缘更整齐,完美贴合射线命中的位置。
内容的提问来源于stack exchange,提问作者Queen

