自学内容网 自学内容网

Unity Shader入门精要——笔记

第1篇 基础篇

第2章:渲染流水线

GPU流水线

顶点数据–> 几何阶段:(顶点着色器–> 曲面细分着色器–>几何着色器–>裁剪–>屏幕映射)–>光栅化阶段(三角形设置–>三角形遍历–>片元着色器–>逐片元操作)–>屏幕图像

顶点着色器:最基本的功能就是把模型的顶点坐标从模型空间转换到齐次裁剪坐标(通常串联成一个矩阵,即MVP矩阵)。

第4章:学习Shader所需的数学基础

数学基础

矢量减法 b-a :理解为b点相对于a点的向量,再坐标系中以a点为尾,b点为头

点积(内积):满足交换律结合律

  1. 可以用来求b在单位向量a上的投影长度
  2. 可以用来求两个向量的夹角(公式二),若结果小于0则为钝角,若结果大于0则为锐角
  3. 可以用来比较两个向量模的大小(性质三)

叉积(外积):只满足反交换律(axb=-bxa)

  1. |axb|为a和b向量构成的正方形面积,若a,b平行,则axb=0
  2. axb的结果代表两个向量的垂直向量,方向则由坐标系决定

矩阵乘法:不满足交换律,满足结合律

顶点的坐标空间变换过程

  • 第一步:将顶点坐标从模型空间变换到世界空间中,这个变换通常叫做模型变换。
  • 第二步:将顶点坐标从世界空间变换到观察空间中,这个变换通常叫做观察变换。
  • 第三步:将顶点坐标从观察空间变换到裁剪空间(齐次裁剪坐标系)中,这个变换通常叫做投影变换。
  • 第四步:将顶点坐标从裁剪空间变换到屏幕空间,这个变换通常叫做屏幕映射,齐次裁剪坐标系的顶点坐标除以w分量(齐次除法),得到归一化设备坐标(NDC),这个过程叫做归一化。再将归一化后的坐标通过屏幕映射获得屏幕上的#D像素坐标。

第2篇 初级篇

第6章:标准光照模型

环境光

c a m b i e n t = g a m b i e n t c_{ambient}=g_{ambient} cambient=gambient

自发光

c e m i s s i v e = m e m i s s i v e c_{emissive}=m_{emissive} cemissive=memissive

漫反射

兰伯特光照模型

c d i f f u s e = ( c l i g h t ⋅ m d i f f u s e ) m a x ( 0 , n ^ ⋅ I ) c_{diffuse}=(c_{light}\cdot m_{diffuse})max(0,\widehat{n}\cdot I) cdiffuse=(clightmdiffuse)max(0,n I)
其中 n n n是表面法线, I I I是指向光源的单位矢量, m d i f f u s e m_{diffuse} mdiffuse是材质的漫反射颜色, c l i g h t c_{light} clight是光源颜色。为防止法线和光源方向点乘的结果为负值,使用取最大值的函数来将其截取到0,这可以防止物体被从后面来的光源照亮。

半兰伯特光照模型

广义的半兰伯特光照模型公式如下:
c d i f f u s e = ( c l i g h t ⋅ m d i f f u s e ) ( α ( n ^ ⋅ I ) + β ) c_{diffuse}=(c_{light}\cdot m_{diffuse})(\alpha(\widehat{n}\cdot I)+\beta) cdiffuse=(clightmdiffuse)(α(n I)+β)
与原兰伯特模型相比,半兰伯特光照模型没有使用 m a x max max操作来防止 n n n I I I的点积为负值,而是对其结果进行了一个 α \alpha α倍的缩放再加上一个 β \beta β大小的偏移。绝大多数情况下, α \alpha α β \beta β的值均为0.5,即公式为:
c d i f f u s e = ( c l i g h t ⋅ m d i f f u s e ) ( 0.5 ( n ^ ⋅ I ) + 0.5 ) c_{diffuse}=(c_{light}\cdot m_{diffuse})(0.5(\widehat{n}\cdot I)+0.5) cdiffuse=(clightmdiffuse)(0.5(n I)+0.5)
通过这样的公式,可以把 n ^ ⋅ I \widehat{n}\cdot I n I的结果范围从[-1,1]映射到[0,1]范围内,也就是说,对于模型的背光面,在原兰伯特光照模型中点积结果将映射到同一个值,即0值处;而在半兰伯特模型中,背光面也可以又明暗变化,不同的点积结果会映射到不同的值上。

