Shader学习笔记
| |||
| 第二章:渲染流水线 | ||
| shader需要牵扯到整个渲染流程 shader更多的是面向GPU的工作方式
| ||
|
应用阶段:准备场景数据、粗粒度剔除(不可见的物体剔除)、每个模型的渲染状态。
渲染图元:输出渲染所需的几何信息
几何阶段:处理所有需要绘制的几何相关的事情(例如:需要绘制的图元是什么,怎样绘制他
们)。
负责和每个渲染图元打交道,进行逐顶点扫描、逐多边形的操作。
把顶点坐标变换到屏幕空间中,再交给光栅器进行处理
光栅化阶:决定每个渲染图元中的哪些像素应该被绘制在屏幕上。
GPU的渲染流水线实现。颜色表示了不同阶段的可配置性或可编程性:绿色表示该流水线阶段是完全可编程控制的,黄色表示该流水线阶段可以配置但不是可编程的,蓝色表示该流水线阶段是由GPU固定实现的,开发者没有任何控制权。实线表示该shader必须由开发者编程实现,虚线表示该Shader是可选的
来自 <http://candycat1992.github.io/unity_shaders_book/unity_shaders_book_images.html>
顶点数据 | 由应用阶段加载到显存中,再由Draw Call 指定 |
| |||
几何阶段 |
|
| 此阶段输出的信息是:屏幕坐标系的顶点位置与他相关的额外信息 (深度值(z坐标),法线方向,视角方向) |
|
|
顶点着色器 | 可编程 | 坐标变换、逐顶点光照 | 顶点着色器的处理单位是顶点,每每输入进来的一个顶点都会调用一次顶点着色器 顶点本身不能销毁、创建任何一个顶点,而且无法获得顶点与顶点之间的关系
| 相互独立性使得GPU可以利用本身的特性并行化处理每一个顶点(耗时短) | 图2.7 图2.8
|
曲面细分着色器 | 可选 | 细分图元 |
|
|
|
几何着色器 | 可选 | 执行逐图元的着色操作、生产更多的图元 |
|
|
|
剪裁 | 可配置 | 剔除不在摄像机视野内的顶点剪裁掉,并剔除某些三角图元的面片 | 一个图元和摄像机的关系:完全在视野内,部分在视野内,完全在视野外 | 无法通过编程来控制剪裁的过程,而是硬件上固定的操作,可以自定义一个剪裁操作来对这一步进行配置 | 图2.9 |
屏幕映射 | 无权限 | 每个图元的坐标转换到屏幕坐标系中 | 这一步输入的坐标为三维坐标(范围为单位立方体内) 把每个图元的X和Y坐标转换到屏幕坐标系(是一个二维坐标,与显示画面的分辨率有关)
| 屏幕映射得到的屏幕坐标决定了这个顶点对应屏幕上那个像素以及距离这个像素有多远 | 图2.10 |
|
|
|
|
|
|
光栅化阶段 |
|
| 目标:每个图元覆盖的像素,计算这些像素的颜色 |
|
|
三角形设置 |
| 固定函数 | 计算光栅化一个三角形的信息 计算三角网格表示数据的过程叫三角形设置 |
|
|
三角形遍历 (扫描变换) |
| 固定函数 | 检查每个像素是否被一个三角网格所覆盖,如果被覆盖就会生成片元 找到哪些像素被三角网格覆盖的过程就是三角形遍历 | 根据上一阶段的结果来判断一个三角线网格覆盖的哪些像素,并使用三角网格的三个顶点信息对整个覆盖区域的像素进行插值 得到一个片元序列. 片元序列不是真正意义上的像素,而是包含了很多状态的集合,这些状态用于计算每个像素的最终颜色(包括但不限于:屏幕坐标、深度信息、几何阶段输出的顶点信息-->法线、纹理坐标) | 图2.12 |
片元着色器 (direct X 称为像素着色器) | 可编程 | 实现逐片元的着色操作 | 负责存储由上一个阶段操作的一些列数据(光栅化阶段不会影响屏幕上每个像素的颜色值,而是产生一些列的数据信息,用来表述一个三角网格是怎样覆盖每个像素的) | 可以完成很多渲染技术:纹理采样 局限性:仅可影响单元片元,不可以将自己的任何结果直接发送给其他片元 可以访问导数信息 | 图2.13 |
OpenGL逐片元操作 Direct X输出合并阶段 | 高度可配置 | 修改颜色、深度缓冲、进行混合 | 决定每个片元的可见性(深度测试、模板测试……) 一个片元通过所有测试后,需要把这个片元的颜色和已经存储在颜色缓冲区的颜色进行合并(混合) OpenGL与DirectX实现细节不同 |
| 图2.14 图2.15 图2.16 |
| ||||
|
图2.7 GPU在每个输入的网格顶点上都会调用顶点着色器。顶点着色器必须进行顶点的坐标变换,需要时还可以计算和输出顶点的颜色。例如,我们可能需要进行逐顶点的光照
来自 <http://candycat1992.github.io/unity_shaders_book/unity_shaders_book_images.html> | |||
| 坐标变换:对顶点的坐标(位置)进行变换,顶点着色器可以在这一步中改变顶点的位置,在顶点动画中非常有用。
通过改变顶点位置来模拟水面、布料等。
在顶点着色器中改变顶点位置时,最重要的一步是把顶点坐标从模型空间转换到其次剪裁空间
归一化的设备坐标(Normalized Device Coordinates)
o.pos=mul(UNITY_MVP,V.position);
OpenGL,Unity 使用的是NDC z∈[-1,1] DirectX z∈[0,1]
顶点着色器有很多种输出方式,最常见的是光栅化后交给片元着色器处理。现在的ShaderModel中,他还可以把数据发送给曲面细分着色器或者几何着色器
| |||
| ||||
| 剪裁:不在摄像机视野范围内的物体不需要被处理
| |||
|
| |||||||
| |||||||
| 模板测试 模板缓冲:GPU首先读取(使用读取掩码)模板缓冲去中该片元位置的模板值,然后将该值和读取到的参考值进行比较,比较函数是由开发者指定(例如,小于舍弃该单元,大于保留) 模板测试通常限制渲染区域 高级用法:渲染阴影、轮廓渲染
深度测试 GPU会把该片元的深度值与已存在于缓冲区中的深度值进行比较,由开发者指定。通常这个比较函数是小于等于的关系。当前片源的深度值大于等于当前深度缓冲区的值会被舍弃。(离摄像机最近的物体被显示) 如果一个片元:没有通过深度测试,他就没有权利更改深度缓冲区的值。 通过深度测试,开发者可以指定是否要用这个片元的深度值覆盖掉原有的深度 值,这是通过开启关闭深度写入做到的
| ||||||
| |||||||
| |||||||
| |||||||
| |||||||
|
图2.15 模板测试和深度测试的简化流程图
合并
不透明物体关闭混合操作-------片元着色器计算得到的颜色值直接覆盖掉颜色缓冲区的像素值
半透明物体-----------混合操作实现
图2.16 混合操作的简化流程图
混合函数与透明通道相关
混合模式决定了该图层和下层图层的混合结果。
Unity渲染流水线中,深度测试在片元着色器之前 。 Early-Z技术
现在的GPU会判断片元着色器中的操作是否和提前测试发生冲突,如有冲突,会禁用提前测试。这样会造成性能上的下降
,因为有更多的片元需要被处理。(透明度测试会导致性能下降)
保证图像是连续的:为了避免我们看到正在进行光栅化的图元,GPU就会交换后置缓冲区和前置缓冲中的内容。前置缓冲区是之前显示在屏幕上的图像。
3.2Unity Shader 的基础:ShaderLab
计算机科学中的任何问题都可以通过增加一层抽象来解决----------大卫·惠特
ShaderLab is a friend you can afford
3.3Unity Shader的结构
Shader "Custom/MyfirstShader"{ } 给shader取名
Properties{
//属性,这些属性会出现在材质面板中
Name(“DisPlayName”,PropertyTYpe)=DefaultValue
//Name shader中可访问,通常为下划线开始
//显示名字为显示在材质面板上的名字
//指定类型 赋默认值
}
表3.1 Properties语义块支持的属性类型
属性类型 | 默认值的定义语法 | 例子 |
Int | number | _Int("Int",Int)=2 |
Float | number | _Float("Float",Float)=1.5 |
Range(min,max) | number | _Range("Range",Range(0.0,5.0))=3.0 |
Color | (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)=""{} |
Cube | "defaulttexture"{} | _Cube("Cube",Cube)="white"{} |
3D | "defaulttexture"{} | _3D("3D",3D)="black"{} |
SubShader
每一个Unity Shader文件可以包含多个SubShader语义块,但最少要一个
不同的显卡能支持的指令数不同,旧的显卡使用计算复杂的较低的做色器,高级的显卡能使用计算复杂度较高的着色器
SubShader{
//表3.2 常见的渲染状态设置
//可选
[Tags] 键值对,键和值都是字符串类型。是渲染引擎之间的桥梁。
//表3.3SubShader的标签类型
Tags { "TagName1"="value1" "TagName2"="Value2" }
//可选
[RenderSetup]
Pass{
[Name]
Name="MyPassName"
UsePass="MyShader/MYPASSNAME" 这样可以使用其他shader的pass,提高复用性,应为Unity会把Pass全部转换成大写,所以UsePass需要用大写
可以使用SubShader的状态设置,也可以使用固定管线的着色器命令
[Tags]
//表 3.4 Pass的标签类型
为了告诉渲染引擎需要怎样渲染该物体
[RenderSetup]
}
//other passes
}
SubShader中定义了一系列的可选状态和标签设置
每个Pass定义了一次完整的渲染流程。(pass数目过多,会引起性能下降)
状态标签可以在Pass中声明。
在SubShader定义的标签和状态可以应用整个SubShader
ShaderLab 状态设置
设置显卡的各种状态
表3.2 常见的渲染状态设置
状态名称 | 设置指令 | 解释 |
Cull | Cull Back |Front |Off | 设置剔除模式:剔除背面/正面/关闭剔除 |
ZTest | Ztest less Greater | LEqual | Gequal | Equal |NotEqqual |Always | 设置深度测试时使用的函数 |
ZWrite | Zwrite On | Off | 开启、关闭 深度写入 |
Blend | Blend SRCFactory DstFactor | 开启并设置混合模式 |
表3.3 SubShader的标签类型
标签类型 | 说明 | 例子 |
Queue | 控制渲染顺序,指定该物体属于哪一个渲染队列,通 过这种方式可以保证所有的透明物体都可以在所有不透 明物体后面被渲染,我们也可以自定义使用的渲染队列 来控制物体的渲染顺序 | Tags{"Queue"="Transparent"} |
RenderType | 对着色器进行分类,例如这是一个透明的着色器或者是 一个不透明的着色器。这可以被用于着色器的替换 (Shader Replacement)功能 | Tags{"RenderType"="Opaque"} |
DisableBatching | 一些SubShader在使用Unity的批处理功能时会出现问题, 例如使用了模型空间下的坐标进行顶点动画,这时间可以通 过该标签来直接指明是否对该SubShader使用批处理 | Tags{“ForceNoShadowCasting”="True"} |
ForceNoShadowCasting | 控制使用该SubShader的物体是否会投射阴影 | Tags{"ForceNoShadowCasting"="True"} |
IgnoreProjector | 如果该标签值为“True”,那么使用该SubShader的 物体将不会受Projector的影响。通常用于半透明物体。 | Tags{"IgnorePropjector"="True"} |
CanUseSpriteAtlas | 当该SubShader使用精灵(sprites)时,将该标签 设置为“False” | Tags{"CanUseSpriteAtlas"="False"} |
PreviewType | 指明材质面板将该如何预览该材质。默认情况下,材质 将显示为一个球形,我们可以通过把该标签的值设为“Plane” “SkyBox” 来改变预览类型 | Tags{“PreviewType”=“Plane”} |
表 3.4 Pass的标签类型
服务标签类型 | 说明 | 例子 |
LightMode | 定义该Pass在Unity的渲染流水线的角色 | Tags{"lightMode"="ForwardBase"} |
RequireOptions | 用于指定当满足某些条件时才渲染该pass,他的值是一个 由空格分割的字符串。目前,Unity支持的选项有: SoftVegetation。在后面的版本中,可能会增加更多的 选项 | Tags{“RequireOption”=“SoftVegetation”} |
FallBack :当显卡不能正常运行SubShader,就会执行FallBack (Fallback "Vertexlit")
3.4Unity Shaderi形式
表面着色器:SubShader语义块中
Pass 语义块中:顶点、片元着色器、固定着色器
表面着色器 (Unity 还是会将他们转换成对应的顶点/片元着色器)
是Unity 对顶点/片元着色器的更高一层的抽象。Unity还是会将它转换成对应的顶点/片元着色器
表面着色器被定义在SubShader语义块中的CGPROGRAM和ENDCG 之间(开发者不需要关系使用多少个Pass,Pass如何渲染的问题)
CGPROGRAM和ENDCG 之间的代码使用Cg/HLSL(经过Unity封装后的语法,与标准的语法几乎一样)编写
顶点/片元着色器
使用Cg/HLSL语言编写顶点/片元着色器
需要定义在CGPROGRAM和ENDCG之间,写在Pass语义块内。
我们需要自己定义每个pass需要使用的Shader代码----------灵活性高,控制渲染的实现细节。
固定函数着色器(被遗弃)
使用哪种Unity Shader 形式
1. 使用可编程管线的着色器(表面着色器、顶点/片元着色器)
2. 表面着色器适合与各种光源打交道。耗费性能
3. 顶点/片元着色器(光源少)
4. 顶点/片元着色器(自定义渲染效果)
| |||
| 4.2 笛卡尔坐标系 | 4.3点和矢量 |
| ||||||||||||
|
图4.4 在屏幕映射时,OpenGL和DirectX使用了不同方向的二维笛卡尔坐标系
图4.6 一个三维笛卡尔坐标系 | |||||||||||
| ||||||||||||
| ||||||||||||
| ||||||||||||
| ||||||||||||
| 三个坐标被称为该坐标系的基矢量
三个坐标轴互相垂直,且长度为1,这样的基矢量被称为标准正交基
三个坐标轴互相垂直,但长度不为1,这样的基矢量被称为正交基
所有二维笛卡尔坐标系都是等价的
如果两个坐标系具有相同的旋向性,那么我们就可以通过旋转的方法让他们的坐标轴指向重合
左手坐标系旋转方向是顺时针
右手坐标系旋转方向是逆时针
同样的视觉效果,左右手坐标系在Z轴上的移动以及旋转方向是不同的
模型空间和饰界空间,Unity使用的是左手坐标系
观察空间Unity使用的是右手坐标系。Z轴坐标的减少意味着场景深度的增加 | |||||||||||
| ||||||||||||
| 矢量的加减法
两个矢量相加减,结果是一个相同维度的矢量
a b=()
a-b=()
一个矢量不能和一个标量相加减,或者和不同维度的矢量相加减
| |||||||||||
| ||||||||||||
| 单位矢量
在计算光照模型时:需要得到顶点的法线方向和光源方向,不用计算矢量有多长。
单位矢量是指模为1的矢量,也被称为被归一化矢量
对于任意非0矢量,把它转化成单位矢量的过程叫归一化
零矢量是不可以被归一化(0不能做分母)
图4.21 二维空间的单位矢量都会落在单位圆上 | |||||||||||
| ||||||||||||
| 矢量的点积
矢量的乘法:点积(内积),叉积(外积)
Unity Shader中 点积用 dot(a,b)
=
矢量的点积 满足交换律
投影
图4.23 点积的符号
点积的符号可以得到两个矢量的方向关系
1. 点积结合标量乘法 2. 点积结合矢量的加减法
3. 一个矢量和本身进行点积的结果是该矢量模的平方
利用点积来求矢量的模
两个单位矢量的点积等于他们之间夹角的余弦值
两个矢量的点积可以表示为两个矢量的模相乘,在乘他们之间夹角的余弦值 夹角<90 夹角=90 夹角>90
两个向量之间的夹角 | |||||||||||
| ||||||||||||
| 矢量的叉积(外积)
矢量的叉积仍是矢量
i,j,k分别是X,Y,Z轴方向的单位向量
叉积不满足交换律、结合率
模-------->
如果a,b平行 ,那么 a*b=0。得到的是零向量
应用:计算垂直于一个平面、三角形的矢量。判断三角面片的朝向 | |||||||||||
|
矩阵
| |||
| 5.2 一个最简单的顶点/片元着色器
· Properties语义并不是必须的
· 编译指令 · 他们将告诉Unity,那个函数包含了顶点做色器代码,那个函数包含片元着色器代码 #pragma vertex name #pragma fragment name
POSITION SV_POSITION 都是Cg/GLSL语义
UNITY_MATRIX_MVP 矩阵是Unity内置的模型观察投影矩阵
SV_Target是HLSL中的一个语义,告诉渲染器,把用户输出颜色存储到一个渲染目标中,这里默认输出到帧缓存中
片元着色器输出的每个分量的范围在【0,1】中 (0,0,0)表示黑色 (1,1,1)表示白色
Unity支持的语义:POSITION ,TANGENT,NORMAL,TEXCOORD0,TEXCOORD1,TEXCOORD2,TEXCOORD3,COLOR
自定义结构体 Struct StructName{ Type Name :Semantic …… }
填充到 POSITION,TANGENT,NORMAL这些语义的数据是在Unity中,数据从该材质MeshRender组件提供的。在每帧调用Draw call 时,mesh Render 组件会把他负责渲染的模型数据发送给Unity Shader
一个模型包含了一组三角面片,每个三角面片由三个顶点构成,每个顶点包含了一些数据(顶点位置、法线、切线、纹理坐标、顶点颜色) | 5.2.3 顶点着色器与片元着色器之间的通讯
从顶点着色器中输出一些数据(模型的法线、纹理坐标)传递给片元着色器
V2f用于顶点、片元之间传递信息
顶点着色器的输出结构中,必须包含一个变量,他的语义是 SV_POSITION。否则渲染器将无法得到剪裁空间中的顶点坐标,也就无法渲染到屏幕上。
顶点着色器是逐顶点调用,而片元着色器是逐片元调用。
片元着色器中的输入实际上是把顶点着色器的输入进行插值后得到的结果 | |
|
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 5.2.4 如何使用属性
为了在Cg中可以访问_Color,需在Cg代码中提前定义一个新变量,变量名称、类型必须与Properties语义块中的属性定义匹配
ShaderLab属性类型和Cg变量类型的匹配关系
unifrom在Unity中可以省略 | 5.3 Unity提供的内置文件及变量
包含文件: CGPROGRAM
#include "UnityCG.cginc"
ENDCG Unity 中常用的包含文件
UnityCG.cginc中一些常用的结构体
UnityCG.cginc中的一些常用的帮助函数
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
5.4 Unity 提供的Cg/HLSL 语义
语义实际上是一个赋给Shader输入、输出的字符串,这个字符串表达了这个参数的含义
这些语义可以让Shader知道从哪里读取数据,并把数据输出到哪里,他们在Cg/HLSL的Shader流水线中是不可缺的
Unity并没有支持所有语义
通常情况下,这些输入输出变量并不需要有特别的含义
DirectX 10 以后 有一个新的系统数值语义,这类语义以SV开头,这些语义在渲染流水线中有特殊含义
使用SV_POSITION语义去修饰顶点着色器的输出变量pos,那么就表示pos包含了可用于光栅化变化后的顶点坐标(齐次剪裁空间的坐标),用这些语义描述的变量不可随便赋值,应为流水线需要它完成特定的目的
从应用阶段传递模型数据给顶点着色器时Unity支持的常用语义
语义 | 描述 |
POSITION | 模型空间中的顶点位置,通常是float4 |
NORMAL | 顶点法线 float3 |
TANGENT | 顶点切线 float4 |
TEXTCOORDn | 该顶点的纹理坐标,TEXCOORD0表示第一组纹理坐标 |
COLOR | 顶点颜色 fixed4 、float4 |
从顶点着色器传递数据给片元着色器Unity使用的常用语义
语义 | 描述 |
SV_POSITON | 剪裁空间中的顶点坐标,结构体中必须包含一个用该语义修饰的变量 等同与DirectX 9 中的POSITION ,但最好是SV_POSITION |
COLOR0 | 通常用于输出第一组顶点颜色,但不是必需的 |
COLOR1 | 通常用于输出第二组顶点颜色,但不是必需的 |
TEXCOORD0~TEXCOORD7 | 通常用于输出纹理左边。但不是必需的 |
片元着色器输出时Unity支持的常用语义
语义 | 描述 |
SV_Target | 输出值将会存储在渲染目标(render target )中。等同DirectX 9中的COLOR语义,但最好是SV_Target |
一个语言可以使用的寄存器只能处理4个浮点值
UNITY_UV_STARTS_AT_TOP 用来判断当前平台是否为DirectX类型的平台
_MainTex_TexelSize.y 是否小于0来检测是否开启抗锯齿
Cg/HLSL中3种精度的数值
类型 | 精度 |
float | 最高精度浮点值,使用32位存储 |
Half | 中等精度浮点值,使用16位存储。范围:-60000~ 60000 |
fixed | 最低精度,11位存储。范围:-2.0~ 2.0 |
不同平台的GPU精度可能会有偏差
1. 大多数现代的桌面GPU会把所有计算都按最高的浮点精度进行计算。Float half fixed 在这些平台上是等价的。在PC上很难看出他们三者之间所带来的影响
2. 在移动平台的GPU上,精度会有不同,而且不同精度浮点值的运算速度会有差异。
3. fixed精度在较旧的移动平台上有用。在大多数现代GPU上,他们的内部把half、fixed当成等同精度
尽可能使用精度较低的类型,可以优化Shader的性能
fixed类型存储颜色和单位矢量,存储更大数据选择half类型,最差的情况下使用float。
不同的Shader Target、不同的着色器阶段,我们可以使用的临时寄存器和指令数目是不同的。
尽量少用分支、循环语句
渲染包含了两部分:决定一个像素的可见性,决定这个像素的光照计算
光照模型用于决定在一个像素上进行怎样的光照计算
6.1 如何看见这个世界
模拟真实光照环境:
1. 光纤从光源发射
2. 光线和场景相交:一些环境被物体吸收,另一些光线被散射到其他地方
3. 摄像机吸收光,产生一张图像
在光学里,使用辐照度来量化光
Cos可以使用光源方向L和表面法线N的点积得到
光由光源发射后与物体相交会得到两个结果:1 散射 2 吸收
散射自改变光线的方向,但不改变光的颜色。吸收只改变光线的密度和颜色,但不改变光的方向
光线在物体表面经过散射后: 1 散射到物体内部(折射、透视) 2 散射到外部 (反射)
光照模型中使用了不同的部分来计算:
1. 高光(Specular)反射部分:表示物体表面是如何反射光线的
2. 漫反(diffuse)射部分:表示有多少光线被折射、吸收、散射出表面
出射度是用来描述出射光线的数量和方向
辐照度和出射度之间是满足线性关系,他们之间的比值就是材质的漫反射和高光反射属性
着色是指根据材质属性(如漫反射属性等)、光源信息(光源方向、辐照度等),使用一个等式去计算沿某个观察方向的出射度的过程。(这个等式称为光照模型,不同的光照模型有不同的目的)
BRDF光照模型是用来解决:当已知光源位置和方向、视角方向时,我们就需要知道一个表面是如何和光照进行交互的
6.2标准光照模型
标准光照模型只关心直接光照(直接从光源发射出来照射到物体表面后,经过物体表面的一次反射直接进入摄像机的光线)
标准光照模型:
1. 自发光(emissive) 用于描述给定一个方向时,一个表面本身会向该方向发射出多少辐射量。如果没有全局光照技术,这些自发光表面并不会真实的照射周围的物体,而是他本身看起来更亮了而已
2. 高光反射(specular) 当光线从光源照射到模型表面时,该表面会在完全镜面反射方向散射多少辐射量
3. 漫反射(diffuse) 当光线从光源照射到模型表面时,该表面会向每个方向散射多少增幅量。
4. 环境光 (ambient)描述其他所有间接光照
标准光照模型 |
|
|
|
环境光 | 使用一种被称为环境光的部分来近似模拟间接光照 | = |
|
自发光 | 光线可以直接由光源发射进入摄像机,而不经过任何物体的反射 |
| |
漫反射 | 用于对那些被物体表面随机散射到各个方向的辐射度进行建模 兰伯特定律:反射光线的强度与表面法线和光源方向之间夹角的余弦值成正比 | 是指向光源的单位矢量 , 是指材质的漫反射颜色,需要注意防止法线和光源方向点乘结果为负值。(使用取最大值的函数来将其截取到0,这样可以防止物体被后面来的光源照亮) | |
高光反射 | 高光反射是一种经验模型,并不完全符合真实世界中的高光反射 |
----------------------------------------------------------------------- |
硬件实现时,如果摄像机和光源距离模型足够远的话,Blinn模型会快于Phone模型 在一些情况下blinn模型更符合实验结果
|
在片元着色器中计算称为逐像素光照
在逐像素关照中,我们会以每个像素为基础,得到他的法线(可以是对顶点法线插值得到的,也可以从法线纹理中采样得到),然后进行光照模型计算。这种面片之间对顶点发现进行插值的技术被称为Phone着色。
在顶点着色器中计算称为逐顶点光照
高洛德着色:在逐顶点光照中,我们在每个顶点计算光照,然后会在渲染图元内部进行线性插值,最后输出成像素颜色。由于顶点数目小于像素数目,所以逐顶点光照计算量小于逐像素计算量。
由于逐顶点光照依赖线性插值来得到像素光照,当光照模型有非线性的计算(计算高光反射)时,逐顶点光照会出现问题。
由于逐顶点光照会在渲染图元内部对顶点颜色进行插值,这会导致渲染图元内部的颜色总是暗于定点出的最高颜色,这在某些情况下会产生明显的菱角现象
6.3 Unity中的环境光和自发光
在Shader中,通过Unity内置的变量UNITY_LIGHTMODEL_AMBIENT获得环境光的颜色和强度信息
计算:在片元着色器输出最后的颜色之前,把材质的自发光颜色添加进去就行
6.4 在Unity Shader 中实现漫反射光照模型
入射光线颜色、强度
材质漫反射系数
表面法线 光源方向
saturate函数防止点积结果为负
函数:saturate
参数: x:用于操作的标量和矢量,可以是float2 float3 float4登类型
描述:把x截取到【0,1】范围内,如果x是一个矢量,那么会对它的每一个分量进行这样的操作
顶点/片元着色器的代码是在Pass语义块中
顶点着色器是吧顶点位置从模型空间转换到剪裁空间中
逐像素光照
逐像素光照可以得到更加平滑的光照效果
6.5 Unity Shader中实现高光反射光照模型
Cg提供了计算反射方向的函数
函数:reflect(i,n)
参数:i,入射方向。n法线方向 (float(2、3))
描述:当给定入射方向i和法线方向n时,reflect函数可以返回反射方向