Unity Shader 知识点总结(二)

  紧接着上一篇文章的shader入门知识的总结,本文主要总结shader中的纹理贴图、透明度混合、顶点动画、后期特效处理等操作。如果有什么地方有错,请指出更正,谢谢。本文的代码主要来自开源书:unity入门精要

一、Unity shader中的纹理

  1、简单纹理  

在unity shader中,纹理的主要作用是用来给模型贴上一个外表,这样得到的模型颜色就具有纹理的颜色混合。在常见的一些shader上,都会有一个_MainTex的选项,这就是我们常常用的主纹理贴图。对于纹理贴图,其对应的需要有纹理坐标。在应用阶段,unity就会将模型的纹理坐标进行整理,存储在语义为TEXCOORD的语义中,通常我们会在shader中定义应用阶段到顶点计算阶段的结构体:

struct a2v{
     vertex:POSITION;
     texcoord:TEXCOORD;
}

  其中的texcoord就是纹理坐标,注意这是模型上顶点的原始纹理坐标,还需要在顶点着色器上进行坐标转换操作:

TRANSFORM_TEX(i.texcoord,_MainTex)
  =  i.texcoord.xy * _MainTex.Scale + _MainTex.Offset

  如果模型的贴图没有缩放和偏移,则可以直接采用原生的顶点纹理坐标。

  基于顶点的纹理坐标,我们可以在模型的纹理贴图中去采用该顶点坐标对应的纹理来作为帧素,从而作为漫反射光照计算的一部分,所以在片元shader中,我们通常会进行这样一步操作:

float4 c = tex2D(_MainTex,i.texcoord)

  通过这步操作,我们可以获取到纹理的rgba四个通道的值,基于得到的值,我们可以用来作为漫反射的颜色计算一部分。

     2、渐变纹理

  如果我们希望在模型上实现一种光照的渐变的效果,渐变纹理是一个不错的选择,通过对渐变纹理采样,其得到的采用结果具有渐变的效应,从而得到的漫反射颜色也带有渐变的效果。在对渐变纹理的采用过程中,通常不是基于纹理坐标来进行的采用操作,而是基于当前顶点的Lambert或者半Lambert光照的结果作为采样的坐标对渐变纹理进行采样:

float halfLam = 0.5*lightResult + 0.5;
float3 c = tex2D(_RampTex,float2(halfLam,halfLam));

  3、遮罩纹理

  前面也提起过,对于高光反射,其计算的结果较为高亮,在某些应用场景中,我们希望对高光的结果进行一定的平滑处理,最常见的操作对高光的结果进行一个因子的相乘。但是如果我们对不同的部位有不同的因子处理,这时候,我们就可以采用遮罩纹理。将影响因子合并在遮罩纹理的某个通道(RGBA)中,通过对遮罩纹理的采样,取出对应的通道的值作为游戏因子,实现遮罩的效果。关键几步代码为:

fixed3 halfDir = normalize(lightDir + viewDir);
fixed  specularMask = tex2D(_SpecularMask,i.uv).r * _SpecularScale;
fixed3 specular = _LightColor0.rgb * _SpecularColor.rgb * pow(max(0,dot(normal,halfDir)),Gloss) * specularMask;

   4、法线纹理

  如果想要实现对模型的细节,常用的是法线纹理,用来在切线空间或者世界空间中实现对光照结果的调节。如果想在世界空间中对法线纹理进行操作,则关键的一点是计算出切线空间到世界空间的转换矩阵。通常这个转换矩阵是切线、副切线、法线按列组成的矩阵,最关键的几步shader为:

float3 worldPos = mul(_Object2World,v.vertex).xyz;
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
fixed3 worldBinormal = cross(worldNormal,worldTangent)*v.tangent.w;

o.TtoW0=float4(worldTangent.x,worldBinomal.x,worldNormal.x,worldPos.x);
o.TtoW1=float4(worldTangent.y,worldBinomal.y,worldNormal.y,worldPos.y);
o.TtoW2=float4(worldTangent.z,worldBinomal.z,worldNormal.z,worldPos.z);

  对于得到的转换空间矩阵,在片元着色器中,其主要用来实现对切线空间的法线纹理采样结果的转换,关键的几步shader为:

fixed3 normal = UnpackNormal(tex2D(_NormalMap,i.uv));
normal = nomalize(half3(dot(i.TtoW0.xyz,normal),dot(i.TtoW1.xyz,normal)),dot(i.TtoW2.xyz,normal)));

  UnpackNormal()函数是对法线纹理的采样结果的一个反映射操作,其对应的法线纹理需要设置为Normal map的格式,才能使用该函数。关于法线纹理的实现细节,可以参看这儿:法线纹理的实现细节

二、unity中的高级纹理

  除了常用的一些纹理,在某些时候,unity中需要使用一些高级纹理来实现更有效的纹理结果。主要可以归纳为立体纹理(CubeMap)、渲染纹理(MRT)以及程序纹理。程序纹理主要通过程序实现一些纹理的类似功能,应用场合比较特殊,这儿就不做过多的扩展,具体应用的时候可以查阅相关资料。

  1、立方体纹理

  立方体纹理是一种应用较多的环境映射纹理,通过将当前物体所在位置的前后左右上下共6个方向的环境映射为一个空间纹理贴图,通过反射、折射、菲涅尔反射等等方式来获取观察位置获取到的采样纹理,关键的一个函数是texCUBE(_CubeMap,dir)。

  立方体纹理有一个不大方便的是其只能反射凸面体,不能映射凹面体,也就是不能自身映射自身,所以在使用凹面体时候,需要注意这些细节。常见的反射和折射可以归纳为:

反射
o.worldRefl =reflect(-o.worldViewDir,o.worldNormal)
--
fixed3 reflection = texCBUE(_Cubemap,i.worldRefl).rgb

折射
o.worldRefr = refract(-o.worldViewDir,o.worldNormal,_RefractRatio);
--
fixed3 refraction = texCUBE(_Cubemap,i.worldRefr).rgb;
--菲涅尔反射
o.worldRefl =reflect(-o.worldViewDir,o.worldNormal)
--
fixed3 reflection = texCBUE(_Cubemap,i.worldRefl).rgb
fixed fresnel = _FresnelScale + (1 - _FresnelScale)*pow(1-dot(worldViewDir,worldNormal),5);
fixed3 color = lerp(diffuse,reflection,saturate(fresnel));

  2、渲染纹理和GrabPass

  随着GPU技术的发展,渲染技术也在同步提升,现代的GPU允许将渲染结果存储在多个渲染目标中,也就是多级渲染对象MRT(Multiply render target)。想要实现MRT技术,则渲染纹理是一种较为常用的方法,通过将渲染结果存储在渲染纹理中,而不是直接存储在缓存中,可以实现对渲染结果的进一步操作。

  渲染纹理的操作较为简单,通过在渲染相机中设置渲染纹理的对象,这样渲染的结果就会存储在制定的渲染纹理中。除了渲染纹理外,unity shader中还有一种获取当前渲染结果的指令:GrabPass 。使用GrabPass可以获取到当前的渲染结果,通过在后续的pass中使用这张渲染结果,我们可以获取进一步的shader表现效果。一般常用的是设置获取到的贴图的名字的方式:GrabPass{"TextureName"},这样,在后期的渲染pass中可以直接采用这个TextureName对应的渲染纹理来进行操作。

三、透明度操作

  透明度操作,主要是实现一些半透明的效果,当然也可以利用透明度操作中的剔除操作来剔除片元。透明度操作主要分为透明度剔除和透明度混合这两个基本的操作,透明度剔除,是设定一个判定的条件和阈值,在对片元进行操作的时候判定当前片元是否满足条件,如果满足则进行下一步的操作,如果不满足,则剔除该片元,可以用关键函数clip来实现。

  在某些应用场景中,物体的透明度是需要根据时间来进行变化的,这时候可以在片元着色器中依赖时间来对透明度进行剔除操作,把与时间相关的操作用来实时的更新物体的透明度,这时候可以得到一种透明度渐变的效果。这可以应用在子弹的拖尾等场合中,这儿就不做深入的代码解释。

  应用的较多的是透明度混合,这对于shader的渲染顺序有极高的要求。在unity shader中,基本的渲染顺序Queue是:

  BlackGround—>Geometry—>AlphaTest—>Transparent—>Overlay,分别对应于背景,不透明物体,透明度测试,透明度混合,最终渲染对象的渲染顺序。

  即使在同一个透明度混合的渲染顺序中,也需要注意两个半透明物体距离摄像机的渲染距离设置,否则会出现一些交叉重叠渲染的情况。这时候,其实是可以通过两个pass来实现规避两个半透明物体重叠交叉渲染的情况,在第一个pass中只需要进行一个深度测试,关键代码是 ColorMask 0,这样在第二个pass中进行的透明度混合就不会出现半透明物体交叉的现象。这样做会带来一些更高的性能损耗,在对性能要求较高的场合还是要慎用。

  基本的透明度操作:Blend oper1 oper2,通过设置不同的oper1 和 oper2,我们可以实现预期的透明度操作,具体的oper可以有哪些参数,这个可以查阅对应的文档即可,最常用的透明度混合操作为:Blend SrcAlpha OneMinusAlpha 。

