有什么实用的小型shader什么意思

Unity 3d可视化节点材质Shader Forge实用教程 初级篇教程介绍_土豆_高清视频在线观看4182人阅读
我在9月份的时候对博客的主页换了个模板,一些童鞋可能会发现边栏多了个小雨伞的动画,再细心的同学可能会发现如果一直开着我的博客电脑耗电更快了……当然啦,也有可能你看到的是一团黑,这说明你该换更高级的浏览器啦!
前几天有人问我这个是怎么实现的,其实我一直都想写一下的。这个例子的灵感是我暑假的时候喝了一杯奶茶(我超爱喝奶茶……),然后觉得杯子上的图案非常可爱(有点类似于吉米的那种画风)。当然啦,用我相信用PS很多人都可以画出来,后来我就想可以不可以在shadertoy上把这个图画出来?于是就有了画卡通伞的想法。
事实上,我和很多人一样,一直惊叹于shadertoy上那些宏大的场景竟然没有使用任何传统模型,而是用代码写出来的。其中一种重要的方法就是使用distance field。而雨伞其实二维的distance field的一个简单应用。
P.S. 如果你很好奇ShaderToy上那些效果都是怎么写的,可以看两个创始人在Siggraph Asia 2014上的(而且是在中国深圳讲的呦)—— Learn to Create Everything in a Fragment Shader。
我的Umbrella:
再P.S. 很兴奋的是,Iq给这个作品留言啦,被偶像说cute好开心呀,哇哈哈哈
再再P.S.小雨伞现已加入我的Github项目Shadertoy_Lab()。
什么是Distance Field
(中文翻译为距离场?)的含义很好理解,我们可以用它来判断一个点是否在一个区域内。我们往往用一个函数来表示某个需要绘制图形的distance function,然后把屏幕上某点的位置代入计算,如果得到的值为负,那么该点就在该图形内部,如果为正,就在图形外部。这种思想看似很简单,但实际上当使用一些复杂的distance function后,就可以得到非常复杂的场景,再配合使用一些光照、图形处理的技术,就可以得到非常出色的画面效果。Iq(Inigo Quilez,Shadertoy的创始人之一)在里概述过distance field的技术。感兴趣的一定要去拜读一下。
在Umbrellar的例子里,我实际上只是非常简单地应用了一下二维空间里的distance field。这些效果都是由简单的圆、椭圆、有宽度的线段变化而来的,配合使用了并集(Union)、交集(Intersection)、差集(Difference)的操作。这些变化大部分是使用了正弦函数和一些简单的线性方程(例如伞上的条纹),只是为了得到比较好的效果需要不断尝试各种参数。
我是怎么实现的
我一开始就计划伞大概可以用一些基本图元来表示,例如伞的主题可以用两个椭圆的交集画,伞柄可以用线段+圆的并集+差集,至于伞上面的条纹其实也是很多椭圆的交集+差集画出来的。看到这里有些人可能会觉得有些混乱,实际上你在脑海里想象一下这些图元的组合关系就可以明白了。
所以,我只需要对三个图元——圆、椭圆和线段定义它们的distance function就可以了(实际上椭圆是圆的超集,但为了方便我还是把圆和椭圆分开了):
float sdfCircle(vec2 center, float radius, vec2 coord) {
vec2 offset = coord -
return sqrt((offset.x * offset.x) + (offset.y * offset.y)) -
float sdfEllipse(vec2 center, float a, float b, vec2 coord) {
float a2 = a *
float b2 = b *
return (b2 * (coord.x - center.x) * (coord.x - center.x) + a2 * (coord.y - center.y) * (coord.y - center.y) - a2 * b2)/(a2 * b2);
float sdfLine(vec2 p0, vec2 p1, float width, vec2 coord) {
vec2 dir0 = p1 - p0;
vec2 dir1 = coord - p0;
float h = clamp(dot(dir0, dir1)/dot(dir0, dir0), 0.0, 1.0);
return (length(dir1 - dir0 * h) - width * 0.5);
上面的代码都很简单,就是利用了圆和椭圆的公式,线段就是斜截式的变种,在之前的文章中都看过很多次。只是需要注意,distance function要保证图元内部的点返回值小于0,外部则大于0,顺序不要搞反。
有了距离,我们就可以据此来画图了。render函数就是做这个用的:
vec4 render(float d, vec3 color, float stroke) {
float anti = fwidth(d) * 1.0;
vec4 colorLayer = vec4(color, 1.0 - smoothstep(-anti, anti, d));
if (stroke & 0.000001) {
return colorL
vec4 strokeLayer = vec4(vec3(0.05, 0.05, 0.05), 1.0 - smoothstep(-anti, anti, d - stroke));
return vec4(mix(strokeLayer.rgb, colorLayer.rgb, colorLayer.a), strokeLayer.a);
render接受三个参数,第一个就是距离值,第二个是需要绘制的颜色,第三个描边的宽度。我首先在render函数里绘制绘制颜色层,并进行了抗锯齿处理(原理参见之前的文章:),然后判断需不需要描边。如果需要的话在下面首先绘制一层描边层,再把颜色层混合上去。
有了这些函数,我们就可以在屏幕上画一些基本的图元了,例如:
那么,伞在哪里呢?完全看不到嘛!别着急,我们还需要把这些基本的图元组合起来,这可以依赖基本的并集、交集和差集操作:
float sdfUnion( const float a, const float b ) {
return min(a, b);
float sdfDifference( const float a, const float b) {
return max(a, -b);
float sdfIntersection( const float a, const float b ) {
return max(a, b);
读者如果还记得初中数学的话应该对上面的概念并不陌生。交集就是取A和B共同的部分,并集就是取A和B加起来的部分,而差集就是取A-B得到的部分。应用到distance field,那么交集就是取距离值a和b中较大的一个,这样只有其中有大于0的(在区域外)结果也会大于0,并集则取两者中较小的,这样只要其只能有小于0的(在区域内)结果也会小于0。而差集首先对b取反,即取b表示区域的补集,再和a对应的区域取交集就可以了。
上面这些函数,就是我们用到的所有函数。现在,就可以在真正开始画伞啦!
等等,在动手写代码前,我们需要首先安排下伞的各个部分的绘制部分。我把整个伞分成了三个部分:伞柄,伞身,和伞上的条纹,它们的绘制顺序也是依次从前往后。我把这三个部分绘制在不同的层上面,最后再按顺序混合它们。
那么,第一步就是画伞柄:
vec4 main(vec2 fragCoord) {
float size = min(iResolution.x, iResolution.y);
float pixSize = 1.0 /
vec2 uv = fragCoord.xy / iResolution.x;
float stroke = pixSize * 1.5;
vec2 center = vec2(0.5, 0.5 * iResolution.y/iResolution.x);
float bottom = 0.08;
float handleWidth = 0.01;
float handleRadius = 0.04;
float d = sdfCircle(vec2(0.5-handleRadius+0.5*handleWidth, bottom), handleRadius, uv);
float c = sdfCircle(vec2(0.5-handleRadius+0.5*handleWidth, bottom), handleRadius-handleWidth, uv);
d = sdfDifference(d, c);
c = uv.y -
d = sdfIntersection(d, c);
c = sdfLine(vec2(0.5, center.y*2.0-0.05), vec2(0.5, bottom), handleWidth, uv);
d = sdfUnion(d, c);
c = sdfCircle(vec2(0.5, center.y*2.0-0.05), 0.01, uv);
d = sdfUnion(c, d);
c = sdfCircle(vec2(0.5-handleRadius*2.0+handleWidth, bottom), handleWidth*0.5, uv);
d = sdfUnion(c, d);
vec4 layer0 = render(d, vec3(0.404, 0.298, 0.278), stroke);
老规矩,首先要计算当前绘制的这点在屏幕上的uv值。我们以水平方向的为基准,即变换后水平方向的值域为[0, 1],而竖直方向的要取决于分辨率。同时,为了方便后面定位一些参数和位置,我提前计算了描边的宽度值stroke,和屏幕中心的位置center。
伞柄还需要进一步细化它的结构。我是从下往上画的。首先,1)画一个落空的圆圈(对两个圆去差集),2)再去掉上半部分只留下半部分(交集),3)之后画一条表示主杆的线段(取并集)。4)伞头我想用一个更大的圆表示,所以又画了一个半径更大的圆(取并集)。5)最后,手握的那里有些突兀,所以又使用了一个圆(取并集)。得到最后的距离值后,使用棕色绘制出来,伞柄部分完成。这个过程可以用下面的图展示。当然啦,里面的位置和参数都是手调的,试了很多次,还要考虑屏幕分辨率的变化。
和伞柄相比,伞身就更加简单了。只需要用两个长短轴不同的椭圆,然后对它们取交集即可:
float a = sdfEllipse(vec2(0.5, center.y * 2.0 - 0.34), 0.25, 0.25, uv);
float b = sdfEllipse(vec2(0.5, center.y * 2.0 + 0.03), 0.8, 0.35, uv);
b = sdfIntersection(a, b);
vec4 layer1 = render(b, vec3(0.32, 0.56, 0.53), fwidth(b) * 2.0);
当然,里面的参数也是手调的,凭感觉。这里描边的时候没有用之前计算的宽度定值strock,就因为椭圆函数的距离值并不是线性的,所以使用定值会使得描边得到的宽度不一致。所以改用导数了。
绘制完这一步就可以得到下面的效果了。
实际上,条纹才是整个shader里最麻烦的部分。我一开始就想到使用正弦函数来模拟波浪的效果,但是为了让这些条纹有从上到下逐渐加宽、弧度逐渐增大的效果,还是调了很一会。
// Draw strips
vec4 layer2 = layer1
float t, r0, r1, r2, e, f
vec2 sinuv = vec2(uv.x, (sin(uv.x * 40.0) * 0.02 + 1.0) * uv.y)
for (float i = 0.0
t = mod(iGlobalTime + 0.3 * i, 3.0) * 0.2
r0 = (t - 0.15) / 0.2 * 0.9 + 0.1
r1 = (t - 0.15) / 0.2 * 0.1 + 0.9
r2 = (t - 0.15) / 0.2 * 0.15 + 0.85
e = sdfEllipse(vec2(0.5, center.y * 2.0+0.37 - t * r2), 0.7 * r0, 0.35 * r1, sinuv)
f = sdfEllipse(vec2(0.5, center.y * 2.0+0.41 - t), 0.7 * r0, 0.35 * r1, sinuv)
f = sdfDifference(e, f)
f = sdfIntersection(f, b)
vec4 layer = render(f, vec3(1.0, 0.81, 0.27), 0.0)
layer2 = mix(layer2, layer, layer.a)
sinuv是基础的波浪式变化的屏幕坐标,所有条纹都是由它延伸出来的。由于这些条纹是随着时间不短向下移动,所以它们的位置可以使用不同的时间点来绘制。首先,我把条纹的循环时间定义为了3秒,这个值决定了后面的许多计算,例如要安排多少个条纹、它们的间距等等。每个条纹都是由两个椭圆取差集得到的,并和之前的结果取交集来不断增加条纹。啊,这里面的椭圆参数我就不解释了,其实如果现在让我再重现之前的实现我也很难做到了……不过可以说一下基本思路。对于每个条纹,我需要确定它们的长短轴的值。由于要实现从上到下弧度逐渐增大的效果,所以长短轴应该随着时间增加而增大。这个增大的幅度是很多实验得到的结果,最后计算得到了r0和r1这两个值。两个椭圆的中心位置,x分量好说,都是0.5,y分量的比较难办,因为想要实现从上到下宽度依次增加的效果,所以也是需要和t有关,但是如果两个椭圆关于t的系数是完全相等的话就无法实现改变宽度的效果,因此最后实验得到了r2这个参数。
完成后,把这三层和背景层混合后就得到了下面这样的效果。
最后,在输出前进行伽马校正,得到最终的效果。
有没有觉得distance field很神奇?的确,数学的魅力就是这么强大,哇哈哈哈。实际上,ShaderToy上很多3D效果也是基于这样的想法。我们看到很多看似很复杂的形状,往往也是由一些非常基本的三维图元变换而来的,例如球、三角锥、圆柱、长方体等等。那些大牛的厉害之处,在于他们可以随手拈来一些数学公式,把平淡无奇的图元逐渐变化成各种不可思议的图像。当然啦,人家的基本功肯定练了很多年了。大家可以在ShaderToy上直接搜distancefield,一定有很多不错的shader可以学习!
总之,希望这篇文章可以对一些人有所帮助。以后我会写些三维的,不过最近比较忙,可能又要搁置一段时间了,没办法,感觉要学要做的好多好多好多好多……
参考知识库
* 以上用户言论只代表其个人观点,不代表CSDN网站的观点或立场
访问:1554728次
积分:13720
积分:13720
排名:第691名
原创:114篇
转载:20篇
译文:21篇
评论:2009条
我叫乐乐,程序媛一枚,就读于上海交通大学软件学院,研究生,数字媒体方向。喜欢用计算机来绘制各种五彩缤纷的画面~欢迎访问我的和 :)
邮件:lelefeng1992 # gmail DOT com
PS:为防止垃圾邮件,请自行转换为正确格式哦~
---------------------
阅读:61054
阅读:32351
文章:45篇
阅读:598738
(2)(1)(3)(1)(2)(2)(1)(1)(2)(2)(4)(2)(3)(6)(3)(8)(5)(6)(3)(6)(5)(7)(1)(2)(8)(1)(4)(5)(4)(3)(15)(2)(3)(27)(5)现代显卡GPU pixel shader小程序合集
> 现代显卡GPU pixel shader小程序合集
现代显卡GPU pixel shader小程序合集
  前言本文引用地址:
  是现代显卡的编程语言,可以用于对屏幕输出图像里的每个象素点进行精确的色彩调整。大型三维游戏里面大量采用了和它的伙伴,Vertex Shader,用于控制各种复杂的场景。
  视频播放通常被认为是2D应用,实际上现在的视频播放已经是部分借助于显卡的3D渲染管线来实现的(VMR7、VMR9等渲染模式),使用可以对最终输出的视频图像进行进一步修正和增强,达到更佳的画面质量,也更充分的发挥显卡(尤其是高端显卡)被闲置的能力。
  背景介绍:HiVi烧友已经开始广泛使用ffdshow里面提供的倍线锐化等后期处理滤镜于播放DVD, 尤其是使用投影仪输出时。这些滤镜能够大幅提升DVD的欣赏品质,达到接近HDTV的效果。但是,同样的方式却不能用于HDTV播放上,原因很简单,因为HDTV的分辨率大大提高,做同样的后期处理,对CPU的要求高了很多,再加上视频解码处理也需要由CPU来负责完成(硬件解码的限制),现有的CPU基本无法胜任这样的工作,导致无法流畅播放。而与此同时,显卡却属于基本闲置状态。
  软硬件需求
  硬件:支持PS1.4或更高的显卡,N卡为FX5200以上,A卡为8500以上(不含9100). 至于CPU等,不太好说,但绝对不支持低端系统。视频特效无止境,硬件越高级越好
  软件:DX9.0, 播放软件需要支持Pixel Shader编程。已知的有Media Player Classic 6.4.8.4 或KMPlayer。但KMPlayer尚有点问题,MPC是目前唯一可用的播放器。
  MPC里面使用Shader的办法
  1. MPC6.4.8.4播放设置调整,修改渲染器设置。除此以外,你还要把解码器等滤镜进行相应调整,在此就不再赘述。试验一下播放是否正常
  2. 启动MPC里面的Shader编辑程序
  3. 输入shader程序并保存为你喜欢的名字,注意屏幕下方的提示,必须成功才有用。图片里的程序仅为示例。
  4. 再次播放影片,并使用你刚才输入的shader程序,看看有什么效果
  以下列举为可用的shader程序
  消除1080i的白条,要求ps1.4
  sampler s0 : register(s0);
  float4 p0 : register(c0);
  #define height (p0[1])
  float4 main(float2 tex : TEXCOORD0) : COLOR
  float h = 88.0f;
  float4 pixel = tex2D(s0,tex);
  float4 fill = float4(0,0,0,0);
  if(height == 1088 && tex.y &=h )
  pixel =
  xsharpen滤镜(完整版),适用于支持ps3.0的显卡,如NVIDIA 6xxx系列卡. 对于ATI X系列卡等也许也可以用
  可以自行修改strength/threshold的值
  sampler2D s0 : register(s0);
  float4 p0 : register(c0);
  #define width (p0[0])
  #define height (p0[1])
  float luma(float4 color)
  float4 lum = {0...};
  return dot(color, lum);
  float4 main(float2 tex : TEXCOORD0) : COLOR0
  float strength = 1.0f; //min 0.0f, max 1.0f
  float threshold = 1.0f; //min 0.0f, max 1.0f
  float4 pmin, pmax,
  float lmin = 1.0f, lmax = 0.0f,
  for(int i=-1; i&=1; i++)
  for(int j=-1; j&=1; j++)
  float4 pcur = tex2D(s0, tex.xy + float2(i/width, j/height));
  float lcur = luma(pcur);
  if (lcur & lmin) { lmin = pmin = }
  if (lcur & lmax) { lmax = pmax = }
  if(i==0 && j==0) { porg =lorg=}
  if(lorg-lmin & lmax-lorg)
  if(lmax-lorg & threshold)
  return pmax*strength+porg*(1-strength);
  if(lorg-lmin & threshold)
  return pmin*strength+porg*(1-strength);
全息投影相关文章:
分享给小伙伴们:
我来说两句……
最新技术贴
微信公众号二
微信公众号一温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!&&|&&
LOFTER精选
网易考拉推荐
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
&&&&&&&&Properties&&&&&&&&{&&&&&&&&&&&&_MainTex("Base&(RGB)",&2D)&=&"white"&{}&&&&&&&&_NoiseTex("NoiseTex&(R)",2D)&=&"white"{}&&&&&&&&_DissolveSpeed("DissolveSpeed&(Second)",Float)&=&1&&&&&&&&&&&&_EdgeWidth("EdgeWidth",Range(0,9))&=&0.1&&&&&&&&&&&&_EdgeColor("EdgeColor",Color)&=&(1,1,1,1)&&&&&&&&&&&&_StartTime("StartTime",Float)&=&0&&&&&&&&}&&&&&&&&&&&&//定义属性&&&&&&&&&&&&SubShader&&&&&&&&{&&&&&&&&&&&&Tags{&"RenderType"&=&"Opaque"&}&&&&&&&&&&&&Pass&&&&&&&&{&&&&&&&&&&&&CGPROGRAM#pragma&vertex&vert_img&&#pragma&fragment&frag&&#include&"UnityCG.cginc"&&&&&&&&&&uniform&sampler2D&_MainT&&&&&&&&uniform&sampler2D&_NoiseT&&&&&&&&uniform&float&_DissolveS&&&&&&&&uniform&float&_EdgeW&&&&&&&&uniform&float4&_EdgeC&&&&&&&&uniform&float&_StartT&&&&&&&&float4&frag(v2f_img&i)&:COLOR&&&&&&&&{&&&&&&&&&&&&//溶解百分比=限制0-1(时间-开始时间/消失速度)&&&&&&&&float&DissolveFactor&=&saturate((_Time.y&-&_StartTime)&/&_DissolveSpeed);&&&&&&&&//噪音数值=tex(2d)的R值&&&&&&&&float&noiseValue&=&tex2D(_NoiseTex,i.uv).g;&&&&&&&&//如果R值小于溶解百分比&&&&&&&&if&(noiseValue&&=&DissolveFactor)&&&&&&&&{&&&&&&&&&&&&//小于某值溶解掉&&&&&&&&}&&&&&&&&//图片颜色&&&&&&&&float4&texColor&=&tex2D(_MainTex,i.uv);&&&&&&&&//边缘因子&&&&&&&&float&EdgeFactor&=&saturate((noiseValue&-&DissolveFactor)&/&(_EdgeWidth*DissolveFactor));&&&&&&&&float4&BlendColor&=&texColor&*&_EdgeC&&&&&&&&//混合颜色=图片颜色+边缘颜色&&&&&&&&//回到lerp函数(texyanse,混合颜色,1-边缘因子)&&&&&&&&return&lerp(texColor,BlendColor,1&-&EdgeFactor);&&&&&&&&}&&&&&&&&&&&&ENDCG&&&&&&&&}&&&&&&&&}&&&&&&&&&&&&FallBack&Off&&&&}
阅读(199)|
用微信&&“扫一扫”
将文章分享到朋友圈。
用易信&&“扫一扫”
将文章分享到朋友圈。
历史上的今天
loftPermalink:'',
id:'fks_',
blogTitle:'unity比较好理解的溶解shader',
blogAbstract:'Shader&\"Custom/rongjie\"&{&&&&&&&&Properties&&&&&&&&{&&&&&&&&&&&&_MainTex(\"Base&(RGB)\",&2D)&=&\"white\"&{}&&&&&&&&_NoiseTex(\"NoiseTex&(R)\",2D)&=&\"white\"{}&&&&&&&&_DissolveSpe',
blogTag:'shader,unity',
blogUrl:'blog/static/',
isPublished:1,
istop:false,
modifyTime:0,
publishTime:5,
permalink:'blog/static/',
commentCount:0,
mainCommentCount:0,
recommendCount:0,
bsrk:-100,
publisherId:0,
recomBlogHome:false,
currentRecomBlog:false,
attachmentsFileIds:[],
groupInfo:{},
friendstatus:'none',
followstatus:'unFollow',
pubSucc:'',
visitorProvince:'',
visitorCity:'',
visitorNewUser:false,
postAddInfo:{},
mset:'000',
remindgoodnightblog:false,
isBlackVisitor:false,
isShowYodaoAd:false,
hostIntro:'',
selfRecomBlogCount:'0',
lofter_single:''
{list a as x}
{if x.moveFrom=='wap'}
{elseif x.moveFrom=='iphone'}
{elseif x.moveFrom=='android'}
{elseif x.moveFrom=='mobile'}
${a.selfIntro|escape}{if great260}${suplement}{/if}
{list a as x}
推荐过这篇日志的人:
{list a as x}
{if !!b&&b.length>0}
他们还推荐了:
{list b as y}
转载记录:
{list d as x}
{list a as x}
{list a as x}
{list a as x}
{list a as x}
{if x_index>4}{break}{/if}
${fn2(x.publishTime,'yyyy-MM-dd HH:mm:ss')}
{list a as x}
{if !!(blogDetail.preBlogPermalink)}
{if !!(blogDetail.nextBlogPermalink)}
{list a as x}
{if defined('newslist')&&newslist.length>0}
{list newslist as x}
{if x_index>7}{break}{/if}
{list a as x}
{var first_option =}
{list x.voteDetailList as voteToOption}
{if voteToOption==1}
{if first_option==false},{/if}&&“${b[voteToOption_index]}”&&
{if (x.role!="-1") },“我是${c[x.role]}”&&{/if}
&&&&&&&&${fn1(x.voteTime)}
{if x.userName==''}{/if}
网易公司版权所有&&
{list x.l as y}
{if defined('wl')}
{list wl as x}{/list}}

我要回帖

更多关于 shader是什么 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信