栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 游戏开发 > 其他

Unity Shader笔记(一)

其他 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

Unity Shader笔记(一)

目录:
  • 渲染流水线
    • 概念流水线的三个阶段
      • 应用阶段
      • 几何阶段
      • 光栅化阶段
    • CPU与GPU的通信
      • 把数据加载到显存中
      • 设置渲染状态
      • 调用Draw Call
    • GPU流水线
    • 着色器语言
      • HLSL
      • GLSL
      • CG
    • 小总结
  • Unity Shader基础
    • 流程
    • ShaderLab
      • 属性 Properties
      • 子着色器 SubShader
      • 降级着色器 FallBack
    • Unity中其它与Shader相关的术语
  • 数学相关知识
    • 坐标系与矢量
    • 变换
      • 齐次坐标
      • 平移矩阵
      • 缩放矩阵
      • 旋转矩阵
    • 坐标空间
      • 模型空间(Model Space)
      • 世界空间(World Space)
      • 观察空间(View Space)
      • 裁剪空间(Clip Space)
        • 透视投影
        • 正交投影
      • 屏幕空间(Screen Space)

渲染流水线

概念流水线的三个阶段

应用阶段

输出渲染图元(点、线、三角面等)
此阶段对于开发者来说的任务有:

  • 要准备好场景数据(包括设计相机的位置、视锥体、模型、光源)
  • 做一个粗粒度的剔除工作,把不可见的物体剔除
  • 设置好每个模型的渲染状态(材质、纹理、Shader)
几何阶段

输出屏幕的顶点信息

光栅化阶段

使用上一阶段的数据来阐述屏幕上的像素,并渲染出最终的图像

CPU与GPU的通信

把数据加载到显存中

数据从硬盘加载到内存,然后网格和纹理等数据又被加载到显卡的存储空间,即显存里

设置渲染状态

定义场景中的网格是怎样被渲染的

调用Draw Call

Draw Call:每次CPU准备数据并调用图形绘制API通知GPU的过程就称之为一个DrawCall

所谓Draw Call,就是一个命令,发起方是CPU,接收方是GPU,仅仅指向一个需要被渲染的图元(点线面)列表,不包含任何材质信息(材质信息已经加载到了显存中)

GPU流水线

几何阶段和光栅化阶段可以分成若干个更小的流水线阶段。这些流水线阶段由GPU来实现,每个姐u但GPU提供不同的可配置性和可编程性

顶点着色器:实现顶点空间变换、顶点着色功能。把顶点坐标从模型空间转换到齐次裁剪空间(类似于归一化)
曲面细分着色器:用于细分图元
几何着色器:用于执行逐图元的着色操作,或者用于产生更多图元
裁剪:将不在摄像机视野内的顶点裁剪掉,剔除某些三角图元的面片

屏幕映射:负责把每个图元的坐标转换到屏幕坐标系中
// 在Unity中,屏幕坐标系是以屏幕左下角为原点,向右为 x 正方向,向上为 y 的正方向,相机世界坐标中 z 坐标的负方向为正方向的坐标系
三角形设置:固定函数阶段,计算光栅化一个三角形网格所需的信息
三角形遍历:固定函数阶段,检查每个像素是否被一个三角网格所覆盖,是的话就生成一个片元
// 一个片元并不是真正意义上的像素,只是包含要渲染的像素所需要的信息的集合,用于计算每个像素的最终颜色
片元着色器(像素着色器):实现逐片元的着色操作
逐片元操作(合并阶段):修改颜色、深度缓冲、进行混合等
输出:屏幕图像

着色器语言

HLSL

DX的着色器语言

GLSL

OpenGL的着色语言

CG

英伟达C for Graphic

小总结

Shader 所在的阶段就是渲染流水线的一部分,更具体来说 Shader 就是GPU流水线上一些可编程的部分。而着色器编译出来的代码最终是在GPU上运行的


Unity Shader基础

Unity 中提供了方便管理着色器、代码以及渲染设置的 Unity Shader

  • 标准表面着色器(Standdard Surface Shader):包含标准光照的表面着色器模板
  • 无光照着色器(Unlit Shader):不包含光照的基本顶点/片元着色器
  • 图像效果着色器(Image Effect Shader):实现各种屏幕后处理效果
  • 计算着色器(Compute Shader):产生一个特殊的Shader文件,旨在利用GPU并行性进行一些与常规渲染流水线无关的计算
  • 光线追踪着色器(Ray Tracing Shader): 执行与光线追踪相关的计算