四、顶点动画

  顶点动画,是一种用来实现模型动画的方式,在粒子特效等消耗较大的情况下,我们可以考虑采用顶点动画的方式来实现一些效果。顶点动画主要分为序列帧动画和基本的顶点动画。

  序列帧动画,是将一个基本的动画分隔为多个UV动画纹理,通过时间来控制纹理的采样,可以获得一种连贯的纹理动画的效果。在一些基本的爆炸效果中,我们就可以采用序列帧动画来实现基本的爆炸动画。在序列帧动画中,最关键的几步shader操作是:

float time = floor(_Time.y * _Speed);
float row = floor ( time/_HorizontalAmount);
float column = time - row * _HorizontalAmount;

half2 uv = i.uv + half2(column,-row);
uv.x /= _HorizontalAmount;
uv.y /= _VerticalAmount;

fixed4  c = tex2D(_MainTex,uv);

     其基本的原理,就是在片元shader计算当前顶点所在的行列对应的纹理贴图的坐标,然后进行纹理采样。

   顶点动画,则是一种在顶点shader中实现的动画,通过设置顶点的位置的偏移,实现一种基本的顶点动画。比如在参看书中实现的一种河流的顶点动画:

float4 offset;
offset.yzw = float3(0,0,0);
offset.x = sin(_Frequency * _Time.y + v.vertex.x *_InvWavelength+v.vertex.y *_InvWavelength + v.vertex.z *_InvWavelength)

o.pos = mul(UNITY_MATRIX_MVP,v.vertex + offset) + float2(0.0,_Time.y * _Speed);

  通过在x方向上实现正弦函数,可以实现模仿河流的流动的顶点动画的效果。

五、屏幕后期特效

  在一些游戏中,不只是基于模型上的材质和shader我们可以实现一些基本的渲染,在更多时候我们需要对整体的游戏场景进行一些特定的渲染。这个时候,如果逐个去改变所有模型的shader,工作量较大,同时带来的性能损耗也需要慎重考虑。这时候可以采用屏幕后期特效处理的方式,对整体的场景进行一些特定的渲染设置。

  屏幕后期特效,则需要采用两个基本的步骤,分别用c#和shader来实现对应的操作。c#主要用来挂载在主相机上,实现对指定相机的操作,shader则实现对应的特效处理。在c#中,最关键的是实现OnRenderImage()函数,结合Graphics.Blit()函数来实现对shader的属性设置和改变。在对应的shader中,我们可以实现指定的一些屏幕特效,比如对比度、透明度的调节,运动模糊,高亮(Bloom)操作,高斯模糊,基于深度纹理和法线纹理实现的一些特效等等。具体的一些实现流程不做过多的代码解释,可以参本文开头的开源项目中的代码。

  至此,基本的shader的一些知识归纳基本完成,后面我会进一步写一些现在用到的一些shader的例子,结合实际的应用来进一步的学习shader,希望大家都努力学习,共同进步~。~

  

时间: 2025-01-12 19:41:25

Unity Shader 知识点总结(二)的相关文章

Unity Shader入门教程(一)

参考文献:http://www.360doc.com/content/13/0923/15/12282510_316492286.shtml Unity Shader是着色器,将纹理.网格信息输入,得到材质的一段程序,具体是个什么东西,还需要亲自实践才知道.一个Unity大神推荐我:如果要学计算机图形编程(游戏编程的基础),可以先学习UnityShader,往后再学习OpenGL和DX.不说废话,依我的风格,都是直接看实例,笔者的教程偏向于傻瓜式的,应该适合入门. 前提:安装了Unity和VS,

【Unity Shader编程】之十五 屏幕高斯模糊(Gaussian Blur)后期特效的实现

