写在前面
Unity部分
Shader "Custom/circle" { Properties { //xy表示圆心在屏幕中的uv值,z为半径,w为圆边缘的平滑值 _parameters("circleParameter",Vector)=(0.5,0.5,10,0) _Color("circleColor",COLOR)=(1,1,1,1) } SubShader { Tags { "RenderType"="Opaque" } LOD 200 Pass{ CGPROGRAM #include "UnityCG.cginc" #pragma fragmentoption ARB_precision_hint_fastest #pragma target 3.0 #pragma vertex vert #pragma fragment frag #define vec2 float2 #define vec3 float3 #define vec4 float4 #define mat2 float2 #define mat3 float3 #define mat4 float4 #define iGlobalTime _Time.y #define mod fmod #define mix lerp #define fract frac #define Texture2D tex2D #define iResolution _ScreenParams float4 _parameters; float4 _Color; float4 _backgroundColor; struct v2f{ float4 pos:SV_POSITION; float4 srcPos:TEXCOORD0; }; v2f vert(appdata_base v){ v2f o; o.pos=mul(UNITY_MATRIX_MVP,v.vertex); o.srcPos=ComputeScreenPos(o.pos); return o; } vec4 main(vec2 fragCoord); float4 frag(v2f iParam):COLOR{ //获取uv对应的当前分辨率下的点 uv范围(0-1) 与分辨率相乘 vec2 fragCoord=((iParam.srcPos.xy/iParam.srcPos.w)*_ScreenParams.xy); return main(fragCoord); } //要先定义方法声明才能使用 vec4 cicle(vec2 pos,vec2 center,float radius,float3 col,float antialias){ //求出点到圆心距离,如果为正则在圆外 负在圆内 我们需要对圆内的点进行上色 即对负值进行处理 float d=length(pos-center)-radius; //判断d的大小 如果小于0则返回0 如果大于antialias返回1 返回值在0-1之间 //smoothstep(a,b,t) 判断t t<a返回0,t>b返回1,t在a-b之间反差值返回0-1 float t=smoothstep(0,antialias,d); //返回颜色值 在圆外的设置alpha=0透明 return vec4(col,1.0-t); } vec4 main(vec2 fragCoord){ vec2 pos=fragCoord; //给背景一个动态的颜色 vec3 temp = 0.5 + 0.5*cos(iGlobalTime+pos.xyx/_ScreenParams.y+vec3(0,2,4)); //获取背景的颜色 vec4 layer1=vec4(temp,1.0); //获取圆 vec4 layer2=cicle(pos,_parameters.xy*iResolution.xy,_parameters.z,_Color.rgb,_parameters.w); //插值处理,使边界更模糊化,layer2中的_parameters.w值越大越模糊 return mix(layer1,layer2,layer2.a); } ENDCG } } FallBack "Diffuse" }
这里查看,关键的代码就是画圆的circle函数。shadertoy大概的原理就是画一个一个的图层,然后用各种效果混合上去,跟PS画图差不多,这知道为什么效果越精彩的shadertoy网页上看起来就越卡,都不知道叠了多少个图层了。
//给背景一个动态的颜色 vec3 temp = 0.5 + 0.5*cos(iGlobalTime+pos.xyx/_ScreenParams.y+vec3(0,2,4));
ShaderToy部分
float t=smoothstep(0,antialias,d);
float t=smoothstep(0.0,antialias,d);
//输入参数(当前点位置,中心点位置,点的半径,颜色,与背景过渡的平滑值) vec4 cicle(vec2 pos,vec2 center,float radius,vec3 col,float antialias){ //求圆心距离 float d=length(pos-center)-radius; //smoothstep(a,b,t)函数 t<a return a, t>b return b float t=smoothstep(0.0,antialias,d); return vec4(col,1.0-t); } void mainImage( out vec4 fragColor, in vec2 fragCoord ) { //获取点的位置 //iResolution为屏幕的分辨率 //fragCoord为当前点的位置 原点是左下角 //返回的uv是以屏幕中心为原点 vec2 uv =(2.0*fragCoord.xy-iResolution.xy) /iResolution.y; //中心点 vec2 point1 = vec2(0,0); //圆的颜色 vec3 color=vec3(1,0,0); // layer1 cos函数 vec3 temp = 0.5 + 0.5*cos(iTime+uv.xyx+vec3(0,2,4)); vec4 layer1= vec4(temp,1); //layer2 平滑的圆 vec4 layer2 = cicle(uv,point1,0.8,color,0.03); // 输出像素 fragColor = mix(layer1,layer2,layer2.a); }
总结
//(iParam.srcPos.xy/iParam.srcPos.w)获取归一化的点 范围在(0-1) //与屏幕分辨率相乘获得实际的像素坐标,坐标原点在正中心 vec2 fragCoord=((iParam.srcPos.xy/iParam.srcPos.w)*_ScreenParams.xy);
//获取点的位置 //iResolution为屏幕的分辨率 //fragCoord为当前点的位置 原点是左下角 //返回的uv是以屏幕中心为原点 并且除以分辨率的一个轴进行缩放 vec2 uv =(2.0*fragCoord.xy-iResolution.xy) /iResolution.y;
PS:将理解的内容描述出来也不是件容易的事情啊。。。
文章来源: 【Shader与ShaderToy】画一个圆