Unity图形学Shader快速回顾
参考知识点来源于: 人间自有韬哥在, 唐老狮,窗外听轩雨 , 呆呆敲代码的小Y little_fat_sheep, AitTech, DeepSeek, 百度, 豆包
目录
- 一、渲染管线
- 1.应用阶段
- 2.几何阶段
- 3.光栅化阶段
- 二、矩阵的几何意义
- 1. 平移
- 2. 旋转
- 3. 缩放
- 4.复合运算
- 三、坐标空间的变换
- 3.1.模型空间→世界空间
- 3.2.世界空间→观察空间
- 3.3.观察空间→裁剪空间
- 3.4.裁剪空间→屏幕空间
- 四、Unity Shader
- 1.基本结构
- 1.1顶点片元着色器
- 1.2.表面着色器
- 2.Shader的属性
- 3.Tags渲染标签
- 3.1.渲染队列 RenderQueue
- 3.2.渲染类型 RenderType
- 3.3.禁用批处理 DisableBatching
- 3.4.禁止阴影投影 ForceNoShadowCasting
- 3.5.忽略投影机 Projector IgnoreProjector
- 3.6.其他标签
- 4.States渲染状态
- 4.1.剔除方式
- 4.2.深度缓冲
- 4.3.深度测试
- 4.4.混合方式
- 4.5.其他渲染状态
- 5.Pass渲染通道
- 5.1.SubShader 与 Pass 的关系
- 5.2.Pass 的相关内容
- 五、CG语法
- 1.CG-基础数据类型
- 1.1.基础数据类型
- 1.2.基础复合数据类型
- 1.3.特殊数据类型
- 2.Swizzle操作符
- 3.CG-运算符
- 4.CG语法流程控制语句
- 4.1.条件分支语句
- 4.2.循环语句
- 5.CG-函数
- 5.1. 无返回值的函数
- 5.2. 有返回值的函数
- 6.CG语法语义
- 7.ShaderLab属性类型和CG变量类型的匹配关系
- 8.CG语法内置函数
- 9.CG-内置文件
- 六、光照模型
- 1. 兰伯特光照模型(Lambertian Lighting Model)
- 2. 半兰伯特光照模型(Half Lambert Lighting Model)
- 3. Phong 式高光反射模型(Phong Specular Reflection Model)
- 4. Blinn-Phong 式高光反射模型(Blinn-Phong Specular Reflection Model)
- 5. Phong 光照模型
一、渲染管线
1.应用阶段
- 应用阶段(由CPU处理):为GPU渲染提供几何信息,输出渲染图元。
- 数据的准备:
- 剔除不需要的数据,如视锥体剔除、遮挡剔除、层级剔除等。
- 根据UI对象在Hierarchy面板深度值顺序(DFS深度优先搜索)设置渲染顺序,其余物体按离摄像机先近后远排队。
- 先将渲染数据从硬盘读取到主存,再把GPU渲染所需数据打包发给显存。
- 设置渲染状态:设置着色器、纹理、材质、灯光等渲染状态,即SetPassCall,告诉GPU用什么渲染模型,SetPassCall次数影响性能。
- 发送DrawCall:CPU调用图形API接口(如glDrawElements或DrawIndexedPrimitive)命令GPU对指定物体渲染,即DrawCall,告诉GPU使用哪个模型数据。
- 性能指标:
- DrawCall:CPU调用图形API命令GPU渲染的操作。
- SetPassCall:设置/切换一次渲染状态。
- Batch:加载数据到显存、设置渲染状态、CPU调用GPU渲染的过程,一个Batch至少包含一个DrawCall。
- 数据的准备:
2.几何阶段
- . 几何阶段(由GPU处理):处理几何相关绘制,将顶点坐标变换到屏幕空间。
- 顶点着色器:流水线第一个可编程阶段,输入顶点信息,每个顶点调用一次。主要工作是坐标转换(将顶点坐标从模型空间转换到齐次裁剪空间)和可选的逐顶点光照(计算输出顶点颜色值)。
- 裁剪:剔除摄像机视野外的物体。图元与摄像机关系有完全在视野内、部分在视野内、完全在视野外三种,完全在视野内的传递给下阶段,完全在视野外的舍弃,部分在视野内的进行裁剪,生成新顶点并舍弃外部顶点。
- 屏幕映射:将实际场景对象映射到屏幕上,对坐标进行放缩。
3.光栅化阶段
- . 光栅化阶段(由GPU处理):使用上阶段数据产生屏幕像素,渲染最终图像。
- 三角形设置:为光栅化提供计算信息,如计算三角形网格边的表达式,判断像素点是否被覆盖。
- 三角形遍历:遍历像素点,判断是否被三角网格覆盖,若覆盖则生成片元(包含屏幕坐标、深度值、法线、纹理等状态信息,由三角形顶点信息插值得到),输出片元序列。
- 片元着色器:可编程着色器阶段,输入是顶点信息插值结果,输出片元颜色值,重要技术是纹理采样(顶点着色器输出纹理坐标,经光栅化阶段插值得到片元纹理坐标),但仅影响单个片元。
- 逐片元操作(OpenGL)/输出合并阶段(DirectX):
- 决定可见性:进行深度测试、模板测试等,判断片元是否可见,通过所有测试的片元才能进入合并阶段。
- 模板测试:开启后,GPU读取模板缓冲区值与参考值比较,根据开发者指定比较函数判断片元是否舍弃,可根据测试结果修改模板缓冲区,常用于限制渲染区域。
- 深度测试:开启后,GPU比较片元深度值和深度缓冲区值,根据开发者设置的比较函数判断片元是否舍弃,通过测试后开发者可指定是否用片元深度值覆盖原有缓冲区深度值。
- 合并操作:通过测试的片元与颜色缓冲区颜色合并。不透明物体可关闭混合,片元颜色直接覆盖原有像素值;半透明物体需混合操作,GPU取出源颜色(片元着色器颜色)和目标颜色(颜色缓冲区颜色)混合。
- 提前测试:为提高性能,大多数GPU尽可能在片元着色器之前进行测试,如Unity渲染流水线中的Early-Z技术(提前深度测试),但可能与片元着色器操作冲突。
二、矩阵的几何意义
1. 平移
2. 旋转
3. 缩放
4.复合运算
- 在进行平移、旋转、缩放的复合运算时,绝大多数情况下,我们约定的变换顺序为:先缩放、再旋转、后平移
- 在进行x轴、y轴、z轴旋转的复合运算时绝大多数情况下,我们约定的变换顺序为:z->x->y,之后我们在Unity中进行Shader开发时,遵从这两个规则即可。
三、坐标空间的变换
变换顺序:模型空间→世界空间→观察空间→裁剪空间→屏幕空间
3.1.模型空间→世界空间
相对世界坐标系的位置 = 平移矩阵 * 旋转矩阵 * 缩放矩阵 * 模型坐标下的点的列矩阵
3.2.世界空间→观察空间
3.3.观察空间→裁剪空间
3.4.裁剪空间→屏幕空间
四、Unity Shader
1.基本结构
1.1顶点片元着色器
//Shader "着色器名字"
{ //第二部分Properties{//材质面板上可以看到的属性}//第三部分SubShader{//顶点-片段着色器 或 表面着色器 或 固定函数着色器// 渲染标签Tags { "标签名1" = "标签值1" "标签名2" = "标签值2" // 可继续添加更多标签}// 渲染状态// 渲染通道Pass{// 第一个渲染通道CGPROGRAM//先通过编译指令指定实现顶点着色器的指定函数#pragma vertex myVert//先通过编译指令指定实现片元着色器的指定函数#pragma fragment myFrag//顶点着色器 回调函数 //POSITION 和 SV_POSITION是CG语言的语义//POSITION:把模型的顶点坐标填充到输入的参数v当中//SV_POSITION:顶点着色器输出的内容是裁剪空间中的顶点坐标//如果没有这些语义来限定输入和输出参数的话,那么渲染器就完全不知道用户输入输出的是什么,就会得到错误的效果float4 myVert(float4 v:POSITION):SV_POSITION{//mul是CG语言提供的矩阵和向量的乘法运算函数(就是一个内置的函数)//UNITY_MATRIX_MVP 代表一个变换矩阵 是Unity内置的模型、观察、投影矩阵的集合//UnityObjectToClipPos它的作用和之前的矩阵乘法是一样的,主要目的就是在进行坐标变换 只不过新版本将其封装起来了 使用更加方便//return mul(UNITY_MATRIX_MVP,v);return UnityObjectToClipPos(v);}//片元着色器 回调函数//SV_Target:告诉渲染器,把用户输出颜色存储到一个渲染目标中,这里将输出到默认的帧缓存中fixed4 myFrag():SV_Target{return fixed4(0, 1, 0, 1); //返回绿色}ENDCG}Pass{// 第二个渲染通道}// 可继续添加更多Pass}SubShader{//更加精简的版本//目的是适配旧设备}// 可以有n个SubShader代码块//第四部分Fallback "备用的Shader"
}
1.2.表面着色器
//Shader "ShaderTeach/Lesson10_NewSurfaceShader"
{ //第二部分Properties{//材质面板上可以看到的属性_Color ("Color", Color) = (1,1,1,1) _MainTex ("Albedo (RGB)", 2D) = "white" {} _Glossiness ("Smoothness", Range(0,1)) = 0.5 _Metallic ("Metallic", Range(0,1)) = 0.0 }//第三部分SubShader{// 表面着色器// 渲染标签Tags { "RenderType" = "Opaque" }// 渲染状态LOD 200 // 渲染通道(表面着色器逻辑区域)CGPROGRAM// 相关编译指令#pragma surface surf Standard fullforwardshadows #pragma target 3.0 // 纹理采样器声明sampler2D _MainTex; // 输入结构体声明struct Input{float2 uv_MainTex; };// 变量声明half _Glossiness; half _Metallic; fixed4 _Color; // 实例化相关区域UNITY_INSTANCING_BUFFER_START(Props)// 可放置更多实例属性UNITY_INSTANCING_BUFFER_END(Props)// 表面着色器函数声明(无具体实现内容)void surf (Input IN, inout SurfaceOutputStandard o){// Albedo comes from a texture tinted by colorfixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;o.Albedo = c.rgb;// Metallic and smoothness come from slider variableso.Metallic = _Metallic;o.Smoothness = _Glossiness;o.Alpha = c.a;}ENDCG}//第四部分Fallback "Diffuse"
}
2.Shader的属性
ShaderLab主要由Shader名字、属性、1至多个子着色器、备用Shader四部分构成。其中,Shader的属性作用关键,为提升Shader可调节性,将部分变量作为开放属性显示于材质面板,供使用者调节。它有两个特点:一是可在材质面板编辑;二是能作为输入变量供所有子着色器使用。
属性声明位于Shader语句块的Properties属性语句块中,语法为_Name("Display Name", type) = defaultValue[{options}]
,各部分含义如下:
_Name
:属性名,前加下划线便于获取。Display Name
:材质面板显示名。type
:属性类型。defaultValue
:指定给材质时的默认值。
属性主要分为三类:
- 数值类型
- 整形:
_Name("Display Name", Int) = number
。 - 浮点型:
_Name("Display Name", Float) = number
(Unity中虽有Int,但编译时会转Float,故多用Float)。 - 范围浮点型:
_Name("Display Name", Range(min,max)) = number
。
- 整形:
- 颜色和向量类型
- 颜色:
_Name("Display Name", Color) = (number1,number2,number3,number4)
,RGBA取值范围0 - 1(映射0 - 255)。 - 向量:
_Name("Display Name", Vector) = (number1,number2,number3,number4)
,XYZW取值无限制 。
- 颜色:
- 纹理贴图类型
-
2D纹理:最常用,如漫反射、法线贴图,
_Name("Display Name", 2D) = "defaulttexture" {}
。 -
2DArray纹理:纹理数组,一般脚本创建,较少用,
_Name("Display Name", 2DArray) = "defaulttexture" {}
。 -
Cube map texture纹理:立方体纹理,如天空盒、反射探针,
_Name("Display Name", Cube) = "defaulttexture" {}
。 -
3D纹理:一般脚本创建,极少用,
_Name("Display Name", 3D) = "defaulttexture" {}
。 -
关于 defaulttexture 默认值取值:
- 不写:默认贴图为空。
white
:默认白色贴图(RGBA: 1,1,1,1)。black
:默认黑色贴图(RGBA: 0,0,0,1)。gray
:默认灰色贴图(RGBA: 0.5,0.5,0.5,1)。bump
:默认凸贴图(RGBA: 0.5,0.5,1,1),一般用于法线贴图默认贴图。red
:默认红色贴图(RGBA: 1,0,0,1)。
-
3.Tags渲染标签
3.1.渲染队列 RenderQueue
- 作用:确定物体的渲染顺序。
- 语法:
Tags{ "Queue" = "标签值" }
- 常用预定义标签值及队列号:
Background
(背景,队列号:1000):最早被渲染的物体队列,常用于渲染天空盒或背景。示例:Tags{ "Queue" = "Background" }
Geometry
(几何,队列号:2000):不透明几何体默认队列,未声明渲染队列时,Unity 会默认使用。示例:Tags{ "Queue" = "Geometry" }
AlphaTest
(透明测试,队列号:2450):有透明通道且需进行 Alpha 测试的几何体使用,在所有 Geometry 队列实体绘制完后再绘制,效率更高。示例:Tags{ "Queue" = "AlphaTest" }
Transparent
(透明的,队列号:3000):半透明物体渲染队列,几何体按由远到近顺序绘制,如玻璃材质、粒子特效等。示例:Tags{ "Queue" = "Transparent" }
Overlay
(覆盖,队列号:4000):最后渲染的队列,用于叠加渲染效果,如镜头光晕等。示例:Tags{ "Queue" = "Overlay" }
- 自定义队列:基于预定义渲染队列标签进行加减运算定义,加减号中间不要有空格,不能在 Shader 中直接赋值数字,若需直接赋值可在材质面板设置。示例:
Tags{ "Queue" = "Geometry+1" }
队列号为 2001Tags{ "Queue" = "Transparent-1" }
队列号为 2999
3.2.渲染类型 RenderType
- 作用:对着色器进行分类,以便利用着色器替换功能,可通过摄像机 API 指定渲染类型替换其他着色器。
- 语法:
Tags{ "RenderType" = "标签值" }
- 常用预定义标签值:
Opaque
(不透明的):用于普通 Shader,如不透明、自发光、反射等。示例:Tags{ "RenderType" = "Opaque" }
Transparent
(透明的):用于半透明 Shader,如透明、粒子。示例:Tags{ "RenderType" = "Transparent" }
TransparentCutout
(透明切割):用于透明测试 Shader,如植物叶子。示例:Tags{ "RenderType" = "TransparentCutout" }
Background
(背景):用于天空盒 Shader。示例:Tags{ "RenderType" = "Background" }
Overlay
(覆盖):用于 GUI 纹理、Halo(光环)、Flare(光晕)。示例:Tags{ "RenderType" = "Overlay" }
- 了解即可的标签值:
TreeOpaque
、TreeTransparentCutout
、TreeBillboard
、Grass
、GrassBillboard
等,用于地形系统中的不同元素。
3.3.禁用批处理 DisableBatching
- 作用:解决使用批处理时,某些使用模型空间顶点数据的 Shader 无法实现预期效果的问题。
- 语法及取值:
Tags{ "DisableBatching" = "True" }
:总是禁用批处理Tags{ "DisableBatching" = "False" }
:不禁用批处理(默认值)Tags{ "DisableBatching" = "LODFading" }
:当 LOD(层级细节)效果激活时才禁用批处理,主要用于地形系统上的树
3.4.禁止阴影投影 ForceNoShadowCasting
- 作用:控制该 SubShader 的物体是否投射阴影。
- 语法及取值:
Tags{ "ForceNoShadowCasting" = "True" }
:不投射阴影Tags{ "ForceNoShadowCasting" = "False" }
:投射阴影(默认值)
3.5.忽略投影机 Projector IgnoreProjector
- 作用:确定物体是否受 Projector(投影机)投射影响。
- 语法及取值:
Tags{ "IgnoreProjector" = "True" }
:忽略 Projector,一般半透明 Shader 需要开启Tags{ "IgnoreProjector" = "False" }
:不忽略 Projector(默认值)
3.6.其他标签
- 是否用于精灵:
Tags{ "CanUseSpriteAtlas" = "False" }
- 预览类型:
Tags{ "PreviewType" = "Panel" }
:材质预览窗口从默认球形改为平面Tags{ "PreviewType" = "SkyBox" }
:材质预览窗口从默认球形改为天空盒
4.States渲染状态
4.1.剔除方式
- 作用:决定模型正面或背面是否被渲染。
- 语法及取值:
Cull Back
:背面剔除(默认)。Cull Front
:正面剔除。Cull Off
:不剔除。
4.2.深度缓冲
- 作用:控制是否写入深度缓冲,深度缓冲是与屏幕像素对应的缓冲区,存储每个像素的深度值。
- 语法及取值:
ZWrite On
:写入深度缓冲(默认)。ZWrite Off
:不写入深度缓冲,常用于做透明等特殊效果时。
4.3.深度测试
- 作用:确保像素按正确深度顺序绘制,创建正确的遮挡关系和透视效果。
- 测试流程:渲染前深度缓冲初始化为最大深度值,渲染时每个像素的深度值与深度缓冲对应位置的值比较。若当前像素深度值小于深度缓冲中的值,说明在其他物体之前,会被绘制并更新深度缓冲;若大于等于,则会被丢弃,不绘制且保持深度缓冲不变。
- 语法及取值:
ZTest Less
:小于当前深度缓冲中的值,通过测试并写入深度缓冲。ZTest Greater
:大于当前深度缓冲中的值,通过测试并写入深度缓冲。ZTest LEqual
:小于等于当前深度缓冲中的值,通过测试并写入深度缓冲(默认)。ZTest GEqual
:大于等于当前深度缓冲中的值,通过测试并写入深度缓冲。ZTest Equal
:等于当前深度缓冲中的值,通过测试并写入深度缓冲。ZTest NotEqual
:不等于当前深度缓冲中的值,通过测试并写入深度缓冲。ZTest Always
:始终通过深度测试写入深度缓冲。
4.4.混合方式
- 作用:设置渲染图像的混合方式,实现多种颜色叠加混合,如透明、半透明效果和遮挡物体的颜色混合。
- 混合流程:在深度测试之后进行,开始混合时先判断是否开启混合。开启混合则先得到源颜色(片元颜色)和目标颜色(颜色缓冲区中的颜色值),混合后更新颜色缓冲区的值;未开启则直接使用片元颜色值。经过多轮计算,最终留在颜色缓冲区的内容即为屏幕上看到的颜色。
- 语法及取值:
Blend One One
:线性减淡。Blend SrcAlpha OneMinusSrcAlpha
:正常透明混合。Blend OneMinusDstColor One
:滤色。Blend DstColor Zero
:正片叠底。Blend DstColor SrcColor
:X光片效果。Blend One OneMinusSrcAlpha
:透明度混合。(默认不进行混合)
4.5.其他渲染状态
- LOD:控制 LOD 级别,在不同距离下使用不同的渲染方式处理,如
LOD 100
。 - ColorMask:设置颜色通道的写入蒙版,默认蒙版为 RGBA ,如
ColorMask RG
表示只允许红色和绿色通道写入到帧缓冲区,蓝色和 alpha 通道被禁用。
5.Pass渲染通道
5.1.SubShader 与 Pass 的关系
SubShader:可将其理解为一组完整的渲染设置集合。在一个 Shader 中,能够存在多个 SubShader。每个 SubShader 都拥有一整套详细的物体渲染设定,例如 Tags 可用于指定渲染队列等关键信息,States 则可对混合模式、深度测试等进行设置。不同的 SubShader 能够适应不同的硬件条件以及特定的渲染需求,这使得开发者可以根据实际情况灵活选择合适的 SubShader 来实现理想的渲染效果。
Pass:是 SubShader 中的具体渲染步骤。一个 SubShader 中可以包含一个或多个 Pass。每个 Pass 都定义了一轮完整的渲染流程,涵盖了顶点和片元着色器的代码编写、纹理的运用以及各种渲染状态的设定等内容。通过在一个 SubShader 中合理运用多个 Pass,能够实现复杂的渲染效果。例如,为实现物体在不同光照条件下的显示效果,可创建一个 SubShader,并在其中包含两个 Pass,一个用于正常光照下的渲染,另一个用于特殊光照条件(如强光照射)下的渲染;或者为达成物体的立体效果,可在一个 SubShader 中通过多个 Pass 分别从不同角度渲染物体,然后将这些渲染结果进行合成,从而呈现出立体的视觉效果。
5.2.Pass 的相关内容
(1)Pass 的名字:对 Pass 进行命名的主要目的是方便在其他 Shader 中复用该 Pass 的代码。通过 UsePass 命令即可实现复用,使用时需在其他 Shader 中输入 UsePass “Shader 路径/Pass 名”。需要注意的是,Unity 内部会将 Pass 名称自动转换为大写字母,因此在使用 UsePass 命令时必须使用大写形式的名字。例如:
Pass
{Name "MyLesson08Pass"
}
在其他 Shader 中复用该 Pass 代码时,则需写成:
UsePass "ShaderTeach/Lesson08_NewUnlitShader/MYLESSON08PASS"
(2)Pass 中的渲染标签:Pass 中的渲染标签语法虽然与 SubShader 中相同,但 SubShader 语句块中的渲染标签不能在 Pass 中使用,Pass 拥有自己专门的渲染标签。
-
Tags{ “LightMode” = “标签值” }:该标签主要用于指定 Pass 应在哪个阶段执行,通过合理设置标签值,能够将着色器代码分配到合适的渲染阶段,进而实现所需的效果。常见的标签值及其含义如下:
- Always:始终进行渲染,且不应用光照。
- ForwardBase:用于前向渲染,会应用环境光、主方向光、顶点/SH 光源和光照贴图。
- ForwardAdd:同样用于前向渲染,应用附加的每像素光源(每个光源对应一个通道)。
- Deferred:在延迟渲染中使用,主要用于渲染 G 缓冲区。
- ShadowCaster:将对象的深度渲染到阴影贴图或深度纹理中。
- MotionVectors:用于计算每对象的运动矢量。
- PrepassBase:在旧版延迟光照中使用,用于渲染法线和镜面反射指数。
- PrepassFinal:在旧版延迟光照中使用,通过组合纹理、光照和反光来渲染最终颜色。
- Vertex:当对象不进行光照贴图时,在旧版顶点光照渲染中使用,应用所有顶点光源。
- VertexLMRGBM:当对象不进行光照贴图时,在旧版顶点光照渲染中使用,适用于光照贴图为 RGBM 编码的平台(如 PC 和游戏主机)。
- VertexLM:当对象不进行光照贴图时,在旧版顶点光照渲染中使用,适用于光照贴图为双 LDR 编码的平台(如移动平台)。
-
Tags{ “RequireOptions” = “标签值” }:主要用于指定只有在满足某些条件时才渲染该 Pass。目前 Unity 仅支持 Tags{ “RequireOptions” = “SoftVegetation” },即仅当 Quality 窗口中开启了 SoftVegetation 时才会渲染此通道。
-
Tags{ “PassFlags” = “标签值” }:一个渲染通道 Pass 可通过该标签指示一些标志,从而更改渲染管线向 Pass 传递数据的方式。目前 Unity 仅支持 Tags{ “PassFlags” = “OnlyDirectional” },在 ForwardBase 向前渲染的通道类型中使用时,此标志的作用是仅允许主方向光和环境光/光照探针数据传递到着色器,这意味着非重要光源的数据将不会传递到顶点光源或球谐函数着色器变量。
(3)Pass 中的渲染状态:在 SubShader 语句块中学习的渲染状态同样适用于 Pass。例如,剔除方式决定了模型正面和背面是否能够被渲染;深度缓冲和深度测试决定了景深关系的确定以及透明效果的正确表达;混合方式决定了透明和半透明颜色的正确表现,以及一些特殊颜色效果的呈现。这些渲染状态都可以在单个 Pass 中进行设置。需要注意的是,如果在 SubShader 语句块中设置渲染状态,会影响之后的所有渲染通道 Pass;而如果在 Pass 语句块中设置,则只会影响当前 Pass 渲染通道,不会对其他 Pass 产生影响。此外,Pass 中还可以使用固定管线着色器的命令。
(4)GrabPass 命令:利用 GrabPass 命令可以将即将绘制对象时的屏幕内容抓取到纹理中,在后续通道中即可使用此纹理,从而执行基于图像的高级效果。例如,将绘制该对象之前的屏幕抓取到 _BackgroundTexture 中,可使用如下代码:
GrabPass
{"_BackgroundTexture"
}
需要注意的是,该命令一般写在某个 Pass 前,在之后的 Pass 代码中可以利用 _BackgroundTexture 变量进行相应的处理。
五、CG语法
1.CG-基础数据类型
1.1.基础数据类型
- 整形:
uint
:32 位无符号整形。int
:32 位整形。
- 浮点型:
float
:32 位浮点数,后缀符号为f
。half
:16 位浮点数,后缀符号为h
。fixed
:12 位浮点数。
- 特殊类型:
bool
:布尔类型。string
:字符串(在 Shader 中很少使用,HLSL/Cg 中不直接支持,一般在高层语言中使用)。sampler
:通用的纹理采样器,可处理各种不同维度和类型的纹理。sampler1D
:用于一维纹理,如从左到右的渐变色。sampler2D
:用于二维纹理,常见的二维图像纹理,如贴图。sampler3D
:用于三维纹理,如体积渲染的体积纹理。samplerCUBE
:用于立方体纹理,常用于环境映射等需要立方体贴图的情况。samplerRECT
:用于处理矩形纹理,满足一些非标准的纹理映射需求。
1.2.基础复合数据类型
- 数组:与 C# 中类似。
- 一维数组:使用
int a[4]
形式声明,CG 语法无法通过Length
获取数组长度,需手动用变量记录,之后使用该变量遍历,如int aLength = 4
。 - 二维数组:使用
int b[2][3]
形式声明,同样需手动记录数组长度,如int bRowsLength = 2
,int bColsLength = 3
。
- 一维数组:使用
- 结构体:
- 与 C# 基本一样。
- 没有访问修饰符。
- 结构体声明结束需加分号。
- 一般在函数外声明。
1.3.特殊数据类型
向量基本构成
数据类型2 = 数据类型2(n1, n2)
数据类型3 = 数据类型3(n1, n2, n3)
数据类型4 = 数据类型4(n1, n2, n3, n4)
示例代码
fixed4 frag(v2f i) : SV_Target
{fixed4 col = tex2D(_MainTex, i.uv);// float2 类型示例float2 vec2 = float2(1.0, 2.0);// fixed3 类型示例fixed3 vec3 = fixed3(1.0, 2.0, 3.0);// int4 类型示例int4 vec4 = int4(1, 2, 3, 4);return col;
}
矩阵基本构成
数据类型’n’x’m’ = {n1m1, n1m2, n1m3…..}
,为增加可读性,书写矩阵时最好手动换行。
** 示例代码**
fixed4 frag(v2f i) : SV_Target
{fixed4 col = tex2D(_MainTex, i.uv);// half2x2 矩阵示例half2x2 mat2x2 = half2x2(1.0h, 2.0h, 3.0h, 4.0h);// fixed3x3 矩阵示例fixed3x3 mat3x3 = fixed3x3(1.0, 2.0, 3.0,4.0, 5.0, 6.0, 7.0, 8.0, 9.0);// int4x4 矩阵示例int4x4 mat4x4 = int4x4(1, 2, 3, 4,5, 6, 7, 8, 9, 10, 11, 12,13, 14, 15, 16);return col;
}
bool 类型特殊使用
bool 类型可像向量一样声明,用于存储逻辑判断结果。
示例代码
fixed4 frag(v2f i) : SV_Target
{fixed4 col = tex2D(_MainTex, i.uv);// bool 类型特殊使用示例half3 a = half3(0.5h, 0.0h, 1.0h);half3 b = half3(0.6h, -0.1h, 0.9h);bool3 c = a < b; // 结果为 bool3(true, false, false) 用ab两个向量的对应值比较return col;
}
2.Swizzle操作符
作用:利用它来提取分量;利用它来重新排列分量;利用它来创建新向量。
示例代码
v2f vert(appdata v)
{v2f o;// 1.利用swizzle来提取分量fixed4 f4 = fixed4(1, 2, 3, 4); // 创建一个包含四个固定点数的向量fixed f = f4.w; //xyzw f=4f = f4.a; //rgba f=4// 2.利用它来重新排列分量f4 = f4.yzxw; // 将f4的分量重新排列为yzxwf4 = f4.abgr; // 将f4的分量重新排列为abgr// 3.利用它来创建新的向量fixed3 f3 = f4.xyz; // 包含f4的前三个分量fixed2 f2 = f3.xz; // 包含f3的x和z分量fixed4 f4_1 = fixed4(f2, 3, 4); // 包含f2一次次,即xz的值加上3和4fixed4 f4_2 = fixed4(f2, f2); //包含f2两次,即xzxzf4_2 = fixed4(f3, f); // 包含f3和f,即xyzareturn o;
}
3.CG-运算符
比较运算符
- 大于
>
- 小于
<
- 大于等于
>=
- 小于等于
<=
- 等于
==
- 不等于
!=
条件运算符
// condition ? value_if_true : value_if_false
其中,condition
是一个条件表达式,若为真则返回 value_if_true
,否则返回 value_if_false
。CG 里条件运算符的使用和 C# 相同。
// 条件运算符
fixed f3 = f1 < f2 ? 4.5 : 6.7; // 变量值: 4.5 (因为 1 < 2)
逻辑运算符
- 逻辑或运算符
||
- 逻辑与运算符
&&
- 逻辑非运算符
!
CG 里逻辑运算符的使用和 C# 一样,但要注意,CG 中不存在 C# 里的“短路”操作。
// 逻辑运算符
bool b2 = (f1 < f2) && (f2 > 1); // 变量值: true (1 < 2 且 2 > 1)
bool b3 = (f1 > f2) || (f2 == 2); // 变量值: true (1 > 2 为 false, 但 2 == 2 为 true)
bool b4 = !(f1 > f2); // 变量值: true (!(1 > 2) 为 true)
数学运算符
- 加法
+
- 减法
-
- 乘法
*
- 除法
/
- 取余
%
- 自增减
++
、--
CG 里数学运算符的使用和 C# 一样,不过需要注意,CG 中取余符号只能对整数取余。
// 数学运算符
int i1 = 2; // 变量值: 2
fixed f4 = f1 + f2; // 加法, 变量值: 3 (1 + 2)
fixed f5 = f2 - f1; // 减法, 变量值: 1 (2 - 1)
fixed f6 = f1 * f2; // 乘法, 变量值: 2 (1 * 2)
fixed f7 = i1 / f1; // 除法, 变量值: 2 (2 / 1)
fixed f8 = f2 % f1; // 取余, 变量值: 0 (2 % 1)
f1++; // 自增, 变量值: 2 (f1 由 1 变为 2)
f2--; // 自减, 变量值: 1 (f2 由 2 变为 1)
4.CG语法流程控制语句
4.1.条件分支语句
CG 中条件分支语句(if
、switch
)与 C# 用法一致。
// if 语句
fixed f1 = 1, f2 = 2, fResult;
if (f1 < f2) fResult = 4.5;
else fResult = 6.7;// switch 语句
fixed fSwitch = 1.0;
switch ((int)fSwitch) {case 0: fSwitch = 0.0; break;case 1: fSwitch = 2.0; break;default: fSwitch = -1.0; break;
}
4.2.循环语句
CG 中循环语句(for
、while
、do while
)与 C# 用法一致。
// for 循环
fixed sumFor = 0.0;
for (int i = 0; i < 3; i++) sumFor += 1.0;// while 循环
fixed sumWhile = 0.0;
int countWhile = 0;
while (countWhile < 3) { sumWhile += 1.0; countWhile++; }// do while 循环
fixed sumDoWhile = 0.0;
int countDoWhile = 0;
do { sumDoWhile += 1.0; countDoWhile++; } while (countDoWhile < 3);
5.CG-函数
5.1. 无返回值的函数
基本结构
void name(in 参数类型 参数名, out 参数类型 参数名)
{函数体
}
void
:表示无返回值。in
:输入参数,由外部传入,内部仅使用不修改,可多个。out
:输出参数,由内部传给调用者,内部须初始化或修改,可多个。
实例
// 无返回值的函数
void test(in fixed inF, out fixed outF)
{outF = inF + 10;
}// 调用示例
fixed f = 10;
fixed f2;
test(f, f2); // f2的值会变成20
注意:in
和 out
可省略,但编写 Shader 时建议保留,能明确参数传递方式,提升代码可读性与可维护性。
5.2. 有返回值的函数
基本结构
type name(in 参数类型 参数名)
{函数体return 返回值;
}
type
:返回值类型。return
:返回指定类型数据。
实例
// 有返回值的函数
float test2(in float inF, out fixed f)
{f = inF + 2;return inF * 2;
}// 调用示例
float f3 = test2(11, f); // f3: 22, f: 13
注意:有返回值函数中可用 out
参数,但不常见,顶点/片元着色器函数多用单返回值处理。
6.CG语法语义
语义的作用
在 CG 语言中,“语义”是一种特殊关键字,用于修饰函数的传入参数和返回值。其主要作用是让 Shader 明确数据的读取来源和输出去向,使得在 Shader 开发过程中能够获取所需数据,并将数据进行传递。需注意的是,Unity 仅支持 CG 中的部分语义。
常用语义
- 应用阶段——>顶点着色器:当应用阶段向顶点着色器传递模型数据时,Unity 所支持的语义,一般应用于顶点着色器回调函数的传入参数中。
- POSITION:表示模型空间中的顶点位置,通常为
float4
类型。 - NORMAL:即顶点法线,通常是
float3
类型。 - TANGENT:顶点切线,一般为
float4
类型。 - TEXCOORDn:例如
TEXCOORD0
、TEXCOORD1
等。代表该顶点的纹理坐标,通常是float2
或者float4
类型。其中TEXCOORD0
表示第一组纹理坐标,依此类推。纹理坐标也叫 UV 坐标,用于表示该顶点在纹理图像上的对应位置。 - COLOR:顶点颜色,通常为
fixed4
或float4
类型。
- POSITION:表示模型空间中的顶点位置,通常为
- 顶点着色器——>片元着色器:从顶点着色器向片元着色器传递数据时,Unity 支持的语义,一般用于顶点着色器回调函数的返回值中。
- SV_POSITION:裁剪空间中的顶点坐标,这是必备的语义。
- COLOR0:通常用于输出第一组顶点颜色,并非必须。
- COLOR1:通常用于输出第二组顶点颜色,不是必需的。
- TEXCOORD0~TEXCOORD7:通常用于输出纹理坐标,不是必要的。
- 片元着色器输出:片元着色器输出时,Unity 支持的常用语义,一般在片元着色器回调函数的返回值中使用。
- SV_Target:输出值会被存储到渲染目标中。
7.ShaderLab属性类型和CG变量类型的匹配关系
对应关系列表
ShaderLab 属性类型 | CG 变量类型 |
---|---|
Color, Vector | float4, half4, fixed4 |
Range, Float, Int | float, half, fixed |
2D | sampler2D |
Cube | samplerCube |
3D | sampler3D |
2DArray | sampler2DArray |
解释
- Color 和 Vector:ShaderLab 中的
Color
和Vector
属性用于表示颜色或者四维向量。在 CG 里可以用float4
、half4
或者fixed4
类型来对应。float4
是 32 位浮点数的四维向量,精度最高;half4
是 16 位浮点数的四维向量,精度适中;fixed4
是 12 位浮点数的四维向量,精度最低,适合对精度要求不高的场景。 - Range、Float 和 Int:
Range
用于定义一个有范围的浮点数,Float
表示普通浮点数,Int
表示整数。在 CG 中可以使用float
、half
或者fixed
类型。同样,float
精度最高,half
次之,fixed
精度最低。 - 2D:ShaderLab 中的
2D
属性用于表示二维纹理,在 CG 中使用sampler2D
类型来进行纹理采样操作。 - Cube:
Cube
属性用于表示立方体纹理,常用于环境映射等,在 CG 中用samplerCube
类型来处理。 - 3D:
3D
属性表示三维纹理,在 CG 里对应sampler3D
类型,用于体积渲染等场景。 - 2DArray:
2DArray
属性代表二维纹理数组,在 CG 中使用sampler2DArray
类型来处理。
了解这些对应关系有助于在 Shader 开发时,正确地在 ShaderLab 中定义属性,并在 CG 代码中使用合适的变量类型来处理这些属性。
8.CG语法内置函数
- 数学函数
- 三角函数相关:
sincos(float x, out s, out c)
:能同时计算输入值x
的正弦值和余弦值,并分别通过s
和c
返回,相较于分别计算效率更高。sin(x)
:计算正弦值。cos(x)
:计算余弦值。tan(x)
:计算正切值。sinh(x)
:计算双曲正弦值。cosh(x)
:计算双曲余弦值。tanh(x)
:计算双曲正切值。asin(x)
:反正弦函数,输入参数范围为[-1,1]
,返回[-π/2,π/2]
区间的角度值。acos(x)
:反余弦函数,输入参数范围是[-1,1]
,返回[0,π]
区间的角度值。atan(x)
:反正切函数,输入参数范围为[-1,1]
,返回[-π/2,π/2]
区间的角度值。atan2(y,x)
:计算y/x
的反正切值,与atan
功能类似,只是输入参数不同(atan(x)=atan2(x,1)
)。
- 向量、矩阵相关:
cross(A,B)
:计算两个三维向量的叉乘。dot(A,B)
:计算两个三维向量的点乘。mul(M,N)
:计算两个矩阵的乘积。mul(M,v)
:计算矩阵与向量的乘积(矩阵左乘向量)。mul(v,M)
:计算向量与矩阵的乘积(向量左乘矩阵)。transpose(M)
:计算矩阵M
的转置矩阵。determinant(m)
:计算矩阵的行列式因子。
- 数值相关:
abs(x)
:返回输入参数的绝对值。ceil(x)
:对输入参数向上取整。floor(x)
:对输入参数向下取整。clamp(x,a,b)
:若x
小于a
,返回a
;若x
大于b
,返回b
;否则返回x
(“夹紧”函数)。radians(x)
:将角度转换为弧度。degrees(x)
:将弧度转换为角度。max(a,b)
:返回a
和b
中的最大值。min(a,b)
:返回a
和b
中的最小值。sqrt(x)
:计算x
的平方根(x
必须大于 0)。pow(x,y)
:计算x
的y
次方的值。round(x)
:对x
进行四舍五入。rsqrt(x)
:计算x
的反平方根(x
必须大于 0)。lerp(a,b,f)
:差值函数,计算(1-f)a + bf
或者a + f*(b-a)
的值。exp(x)
:计算e
的x
次方的值(e=2.71828182845904523536
)。exp2(x)
:计算2
的x
次方的值。fmod(x,y)
:返回x/y
的余数(y
不为 0)。frac(x)
:返回标量或每个矢量分量的小数部分。frexp(x,out exp)
:将浮点数x
分解为尾数和指数,即x = m * 2
的exp
次方,返回m
,并将指数存储在exp
中。isfinite(x)
:判断标量或者向量中的每个数据是否为有限数,若是返回true
,否则返回false
。isinf(x)
:判断标量或者向量中的每个数据是否为无限,若是返回true
,否则返回false
。isnan(x)
:判断标量或者向量中的每个数据是否为非数值,若是返回true
,否则返回false
。ldexp(x,n)
:计算x * 2
的n
次方的值。log(x)
:计算ln(x)
的值(x
必须大于 0)。log2(x)
:计算log2(x)
的值(x
必须大于 0)。log10(x)
:计算log10(x)
的值(x
必须大于 0)。saturate(x)
:若x
小于 0,返回 0;若x
大于 1,返回 1;否则返回x
。sign(x)
:若x
大于 0,返回 1;若x
小于 0,返回 -1;否则返回 0。smoothstep(min,max,x)
:当值x
位于min
、max
区间内时,若x = min
,返回 0;若x = max
,返回 1;若在两者之间,返回-2* ((x-min)/(max - min))
的三次方+ 3* ((x - min)/(max - min))
的二次方。step(a,x)
:若x < a
,返回 0;否则返回 1。all(x)
:输入参数均不为 0,则返回true
;否则返回false
,相当于逻辑与&&
。any(x)
:输入参数只要有一个不为 0,则返回true
,相当于逻辑或||
。
- 其他:
lit(NdotL,NdotH,m)
:其中N
表示法向量,L
表示入射光向量,H
表示半角向量,m
表示高光系数。该函数用于计算环境光、散射光、镜面光的贡献,返回一个 4 维向量,x
位表示环境光贡献,y
位表示散射光贡献,z
位表示镜面光贡献,w
始终为 1。noise(x)
:噪声函数,返回值始终在0~1
之间,对于相同的输入始终返回相同值,并非真正意义的随机噪声。
- 三角函数相关:
- 几何函数:
length(v)
:返回一个向量的模长。normalize(v)
:对向量进行归一化处理。distance(p1,p2)
:计算两点之间的距离。reflect(I,N)
:计算反射光方向向量,I
为入射光(指向顶点),N
为顶点法向量,I
和N
必须被归一化且为 3 维向量。refract(I,N,eta)
:计算折射向量,I
为入射光(指向顶点),N
为顶点法向量,eta
为折射系数,I
和N
必须被归一化且为 3 维向量。
- 纹理函数:这些纹理采样函数返回值均为
fixed4
类型的颜色值。-
二维纹理:
tex2D(sampler2D tex, float2 s)
:进行二维纹理查询。tex2D(sampler2D tex, float2 s, float2 dsdx, float2 dsdy)
:使用导数值查询二维纹理。tex2D(sampler2D tex, float3 sz)
:二维纹理查询,并进行深度值比较。tex2D(sampler2D tex, float3 sz, float2 dsdx, float2 dsdy)
:使用导数值查询二维纹理,并进行深度值比较。tex2Dproj(sampler2D tex, float3 sq)
:二维投影纹理查询。tex2Dproj(sampler2D tex, float4 szq)
:二维投影纹理查询,并进行深度值比较。
-
立方体纹理:
texCUBE(samplerCUBE tex, float3 s)
:查询立方体纹理。texCUBE(samplerCUBE tex, float3 s, float3 dsdx, float3 dsdy)
:结合导数值查询立方体纹理。texCUBEDproj(samplerCUBE tex, float4 sq)
:查询立方体投影纹理,并进行深度值比较。
-
其他纹理:
- 一维纹理:
tex1D(sampler1D tex, float s)
:一维纹理查询。tex1D(sampler1D tex, float s, float dsdx, float dsdy)
:使用导数值查询一维纹理。tex1D(sampler1D tex, float2 sz)
:一维纹理查询,并进行深度值比较。tex1D(sampler1D tex, float2 sz, float dsdx, float dsdy)
:使用导数值查询一维纹理,并进行深度值比较。tex1Dproj(sampler1D tex, float2 sq)
:一维投影纹理查询。tex1Dproj(sampler1D tex, float3 szq)
:一维投影纹理查询,并进行深度值比较。
- 矩形纹理:
texRECT(samplerRECT tex, float2 s)
:矩形纹理查询。texRECT(samplerRECT tex, float2 s, float2 dsdx, float2 dsdy)
:使用导数值查询矩形纹理。texRECT(samplerRECT tex, float3 sz)
:矩形纹理查询,并进行深度值比较。texRECT(samplerRECT tex, float3 sz, float2 dsdx, float2 dsdy)
:使用导数值查询矩形纹理,并进行深度值比较。texRECTproj(samplerRECT tex, float3 sq)
:矩形投影纹理查询。texRECTproj(samplerRECT tex, float3 szq)
:矩形投影纹理查询,并进行深度值比较。
- 三维纹理:
tex3D(sampler3D tex, float3 s)
:查询三维纹理。tex3D(sampler3D tex, float3 s, float3 dsdx, float3 dsdy)
:结合导数值查询三维纹理。tex3DDproj(sampler3D tex, float4 sq)
:查询三维投影纹理,并进行深度值比较。
- 一维纹理:
-
9.CG-内置文件
(1)Unity 中常用的内置文件如下:
- UnityCG.cginc:包含了最常用的帮助函数、宏和结构体等。
- Lighting.cginc:包含各种内置光照模型。若编写的是 Surface Shader(标准表面着色器),该文件会自动被包含进来。
- UnityShaderVariables.cginc:在编译 Unity Shader 时,会自动包含此文件,其中包含许多内置的全局变量。
- .HLSLSupport.cginc:同样在编译 Unity Shader 时自动包含,声明了很多用于跨平台编译的宏和定义等。
(2)如何使用 CG 内置文件
在 CG 语句块中,通过编译指令 #include "内置文件名.cginc"
的形式进行引用,如此便可以在 CG 语言中使用其中的内容。虽然一些常用的函数、宏、变量,即便不引用,Unity 在编译时也可能自动识别,但为避免报错,建议都进行引用。
“宏”通常是一种预处理指令或代码片段,用于在代码中进行文本替换。即程序员定义一个标识符(通常为大写字母)来代表一个代码片段,编译时该标识符会被替换为相应的代码,这个过程称为宏展开。通俗来讲,就是给一些代码片段取别名以方便使用,在真正编译时再将别名翻译成对应的代码。
(3)常用内容
-
方法(UnityCG.cginc 中):
float3 WorldSpaceViewDir(float4 v)
:输入模型空间中的顶点位置,返回世界空间中从该点到摄像机的观察方向。float3 ObjSpaceViewDir(float4 v)
:输入模型空间中的顶点位置,返回模型空间中从该点到摄像机的观察方向。float3 WorldSpaceLightDir(float4 v)
:仅用于向前渲染,输入模型空间中的顶点位置,返回世界空间中从该点到光源的光照方向(返回值未归一化)。float3 ObjSpaceLightDir(float4 v)
:仅用于向前渲染,输入模型空间中的顶点位置,返回模型空间中从该点到光源的光照方向(返回值未归一化)。float3 UnityObjectToWorldNormal(float3 norm)
:将法线方向从模型空间转换到世界空间。float3 UnityObjectToWorldDir(in float3 dir)
:把方向矢量从模型空间转换到世界空间。float3 UnityWorldToObjectDir(float3 dir)
:把方向矢量从世界空间转换到模型空间。
-
结构体(UnityCG.cginc 中):
- appdata_base:用于顶点着色器输入,包含顶点位置、顶点法线、第一组纹理坐标。
- appdata_tan:用于顶点着色器输入,包含顶点位置、顶点法线、顶点切线、第一组纹理坐标。
- appdata_full:用于顶点着色器输入,包含顶点位置、顶点法线、顶点切线、四组(或更多)纹理坐标。
- appdata_img:用于顶点着色器输入,包含顶点位置、第一组纹理坐标。
- v2f_img:用于顶点着色器输出,包含裁剪空间中的位置、纹理坐标。
-
变换矩阵宏(UnityShaderVariables.cginc 中):
坐标空间变换顺序为:模型空间 -> 世界空间 -> 观察空间 -> 裁剪空间 -> 屏幕空间。- UNITY_MATRIX_MVP:当前的模型观察投影矩阵,用于将顶点/方向向量从模型空间变换到裁剪空间。
- UNITY_MATRIX_MV:当前的模型*观察矩阵,用于将顶点/方向向量从模型空间变换到观察空间。
- UNITY_MATRIX_V:当前的观察矩阵,用于将顶点/方向向量从世界空间变换到观察空间。
- UNITY_MATRIX_P:当前的投影矩阵,用于将顶点/方向向量从观察空间变换到裁剪空间。
- UNITY_MATRIX_VP:当前的观察*投影矩阵,用于将顶点/方向向量从世界空间变换到裁剪空间。
- UNITY_MATRIX_T_MV:UNITY_MATRIX_MV 的转置矩阵。
- UNITY_MATRIX_IT_MV:UNITY_MATRIX_MV 的逆转置矩阵,可用于将法线从模型空间变换到观察空间,也能得到 UNITY_MATRIX_MV 的逆矩阵。
- _Object2World:当前的模型矩阵,用于将顶点/方向矢量从模型空间变换到世界空间(Unity 5.5 版本中已变为 unity_ObjectToWorld)。
- _World2Object:_Object2World 的逆矩阵,用于将顶点/方向矢量从世界空间变换到模型空间(Unity 5.5 版本中已变为 unity_WorldToObject)。由于 Unity 的向下兼容性,自动改写后不会出错,且会在代码最上方出现自动改后的提示。
变换矩阵宏使用示例:
//使用UnityCG.cginc内置结构体v2f_img和appdata_base
v2f_img vert(appdata_base data)
{//...//把模型的点转换成世界坐标float worldPos = mul(unity_ObjectToWorld,data.vertex);//...
}
- 变量:
- _Time:自关卡加载以来的时间(t/20、t、t2、t3),可用于对着色器内的事物进行动画处理,无需引用,可直接使用。
- _LightColor0:向前渲染时,在 UnityLightingCommon.cginc 中;延迟渲染时,在 UnityDeferredLibrary.cginc 中,代表光的颜色等。
六、光照模型
1. 兰伯特光照模型(Lambertian Lighting Model)
兰伯特光照模型是一种简单且经典的漫反射光照模型。它基于兰伯特定律,该定律指出漫反射表面反射的光强度与表面法线和光源方向之间夹角的余弦值成正比。
原理:在该模型中,假设物体表面是理想的漫反射表面,即光线在表面向各个方向均匀散射。对于一个给定的点,其受到的漫反射光照强度仅取决于该点的表面法线方向和光源方向。
2. 半兰伯特光照模型(Half Lambert Lighting Model)
半兰伯特光照模型是对兰伯特光照模型的改进。兰伯特模型的一个缺点是当表面法线与光源方向夹角大于 90 度时,表面完全变黑(漫反射光强度为 0),这在某些情况下会导致视觉效果不自然。半兰伯特模型通过引入一个偏移量来改善这种情况。
原理:它在计算光照强度时,将兰伯特模型中的 ( \vec{N} \cdot \vec{L} ) 进行了修改,使得即使表面点背对光源,也能有一定的光照强度,从而增加了暗部的细节和层次感。
3. Phong 式高光反射模型(Phong Specular Reflection Model)
Phong 式高光反射模型用于模拟物体表面的镜面反射效果,即当光线照射到光滑表面时,会在特定方向上产生明亮的高光区域。
原理:该模型基于这样的假设,即高光的强度取决于观察者方向、反射光线方向以及表面的光滑程度。当反射光线方向与观察者方向接近时,会看到明显的高光。
4. Blinn-Phong 式高光反射模型(Blinn-Phong Specular Reflection Model)
Blinn-Phong 式高光反射模型是对 Phong 模型的优化和改进。在 Phong 模型中,计算反射光线方向 ( \vec{R} ) 的计算量较大,而 Blinn-Phong 模型通过引入半角向量简化了计算。
原理:它使用半角向量 ( \vec{H} )(即光源方向 ( \vec{L} ) 和观察者方向 ( \vec{V} ) 的角平分线方向向量)来替代 Phong 模型中的反射光线方向 ( \vec{R} ) 进行高光计算。当半角向量 ( \vec{H} ) 与表面法线 ( \vec{N} ) 接近时,产生高光。
5. Phong 光照模型
严格来说,Phong 光照模型是一个综合性的光照模型,它同时考虑了漫反射和高光反射,是在兰伯特漫反射模型的基础上增加了 Phong 式高光反射部分。
原理:一个表面点的最终光照强度由漫反射光强度和高光反射光强度以及环境光强度(通常是一个常量,表示场景中的环境光对物体的影响)共同组成。
相关文章:
Unity图形学Shader快速回顾
参考知识点来源于: 人间自有韬哥在, 唐老狮,窗外听轩雨 , 呆呆敲代码的小Y little_fat_sheep, AitTech, DeepSeek, 百度, 豆包 目录 一、渲染管线1.应用阶段2.几何阶段3.光栅化阶段 二、矩阵的几何意义1. 平移2. 旋转3. 缩放4.复合运算 三、…...
十六进制(Hexadecimal)简介
十六进制(Hexadecimal)简介 1.1 什么是十六进制? 十六进制是一种使用16个符号表示数值的系统:数字0-9(表示0-9),字母A-F(表示10-15)。 1.2 十六进制表示法 在编程中&a…...
1、pytest基本用法
目录 先给大家分享下学习资源 1. 安装pytest 2. 编写用例规则 3. 执行用例 最近在学习pytest的用法 并且用这套框架替换了原来的unittest, 同是测试框架 确实感觉到pytest更加便捷 这边分享给大家我得学习心得 先给大家分享下学习资源 1 官方文档 pytest 官方…...
2024年3月全国计算机等级考试真题(二级C语言)
😀 第1题 下列叙述中正确的是 A. 矩阵是非线性结构 B. 数组是长度固定的线性表 C. 对线性表只能作插入与删除运算 D. 线性表中各元素的数据类型可以不同 题目解析: A. 矩阵是非线性结构 错误。矩阵通常是二维数组,属…...
GitHub高级筛选小白使用手册
GitHub高级筛选小白使用手册 GitHub 提供了强大的搜索功能,允许用户通过高级筛选器来精确查找仓库、Issues、Pull Requests、代码等。下面是一些常用的高级筛选用法,帮助你更高效地使用 GitHub 搜索功能。 目录 搜索仓库搜索Issues搜索Pull Requests搜…...
如何用腾讯云建站做好一个多语言的建筑工程网站?海外用户访问量提升3倍!分享我的经验
作为新疆地区领先的工程建筑企业,我们深知在数字化浪潮中,一个专业、高效且具备国际视野的官方网站是企业形象与业务拓展的“门面担当”。然而,传统的建站流程复杂、技术门槛高、多语言适配难等问题,曾让我们在数字化转型中举步维…...
SpringBoot-配置文件中敏感信息的加密保姆级教程
前言 公司安全部门检查,要求系统配置文件中的敏感信息如数据库密码等,进行加密处理,否则将受到公司的安全处罚,无奈只要按照公司要求,对springboot项目配置文件的敏感信息进行加密和解密处理。详细教程如下。 快速上…...
数据结构——串
串是一种数据元素为字符的特殊的线性表。 1. 串的定义 零个或多个字符(字母、数字或其他字符)组成的有限序列。记为 S"a1a2...an"S"a1a2...an",长度为 nn,空串长度为0。 2.串的术语 串长度…...
使用python爬取网络资源
整体思路 网络资源爬取通常分为以下几个步骤: 发送 HTTP 请求:使用requests库向目标网站发送请求,获取网页的 HTML 内容。解析 HTML 内容:使用BeautifulSoup库解析 HTML 内容,从中提取所需的数据。处理数据ÿ…...
【MySQL | 七、存储引擎是什么?】
存储引擎是什么?作用于哪里? 1. 存储引擎的定义 存储引擎(Storage Engine)是数据库管理系统中负责 数据的存储、检索和管理 的核心组件。它决定了数据如何存储在磁盘上,以及如何从磁盘中读取数据。存储引擎是数据库与…...
Linux -- 进程间通信(IPC)-- 进程间通信、管道、system V 共享内存、system V 消息队列、责任链模式 、system V 信号量
一、什么是进程间通信 1.进程间通信的目的 数据传输:一个进程需要将它的数据发送给另一个进程。资源共享:多个进程之间共享同样的资源。通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发…...
远程登录服务(ssh)
一、远程登录服务概述 1. 概念 远程登录服务就像是一个神奇的桥梁,它让你能够跨越物理距离,通过网络连接到另一台计算机上进行操作。无论你身在何处,只要有网络连接,你就可以像坐在目标计算机前一样进行各种操作。 2. 功能 分享…...
【从零实现Json-Rpc框架】- 项目设计篇
📢博客主页:https://blog.csdn.net/2301_779549673 📢博客仓库:https://gitee.com/JohnKingW/linux_test/tree/master/lesson 📢欢迎点赞 👍 收藏 ⭐留言 📝 如有错误敬请指正! &…...
EtherCAT转CANopen配置CANopen侧的PDO映射
EtherCAT转CANopen配置CANopen侧的PDO映射 在工业自动化领域,EtherCAT和CANopen是两种广泛应用的通信协议。它们各自具有独特的优势,但在某些应用场景下,需要将这两种协议进行转换以实现设备间的高效数据交换。本文将详细介绍如何在使用Ethe…...
Vite管理的Vue3项目中monaco editer的使用以及组件封装
文章目录 背景环境说明安装流程以及组件封装引入依赖封装组件 外部使用实现效果 v-model实现原理 背景 做oj系统的时候,需要使用代码编辑器,决定使用Monaco Editor,但是因为自身能力问题,读不懂官网文档,最终结合ai和网友的帖子成功引入&…...
完整的类在JVM中的生命周期详解
首先给出一个示例代码: 示例的目标是展示一个多功能的类结构,包含继承、接口实现、静态成员、本地方法、线程安全等特性,同时模拟一个简单的“计算器”场景,计算并管理数字。(尽量将所有的 Java 组件和关键字都给出&am…...
大数据学习栈记——HBase操作(shell java)
本文介绍HBase在shell终端的常见操作以及如何利用java api操作HBase,操作系统:Ubuntu24.04 参考: https://blog.51cto.com/u_16099228/8016429 https://blog.csdn.net/m0_37739193/article/details/73618899 https://cloud.tencent.com/d…...
【商城实战(65)】退换货流程全解析:从前端到后端的技术实现
【商城实战】专栏重磅来袭!这是一份专为开发者与电商从业者打造的超详细指南。从项目基础搭建,运用 uniapp、Element Plus、SpringBoot 搭建商城框架,到用户、商品、订单等核心模块开发,再到性能优化、安全加固、多端适配,乃至运营推广策略,102 章内容层层递进。无论是想…...
改进BM25稀疏检索和BGE稠密检索
改进BM25稀疏检索和BGE稠密检索 检索算法层面 混合检索策略优化 自适应加权融合:在BM25和BGE等混合检索时,根据查询文本的特征(如长度、专业术语占比等)动态调整两者的权重。例如,对于包含大量专业术语的查询,增加BGE的权重;对于关键词明确的简单查询,增加BM25的权重。…...
WPS二次开发系列:以自动播放模式打开PPT文档
在前面文章中 android 调用wps打开文档并感知保存事件 介绍了如何使用WPS SDK打开文档,那么我们是否能够实现在打开WPS 文档的时候能够传递一些参数来控制打开文档的行为呢,经过研究WPS SDK相关文档和API,最终实现了 以自动播放方式打开PPT文…...
当AI重构编程范式:Java 24的进化逻辑与技术深水区的战略突围
一、语言进化的底层密码:从“工具适配”到“定义规则” 在2025年3月19日发布的Java 24中,Oracle以"30周年技术宣言"的姿态展示了编程语言进化的新范式。该版本不仅包含模式匹配、结构化并发等21项JEP特性,更通过后量子加密、AI原生…...
air780eq 阿里云
硬件:APM32F030C8 Air 780eq 参考文档: 合宙780E-4G模块通过AT指令连接到阿里云平台,实现信息的收发_air780e上传阿里云属性值at命令-CSDN博客 阿里云 - atair780eq - 合宙文档中心 4G模块接入阿里云-实现数据上传和命令下发_4g模块上传…...
网络安全之vlan实验
在对vlan进行一定的学习之后我们来练习一个小实验来加深理解记忆 首先是对实验进行一个搭建 第一部分:给交换机配置vlan 首先是sw1 [Huawei]vlan batch 2 to 5 [Huawei]int g0/0/1 [Huawei-GigabitEthernet0/0/1]port hybrid tagged vlan 2 [Huawei-GigabitEthe…...
mac命令行快捷键
光标移动 Ctrl A: 将光标移动到行首。Ctrl E: 将光标移动到行尾。Option 左箭头: 向左移动一个单词。Option 右箭头: 向右移动一个单词。 删除和修改 Ctrl K: 删除从光标到行尾的所有内容。Ctrl U: 删除从光标到行首的所有内容。Ctrl W: 删除光标前的一个单词。Ctrl …...
计算机网络 - OSI 七层模型
OSI 七层模型 OSI(Open System Interconnection,开放系统互联)模型由 ISO(国际标准化组织) 制定,目的是为不同计算机网络系统之间的通信提供一个标准化的框架。它将网络通信划分为 七个层次,每…...
TCP/IP 协议族详细知识点清单
📚 TCP/IP 协议族详细知识点清单 一、概述与体系结构 🌐 TCP/IP 协议模型(四层模型) 层次协议功能应用层HTTP、FTP、DNS、SMTP提供应用服务传输层TCP、UDP端到端传输,可靠或不可靠网络层IP、ICMP、ARP、RARP寻址、路…...
Vue3(自定义指令directive详解)
文章目录 前言一、自定义指令的生命周期钩子二、自定义指令的创建与注册使用三、扩展 简化形式总结 前言 在Vue3中,自定义指令是一种强大的工具,允许开发者扩展和增强HTML元素的功能。以下是对Vue3中自定义指令的详细解析: 一、自定义指令…...
Redis--redis客户端
目录 一、引言 二、数据库管理命令 三、redis客户端 四、Java客户端使用Redis 五、相关命令使用 1.get,set 2.exists,del 3.keys 4.expire,ttl 六、总结 一、引言 在之前学了redis相关类型命令之后,本篇文章,…...
【高项】信息系统项目管理师(十)项目风险管理【5分】
项目风险是一种不确定的事件或条件,一旦发生,会对项目目标产生某种正面或负面的影响。项目风险既包括对项目目标的威胁,也包括促进项目目标的机会。已知风险是那些已经经过识别和分析的风险,对于已知风险,对其进行规划,寻找应对方案是可行的;虽然项目经理们可以依据以往…...
jenkins批量复制视图项目到新的视图
1、当前视图为 测试2分支,创建了新的视图为国际化预生产 2、进入系统设置的脚本管理 import hudson.model.* //源view def str_view "测试2分支" //目标view def str_new_view "国际化预生产" //源job名称(模糊匹配) def str_search &qu…...
uv:Rust 驱动的 Python 包管理新时代
在 Python 包管理工具层出不穷的今天,pip、pip-tools、poetry、conda 等各有千秋。而今天要介绍的 uv,则是一款由 Astral 团队推出、采用 Rust 编写的全新工具,目标直指成为 “Python 的 Cargo”。它不仅在性能上表现优异,而且在功…...
GD32 ISP下载程序(串口烧录)
一、下载烧录软件 下载地址兆易创新GigaDevice-资料下载兆易创新GD32 MCUhttps://www.gd32mcu.com/cn/download?kwGD32All-In-OneProgrammer&lancn 二、使用USB转串口连接GD32开发板 这里使用GD32E230C8T6为例: GD32E230C8T6USB 转串口模块说明PA9ÿ…...
Spring MVC 配置详解与入门案例
目录 引言 一、Spring MVC 的发展背景 1. Model I 与 Model II 2. MVC 模式 二、Spring MVC 入门案例 1. 创建 WEB 工程并引入依赖 2. 配置 web.xml 3. 配置 springmvc.xml 4. 创建控制器和视图 5. 部署并测试 三、Spring MVC 原理 1. 核心组件 2. 请求处理流程 …...
【10万QPS压力测试】Redis三主三从高可用集群基准测试
📕我是廖志伟,一名Java开发工程师、《Java项目实战——深入理解大型互联网企业通用技术》(基础篇)、(进阶篇)、(架构篇)清华大学出版社签约作家、Java领域优质创作者、CSDN博客专家、…...
git的进阶使用
一.协作冲突 举个简单的例子,公司里两个人(A,B)同一天上班,都拉取了远程仓库数据。然后A做完了所有的工作,进行了x文件的修改并提交至远程仓库。而B在做自己工作的时候不小心或者需要修改x文件,B认为A没有操作x文件直接push没有问…...
23种设计模式-责任链(Chain of Responsibility)设计模式
责任链设计模式 🚩什么是责任链设计模式?🚩责任链设计模式的特点🚩责任链设计模式的结构🚩责任链设计模式的优缺点🚩责任链设计模式的Java实现🚩代码总结🚩总结 🚩什么是…...
MySQL复习
1基本操作复习 1.1数据库创建 创建数据库create database 数据库名;判断再创建数据库create database if not exists 数据库名;创建数据库指定字符集create database 数据库名 character set 字符集;创建数据库指定排序方式create database 数据库名 collate 排序方式;创建数据…...
【嵌入式学习2】c语言重点整理
目录 ## 重点掌握 1、数组 2、指针 3、结构体 4、函数 回调函数的常见用途 ## 如何区分数组指针,指针数组,函数指针,结构体指针,指针偏移量 ## 重点掌握 1、数组 https://blog.csdn.net/weixin_60546365/article/details…...
java项目之基于ssm的个人博客网站(源码+文档)
项目简介 个人博客网站实现了以下功能: 个人博客网站在Eclipse环境中,使用Java语言进行编码,使用Mysql创建数据表保存本系统产生的数据。系统可以提供信息显示和相应服务,其管理员审核博客文章和相册分享信息,管理文…...
C++学习之路:从头搞懂配置VScode开发环境的逻辑与步骤
目录 编辑器与IDE基于vscode的C开发环境配置1. 下载vscode、浅尝编译。番外篇 2. 安装插件,赋能编程。3. 各种json文件的作用。c_cpp_properties.jsontask.jsonlaunch.json 总结&&彩蛋 编辑器与IDE 上一篇博客已经介绍过了C程序的一个编译流程,从…...
deploy myEclipse j2ee project to server没反应
解决办法 1.如果工作空间的问题,那么需要删除你工作空间的一个文件就可以解决了。 这个文件在Myeclipse工作区(workspace) .metadata\.plugins\org.eclipse.core.runtime\.settings目录...
react项目中当组件渲染的时候如何执行接口
最近遇到一个场景,就是组件渲染的时候去调用接口进行数据回填。这个在vue中很简单,在created生命周期函数中,直接调用接口即可。但是react没有created生命周期,所以在react中我们需要用到useEffect钩子函数。 在 React 函数组件中…...
python虚拟环境安装opus(windows)
python -m venv venv 创建虚拟环境后,并且安装软件包后,运行项目报错,提示如下: Could not find Opus library. Make sure it is installed 原因是缺少opus.dll, (先把项目内所有使用的第三方库都安装完成) 从以下页面下载.dll文件之后,放入venv\Scripts目录下即可 https://…...
手机怎么换网络IP有什么用?操作指南与场景应用
在数字化时代,手机已经成为我们日常生活中不可或缺的一部分,无论是工作、学习还是娱乐,手机都扮演着至关重要的角色。而在手机的使用过程中,网络IP地址作为设备在互联网上的唯一标识符,其重要性和作用不容忽视。本文将…...
小程序内表格合并功能实现—行合并
功能介绍:支付宝小程序手写表格实现行内合并,依据动态数据自动计算每次需求合并的值,本次记录行内合并,如果列内合并,同理即可实现 前端技术:grid布局 display:grid 先看实现效果: axml&…...
基于Flask的通用登录注册模块,并代理跳转到目标网址
实现了用户密码的加密,代理跳转到目标网址,不会暴露目标路径,未登录的情况下访问proxy则自动跳转到登录页,使用时需要修改配置项config,登录注册页面背景快速修改,可以实现登录注册模块的快速复用。 1.app…...
nlohmann::json教程
nlohmann::json 核心函数和方法 1. 基础构造与初始化 函数/方法描述示例json j;创建一个空的 JSON 对象(默认是 object 类型)json j;json::object()显式创建一个空的 JSON 对象json j json::object();json::array()显式创建一个空的 JSON 数组json ar…...
多层感知机从0开始实现
《动手学深度学习》-4.2-笔记 多层感知机在输出层和输入层之间增加一个或多个全连接隐藏层,并通过激活函数转换隐藏层的输出。 常用的激活函数包括ReLU函数、sigmoid函数和tanh函数。 import torch from torch import nn from d2l import torch as d2lbatch_size …...
在K8S中使用ArgoCD做持续部署
一、了解argocd ArgoCD是一个基于Kubernetes的GitOps持续交付工具,应用的部署和更新都可以在Git仓库上同步实现,并自带一个可视化界面。本文介绍如何使用GitArgocd方式来实现在k8s中部署和更新应用服务。关于ci这一块这里不多介绍。主要讲解argocd如何实…...
Python中数据结构元组详解
在Python中,元组(Tuple)是一种不可变的序列类型,常用于存储一组有序的数据。与列表(List)不同,元组一旦创建,其内容无法修改。本文将详细介绍元组的基本操作、常见运算、内置函数以及…...