本系列文章由@浅墨_毛星云 出品,转载请注明出处.   文章链接:http://blog.csdn.net/poem_qianmo/article/details/51871531 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 本文工程使用的Unity3D版本: 5.2.1  本篇文章将分析如何在Unity中基于Shader实现高斯模糊屏幕后期特效. 首先放出最终的实现效果.如下几幅图,是在Unity中使用本文所实现的Shader得到的高斯模糊屏幕

【Unity Shader】(九) ------ 高级纹理之渲染纹理及镜子与玻璃效果的实现

笔者使用的是 Unity 2018.2.0f2 + VS2017,建议读者使用与 Unity 2018 相近的版本,避免一些因为版本不一致而出现的问题. [Unity Shader](三) ------ 光照模型原理及漫反射和高光反射的实现 [Unity Shader](四) ------ 纹理之法线纹理.单张纹理及遮罩纹理的实现 [Unity Shader](五) ------ 透明效果之半透明效果的实现及原理 [Unity Shader](六) ------ 复杂的光照(上) [Unity

unity shader序列帧动画代码,顺便吐槽一下unity shader系统

http://www.cnblogs.com/hellohuan/archive/2014/01/10/3512784.html 一.看到UNITY论坛里有些人求unity shader序列帧动画,写shader我擅长啊,就顺势写了个CG的shader.代码很简单,就是变换UV采样序列帧贴图,美术配置行数列数以及变换速度. Shader "HELLOHUAN/Hello_Sequence" { Properties { _Color ("Main Color", C

【Unity Shader编程】之十四 边缘发光Shader(Rim Shader)的两种实现形态

本系列文章由@浅墨_毛星云 出品,转载请注明出处.   文章链接:http://blog.csdn.net/poem_qianmo/article/details/51764028 作者:毛星云(浅墨)    微博:http://weibo.com/u/1723155442 本文工程使用的Unity3D版本: 5.2.1  这篇文章主要讲解了如何在Unity3D中分别使用Surface Shader和Vertex & Fragment Shader来编写边缘发光Shader. 一.最终实现的效果

《Unity Shader 与 计算机图形学》第二章

提示:本篇将会非常长~ 本系列文章分为 硬件 编程入门 工程实践 上一篇 主要介绍了GPU的特征工作原理 以及渲染的底层流程 其实对于新架构而言还有所不同 Shader描述了如何渲染物体的信息,包括: Texture Setup.纹理设置 Material Property.材质设置 Render State.渲染状态 Blend Setup.混合设置 Pixel Shader.像素着色 Vertex Shader.定点着色 Render Target Setup 渲染目标设置 Shader并不

【我的书】Unity Shader的书 — 目录(2015.09.04更新)

写在前面 感谢所有点进来看的朋友.没错,我目前打算写一本关于Unity Shader的书. 出书的目的有下面几个: 总结我接触Unity Shader以来的历程,给其他人一个借鉴.我非常明白学Shader的艰难,在群里也见了很多人提出的问题.我觉得学习Shader还是一件有规律可循的事情,但问题是中文资料难觅,而大家又不愿意去看英文...这对我有什么好处呢?强迫我对知识进行梳理,对细节问题把握更清楚. 第二个原因你懂的. 关于本书的定位问题: 面向Unity Shader初学者,但要: 有一定的

【我的书】Unity Shader的书 — 目录(2016.1.29更新)

写在前面 感谢所有点进来看的朋友.没错,我目前打算写一本关于Unity Shader的书. 出书的目的有下面几个: 总结我接触Unity Shader以来的历程,给其他人一个借鉴.我非常明白学Shader的艰难,在群里也见了很多人提出的问题.我觉得学习Shader还是一件有规律可循的事情,但问题是中文资料难觅,而大家又不愿意去看英文...这对我有什么好处呢?强迫我对知识进行梳理,对细节问题把握更清楚. 第二个原因你懂的. 关于本书的定位问题: 面向Unity Shader初学者,但要: 有一定的

【我的书】Unity Shader的书 — 文件夹(2015.12.21更新)

写在前面 感谢全部点进来看的朋友.没错.我眼下打算写一本关于Unity Shader的书. 出书的目的有以下几个: 总结我接触Unity Shader以来的历程,给其它人一个借鉴.我非常明确学Shader的艰难,在群里也见了非常多人提出的问题. 我认为学习Shader还是一件有规律可循的事情,但问题是中文资料难觅,而大家又不愿意去看英文...这对我有什么优点呢?强迫我对知识进行梳理,对细节问题把握更清楚. 第二个原因你懂的. 关于本书的定位问题: 面向Unity Shader刚開始学习的人,但要