如何将方形图片磨成圆形图片

如何将方形图片磨成圆形图片

在开发中经常会有一些需求,比如显示头像,显示一些特殊的需求,将图片显示成圆角或者圆形或者其他的一些形状。但是往往我们手上的图片或者从服务器 获取到的图片都是方形的。这时候就需要我们自己进行处理,将图片处理成所需要的形状。

使用Xfermode 两图相交方式

通过查找资料通过查找资料发现android中可以设置画笔的Xfermode即相交模式,从而设置两张图相交之后的显示方式,具体模式见下图,源码可以去android apidemo。(SRC 为我们要画到目标图上的图即原图,DST为目标图)

- 这里写图片描述

  • 代码块高亮

private static Bitmap getCircleAvatar(Context context, Bitmap avatar) {
Bitmap bitmap=Bitmap.createBitmap(avatar.getWidth(),avatar.getHeight(),Bitmap.Config.ARGB_8888);

//创建画布
Canvas canvas=new Canvas(bitmap);
//创建画笔
Paint paint=new Paint(Paint.ANTI_ALIAS_FLAG);

//画圆   半径radius为 宽,高的一半
//设置画笔的颜色
paint.setColor(Color.BLACK);
//半径radius为 方形图宽,高的一半
float radius=Math.min(avatar.getWidth(), avatar.getHeight())/2;
//画一个空心圆
canvas.drawCircle(avatar.getWidth()/2, avatar.getHeight()/2, radius, paint);

//添加混合模式给paint, 调用PorterDuffXfermode方法,选择重叠部分, 重叠部分为原来头像(Mode.SRC_IN)
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
//混合模式中的SRC
canvas.drawBitmap(avatar, 0,0, paint);

//描边
paint.setColor(Color.WHITE);
paint.setStyle(Paint.Style.STROKE);
float storkeWidth=TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
        2,
        context.getResources().getDisplayMetrics());
paint.setStrokeWidth(storkeWidth);
canvas.drawCircle(avatar.getWidth()/2, avatar.getHeight()/2, radius, paint);

return bitmap;

}

