Unity中Sprite转Texture出现拉伸问题及脚本设置纹理渲染模式为Cutout的实现方法问询
嘿,这俩问题我之前做复古风格FPS的时候也碰到过,咱们一步步来解决:
一、用脚本自动将材质切换为Cutout渲染模式
首先得纠正一个小误区:你说的“纹理渲染模式”其实是材质的Shader参数,纹理本身没有Opaque/Cutout的属性,是Standard Shader这类材质通过配置来控制渲染逻辑的。
要实现自动切换到Cutout,你需要修改材质的几个关键参数:开启Alpha测试、设置裁剪阈值、调整渲染队列和混合模式。我给你写个通用的工具方法,直接调用就行:
// 先定义一个枚举方便调用 public enum RenderMode { Opaque, Cutout, Fade, Transparent } // 传入目标材质和要切换的渲染模式 public void SetMaterialRenderMode(Material targetMaterial, RenderMode mode) { switch (mode) { case RenderMode.Cutout: // 切换到Cutout模式的核心参数 targetMaterial.SetFloat("_Mode", 1); // Cutout对应的值是1 targetMaterial.SetInt("_SrcBlend", (int)UnityEngine.Rendering.BlendMode.One); targetMaterial.SetInt("_DstBlend", (int)UnityEngine.Rendering.BlendMode.Zero); targetMaterial.SetInt("_ZWrite", 1); // 启用Alpha测试的关键词 targetMaterial.EnableKeyword("_ALPHATEST_ON"); targetMaterial.DisableKeyword("_ALPHABLEND_ON"); targetMaterial.DisableKeyword("_ALPHAPREMULTIPLY_ON"); // 设置Cutout的裁剪阈值(可根据你的Sprite透明度调整,0.5是常用值) targetMaterial.SetFloat("_Cutoff", 0.5f); // 调整渲染队列,确保Cutout物体在正确的顺序渲染 targetMaterial.renderQueue = 2450; break; // 如果需要其他模式,可以继续补充Opaque/Fade/Transparent的逻辑 } }
然后在你的Start方法里调用它就行,注意一定要用material而不是sharedMaterial,不然会修改原始材质文件,导致所有用这个材质的物体都被影响:
void Start() { var targetSprite = spr[0]; // 先处理Sprite转Texture的逻辑,后面会讲 Texture2D fixedTexture = CreateTextureFromSprite(targetSprite); // 获取面板的Renderer组件 Renderer panelRenderer = panel.GetComponent<Renderer>(); // 实例化材质副本,避免修改原始材质 Material materialInstance = new Material(panelRenderer.material); materialInstance.mainTexture = fixedTexture; // 调用方法切换到Cutout模式 SetMaterialRenderMode(materialInstance, RenderMode.Cutout); // 将修改后的材质赋值给面板 panelRenderer.material = materialInstance; }
二、解决Sprite转Texture的拉伸问题
你的原始代码里texture = spr[0].texture;这一步踩了个坑:Sprite的texture属性其实是它所在的整个图集纹理,而Sprite只是这个大纹理里的一块区域。直接把整个图集纹理赋值给面板,肯定会因为尺寸不匹配导致拉伸。
正确的做法是从Sprite中提取它对应的像素区域,创建一个和Sprite尺寸完全一致的新Texture2D。我写了个工具方法来做这件事:
public Texture2D CreateTextureFromSprite(Sprite sprite) { // 根据Sprite的实际尺寸创建Texture2D Texture2D newTexture = new Texture2D( (int)sprite.rect.width, (int)sprite.rect.height, TextureFormat.ARGB32, false ); // 从原始纹理中提取Sprite对应的像素区域 Color[] spritePixels = sprite.texture.GetPixels( (int)sprite.rect.x, (int)sprite.rect.y, (int)sprite.rect.width, (int)sprite.rect.height ); // 将像素赋值给新纹理并应用 newTexture.SetPixels(spritePixels); newTexture.Apply(); // 可选:设置纹理的过滤模式,复古风格游戏可以用Point过滤避免模糊 newTexture.filterMode = FilterMode.Point; return newTexture; }
额外优化建议
如果你的面板是3D物体,创建完纹理后,最好调整面板的缩放比例,让它和Texture的宽高比一致,这样就完全不会拉伸了。比如:
// 调整面板缩放比例匹配纹理宽高比 float aspectRatio = (float)fixedTexture.width / fixedTexture.height; panel.transform.localScale = new Vector3(aspectRatio, 1, 1);
如果是用UGUI的Panel,其实更简单:直接用Image组件,把Sprite赋值给Image.sprite,然后把Image的Image Type设为Single,材质用UI/Default Cutout就行,完全不用手动转Texture,Unity会自动处理所有细节。
内容的提问来源于stack exchange,提问作者John Doe