需要注意的是,半兰伯特是没有任何物理依据的,它仅仅是一个视觉加强技术。

高光反射

在硬件实现时,如果摄像机和光源距离模型足够远的话,Blinn模型会快于Phong模型,这是因为,此时可以认为 v ^ \widehat{v} v I ^ \widehat{I} I 都是定值,因此 h ^ \widehat{h} h 将是一个常量。但是,当 v ^ \widehat{v} v I ^ \widehat{I} I 不是定值时,Phong模型可能反而更快一些。需要注意的是,这两种光照模型都是经验模型,也就是说,不应该认为Blinn模型是对“正确的”Phong模型的近似。实际上,在一些情况下,Blinn模型更符合实验结果。

Phong光照模型

计算反射方向:
r = 2 ( n ^ ⋅ I ) n ^ − I r=2(\widehat{n} \cdot I)\widehat{n}-I r=2(n I)n I
通过Phong模型来计算高光反射的部分:
c s p e c u l a r = ( c l i g h t ⋅ m s p e c u l a r ) m a x ( 0 , v ^ ⋅ r ) m g l o s s c_{specular}=(c_{light} \cdot m_{specular})max(0,\widehat{v} \cdot r)^{m_{gloss}} cspecular=(clightmspecular)max(0,v r)mgloss
其中 n n n是表面法线, v ^ \widehat{v} v 是视角方向, I I I是光源方向, r r r是反射方向, m g l o s s m_{gloss} mgloss是材质的光泽度,也被称为反光度。它用于控制高光区域的“亮点”有多宽, m g l o s s m_{gloss} mgloss越大,亮点就越小。 m s p e c u l a r m_{specular} mspecular是材质的高光反射颜色,它用于控制该材质对于高光反射的强度和颜色。 c l i g h t c_{light} clight则是光源的颜色和强度。同样,这里也需要防止 v ^ ⋅ r ^ \widehat{v} \cdot \widehat{r} v r 的结果为负数。

Blinn-Phong光照模型

它的基本思想是,避免计算反射方向 r ^ \widehat{r} r 。为此,Blinn模型引入了一个一个新的矢量 h ^ \widehat{h} h ,他是通过对 v ^ \widehat{v} v I ^ \widehat{I} I 的取平均后再归一化得到的。即:
h ^ = v ^ + I ^ ∣ v ^ + I ^ ∣ \widehat{h} = \frac{\widehat{v}+\widehat{I}}{\left\vert \widehat{v}+\widehat{I} \right\vert} h = v +I v +I
然后,使用 n ^ \widehat{n} n h ^ \widehat{h} h 之间的夹角进行计算,而非 v ^ \widehat{v} v r ^ \widehat{r} r 之间的夹角。
总结一下,Bilinn模型的公式如下:
c s p e c u l a r = ( c l i g h t ⋅ m s p e c u l a r ) m a x ( 0 , n ^ ⋅ h ^ ) m g l o s s c_{specular}=(c_{light} \cdot m_{specular})max(0,\widehat{n} \cdot \widehat{h})^{m_{gloss}} cspecular=(clightmspecular)max(0,n h )mgloss

Shader 常用函数

//取得小数部分  
frac(x);  
  
//将x限制在[0,1]范围中  
saturate(x)  
  
//返回一个布尔值,如果输入的向量的任何分量为非零,返回true,否则返回false  
any(x)  

//环境光颜色
UNITY_LIGHTMODEL_AMBIENT

//世界空间下的光源方向
_WorldSpaceLightPos0

//光源的颜色与强度信息(注意,想要的到正确的值需要定义合适的LightMode标签)
_LightColor0

//用于高光反射中计算反射方向reflectDir,CG的reflect函数的入射方向要求是由光源指向交点处的,因此需要对worldLightDir取反后再传给reflect函数
reflect(-worldLightDir, worldNormal));

