解读Unity中的CG编写Shader系列四??unity中的圆角矩形shader

转自 http://www.itnose.net/detail/6097625.html

上篇文章中我们掌握了表面剔除和剪裁模式

这篇文章将利用这些知识实现一个简单的,但是又很常用的例子:把一张图片做成圆角矩形

例3:圆角矩形Shader

好吧我承认在做这个例子的时候走了不少弯路,由于本人对矩阵的知识掌握已经悉数还给老师,所以一开始用了一些笨办法计算圆角矩形区域。

我们知道TEXTCOORD0是一个以对象为坐标系的坐标,并且范围在该坐标的第一象限,取值为(0,0)到(1,1)

那么我们把每一张图片都看做一张1X1大小的矩形

我们要在1X1大小的矩形中擦除4个角,应该是这样:

以左上角为例,我们做一个辅助圆内切于这个角,半径为0.1,那么我们将这个圆擦掉3/4,剩下的黄色弧线与这个角形成的区域就是我们要擦除的区域:

这个道理很简单,那么我们对4个角分别擦除掉这样的区域就能得到一个半径为10%原图尺寸的圆角矩形

我一开始走的弯路就在于计算这个区域,是用4个圆的方程来算呢还是用距离来计算

由于给出4个圆的方程太过于复杂,我这里直接给出计算外点距离算法的示意图:

首先在这个大矩形的内部以4个圆心为顶点做出一个内矩形,心算得边长为0.8

其次忽略内矩形内部的点,在这个红色矩形内给定任意一个蓝色矩形的外点p  ,只要能够得到p到蓝色矩形的距离,距离大于0.1(半径),那么就在圆角矩形外部,直接擦除,如图:

我们看p1,p2,p3,3个点到蓝色矩形的距离分别是p1的距离<半径,根号2倍半径>p2的距离>半径,p3的距离等于半径

所以p2在圆角矩形外,p1,p3在圆角矩形内部或边缘,我们将p2擦除掉

其中p3的距离恰好是 p3到4条直线的距离最小值

p1同理

而p2的距离不能再这样计算,而应该是计算p2到4个顶点距离的最小值

按这个算法只要算出所有外点中的点到蓝色矩形区域的距离,然后与半径判断大小,大于则discard就能得到圆角矩形

一开始我按照这个思路得出一个极端无脑的方法:

给定任意一点p,求p到4条直线,4个顶点的距离,然后在8个距离中求最小值作为最终的距离拿来与半径比较

兴奋地写完代码知道我错了,检查了很久才明白,像p3这种点,算出来的8个距离中,最小距离并不是到顶点的距离,而是到两条边的延长线的距离

于是最终8距离求最小值算法以失败告终

还是得老老实实分情况

那么有几种情况呢

其实只有1种,但是先按正常逻辑分为2种:

我们来看绿色区域和紫色区域的外点们

1、当外点在紫色区域时,距离应是点到4顶点的距离最小值

2、当外点在绿色区域时,距离应是点到4条直线的距离最小值

按照这个思路那么我们可以对整个 坐标系内的任意点(x,y)进行判断:

1、如果点在白色区域或者绿色区域内,  还计算个毛线距离啊,肯定是不discard啊~~~~(之前我傻乎乎的还真去算了)

2、紫色区域内计算点到4顶点距离,然后取最小值,然后将大于0.1的部分剔除掉

最后我们需要将这个0.1作为变量提取出来,不能写死,这样可以在Inspector中方便调节,或者在script中去设置,也就是给我们的shader定义一个float或rang型的属性

最后代码为:

Shader "Custom/RoundRect" {
	Properties {
		//两种内容模式,图片模式
		_MainTex ("Base (RGB)", 2D) = "white" {}
		//纯色模式
		//_MainColor ("Color", COLOR) = (1,1,1,1)
		//圆角半径,默认为0.1
		_RoundRadius("Radius",float) = 0.1
	}
	SubShader {

		Pass{
			CGPROGRAM

			#pragma fragment frag

			#include "UnityCG.cginc"
			//获取3个属性 并传值到CG代码段
			sampler2D _MainTex;
			float _RoundRadius;
			float4 _MainColor;

			//片段着色器输入结构体(可省略)
			struct FragInput{
				float2 texcoord:TEXCOORD0;

			};

			//片段着色器入口函数
			float4 frag(FragInput input) : COLOR
			{

				float4 c=tex2D(_MainTex,input.texcoord);//将图片信息按坐标转换成颜色
				//float4 c=_MainColor;	//纯色

				//x,y两个变元,区间均为[0,1]
				float x=input.texcoord.x;
				float y=input.texcoord.y;

				//4条直线的常数部分
				float xt=1-_RoundRadius;
				float xb=_RoundRadius;
				float yl=_RoundRadius;
				float yr=1-_RoundRadius;
				//如果(x,y)不在4条直线构成的矩形中(上图的白色区域)
				if(!(x<xt&&x>xb&&y>yl&&y<yr))
				{

					//如果(x.y)不在上图的绿色区域
					if(!((x<xt&&x>xb) || (y>yl&&y<yr) ))
						//数学不好,好像判断的复杂了,如果您可以直接写出紫色区域
						//的不等式组那么可以简单点
				{
				//计算四个顶点的坐标
				float2 plb=float2(_RoundRadius,_RoundRadius);
				float2 plt=float2(_RoundRadius,1-_RoundRadius);
				float2 prt=float2(1-_RoundRadius,1-_RoundRadius);
				float2 prb=float2(1-_RoundRadius,_RoundRadius);

				//计算x,y分别到4个顶点的距离
				float distlb=sqrt(pow((x-plb.x),2)+pow((y-plb.y),2));
				float distlt=sqrt(pow((x-plt.x),2)+pow((y-plt.y),2));
				float distrt=sqrt(pow((x-prt.x),2)+pow((y-prt.y),2));
				float distrb=sqrt(pow((x-prb.x),2)+pow((y-prb.y),2));

				//对4个距离取最小值
				float dist=min(distlb,distlt);
				dist=min(dist,distrt);
				dist=min(dist,distrb);

				//将大于半径的表面剔除
				if(dist>_RoundRadius)
						discard;

				}

				}

				return c;

			}
			ENDCG
		}

	}
	FallBack "Diffuse"
}

  

最后运行的效果,不同Radius不同尺寸的图片进行圆角矩形剔除

当radius设为0.25时我们可以得到一个圆,所以我们的RoundRadius属性可以设置为一个0.01~0.25的rang

由于我是windows系统,mac os下好像有点问题,不知道是不是省略了顶点着色器的问题,发现原因后再来补正

时间: 2024-08-05 15:25:43

解读Unity中的CG编写Shader系列四??unity中的圆角矩形shader的相关文章

解读Unity中的CG编写Shader系列四——unity中的圆角矩形shader

上篇文章中我们掌握了表面剔除和剪裁模式 这篇文章将利用这些知识实现一个简单的,但是又很常用的例子:把一张图片做成圆角矩形 例3:圆角矩形Shader 好吧我承认在做这个例子的时候走了不少弯路,由于本人对矩阵的知识掌握已经悉数还给老师,所以一开始用了一些笨办法计算圆角矩形区域. 我们知道TEXTCOORD0是一个以对象为坐标系的坐标,并且范围在该坐标的第一象限,取值为(0,0)到(1,1) 那么我们把每一张图片都看做一张1X1大小的矩形 我们要在1X1大小的矩形中擦除4个角,应该是这样: 以左上角

解读Unity中的CG编写Shader系列一

CG=C for Graphics  用于计算机图形编程的C语言超集 前提知识点: 1.CG代码必须用 CGPROGRAM ... ENDCG括起来 2.顶点着色器与片段着色器的主函数名称可随意,但需要再#pragma vert 与#pragma fragment中声明并且与主函数名完全匹配,shader才会找到入口 3.float4是一种压缩数组,float4 vert与float vert[4]严格意义上讲不同,虽然都是存放4个float,但float4作为向量类型做点乘.内积等处理更快速

解读Unity中的CG编写Shader系列6——漫反射

