渲染流水线
1.有什么用
染流水线的最终目的在于生成或者说是渲染一张二维纹理,即我们在电脑屏幕上看到的所有效果。它的输入是一个虚拟摄像机、一些光源、一些Shader以及纹理等。
2.1.2 什么是渲染流水线
- 渲染流水线的工作任务在于由一个三维场景出发、生成(或者说渲染)一张二维图像(计算机需要从一系列的顶点数据、纹理等信息出发,把这些信息最终转换成一张人眼可以看到的图像。)
- 《Render-Time Rendering, Third Edition》[1]一书中将一个渲染流程分成3个阶段:
- 应用阶段(Applicaion Stage)(通常由CPU负责, 开发者具有绝对控制权)
- 准备好场景数据,比如摄像机的位置、视锥体、场景中的模型,光源信息
- 为了提高渲染性能,我们往往需要做一个粗粒度剔除(culling)工作,以把那些不可见的物体剔除出去,这样就不需要再移交给几何阶段进行处理
- 为每个模型设置渲染状态,包括但不限于它使用的材质(漫反射颜色、高光反射颜色)、使用的纹理、使用的Shader
- 输出渲染图元(渲染所需的几何信息),渲染图元可以是点、线、三角面
- 几何阶段(Geometry Stage)(GPU 处理所有绘制几何相关的事情)
- 负责和每个渲染图元打交道,进行逐顶点、逐多边形的操作
- 顶点坐标变换到屏幕空间
- 一阶段将会输出屏幕空间的二维顶点坐标、每个顶点对应的深度值、着色等相关信息
- 光栅化(Rasterizer Stage)(GPU)
- 一阶段将会输出屏幕空间的二维顶点坐标、每个顶点对应的深度值、着色等相关信息
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aBa7xTbm-1673939609968)(02359BEFAE3340A9BE7C2AC365ED5C21)]
- 一阶段将会输出屏幕空间的二维顶点坐标、每个顶点对应的深度值、着色等相关信息
- 应用阶段(Applicaion Stage)(通常由CPU负责, 开发者具有绝对控制权)
2.2 CPU和GPU之间的通信
应用阶段
- 把数据加载到显存中
- 设置渲染状态。
- 调用Draw Call
2.2.1 把数据加载到显存中
- 所有渲染所需的数据从硬盘(Hard Disk Drive, HDD)中加载到系统内存(Random Access Memory, RAM)
- 网格和纹理等数据又被加载到显卡上的存储空间——显存(Video Random Access Memory, VRAM)中, (显卡对于显存的访问速度更快,而且大多数显卡对于RAM没有直接的访问权利)【顶点的位置信息、法线方向、顶点颜色、纹理坐标】
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LXRjDAqQ-1673939609969)(9307ED5000DF4CEDBCB036DAAC09FEA0)]
2.2.2 设置渲染状态
什么是渲染状态:这些状态定义了场景中的网格是怎样被渲染的?(例如,使用哪个顶点着色器(Vertex Shader)/片元着色器(Fragment Shader)、光源属性、材质等。如果我们没有更改渲染状态,那么所有的网格都将使用同一种渲染状态。)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OsoV0slB-1673939609969)(C7281E9E5B7C4B1EBBB4E4787DAF3C88)]在同一状态下渲染3个网格。由于没有更改渲染状态,因此3个网格的外观看起来像是同一种材质的物体
2.2.2 调用Draw Call
Draw Call就是一个命令,它的发起方是CPU,接收方是GPU
当给定了一个Draw Call时,GPU就会根据渲染状态(例如材质、纹理、着色器等)和所有输入的顶点数据来进行计算,最终输出成屏幕上显示的那些漂亮的像素。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7a8326HR-1673939609970)(B3207EACD5D9440DB0CBA53919C870F3)]
2.3 GPU流水线
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9du7Ywur-1673939609970)(A61DC5AD61244B8CAA6FDBBDDB550083)]
- 顶点着色器(Vertex Shader) 完全可编程 ,通常用于实现顶点的空间变换、顶点着色等功能
- 曲面细分着色器(Tessellation Shader) 一个可选的着色器,它用于细分图元
- 几何着色器(Geometry Shader) 一个可选的着色器,它可以被用于执行逐图元(Per-Primitive)的着色操作,或者被用于产生更多的图元
- 裁剪(Clipping) 目的是将那些不在摄像机视野内的顶点裁剪掉,并剔除某些三角图元的面片。这个阶段是可配置的。
- 屏幕映射(Screen Mapping) 这一阶段是不可配置和编程的,它负责把每个图元的坐标转换到屏幕坐标系中。
- 三角形设置(Triangle Setup)和 三角形遍历(Triangle Traversal)
- 片元着色器(Fragment Shader)则是完全可编程的,它用于实现逐片元(Per-Fragment)的着色操作
- 逐片元操作(Per-Fragment Operations) 例如修改颜色、深度缓冲、进行混合等,它不是可编程的,但具有很高的可配置性。
2.3.2 顶点着色器
顶点着色器(Vertex Shader)是流水线的第一个阶段
输入:CPU
处理单位:顶点
主要工作:坐标变换和逐顶点光照 还可以输出后续阶段所需的数据
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-noWhO2b7-1673939609970)(C041B5BD26374BC2A466A4538BEA4CCC)]
- 把顶点坐标从模型空间转换到齐次裁剪空间
o.pos = mul(UNITY_MVP, v.position);
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R48n0YtK-1673939609971)(385D4846DDF549438628D836495D6408)]
2.3.3 裁剪
设备坐标(Normalized Device Coordinates , NDC)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-okakOn7G-1673939609971)(D460104A032C4618B275753FCB52A9E0)]
不可编程,是硬件上饿固定操作,可以自定义一个裁剪操作来对这一步进行配置
- 获得一个三维坐标系下的坐标(范围在单位立方体内)
2.3.4 屏幕映射
- 屏幕映射(ScreenMapping)的任务是把每个图元的x和y坐标转换到屏幕坐标系(Screen Coordinates)下。与显示画面的分辨率有关系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WWjcPmCn-1673939609971)(437A7F6C84CC4C269FA9E607C009AEC5)] - 屏幕映射得到的屏幕坐标决定了这个顶点对应屏幕上哪个像素以及距离这个像素有多远。
- 屏幕坐标系在OpenGL和DirectX之间的差异问题
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hp9UWDFI-1673939609972)(A8B1C9A1D78F4B0C9BB09F8BC0F382D4)]
最终输出屏幕坐标系下的顶点位置以及和它们相关的额外信息,如深度值(z坐标)、法线方向、视角方向等
2.3.5 三角形设置
目标:计算每个图元覆盖了哪些像素,以及为这些像素计算它们的颜色。
- 三角形设置(Triangle Setup) 计算光栅化一个三角网格所需的信息,因为上一个阶段输出的都是三角网格的顶点,即我们得到的是三角网格每条边的两个端点
2.3.6 三角形遍历
- 三角形遍历(Triangle Traversal)
将会检查每个像素是否被一个三角网格所覆盖.如果被覆盖的话,就会生成一个片元(fragment) ,这样找到哪些像素被三角网格覆盖的过程就是三角形遍历,也被成为扫描变换(Scan Conversion)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ginvpoln-1673939609972)(BE824E5EEDE0437EBDE78CC011E90B7E)]
得到一个包含很多状态(包括但不限于屏幕坐标、深度信息,以及其他从几何阶段输出的顶点信息,例如法线、纹理坐标等)的片元序列。 是插值得到的结果
2.3.7 片元着色器
片元着色器(Fragment Shader)是另一个非常重要的可编程着色器阶段。在DirectX中,片元着色器被称为像素着色器(Pixel Shader)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tmJxXis5-1673939609972)(15541966A66D41D7980C6B3E8E7FC6DD)]
很多重要的渲染技术会在这一阶段完成
- 在片元着色器中进行纹理采样(在顶点着色器阶段输出每个顶点对应的纹理坐标,然后经过光栅化阶段对三角网格的3个顶点对应的纹理坐标进行插值后,就可以得到其覆盖的片元的纹理坐标了)
- 仅影响单个片元
2.3.8 逐片元操作
逐片元操作(Per-Fragment Operations)是OpenGL中的说法,在DirectX中,这一阶段被称为输出合并阶段(Output-Merger)。需要指明的是,逐片元操作阶段是高度可配置性的,即我们可以设置每一步的操作细节。
- 决定每个片元的可见性。这涉及了很多测试工作,例如深度测试、模板测试等。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yeyVHTOk-1673939609973)(F6AAEDFF7CBB43D49AFC1839AAFCADC8)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4aNUs1WH-1673939609973)(FF4FB9B800E04D598E67E587DA40A0D5)] - 如果一个片元通过了所有的测试,就需要把这个片元的颜色值和已经存储在颜色缓冲区中的颜色进行合并,或者说是混合。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eGdgA4p4-1673939609973)(777AE6F8F8064AB0B76C55118CCA9CCD)]- 混合操作也是可以高度配置的:开发者可以选择开启/关闭混合功能。如果没有开启混合功能,无法得到透明效果
2.4 一些容易困惑的地方
2.4.1什么是OpenGL/DirectX
- OpenGL和DirectX是图像应用编程接口,这些接口用于渲染二维或三维图形
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zM12Dy2f-1673939609974)(1F095C6ECEEC44908E6E013600FC080B)] - 显卡组成有图像处理单元GPU和显存(Video Random Access Memory,VRAM)GPU可以在显存中存储任何数据,但对于渲染来说一些数据类型是必需的,例如用于屏幕显示的图像缓冲、深度缓冲等
- 显卡驱动是图像编程接口(OpenGL和DirectX)和GPU的支持中介
2.4.2 什么是HLSL、GLSL、CG
着色语言是专门用于编写着色器的,常见的着色语言有DirectX的HLSL(High Level Shading Language)、OpenGL的GLSL(OpenGL Shading Language)以及NVIDIA的CG(C for Graphic)。HLSL、GLSL、CG都是“高级(High-Level)”语言,但这种高级是相对于汇编语言来说的,而不是像C#相对于C的高级那样。这些语言会被编译成与机器无关的汇编语言,也被称为中间语言(Intermediate Language, IL)。这些中间语言再交给显卡驱动来翻译成真正的机器语言,即GPU可以理解的语言。
2.4.3 什么是Draw Call
Draw Call本身的含义很简单,就是CPU调用图像编程接口,如OpenGL中的glDrawElements命令或者DirectX中的DrawIndexedPrimitive命令,以命令GPU进行渲染的操作。
- 问题一:CPU和GPU是如何实现并行工作的?
使用一个命令缓冲区(Command Buffer)解决GPU和CPU并行工作。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XRlVdxRi-1673939609974)(0B16D24CD4F641C0956D0E65DB1682F0)] - 问题二:为什么Draw Call多了会影响帧率?
渲染速度往往快于CPU提交命令的速度。如果Draw Call的数量太多,CPU就会把大量时间花费在提交Draw Call上,造成CPU的过载
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5bbLAlWf-1673939609975)(EFACAFBE775040F59D1A3B1062235CBD)] - 问题三:如何减少Draw Call?
批处理(Batching)
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-254ofpXE-1673939609975)(F9E4BF20C7974CC89C21BD1699F49ADC)]
在游戏开发过程中,为了减少Draw Call开销的注意点:
- 避免使用大量很小的网格。当不可避免地需要使用很小的网格结构时,考虑是否可以合并它们。
- 避免使用过多的材质。尽量在不同的网格之间共用同一个材质。
2.4.4 什么是固定管线渲染
固定函数的流水线(Fixed-Function Pipeline),也简称为固定管线,通常是指在较旧的GPU上实现的渲染流水线。这种流水线只给开发者提供一些配置操作,但开发者没有对流水线阶段的完全控制权。
2.5 什么是Shader?
- GPU流水线上一些可高度编程的阶段,而由着色器编译出来的最终代码是会在GPU上运行的(对于固定管线的渲染来说,着色器有时等同于一些特定的渲染设置)
- 有一些特定类型的着色器,如顶点着色器、片元着色器等
- 依靠着色器我们可以控制流水线中的渲染细节,例如用顶点着色器来进行顶点变换以及传递数据,用片元着色器来进行逐像素的渲染。
- 出色的游戏画面是需要包括Shader在内的所有渲染流水线阶段的共同参与才可完成:设置适当的渲染状态,使用合适的混合函数,开启还是关闭深度测试/深度写入等。
第3章 Unity Shader基础
3.1 Unity Shader概述
3.1.1 材质和Unity Shader
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hx3Yb3bt-1673939609975)(7F8519941E304C1196398104E8F80D72)]
3.1.2 Unity中的的材质
Unity的材质和许多建模软件(如Cinema 4D、Maya等)中提供的材质功能类似,它们都提供了一个面板来调整材质的各个参数
3.1.3 Unity中的Shader
- Standard Surface Shader(产生一个包含了标准光照模型的表面着色器模板)
- Unlit Shader(会产生一个不包含光照(但包含雾效)的基本的顶点/片元着色器)
- Image Effect Shader(实现各种屏幕后处理效果提供了一个基本模板)
- Compute Shader()
- Ray Tracing Shader
3.2 Unity Shader的基础:ShaderLab
ShaderLab是Unity提供的编写Unity Shader的一种说明性语言
Shader "ShaderName"
{
Properties{
//属性
}
SubShader{
//显卡A使用的子着色器
}
SubShader{
//显卡B使用的子着色器
}
Fallback "VertexLit"
}
3.3 Unity Shader的结构
3.3.1 Shader命名
第一行通过Shader语义指定改 Unity Shader的名字
Shader "Custom/MyShader" {}
3.3.2 材质和Unity Shader的桥梁:Properties
Shader "Custom/ShaderLabProperties"
{
Properties{
_Int ("Int", Int) = 2
_Float ("Float", Float) = 1.5
_Range ("Range", Range(0.0, 5.0)) = 3.0
_Color ("Color", Color) = (1, 1, 1, 1)
_Vector ("Vector", Vector) = (2, 3, 6, 1)
_2D("2D", 2D) = "" {}
_Cube ("Cube", Cube) = "white" {}
_3D ("3D", 3D) = "black" {}
}
FallBack "Disffuse"
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-YS3kHkOw-1673939609976)(F2B978F613C3408CAF5AB19BDA4840F4)]
Properties语义块的作用仅仅是为了让这些属性可以出现在材质面板中,即使我们不在Properties语义块中声明这些属性,也可以直接在CG代码片中定义变量
3.3.3 重量级成员:SubShader
一个Unity Shader文件至少要有一个SubShader
SubShader{
//可选的
[Tags]
//可选的
[RenderSetup]
Pass{
}
//Other Passes
}
-
状态设置 ShaderLab提供了一系列渲染状态的设置指令,这些指令可以设置显卡的各种状态,例如是否开启混合/深度测试等
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7lQyjvXa-1673939609977)(48AA23C879844EB78C4F9B5C9D2E571C)] -
SubShader的标签 SubShader的标签(Tags)是一个键值对(Key/Value Pair),用来告诉Unity的渲染引擎:SubShader我希望怎样以及何时渲染这个对象。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lxpuHbVE-1673939609977)(B46C63B4F73D4A0B9CF8EA16F4E6A7DD)]
- Pass语义块
语法:
Pass{
[Name]
[Tags]
[RenderSetup]
//Other code
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PthJokDl-1673939609978)(2BB12844CACC42E9816D06581110D83C)]
- UsePass:如我们之前提到的一样,可以使用该命令来复用其他Unity Shader中的Pass(Pass名字要是大写)
- GrabPass:该Pass负责抓取屏幕并将结果存储在一张纹理中,以用于后续的Pass处理
3.3.4 留一条后路:Fallback
Fallback "name"
Fallback Off
Fallback还会影响阴影的投射
3.3.5 ShaderLab还有其他的语义吗
- CustomEditor语义来扩展编辑界面
- Category语义来对Unity Shader中的命令进行分组
3.4 Unity Shader的形式
- 设置渲染状态
- 指定各种着色器所需的代码
- SubShader语义块中(表面着色器的做法)
- Pass语义块中(顶点/片元着色器和固定函数着色器的做法)
Shader "MyShader"
Properties{
//所需的各种属性
}
SubShader{
//真正意义上的Shader代码会出现在这里
//表面着色器(Surface Shader)
//顶点/片元着色器(Vertex/Fragment Shader)
//固定函数着色器(Fixed Function Shader)
}
SubShader{
//和上一个SubShader类似
}
3.4.1 Unity的宠儿:表面着色器
表面着色器(Surface Shader)是Unity对顶点/片元着色器的更高一层的抽象,Unity处理了很多光照细节
3.4.2 最聪明的孩子:顶点/片元着色器
我们可以使用CG/HLSL语言来编写顶点/片元着色器(Vertex/Fragment Shader),灵活性很高,可以控制渲染的实现细节
3.4.3 被抛弃的角落:固定函数着色器
3.4.4 选择哪种Unity Shader形式
- 除非你有非常明确的需求必须要使用固定函数着色器,例如需要在非常旧的设备上运行你的游戏(这些设备非常少见),否则请使用可编程管线的着色器,即表面着色器或顶点/片元着色器。
- 如果你想和各种光源打交道,你可能更喜欢使用表面着色器,但需要小心它在移动平台的性能表现。
- 如果你需要使用的光照数目非常少,例如只有一个平行光,那么使用顶点/片元着色器是一个更好的选择。
- 最重要的是,如果你有很多自定义的渲染效果,那么请选择顶点/片元着色器。
3.6 答疑解惑
3.6.1 Unity Shader ! = 真正的Shader
在Unity Shader(或者说是ShaderLab文件)里,我们可以做的事情远多于一个传统意义上的Shader
- 在传统的Shader中,我们仅可以编写特定类型的Shader,例如顶点着色器、片元着色器等。而在Unity Shader中,我们可以在同一个文件里同时包含需要的顶点着色器和片元着色器代码。
- 在传统的Shader中,我们无法设置一些渲染设置,例如是否开启混合、深度测试等,这些是开发者在另外的代码中自行设置的。而在Unity Shader中,我们通过一行特定的指令就可以完成这些设置。
- 在传统的Shader中,我们需要编写冗长的代码来设置着色器的输入和输出,要小心地处理这些输入输出的位置对应关系等。而在Unity Shader中,我们只需要在特定语句块中声明一些属性,就可以依靠材质来方便地改变这些属性。而且对于模型自带的数据(如顶点位置、纹理坐标、法线等), Unity Shader也提供了直接访问的方法,不需要开发者自行编码来传给着色器。
3.6.2 Unity Shader和CG/HLSL之间的关系
- Unity Shader是用ShaderLab语言编写的
- 可以在ShaderLab内部嵌套CG/HLSL语言来编写表面着色器和顶点/片元着色器
- CG/HLSL代码是嵌套在CGPROGRAM和ENDCG之间
- Unity Shader只有两种形式:顶点/片元着色器和固定函数着色器
Pass{
//Pass的标签和状态设置
CGPROGRAM
//编译指令,例如
#pragma vertex vert
#pragma fragment frag
//CG代码
ENDCG
//其他的一些设置
}
第4章 学习Shader所需的数学基础
4.2.1 二维笛卡儿坐标系
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ue4OIilN-1673939609979)(1D61C440C3814967A889E554D4B88ECA)]在屏幕映射时,OpenGL和DirectX使用了不同方向的二维笛卡儿坐标系
- 从某种意义上来说,所有的二维笛卡儿坐标系都是等价的
4.2.2 三维笛卡儿坐标系
- 基矢量(basis vector) xyz轴
- 标准正交基(orthonormal basis)互相垂直的,且长度为1的基矢量
- 正交基(orthogonal basis)互相垂直但长度不为1
- 三维笛卡儿坐标系并不都是等价的
4.2.3 左手坐标系和右手坐标系
- 左手法则 举起你的左手,握拳,伸出大拇指让它指向旋转轴的正方向,那么旋转的正方向就是剩下4个手指的弯曲方向。右手同理
4.2.4 Unity使用的坐标系
问题:在Unity中,新建的场景中主摄像机的位置位于世界空间中的(0, 1, -10)位置。在不改变摄像机的任何设置(如保持Rotation为(0, 0, 0), Scale为(1, 1,1))的情况下,在世界空间中的(0, 1, 0)位置新建一个球体,在摄像机的观察空间下,该球体的z值是多少?在摄像机的模型空间下,该球体的z值又是多少?
答案:
-10。10。这是因为,在Unity中,模型空间使用的是左手坐标系。球体所在的位置位于摄像机模型空间中的z轴正方向,因此在模型空间下其z值为10。而观察空间使用的右手坐标系,摄像机的正前方是z轴的负方向,因此在观察空间下其z值为-10。
4.3 点和矢量
- 点(point)是n维空间(游戏中主要使用二维和三维空间)中的一个位置
- 矢量(vector,也被称为向量)指n维空间中一种包含了模(magnitude)和方向(direction)的有向线段
- 矢量的模指的是这个矢量的长度。一个矢量的长度可以是任意的非负数
- 矢量的方向则描述了这个矢量在空间中的指向
- 矢量的头(head)指的是它的箭头所在的端点处,而尾(t ail)指的是另一个端点处
- 通常,矢量被用于表示相对于某个点的偏移(displacement),也就是说它是一个相对量
4.3.1 点和矢量的区别
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fcBZtkN2-1673939609979)(C5BC0E8A746E4654A17D03B554CA0D6B)]
- 矢量和标量的乘法/除法
- 矢量的加法和减法(想要计算b点相对于a点的位移,用b减去a
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CLaYODzn-1673939609980)(C7951FE075714ECE8C5A3107124C5373)] - 矢量的模
- 单位矢量 单位矢量也被称为被归一化的矢量(normalized vector)。对任何给定的非零矢量,把它转换成单位矢量的过程就被称为归一化(normalization)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E5xNcSfy-1673939609981)(622EC5D8971C433AA0DCA247CB396F4E)] - 矢量的点积
- 点积(dot product,也被称为内积,inner product)
- 叉积(cross product,也被称为外积,outer product)
第2篇 初级篇
第5章
5.3 Unity提供的内置文件和变量
- 包含文件(include file)
CGPROGRAM
// ...
#include "UnityCG.cginc"
// ...
ENDCG
找到UnityCG.cginc文件并查看部分结构体的声明和函数定义
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NWY2UfiC-1673939609982)(C93723633F674EAFA5BC0AA00BAF9D50)]
- UnityCG.cginc中一些常用的结构体
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3232KS0H-1673939609982)(90F7B2D0A9A249338FA148DF9D1A4BA8)] - UnityCG.cginc中一些常用的帮助函数
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lzdzsh8V-1673939609983)(7FA8E14507A545D0B000F556A9A1B956)]
5.4 Unity提供的CG/HLSL语义
微软DirectX文档链接 https://msdn.microsoft.com/en-us/library/windows/desktop/bb509647(v=vs.85).aspx#VS
语义实际上就是一个赋给Shader输入和输出的字符串,这个字符串表达了这个参数的含义。通俗地讲,这些语义可以让Shader知道从哪里读取数据,并把数据输出到哪里,它们在CG/HLSL的Shader流水线中是不可或缺的。
5.4.2 Unity支持的语义
- 从应用阶段传递模型数据给顶点着色器时Unity支持的常用语义
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6K0douPW-1673939609983)(369ABBE24A3F4DF8AD1760F8F52CF5E9)] - 从顶点着色器传递数据给片元着色器时Unity使用的常用语义
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SE080UA5-1673939609984)(A6021B3F3B9E4E06B1CDDBFE91F1CD16)] - 片元着色器输出时Unity支持的常用语义
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1qzk2u1u-1673939609984)(29016A7BE44B45D580AF072CE64C2886)]
5.4.3 如何定义复杂的变量类型
一个语义可以使用的寄存器只能处理4个浮点值(float)。
struct v2f {
float4 pos : SV_POSITION;
fixed3 color0 : COLOR0;
fixed4 color1 : COLOR1;
half value0 : TEXCOORD0;
float2 value1 : TEXCOORD1;
};
5.6 小心:渲染平台的差异
5.6.1 渲染纹理的坐标差异
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aAIvMeVt-1673939609984)(FDF3FF4E714943EC91AD8CAE561CF7EF)]
5.6.2 Shader的语法差异
5.6.3 Shader的语义差异
5.7 Shader整洁之道
5.7.1 float、half还是fixed
- CG/HLSL中3种精度的数值类型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S8j3bpW7-1673939609985)(3CBF25F26827408BBE94E6572EF3FD68)]
上面的精度范围并不是绝对正确的,尤其是在不同平台和GPU上,它们实际的精度可能和上面给出的范围不一致。通常来讲。 - 尽可能使用精度较低的类型,因为这可以优化Shader的性能,这一点在移动平台上尤其重要。
- 我们可以使用fixed类型来存储颜色和单位矢量,如果要存储更大范围的数据可以选择half类型,最差情况下再选择使用float。
5.7.2 规范语法
5.7.3 避免不必要的计算
5.7.4 慎用分支和循环语句
5.7.5 不要除以0
6.4 在Unity Shader中实现漫反射光照模型
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MeZtTQwN-1673939609985)(6580FDBE458245668FED828A8D5DE27B)]
- 入射光线的颜色和强度clight
- 材质的漫反射系数mdiffuse
- 表面法线n^
- 以及光源方向I^