第7章:基础纹理

凹凸映射

  1. 高度映射:使用一张高度纹理来模拟表面位移,得到一个修改后的法线值。
  2. 法线映射:使用一张法线纹理来直接存储表面法线。
高度纹理

高度图存储的是强度值,用于表示模型表面局部的海拔高度,颜色越浅表明该位置的表面越向外凸起,而颜色越深表明该位置越向里凹。

法线纹理

法线纹理存储的就是表面的法线方向,由于法线方向的分量范围在[-1,1],而像素的分量范围为[0,1],因此需要做一个映射:
p i x e l = n o r m a l + 1 2 pixel = \frac{normal +1}{2} pixel=2normal+1
在Shader中对法线纹理进行纹理采样后,还需要对结果进行一次反映射的过程,以得到原先的法线方向。反映射的过程实际就是使用上面映射函数的逆函数:
n o r m a l = p i x e l ∗ 2 − 1 normal=pixel*2-1 normal=pixel21

Shader 常用函数

//_MainTex_ST可以得到该纹理的缩放和平移值,_MainTex_ST.xy存储的是缩放值,_MainTex_ST.zw存储的是偏移值;首先使用缩放属性对纹理坐标进行缩放,然后再使用偏移属性对结果进行偏移。
o.uv=v.texcoord.xy*_MainTex_ST.xy+_MainTex_ST.zw;
//Unity提供了一个内置宏TRANSFORM_TEX来计算上述过程。
o.uv=TRANSFORM_TEX(v.texcoord,_MainTex);

//使用内置的UnpackNormal函数对法线进行采样和解码(需要把法线纹理的格式标识成Normal map)
fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw));  
bump.xy *= _BumpScale;  
bump.z = sqrt(1.0 - saturate(dot(bump.xy, bump.xy)));

第8章:透明效果

基础概念

透明度测试:它采用一种“霸道极端”的机制,只要一个片元的透明度不满足条件(通常是小于某个阀值),那么它对应的片元就会被舍弃。被舍弃的片元将不会再进行任何处理也不会对颜色缓冲产生任何影响:否则,就会按照普通的不透明物体的处理方式来处理它,即进行深度测试、深度写入等。也就是说,透明度测试是不需要关闭深度写入的,它和其他不透明物体最大的不同就是它会根据透明度来舍弃一些片元。虽然简单,但是它产生的效果也很极端,要么完全透明,即看不到,要么完全不透明,就像不透明物体那样。
透明度混合:这种方法可以得到真正的半透明效果。它会使用当前片元的透明度作为混合因子,与已经存储在颜色缓冲中的颜色值进行混合,得到新的颜色。但是,透明度混合需要关闭深度写入(我们下面会讲为什么需要关闭),这使得我们要非常小心物体的渲染顺序。需要注意的是,透明度混合只关闭了深度写入,但没有关闭深度测试。这意味着,当使用透明度混合渲染一个片元时,还是会比较它的深度值与当前深度缓冲中的深度值,如果它的深度值距离摄像机更远,那么就不会再进行混合操作。这一点决定了,当一个不透明物体出现在一个透明物体的前面,而我们先渲染了不透明物体,它仍然可以正常地遮挡住透明物体。也就是说,对于透明度混合来说,深度缓冲是只读的。

混合命令

//混合因子命令
Blend SrcFactor DstFactor
Blend SrcFactor DstFactor,SrcFactorA DstFactorA

进行加法时使用的混合公式:

O r g b = S r c F a c t o r × S r g b + D s t F a c t o r × D r g b O_{rgb}=SrcFactor \times S_{rgb}+DstFactor \times D_{rgb} Orgb=SrcFactor×Srgb+DstFactor×Drgb
O a = S r c F a c t o r A × S a + D s t F a c t o r A × D a O_{a}=SrcFactorA \times S_{a}+DstFactorA \times D_{a} Oa=SrcFactorA×Sa+DstFactorA×Da

混合操作

BlendOp BlendOperation

