Shader入门精要笔记3-Shader基础-shader warming

2023-08-18 08:36:07

 万丈高楼平地起,一砖一瓦皆根基

前言

最近笔者在学习冯老师的《Unity Shader入门精要》这本书,为了方便快速掌握和巩固书中的知识点,笔者做了一些思维导图和笔记,现在分享给大家。

笔者建议大家,最好把冯老师的书籍先自己看一遍,本系列只是笔者的笔记,不会如原书一样面面俱到,而是将书中重点内容进行浓缩整理,在一定基础上进行了一点小拓展。

笔者是第一次写文章,如有有任何问题,麻烦请在评论里指出。

1 基本结构

整个Shader分为4个基础部分:

1、Shader Head Shader头

2、Properties 属性

3、SubShader 子Shader

4、FallBack 后路

1.1 Shader头

Shader "Shader-Learning/Chapter1/FirstShader"

主要作用就是声明该Shader在Shader下拉菜单的位置和名字。

最后一个斜杠"/"是Shader名字,前面的斜杠“/”是子菜单。

1.2 Properties

Properties { _MyTexture ("Texture", 2D) = "white" {} _MyColor ("Color", Color) = (1, 1, 1, 1) _MyFloat ("Float", Float) = 1.0 _MyRange ("Range", Range(0, 1.0)) = 0.5 }

主要作用就是声明属性,这个属性在材质面板上可见。

注意:

1、声明属性时,不需要以分号结尾 。

2、在SubShader中的Pass使用前,还需要再次声明这些属性。

1.2.1 Properties中的属性

在Properties中声明格式:

_Name ("display name", PropertyType) = DefaultValue

_Name:在Shader中这个属性的名字

“display name”:展示在材质面板中显示的label

PropertyType:指属性的类型 DefaultValue:即默认值属性类型默认值的定义语法例子Intnumber_Int ("Int", Int) = 2Floatnumber_Float ("Float", Float) = 1.5Range(min, max)number_Range ("Range", Range(0,.0 1.0)) = 0.5Color(number,number,number,number)_Color ("Color", Color) = (1, 1, 1, 1)Vector(number,number,number,number)_Vector ("Vector", Vector) = (2, 3, 6, 1)2D"defaulttexture" {}_2D ("2D", 2D) = "white" {}Cube"defaulttexture" {}_Cube ("Cube", Cube) = "white" {}3D"defaulttexture" {}_3D ("3D", 3D) = "white" {}

其中,对于2D、Cube、3D这3种纹理类型,双引号中带的是内置的纹理名称,有“white”,“black”,“gray”和“bump”;花括号中是为了指定纹理属性,现在已经弃用,如果需要,可以自己在顶点着色器中编写计算相应纹理坐标的代码。

1.2.2 Pass中的属性

在Pass中使用前,需要声明,声明的格式就是

CGType _Name;

CGType:是CG变量的类型

_Name:属性的名字ShaderLab属性类型CG变量类型例子Color,Vectorfloat4,half4,fixed4Float4 _MyColor;Range,Floatfloat,half,fixedFloat _MyRange2Dsampler2DSampler2D _My2DCubesamplerCubeSamplerCube _MyCube3Dsampler3DSampler3D _My3D

1.2.3 float、half、fixed的精度

CG/HLSL中3种精度的数值类型

类型描述float高精度类型,32位,通常用于世界坐标下的位置,纹理UV,或涉及复杂函数的标量计算,如三角函数、幂运算等。half中精度类型,16位,数值范围为[-60000,+60000],通常用于本地坐标下的位置、方向向量、HDR颜色等。fixed低精度类型,11位,数值范围为[-2,+2],通常用于常规的颜色与贴图,以及低精度间的一些运算变量等。

PC平台下统统会被当做float处理。只有在移动端才会有差别。

1.2.4 sampler2D、sampler3D与samplerCube的精度问题

纹理,默认情况下在移动平台纹理会被自动转换成低精度的纹理类型,如果你需要中精度的或者高精度的需要用以下方式来声明:

sampler2D_half(中精度2D纹理) sampler2D_float(高精度2D纹理)

sampler3D_half(中精度3D纹理) sampler3D_float(高精度3D纹理)

samplerCUBE_halft(中精度立方体纹理) samplerCUBE_float(高精度立方体纹理)

1.3 SubShader

每一个SubShader都可以完整的执行渲染

为了适配不同的显卡,一个Shader中可以声明多个SubShader,但是只会有一个SubShader运行