流程

创建一个材质(Material),创建一个着色器(Shader),将Shader赋予到材质上,再把材质赋予给游戏对象,在材质面板中调整属于以达到满意的效果。

ShaderLab

ShaderLab 是 Unity 中用于编写着色器的特定语言

Shader"_ShaderName"
{
    Properties{ //属性}
    SubShader{ //子着色器}
    SubShader{ //子着色器}
	Fallback "VertexLit"	//SubShader都无法使用时调用Fallback
}

属性 Properties

属性是连接材质和Shader的桥梁,这些属性将出现在材质的面板中

  • Int
  • Float
  • Range(min, max)
  • Color
  • Vertor
  • 2D
  • Cube
  • 3D
子着色器 SubShader

SubShader可以有多个且至少有一个;
当需要加载Shader时,Unity会扫描所有SubShader并选择一个能够在目标平台运行的SubShader;
若所有SubShader都不支持则调用Fallback

SubShader{
	// 可选的[Tags]
    // 可选的[RenderSetup]
    Pass{}	// 每个Pass定义了一次完成的渲染流程,但Pass数目过多性能会下降
    // Other Passes
}

常见的渲染状态设置选项:

  • Cull(剔除)
    • Back 背面
    • Front 正面
    • Off 关闭
  • ZTest(深度测试)
    • Less Greater
    • LEqual
    • GEqual
    • Equal
    • NotEqual
    • Always
  • Zwrite(深度写入)
    • On
    • Off
  • Blend(混合模式)
    • SrcFactor
    • DstFactor

SubShader 的标签类型:

  • Queue:控制渲染顺序
  • RenderType:对着色器进行分类
  • DisableBatching:指定是否使用批处理
  • ForceNoShadowCasting:控制投射阴影
  • IgnoreProjector:控制是否受 Projector 的影响。常用于半透明物体
  • CanUseSpriteAtlas: 使用此子着色器的精灵是否与 Legacy Sprite Packer 兼容
  • PreviewType:指明材质面板如何预览该材质,默认球形

Pass 的标签类型:

  • LightMode:定义该 Pass 在渲染流水线中的角色
  • RequireOptions:指定当满足某些条件时才渲染该 Pass
降级着色器 FallBack
FallBack "name"	// 使用的最低级着色器
FallBack Off	// 关闭 Fallback 功能

Unity中其它与Shader相关的术语
  • 着色器/着色器程序:在 GPU 上运行的程序。默认着色器程序是图形管线的一部分
  • Shader 对象:Shader类的一个实例。Shader对象是着色器程序和其他信息的封装器
  • ShaderLab:Unity中用于编写着色器的特定语言
  • Shader Graph:一种不用代码创建着色器的工具
  • 着色器资源:Unity 项目中扩展名为 .shader 的文件。它定义一个 Shader 对象
  • Shader Graph 资源:Unity 项目中的文件。它定义一个 Shader 对象

传统的 Shader 仅可以编写特定的Shader,例如顶点着色器或片元着色器,而 Unity Shader 中可以在一个文件中同时包含顶点着色器和片元着色器的代码。
传统的 Shader 中无法设置一些渲染设置,例如是否开启混合、深度测试等,而 Unity Shader 中可以通过特定指令来完成。


数学相关知识

坐标系与矢量

Unity:左手坐标系,但是观察空间为右手
UE:左手坐标系
// 判断小技巧:对应手的四指从 x 指向 y,大拇指方向为 z 轴
矢量点积 => 数
几何意义:投影长度
矢量叉积 => 矢量
几何意义:垂直于两个矢量的新矢量(模长为原矢量为边的平行四边形面积)
// UE中的mutiply节点既不是点积也不是叉积,而是各个分量的简单相乘而已

变换

矩阵在三维渲染中的主要作用就是变换,包括对物体的平移、旋转、缩放,对坐标空间的变换等
线性变换:保留矢量加和标量乘
仿射变换:合并线性变换和平移变换的变换类型

齐次坐标

线性变换可以做到缩放、旋转和正交投影,但是不能满足平移的需求。因此需要将三维矢量转换成四维矢量
[ M t 0 1 ] left[ begin{array}{c|c} M&t\ hline 0&1 end{array} right] [M0​t1​​]

左上角M为表示旋转和缩放的3×3矩阵
M = [ 1 0 0 0 1 0 0 0 1 ] M=begin{bmatrix} 1&0&0\ 0&1&0\ 0&0&1\ end{bmatrix} M=⎣⎡​100​010​001​⎦⎤​

右上角为平移矩阵
t = [ t x t y t z ] t=begin{bmatrix} t_x \ t_y\ t_z \ end{bmatrix} t=⎣⎡​tx​ty​tz​​⎦⎤​

左下角为0矩阵,右下角为标量1

平移矩阵

点 P(x, y, z) 平移 (tx, ty, tz)

[ 1 0 0 t x 0 1 0 t y 0 0 1 t z 0 0 0 1 ] ⋅ [ x y z 1 ] = [ x + t x y + t y z + t z 1 ] begin{bmatrix} 1&0&0&t_x\ 0&1&0&t_y\ 0&0&1&t_z\ 0&0&0&1 end{bmatrix} cdot begin{bmatrix} x\ y\ z\ 1\ end{bmatrix} = begin{bmatrix} x+t_x\ y+t_y\ z+t_z\ 1\ end{bmatrix} ⎣⎢⎢⎡​1000​0100​0010​tx​ty​tz​1​⎦⎥⎥⎤​⋅⎣⎢⎢⎡​xyz1​⎦⎥⎥⎤​=⎣⎢⎢⎡​x+tx​y+ty​z+tz​1​⎦⎥⎥⎤​

缩放矩阵

[ k 1 0 0 0 0 k 2 0 0 0 0 k 3 0 0 0 0 1 ] ⋅ [ x y z 1 ] = [ k 1 ⋅ x k 2 ⋅ y k 3 ⋅ z 1 ] begin{bmatrix} k_1&0&0&0\ 0&k_2&0&0\ 0&0&k_3&0\ 0&0&0&1 end{bmatrix} cdot begin{bmatrix} x\ y\ z\ 1\ end{bmatrix} = begin{bmatrix} k_1cdot x\ k_2cdot y\ k_3cdot z\ 1\ end{bmatrix} ⎣⎢⎢⎡​k1​000​0k2​00​00k3​0​0001​⎦⎥⎥⎤​⋅⎣⎢⎢⎡​xyz1​⎦⎥⎥⎤​=⎣⎢⎢⎡​k1​⋅xk2​⋅yk3​⋅z1​⎦⎥⎥⎤​

旋转矩阵

[ 1 0 0 0 0 c o s θ − s i n θ 0 0 s i n θ c o s θ 0 0 0 0 1 ] cot ⁡ [ x y z 1 ] = [ x y ⋅ c o s θ − z ⋅ s i n θ y ⋅ s i n θ + z ⋅ c o s θ 1 ] begin{bmatrix} 1&0&0&0\ 0&cosθ&-sinθ&0\ 0&sinθ&cosθ&0\ 0&0&0&1 end{bmatrix} cot begin{bmatrix} x\ y\ z\ 1\ end{bmatrix} = begin{bmatrix} x\ ycdot cosθ-zcdot sinθ\ ycdot sinθ+zcdot cosθ\ 1\ end{bmatrix} ⎣⎢⎢⎡​1000​0cosθsinθ0​0−sinθcosθ0​0001​⎦⎥⎥⎤​cot⎣⎢⎢⎡​xyz1​⎦⎥⎥⎤​=⎣⎢⎢⎡​xy⋅cosθ−z⋅sinθy⋅sinθ+z⋅cosθ1​⎦⎥⎥⎤​

绕 y 轴旋转 θ 度

[ c o s θ 0 s i n θ 0 0 1 0 0 − s i n θ 0 c o s θ 0 0 0 0 1 ] cot ⁡ [ x y z 1 ] = [ x ⋅ c o s θ + z ⋅ s i n θ y − x ⋅ s i n θ + z ⋅ c o s θ 1 ] begin{bmatrix} cosθ&0&sinθ&0\ 0&1&0&0\ -sinθ&0&cosθ&0\ 0&0&0&1 end{bmatrix} cot begin{bmatrix} x\ y\ z\ 1\ end{bmatrix} = begin{bmatrix} xcdot cosθ+zcdot sinθ\ y\ -xcdot sinθ+zcdot cosθ\ 1\ end{bmatrix} ⎣⎢⎢⎡​cosθ0−sinθ0​0100​sinθ0cosθ0​0001​⎦⎥⎥⎤​cot⎣⎢⎢⎡​xyz1​⎦⎥⎥⎤​=⎣⎢⎢⎡​x⋅cosθ+z⋅sinθy−x⋅sinθ+z⋅cosθ1​⎦⎥⎥⎤​

绕 z 轴旋转 θ 度
[ c o s θ − s i n θ 0 0 s i n θ c o s θ 0 0 0 0 1 0 0 0 0 1 ] cot ⁡ [ x y z 1 ] = [ x ⋅ c o s θ − y ⋅ s i n θ x ⋅ s i n θ + y ⋅ c o s θ z 1 ] begin{bmatrix} cosθ&-sinθ&0&0\ sinθ&cosθ&0&0\ 0&0&1&0\ 0&0&0&1 end{bmatrix} cot begin{bmatrix} x\ y\ z\ 1\ end{bmatrix} = begin{bmatrix} xcdot cosθ-ycdot sinθ\ xcdot sinθ+ycdot cosθ\ z\ 1\ end{bmatrix} ⎣⎢⎢⎡​cosθsinθ00​−sinθcosθ00​0010​0001​⎦⎥⎥⎤​cot⎣⎢⎢⎡​xyz1​⎦⎥⎥⎤​=⎣⎢⎢⎡​x⋅cosθ−y⋅sinθx⋅sinθ+y⋅cosθz1​⎦⎥⎥⎤​

一般的顺序是:缩放 -> 旋转 -> 平移

坐标空间

模型空间(Model Space)

// 又称对象空间(Object Space)或局部空间(Local Space)

世界空间(World Space)

观察空间(View Space)

裁剪空间(Clip Space)

把顶点从观察空间转换到裁剪空间中的矩阵叫做裁剪矩阵,也被称作投影矩阵。最终渲染的部分是由视锥体决定的。
Unity 中的相机参数如下:


视锥体投影类型有两种:正交投影、透视投影

透视投影

近裁剪平面和远裁剪平面决定可以看到的最近和最远距离

n e a r C l i p P l a n e H e i g h t = 2 ⋅ N e a r ⋅ tan ⁡ F O V 2 nearClipPlaneHeight=2cdot Nearcdot tanfrac{FOV}{2} nearClipPlaneHeight=2⋅Near⋅tan2FOV​
F a r C l i p P l a n e H e i g h t = 2 ⋅ F a r ⋅ tan ⁡ F O V 2 FarClipPlaneHeight=2cdot Farcdot tanfrac{FOV}{2} FarClipPlaneHeight=2⋅Far⋅tan2FOV​

宽度由计算出的高度和设置的宽高比 Aspect 可以计算出。
透视投影矩阵:
[ cot ⁡ F O V 2 A s p e c t 0 0 0 0 cot ⁡ F O V 2 0 0 0 0 − F a r + N e a r F a r − N e a r − 2 ⋅ N e a r ⋅ F a r F a r − N e a r 0 0 − 1 0 ] begin{bmatrix} frac{cotfrac{FOV}{2}}{Aspect}&0&0&0\ 0&cotfrac{FOV}{2}&0&0\ 0&0&-frac{Far+Near}{Far-Near}&-frac{2cdot Nearcdot Far}{Far-Near}\ 0&0&-1&0 end{bmatrix} ⎣⎢⎢⎢⎡​Aspectcot2FOV​​000​0cot2FOV​00​00−Far−NearFar+Near​−1​00−Far−Near2⋅Near⋅Far​0​⎦⎥⎥⎥⎤​

把顶点从观察空间转换到裁剪空间,将透视投影矩阵点乘顶点坐标:
[ cot ⁡ F O V 2 A s p e c t 0 0 0 0 cot ⁡ F O V 2 0 0 0 0 − F a r + N e a r F a r − N e a r − 2 ⋅ N e a r ⋅ F a r F a r − N e a r 0 0 − 1 0 ] ⋅ [ x v y v z v 1 ] = [ x c y c z c − z v ] begin{bmatrix} frac{cotfrac{FOV}{2}}{Aspect}&0&0&0\ 0&cotfrac{FOV}{2}&0&0\ 0&0&-frac{Far+Near}{Far-Near}&-frac{2cdot Nearcdot Far}{Far-Near}\ 0&0&-1&0 end{bmatrix}cdot begin{bmatrix} x_v\ y_v\ z_v\ 1\ end{bmatrix} = begin{bmatrix} x_c\ y_c\ z_c\ -z_v\ end{bmatrix} ⎣⎢⎢⎢⎡​Aspectcot2FOV​​000​0cot2FOV​00​00−Far−NearFar+Near​−1​00−Far−Near2⋅Near⋅Far​0​⎦⎥⎥⎥⎤​⋅⎣⎢⎢⎡​xv​yv​zv​1​⎦⎥⎥⎤​=⎣⎢⎢⎡​xc​yc​zc​−zv​​⎦⎥⎥⎤​

转换后顶点的 w 分量不再为1而是观察空间 z 坐标的相反数,使用该 w 分量判断顶点是否在视锥体内
− w ≤ x ≤ w − w ≤ y ≤ w − w ≤ z ≤ w -wleq x leq w \ -wleq y leq w \ -wleq z leq w −w≤x≤w−w≤y≤w−w≤z≤w

正交投影

正交投影的视锥体是一个长方体

n e a r C l i p P l a n e H e i g h t = F a r C l i p P l a n e H e i g h t = 2 ⋅ S i z e nearClipPlaneHeight=FarClipPlaneHeight=2cdot Size nearClipPlaneHeight=FarClipPlaneHeight=2⋅Size同理宽度也是由计算出的高度和设置的宽高比 Aspect 可以计算出。正交投影矩阵:
[ 1 A s p e c t ⋅ S i z e 0 0 0 0 1 S i z e 0 0 0 0 − 2 F a r − N e a r − F a r + N e a r F a r − N e a r 0 0 0 1 ] begin{bmatrix} frac{1}{Aspectcdot Size}&0&0&0\ 0&frac{1}{Size}&0&0\ 0&0&-frac{2}{Far-Near}&-frac{Far+Near}{Far-Near}\ 0&0&0&1\ end{bmatrix} ⎣⎢⎢⎡​Aspect⋅Size1​000​0Size1​00​00−Far−Near2​0​00−Far−NearFar+Near​1​⎦⎥⎥⎤​把顶点从观察空间转换到裁剪空间,将正交投影矩阵点乘顶点坐标:
[ 1 A s p e c t ⋅ S i z e 0 0 0 0 1 S i z e 0 0 0 0 − 2 F a r − N e a r − F a r + N e a r F a r − N e a r 0 0 0 1 ] ⋅ [ x v y v z v 1 ] = [ x c y c z c 1 ] begin{bmatrix} frac{1}{Aspectcdot Size}&0&0&0\ 0&frac{1}{Size}&0&0\ 0&0&-frac{2}{Far-Near}&-frac{Far+Near}{Far-Near}\ 0&0&0&1\ end{bmatrix}cdot begin{bmatrix} x_v\ y_v\ z_v\ 1\ end{bmatrix} = begin{bmatrix} x_c\ y_c\ z_c\ 1\ end{bmatrix} ⎣⎢⎢⎡​Aspect⋅Size1​000​0Size1​00​00−Far−Near2​0​00−Far−NearFar+Near​1​⎦⎥⎥⎤​⋅⎣⎢⎢⎡​xv​yv​zv​1​⎦⎥⎥⎤​=⎣⎢⎢⎡​xc​yc​zc​1​⎦⎥⎥⎤​变换后 w 分量仍然是1,判断是否在视锥体内的公式仍然不变

− w ≤ x ≤ w − w ≤ y ≤ w − w ≤ z ≤ w -wleq x leq w \ -wleq y leq w \ -wleq z leq w −w≤x≤w−w≤y≤w−w≤z≤w

屏幕空间(Screen Space)

顶点变换的最后一步,变换到屏幕空间,也就是把视锥体投影到屏幕空间。
用齐次除法(或者叫透视除法),将齐次坐标系 w 分量除以 x、y、z 分量,变换到一个立方体内。再进行屏幕映射求屏幕上的像素坐标(Screenx,Screeny):
S c r e e n x = c l i p x ⋅ p i x e l W i d t h 2 ⋅ c l i p w + p i x e l W i d t h 2 Screen_x=frac{clip_xcdot pixelWidth}{2cdot clip_w}+frac{pixelWidth}{2} Screenx​=2⋅clipw​clipx​⋅pixelWidth​+2pixelWidth​
S c r e e n y = c l i p y ⋅ p i x e l H e i g h t 2 ⋅ c l i p w + p i x e l H e i g h t 2 Screen_y=frac{clip_ycdot pixelHeight}{2cdot clip_w}+frac{pixelHeight}{2} Screeny​=2⋅clipw​clipy​⋅pixelHeight​+2pixelHeight​

  • clip_x:裁剪空间中顶点的 x 分量
  • clip_y:裁剪空间中顶点的 y 分量
  • clip_w:裁剪空间中顶点的 w 分量
  • pixelWidth:屏幕宽度像素分辨率
  • pixelHeight:屏幕高度像素分辨率

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/902054.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号