//如
//将混合后的源颜色和目的颜色相加。默认的混合操作。
BlendOp Add
//将混合后的源颜色减去目的颜色。
BlendOp Sub
//将混合后的目的颜色减去源颜色。
BlendOp RevSub
//使用源颜色和目的颜色中较小的值,是逐分量比较的。
BlendOp Min
//使用源颜色和目的颜色中较大的值,是逐分量比较的。
BlendOp Max

常见的混合类型

//正常(Wormal),即透明度混合
Blend SrcAlpha OneMinusSrcAlpha

//柔和相加(Soft Additive)
Blend OneMinusDstColor One

//正片叠底(Multiply),即相乘
Blend DstColor Zero

//两倍相柔(2x Multiply)
Blend DstColor SrcColor

//变暗(Darken)
BlendOp Min
Blend One One

//变亮(Lighten)
BlendOp Max
Blend One One

//滤色(Screen)
Blend OneMinusDstColor One
//等同于
Blend One OneMinusSrcColor

//线性减淡(Linear Dodge)
Blend One One

Shader 常用函数

//透明度测试
SubShader{
Tag
{
//在AlphaTest渲染队列
"Queue"="AlphaTest"
//意味着这个SubShader不会受到投影器(Projectors)的影响
    "IgnoreProjector"="True"
    //加入提前定义的组中
    "RenderType"="TransparentCutout"
}
Pass{
···
}
}

//透明度混合
SubShader{
Tag
{
//在Transparent渲染队列
"Queue"="Transparent"
//意味着这个SubShader不会受到投影器(Projectors)的影响
    "IgnoreProjector"="True"
    //加入提前定义的组中
    "RenderType"="Transparent"
}
Pass{
//关闭深度写入
ZWrite Off
//设置Blend的混合模式
Blend SrcAlpha OneMinusSrcAlpha
···
}
}

函数: void clip(foat4 x); void clip(float3 x); void clip(float2 x); void clip(floatl x); void clip(float x);
参数:裁剪时使用的标量或矢量条件。
描述:如果给定参数的任何一个分量是负数,就会舍弃当前像素的输出颜色。它等同于下面的代码:
void clip(float4 x)
{
if( any( x < 0 ) )
discard;
}

//渲染命令,用于设置颜色通道的写掩码(write mask)
//当ColorMask设为0时,意味着该Pass不写入任何颜色通道,即不会输出任何颜色。
ColorMask RGB | A | 0 | 其他任何R、G、B、A的组合

第3篇 中级篇

第9章:更复杂的光照

Shader 常用函数

//阴影计算三剑客
#include "AutoLight.cginc"
//声明一个用于对阴影纹理采样的坐标,参数是下一个可用的插值寄存器的索引值
SHADOW_COORDS(2)
//在顶点着色器种计算声明的阴影纹理坐标
TRANSFER_SHADOW(o)
//在片元着色器种计算阴影值
SHADOW_ATTENUATION(i)

//Unity内置的用于计算光照衰减和阴影的宏,它会将光照衰减和阴影值相乘后的结果存储到第一个参数种;第二个参数是结构体v2f;第三个参数是世界空间的坐标,它会用于计算光源空间下的坐标,再对光照衰减纹理采样来得到光照衰减。
UNITY_LIGHT_ATTENUATION(atten, i, i.worldPos);

第10章:高级纹理

Shader 常用函数

//Compute th reflect dir in world space  
o.worldRefl = reflect(-o.worldViewDir, o.worldNormal);

//Compute the refract dir in world space  
o.worldRefr = refract(-normalize(o.worldViewDir), normalize(o.worldNormal), _RefractRatio);

第11章:让画面动起来

Shader 常用函数

//ShadowCaster Pass
Tags {"LightMode"="ShadowCaster"}

#pragma multi_compile_shadowcaster

struct v2f  
{
//在v2f结构体中定义阴影投射需要定义的变量
    V2F_SHADOW_CASTER;  
};

//顶点着色器中
TRANSFER_SHADOW_CASTER_NORMALOFFSET(o);

//片元着色器中,把结果输出到深度图和阴影映射纹理中
SHADOW_CASTER_FRAGMENT(i);

第4篇 高级篇

第12章:屏幕后处理效果

Shader 常用函数



原文地址:https://blog.csdn.net/qqagsd155451/article/details/140640400

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!