SubShader { // optional [Tags] // optional [RenderSetup] Pass {... } }

SubShader中定义了一系列Pass以及可选的标签和状态,SubShader中的设置会应用到所有的Pass中,但是Pass中可以继续设定,以覆盖SubShader中的设定。

1.3.1 Tags标签

这是一个键值对,键和值都是字符串类型,用来告诉Unity引擎应该如何渲染对象。

SubShader的标签类型

标签类型说明例子Queue控制渲染顺序,指定该物体属于哪一个渲染队列,通过这种方式可以保证所有的透明物体可以在所有不透明物体后面被渲染。

我们也可以自定义使用的渲染队列来控制物体的渲染顺序Tags {“Queue” = “Transparent“}RenderType对着色器进行分类,例如这是一个不透明的着色器,或是一个透明的着色器。

这可以被用于着色器替换(Shader Replacement)功能。Tags {“RenderType” = “Opaque“}DisableBatching一些SubShader在使用Unity的批处理功能会出现问题,例如使用了模型空间下的坐标进行顶点动画。这时可以通过该标签来直接指明是否对该SubShader使用批处理Tags {“DisableBatching" = "True"}ForceNoShadowCasting控制使用该SubShader的物体是否会投射阴影Tags {“DisableBatching" = "True"}IgnoreProjector如果该标签值为“True”,那么使用该SubShader的物体将不会收到Projector的影响。通常用于半透明物体Tags {“DisableBatching" = "True"}CanUseSpriteAtlas当该SubShader是用于Sprite时,将该标签设为“False”Tags {"CanUseSpriteAtlas" = "False"}PreViewType指明材质面板将如何预览该材质。默认情况下,材质将显示为一个球,我们可以通过把该标签的值设为"Plane""SkyBox"来改变预览类型Tags {"CanUseSpriteAtlas" = "False"}

1.3.2 RenderSetup状态设置

ShaderLab提供了一系列渲染状态的设置指令,这些指令用来设置显卡的各种状态。

常见的渲染状态设置选项

状态名称设置命令解释CullCull Back | Front | Off设置剔除模式,剔除背面/正面/关闭剔除ZTestZTest Less Greater | LEqual | GEqual | Equal | NotEqual | Always设置深度测试时使用的函数ZWriteZWrite On | Off开启/关闭深度写入BlendBlend SrcFactor DstFactor开启并设置混合模式

1.4 Pass

渲染的主要工具。

如果SubShader中有多个Pass,那么Pass之间是相互独立的。

例如需要渲染同意材质的A、B物体的两个Pass。那么渲染顺序不是先执行A的两个Pass再执行B的两个Pass,而是先执行A、B的一个Pass再执行A、B的另外一个Pass。

因为这是要较少渲染管线状态的切换次数!

Pass { // optinal [Name] // optinal [Tags] // optinal [RenderType] CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCg.cginc" struct a2v {... } struct v2f {... } fixed4 _BaseColor; v2f vert (a2v v) {... } fixed4 frag(v2f i) : SV_Target {... } ENDCG }

[Name]:可以在Pass中定义Pass的名称,通过这个名称我们可以进行其他操作

形式:Name “MyPassName”

UsePass:使用该命令复用其他Unity Shader中的Pass,名字必须全是大写字母

GrabPass:该Pass复杂抓取屏幕并将结果存储在一张纹理中,以用于后续的Pass处理

1.4.1 [Tags]:Pass中的标签

标签类型说明例子LightMode定义该Pass在Unity的渲染流水线中的角色Tags {“LightMode” = “ForwardBase”}RequireOptions定义该Pass在Unity的渲染流水线中的角色Tags {“RequireOptions” = “SoftVegetation”}

顶点/片元着色器编译指令

定义顶点/片元着色器函数的名称,这是必要的。

// 将顶点函数的名字设定为vert // 将片元函数的名字设定为frag #pragma vertex vert #pragma fragment frag

1.5 两个结构体

一个是用于声明传递给顶点函数的数据,书中命名为a2v

另外一个是用于顶点函数和片元函数的数据传递,书中命名为v2f
struct structName { Type Name : 语义; Type Name : 语义; Type Name : 语义; }

从应用阶段传递模型数据给顶点着色器时Unity支持的常用语义

语义描述POSITION模型空间中的顶点位置,通常是Float4类型NORMAL顶点法线,通常是Float3类型TANGENT顶点切线,通常是Float4类型TEXCOORDn,如TEXCOORD0~TEXCOORD1该顶点的纹理坐标,TEXCOORD0表示第一组纹理坐标,依次类推,通常是Float2或Float4类型COLOR顶点颜色,通常是Fixed4或Float4类型

从顶点着色器传递数据给片元着色器时Unity支持的常用语义

语义描述SV_POSITION裁剪空间中的顶点坐标,结构体中必须包含一个用该语义修饰的变量。COLOR0通常用于输出第一组顶点颜色,不是必须COLOR1通常用于输出第二组顶点颜色,不是必须TEXCOORD0~TEXCOORD7通常用于输出纹理坐标,不是必须

1.6 顶点函数和片元函数

这两个是主要实现的函数。

v2f vert(a2v v) { v2f o; // 进行裁剪变换 o.pos = UnityObjectToClipPos(v.vertex); // 计算后面阶段用到的数据 ... return o; } fixed4 frag(v2f i) : SV_Target { // 采样纹理和计算光照 ... // 返回像素点的颜色 return color; }

片元着色器输出时Unity支持的常用语义

语义描述SV_Target输出值将会存储到渲染目标中

1.7 FallBack

如果上面声明的SubShader都不管用,将使用这个声明的Shader

FallBack "Diffuse"

2 实现

Shader "Shader-Learning/Chapter3/FirstShader" { // 声明属性 Properties { // 将一个Float值声明为一个下拉列表,下拉列表有3个字选项,他们的值分别为0,1,2 [Enum(Normal,0,Tangent,1,Binomial,2)]_BasicSelection("Basic selection", Float) = 0 // 声明一个基础颜色 _BaseColor ("Base Color", Color) = (1, 1, 1, 1) } // 子着色器 SubShader { // 标签,用于设定如何渲染对象 Tags{"LightMode" = "ForwardBase"} // 渲染的主要实现 Pass { // CG片段 CGPROGRAM // 定义两个着色器函数的名字 // 将顶点函数的名字命名为vert #pragma vertex vert // 将片元函数的名字命名为frag #pragma fragment frag // 包含的文件 #include "UnityCG.cginc" // 声明传递给顶点函数的结构体数据 struct a2v { // 获取模型的顶点坐标 float4 vertex : POSITION; // 获取模型顶点的法线坐标 float3 normal : NORMAL; // 获取模型顶点的切线坐标 float4 tangent : TANGENT; }; // 声明用于顶点函数和片元函数数据传递的结构体数据 struct v2f { // 用来储存已经变换到裁剪空间中的顶点坐标 float4 pos : SV_POSITION; // 用来存储顶点的颜色 fixed3 color : COLOR0; }; // 声明属性变量 float _BasicSelection; fixed4 _BaseColor; // 顶点函数 v2f vert (a2v v) { v2f o; // 将顶点坐标从模型空间变换到世界空间下 o.pos = UnityObjectToClipPos(v.vertex); // 计算颜色 // 在实际项目中,最好不使用条件判断,具体的原因之后的文章中会提到 if (_BasicSelection == 0) // 通过获取的模型顶点的法线坐标计算顶点的颜色 o.color = (v.normal * 0.5 + fixed3(0.5, 0.5, 0.5)) * _BaseColor; else if (_BasicSelection == 1) // 通过获取的模型顶点的切线坐标计算顶点的颜色 o.color = (v.tangent.xyz * 0.5 + fixed3(0.5, 0.5, 0.5)) * _BaseColor; else{ // 通过模型顶点的法线坐标和切线坐标计算副切线,这个概念之后会细说 // 通过副切线计算模型顶点的颜色 fixed3 binormal = cross(v.normal, v.tangent.xyz) * v.tangent.w; o.color = (binormal * 0.5 + fixed3(0.5, 0.5, 0.5)) * _BaseColor; } return o; } // 片元函数 fixed4 frag (v2f i) : SV_Target { // 将像素的颜色返回 return fixed4(i.color, 1.0); } ENDCG } } // 如果上面的所有SubShader都不管用,将使用内置的Diffuse.Shader FallBack "Diffuse" }

(实现出来是圆滑效果,录制的软件出了点小毛病)

给大家弄个好玩的!具体效果如何,就靠大家自己去实现了哈!

((normaShader "Shader-Learning/Chapter3/Beautiful" { SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float3 normal :NORMAL; }; struct v2f { float3 normal : TEXCOORD0; float4 vertex : SV_POSITION; }; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.normal = UnityObjectToWorldNormal(v.normal); return o; } fixed4 frag (v2f i) : SV_Target { float3 col = 0.5 + 0.5 * cos(_Time.y + i.normal + float3(0, 2, 4)); return float4(col, 1.0); } ENDCG } } }

3 Shader整洁之道

3.1 避免不必要的计算

如果毫无节制的在Shader(尤其是片元着色器)中进行大量的计算,Unity会报错,因为过多得计算需要临时寄存器数目或指令数目超过了当前可支持的数目。

3.2 慎用分支和循环语句

大家可以去CSDN看一下我另外一篇文章。

自产自销(滑稽!)

3.3 不要除以0

解决方法:对于那些除数可能为0的情况,强制截取到非0范围

4 后话

这一章概念性知识比较多,但是没关系,我已经整理成思维导图。方便大家记忆和翻阅。

最后,最重要的话:

欢迎大家提出自己的问题,就算是已经解决的问题也没关系,只要是好问题就可以分享出来!有个大佬说得好,学习就要打破砂锅问到底,学得明白透彻了,才是自己的东西!但是有时候,我们在学习的时候往往会忽略掉一些问题。所以希望大家能多多分享一下自己的问题哦!


以上就是关于《Shader入门精要笔记3-Shader基础-shader warming》的全部内容,本文网址:https://www.7ca.cn/baike/68678.shtml,如对您有帮助可以分享给好友,谢谢。
标签:
声明

排行榜