<think>在Unity中将方形图片裁剪成圆形,通常可以通过以下两种方法实现:1.使用遮罩(Mask)组件和圆形精灵(Sprite)作为遮罩。2.使用Shader进行圆形裁剪。下面分别给出两种方法的详细实现。方法一:使用遮罩(Mask)步骤:a.创建一个Image作为父物体,添加Mask组件(注意:Mask会隐藏超出范围的子物体,但不会改变子物体的形状)。b.在父物体下创建一个子物体Image,并设置其图片为原始方形图片。c.父物体上需要有一个圆形精灵(比如一个圆形的Sprite)作为遮罩的图形。注意,Mask组件默认使用父物体的Image组件的alpha通道作为遮罩。因此,如果父物体的Image是一个圆形图片(透明背景,中间圆形区域不透明),那么子物体的图片就会被裁剪成圆形。具体操作:1.在场景中创建一个GameObject,命名为"CircleImage"。2.给这个GameObject添加一个Image组件,并设置SourceImage为一个圆形的纹理(例如一个圆形的Sprite)。3.添加一个Mask组件,并确保勾选了"ShowMaskGraphic"(这样可以在编辑时看到遮罩图形,运行时可以取消勾选以隐藏圆形边框)。4.创建一个子物体,添加Image组件,并设置SourceImage为需要裁剪的方形图片。5.调整子物体的大小和位置,使其适应父物体的圆形遮罩。方法二:使用Shader通过编写一个简单的Shader,在片段着色器中将方形纹理裁剪成圆形。原理是只显示距离纹理中心一定半径范围内的像素。以下是一个简单的圆形裁剪Shader:```glslShader"UI/CircleClip"{Properties{[PerRendererData]_MainTex("SpriteTexture",2D)="white"{}_Color("Tint",Color)=(1,1,1,1)_Radius("Radius",Range(0,0.5))=0.5_Feather("Feather",Range(0,0.5))=0.05}SubShader{Tags{"Queue"="Transparent""IgnoreProjector"="True""RenderType"="Transparent""PreviewType"="Plane""CanUseSpriteAtlas"="True"}CullOffLightingOffZWriteOffZTest[unity_GUIZTestMode]BlendSrcAlphaOneMinusSrcAlphaPass{CGPROGRAM#pragmavertexvert#pragmafragmentfrag#include"UnityCG.cginc"#include"UnityUI.cginc"structappdata_t{float4vertex:POSITION;float4color:COLOR;float2texcoord:TEXCOORD0;};structv2f{float4vertex:SV_POSITION;fixed4color:COLOR;float2texcoord:TEXCOORD0;float4worldPosition:TEXCOORD1;};fixed4_Color;sampler2D_MainTex;float_Radius;float_Feather;v2fvert(appdata_tIN){v2fOUT;OUT.worldPosition=IN.vertex;OUT.vertex=UnityObjectToClipPos(OUT.worldPosition);OUT.texcoord=IN.texcoord;OUT.color=IN.color*_Color;returnOUT;}fixed4frag(v2fIN):SV_Target{half4color=tex2D(_MainTex,IN.texcoord)*IN.color;//将纹理坐标中心移动到(0.5,0.5)float2uv=IN.texcoord-float2(0.5,0.5);//计算当前点到中心点的距离floatdist=sqrt(uv.x*uv.x+uv.y*uv.y);//使用smoothstep实现边缘羽化floatalpha=smoothstep(_Radius+_Feather,_Radius-_Feather,dist);color.a*=alpha;returncolor;}ENDCG}}}```使用步骤:1.创建一个材质,使用上面的Shader。2.在场景中创建一个Image,将材质赋给Image的Material属性。3.调整Shader参数:-_Radius:控制圆形的半径(相对于图片尺寸的一半,0.5表示刚好是图片宽度或高度的一半,即圆形直径为图片的宽度或高度)。-_Feather:边缘羽化值,使边缘有渐变透明的效果。注意:使用Shader方法时,图片的原始纹理应该是方形的,通过Shader裁剪后显示为圆形。两种方法比较:-遮罩方法:简单易用,不需要编写Shader,但需要一张圆形图片作为遮罩模板。另外,遮罩会多使用一个DrawCall。-Shader方法:灵活,可以动态调整参数(如半径、羽化值),性能相对较好(只增加一个Shader的开销),但需要一定的Shader知识。根据项目需求选择合适的方法。下面给出方法二(Shader)的完整代码和操作步骤:操作步骤:1.在Unity中创建一个新的Shader,命名为"CircleClip",将上面的代码复制进去。2.创建一个材质,选择Shader为"UI/CircleClip",命名为"CircleMaterial"。3.在场景中创建一个UIImage,将需要裁剪的方形图片赋给Image的SourceImage。4.将材质"CircleMaterial"拖到Image的Material属性上。5.调整材质参数:-Radius:通常设置为0.5(这样会使用整个图片的宽度作为直径),如果图片不是正方形,可能需要调整(但注意,这个半径是相对于图片的UV坐标,范围是0~0.5,0.5表示从中心到边缘)。-Feather:边缘羽化程度,根据需要调整。注意:如果图片不是正方形,那么裁剪出来的圆形会变形(因为UV坐标是正方形,而图片被拉伸)。为了避免变形,应使用正方形图片。如果图片不是正方形,我们可以修改Shader,使其在非正方形的纹理上也能保持圆形。修改思路:将UV坐标按宽高比进行校正。修改后的片段着色器部分(考虑宽高比):```glslfixed4frag(v2fIN):SV_Target{half4color=tex2D(_MainTex,IN.texcoord)*IN.color;//获取图片的宽高比float2aspect=float2(1,1);#ifdefined(UNITY_UI_CLIP_RECT)//通过_MainTex_TexelSize获取纹理的尺寸信息(如果定义了UNITY_UI_CLIP_RECT)//但这里我们使用另一种方法:通过顶点着色器传递的纹理坐标可以计算出宽高比,但需要知道原始纹理的尺寸。//实际上,由于UIImage会自动调整比例,我们可以使用Image的RectTransform的宽高比。//但是,在Shader中获取RectTransform的尺寸比较困难,因此我们可以在Properties中增加一个_AspectRatio属性,由脚本设置。//或者,我们可以通过将uv坐标乘以一个比例因子来校正。//这里我们假设纹理的宽高比已经通过外部设置(比如通过脚本计算并传递给Shader),但为了简单,我们假设图片是正方形(1:1)的。//如果图片不是正方形,我们可以在材质中手动设置一个_AspectRatio参数。#else//假设我们添加了一个_AspectRatio属性//这里我们暂时不处理,所以还是按正方形处理#endif//将纹理坐标中心移动到(0.5,0.5)float2uv=IN.texcoord-float2(0.5,0.5);//校正宽高比:假设我们有一个_AspectRatio属性,表示宽高比(宽度/高度)//例如,如果图片宽度是高度的两倍,那么_AspectRatio=2,则我们调整y坐标乘以2,这样圆形就会在屏幕上显示为正圆。//但注意,我们这里没有定义_AspectRatio,所以暂时不校正。//如果我们希望圆形图片上显示为正圆,而图片可能被拉伸,那么我们需要知道图片在屏幕上的实际宽高比。//这通常需要在脚本中计算并传递给Shader。但为了简化,我们只处理正方形图片。//计算当前点到中心点的距离floatdist=sqrt(uv.x*uv.x+uv.y*uv.y);//使用smoothstep实现边缘羽化floatalpha=smoothstep(_Radius+_Feather,_Radius-_Feather,dist);color.a*=alpha;returncolor;}```对于非正方形图片,我们可以在C#脚本中计算宽高比,并传递给Shader。例如:创建一个脚本,附加到Image上,计算宽高比并设置材质参数:```csharpusingUnityEngine;usingUnityEngine.UI;[ExecuteInEditMode]publicclassCircleImageAspect:MonoBehaviour{privateMaterial_material;privateImage_image;voidOnEnable(){_image=GetComponent<Image>();if(_image.material!=null){_material=_image.material;}}voidUpdate(){if(_material==null||_image==null)return;//获取图片的宽高Rectrect=_image.rectTransform.rect;floataspect=rect.width/rect.height;//设置宽高比到Shader_material.SetFloat("_AspectRatio",aspect);}}```然后在Shader中增加一个_AspectRatio属性,并在片段着色器中使用:```glslProperties{...//其他属性_AspectRatio("AspectRatio",Float)=1.0}``````glslfixed4frag(v2fIN):SV_Target{...//前面代码//校正宽高比uv.y/=_AspectRatio;//或者乘以宽高比的倒数,取决于你如何定义...//后面代码}```注意:校正的方式取决于你的需求。上面的校正方式假设我们保持x方向不变,调整y方向,这样在非正方形图片上,圆形会沿着宽度方向拉伸。如果我们希望圆形在屏幕上显示为正圆,那么需要根据屏幕上的宽高比来调整。这需要更复杂的计算,因为涉及到画布的缩放等。由于问题复杂,通常建议使用正方形图片进行圆形裁剪,这样可以避免宽高比问题。综上所述,如果使用Shader方法,并且图片是正方形,那么直接使用第一个Shader即可。如果图片不是正方形,则需要额外处理宽高比。根据实际情况选择合适的方法。§§相关问题§§:1.在Unity中如何动态改变遮罩的形状?2.如何优化使用遮罩的UI性能?3.Shader中如何实现其他形状的裁剪(例如圆角矩形)?4.在UnityUI中,如何实现图片的渐变色效果?</think># 在Unity中将方形图片裁剪成圆形 在Unity中将方形图片裁剪成圆形有多种方法,下面我将详细介绍三种最常用的技术方案,并提供完整的实现代码。 ## 方法一:使用遮罩组件(Mask) 这是最简单且无需编程的方法,适合大多数UI场景。 ### 实现步骤 1. 创建一个圆形精灵作为遮罩 2. 将遮罩应用到方形图片 ```csharp // 不需要代码,只需在Unity编辑器中操作 ``` ### 操作流程 1. **创建圆形遮罩**: - 在Hierarchy面板右键 -> UI -> Image - 将圆形纹理(如透明背景的圆形PNG)赋给Source Image - 添加Mask组件:选中GameObject -> Add Component -> UI -> Mask - 取消勾选"Show Mask Graphic"(可选) 2. **添加方形图片**: - 将方形图片拖拽为Mask对象的子对象 - 调整子对象图片的大小和位置 ### 优缺点 - **优点**:简单快速,无需编码 - **缺点**:需要额外的圆形纹理,性能开销稍大 ## 方法二:使用Shader裁剪 这是最高效的方法,适合性能敏感的场景。 ### 圆形裁剪Shader ```glsl Shader "UI/CircleClip" { Properties { [PerRendererData] _MainTex ("Sprite Texture", 2D) = "white" {} _Color ("Tint", Color) = (1,1,1,1) _Radius ("Circle Radius", Range(0, 0.5)) = 0.5 _Feather ("Edge Feather", Range(0, 0.1)) = 0.02 } SubShader { Tags { "Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "PreviewType"="Plane" "CanUseSpriteAtlas"="True" } Cull Off Lighting Off ZWrite Off Blend SrcAlpha OneMinusSrcAlpha Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata_t { float4 vertex : POSITION; float4 color : COLOR; float2 texcoord : TEXCOORD0; }; struct v2f { float4 vertex : SV_POSITION; fixed4 color : COLOR; float2 texcoord : TEXCOORD0; float2 centerPos : TEXCOORD1; }; sampler2D _MainTex; fixed4 _Color; float _Radius; float _Feather; v2f vert(appdata_t IN) { v2f OUT; OUT.vertex = UnityObjectToClipPos(IN.vertex); OUT.texcoord = IN.texcoord; OUT.color = IN.color * _Color; // 计算中心位置(0.5, 0.5) OUT.centerPos = IN.texcoord - float2(0.5, 0.5); return OUT; } fixed4 frag(v2f IN) : SV_Target { fixed4 color = tex2D(_MainTex, IN.texcoord) * IN.color; // 计算到中心的距离 float distance = length(IN.centerPos); // 圆形裁剪:在半径范围内的显示,半径外的透明 float alpha = smoothstep(_Radius + _Feather, _Radius - _Feather, distance); color.a *= alpha; return color; } ENDCG } } } ``` ### 使用步骤 1. 创建材质: - Assets右键 -> Create -> Material - 选择Shader为"UI/CircleClip" - 调整参数: - Circle Radius:0.5(完整圆形) - Edge Feather:边缘羽化值 2. 应用材质: - 创建UI Image - 将方形图片赋给Source Image - 将创建的材质拖到Material属性上 ### 优缺点 - **优点**:高性能,可动态调整参数 - **缺点**:需要Shader知识,不支持UI批处理 ## 方法三:使用Texture2D编程裁剪(运行时处理) 这种方法适合需要在运行时动态处理图片的情况。 ### 圆形裁剪脚本 ```csharp using UnityEngine; using UnityEngine.UI; public class CircleTextureCreator : MonoBehaviour { public Texture2D sourceTexture; // 原始方形纹理 public RawImage targetImage; // 显示结果的RawImage public float featherRadius = 0.05f; // 边缘羽化范围 void Start() { if (sourceTexture != null && targetImage != null) { Texture2D circleTexture = CreateCircleTexture(sourceTexture); targetImage.texture = circleTexture; } } Texture2D CreateCircleTexture(Texture2D source) { int width = source.width; int height = source.height; Texture2D circleTex = new Texture2D(width, height, TextureFormat.ARGB32, false); Vector2 center = new Vector2(width / 2f, height / 2f); float radius = Mathf.Min(width, height) / 2f; float featherStart = radius * (1 - featherRadius); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { float distance = Vector2.Distance(new Vector2(x, y), center); Color pixel = source.GetPixel(x, y); if (distance > radius) { pixel.a = 0; // 完全透明 } else if (distance > featherStart) { // 边缘羽化 float alpha = 1 - (distance - featherStart) / (radius - featherStart); pixel.a *= alpha; } circleTex.SetPixel(x, y, pixel); } } circleTex.Apply(); return circleTex; } } ``` ### 使用步骤 1. 创建RawImage: - Hierarchy右键 -> UI -> Raw Image 2. 添加脚本: - 将脚本附加到GameObject上 - 拖拽原始方形纹理到Source Texture - 拖拽RawImage到Target Image ### 优缺点 - **优点**:完全运行时控制,不需要特殊纹理或Shader - **缺点**:性能开销大(CPU处理),不适合移动设备频繁使用 ## 方法选择指南 | 方法 | 适用场景 | 性能 | 难度 | 动态调整 | |------|----------|------|------|----------| | Mask遮罩 | 简单UI,少量元素 | 中等 | 简单 | 否 | | Shader | 高性能需求,大量元素 | 高 | 中等 | 是 | | 运行时处理 | 动态生成内容,离线处理 | 低 | 中等 | 是 | ## 高级技巧:自适应圆形裁剪 如果需要处理非正方形纹理,可以使用以下改进的Shader: ```glsl // 在之前的Shader基础上修改frag函数 fixed4 frag(v2f IN) : SV_Target { fixed4 color = tex2D(_MainTex, IN.texcoord) * IN.color; // 计算宽高比 float2 aspect = float2(1, _ScreenParams.y / _ScreenParams.x); float2 adjustedPos = IN.centerPos * aspect; // 计算到中心的距离 float distance = length(adjustedPos); // 使用0.5作为最大半径(适配不同宽高比) float maxRadius = 0.5 * min(aspect.x, aspect.y); float radius = _Radius * maxRadius; // 圆形裁剪 float alpha = smoothstep(radius + _Feather, radius - _Feather, distance); color.a *= alpha; return color; } ``` ## 常见问题解决 1. **边缘锯齿问题**: - 解决方案:在Shader中使用`smoothstep`并设置羽化值 - 推荐羽化值:0.01-0.05 2. **性能优化**: - 对静态图片:使用预处理的圆形纹理 - 对动态内容:使用Shader方案 3. **非正方形图片处理**: - 在Shader中根据宽高比调整距离计算 - 在运行时处理中保持宽高比一致
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值