如果前面几个系列文章的内容过于冗长缺乏趣味着实见谅,由于时间原因前面的混合部分还没有写完,等以后再补充,现在开始关于反射的内容了. 折射与反射 在物理世界中,光的反射与折射往往是同时存在的,光源由真空或者空气中射入一种材料,光在进入这种材料的同时就发生了折射,折射的程度与各个介质的折射率有关,使光的传播路线偏离原来的路线: 继而如果光在通过不同传播介质的表面时,会像乒乓球一样弹回来,我们人眼能够看到东西,都是因为东西会反射光源,如果一种物质无法反射光,或者没有光源,我们就看不到东西.同样对于不同

解读Unity中的CG编写Shader系列4——unity中的圆角矩形shader

上篇文章中我们掌握了表面剔除和剪裁模式 这篇文章将利用这些知识实现一个简单的,可是又非经常常使用的样例:把一张图片做成圆角矩形 例3:圆角矩形Shader 好吧我承认在做这个样例的时候走了不少弯路,因为本人对矩阵的知识掌握已经悉数还给老师,所以一開始用了一些笨办法计算圆角矩形区域. 我们知道TEXTCOORD0是一个以对象为坐标系的坐标,而且范围在该坐标的第一象限,取值为(0,0)到(1,1) 那么我们把每一张图片都看做一张1X1大小的矩形 我们要在1X1大小的矩形中擦除4个角,应该是这样: 以

RxJava入门系列四,Android中的响应式编程

RxJava入门系列四,Android中的响应式编程 在入门系列1,2,3中,我基本介绍了RxJava是如何使用的.但是作为一名Android开发人员,你怎么让RxJava能为你所用呢?这篇博客我将针对Android开发来介绍一下RxJava的使用场景. RxAndroid RxAndroid是为Android打造的RxJava扩展.通过RxAndroid可以让你的Android开发变得更轻松. 首先,RxAndroid中提供了AndroidSchedulers,你可以用它来切换Android线

解读Unity中的CG编写Shader系列9——镜面反射

讨论完漫反射之后,接下来肯定就是镜面反射了 在开始镜面反射shader的coding之前,要扩充一下前面提到的知识,加深理解镜面反射与漫反射的区别. 引用一下一位前人博文中的一些基础概念,特别是关于冯氏反射模型的: 平行光(directional light) 一种是从特定方向射入并只会照亮面对入射方向的物体,我们称之为平行光(directional light). 环境光(ambient light) 另一种光是来自所有方向并且会照亮所有物体,不管这些物体的朝向如何,我们称之为环境光(ambi

解读Unity中的CG编写Shader系列十 (光滑的镜面反射(冯氏着色))

前文完成了最基本的镜面反射着色器,单平行光源下的逐顶点着色(per-vertex lighting),又称为古罗着色(Gouraud shading).这篇文章作为后续讨论更光滑的镜面反射方式,逐像素着色(per-pixcel lighting),又称为冯氏着色(Phong shading) 逐像素着色Per-Pixel Lighting (冯氏着色Phong Shading) 别把冯氏着色与冯氏反射模型搞混淆了,前问提到了冯氏反射模型,冯氏反射模型是为使计算机模拟接近真实的物体表面光泽提出的模

解读Unity中的CG编写Shader系列10——光滑的镜面反射(冯氏着色)

前文完成了最基本的镜面反射着色器,单平行光源下的逐顶点着色(per-vertex lighting),又称为古罗着色(Gouraud shading).这篇文章作为后续讨论更光滑的镜面反射方式,逐像素着色(per-pixcel lighting),又称为冯氏着色(Phong shading) 逐像素着色Per-Pixel Lighting (冯氏着色Phong Shading) 别把冯氏着色与冯氏反射模型搞混淆了,前问提到了冯氏反射模型,冯氏反射模型是为使计算机模拟接近真实的物体表面光泽提出的模

解读Unity中的CG编写Shader系列6——不透明度与混合

1.不透明度 当我们要将两个半透的纹理贴图到一个材质球上的时候就遇到混合的问题,由于前面的知识我们已经知道了片段着色器以及后面的环节的主要工作是输出颜色与深度到帧缓存中,所以两个纹理在每个像素上的颜色到底以怎样的形式混合在一起最后输出到帧缓存中是我们现在首要讨论的内容. 1.混合(blending) 上一篇文章中的管道环节中的"逐帧操作"环节中的一项操作就是混合操作,可见混合操作是不可编程的固定功能环节. 在混合操作中,我们将片段着色器中计算出来的颜色称之为 "源颜色&quo