C#精粹
零、特性1 预处理器指令编译器是一种翻译程序它用于将源语言程序翻译为目标语言程序
源语言程序: 某种程序设计语言写成的, 比如 C#、C、C++、Java 等语言写的程序
目标语言程序: 二进制数表示的伪机器代码写的程序
预处理器指令指导编译器在实际编译开始之前对信息进行预处理预处理器指令都是以#开始预处理器指令不是语句,所以它们不以分号 ; 结束
title:折叠代码123#region 折叠块名字... //代码#endregion
12345#define //定义一个符号,类似一个没有值的变量#undef //取消define定义的符号,让其失效//两者都是写在脚本文件最前面//一般配合if指令使用或配合特性
123456#if#elif //即elseif#else#endif//和if语句规则一样,一般配合#define定义的符号使用//用于告诉编译器进行编译代码的流程控制
1234#warning#error//告诉编译器是报警告还是报错误//一般还是配合if使用
title:案例1234567891011#define tag1#define tag2# ...
C++精粹
零、预处理器确保头文件多次包含仍能安全工作的常用技术是预处理器,预处理器是在编译之前执行的一段程序,任何以 # 开头的东西,都被称为预处理器命令或者预处理器指令。如 #include ,当预处理器看到 #include 标记时就会用指定的头文件内容代替 #include 。
头文件保护符下面来说 # pargma once
pragma 本质上是一个被发送到编译器或预处理器的预处理指令。pragma once 阻止我们单个头文件多次被 include 在同一个 cpp 文件里。
12345678//新方法#pargma once//旧方法#ifndef _PROJECT_A#define _PROJECT_A...#endif
include 用于告诉编译器,包含文件的路径是什么,有两种写法:
#include< >: 在所有 include 路径中搜索文件
#include" " :引用相对路径,现在也有 #include< > 的功能,作用范围更大
宏 macro#define #宏 #macro
预处理阶段 :当编译 C+ ...
PBR理论
基于物理的渲染(Physically Based Rendering) 是指使用基于物理原理和微表面理论建模的着色/光照模型,以及使用从现实中测量的表面参数来准确表示真实世界材质的渲染理念。
三大组成部分:
基于物理的材质(Material)
基于物理的光照(Lighting)
基于物理的摄像机(Camera)
SIGGRAPH 2014 《Moving Frostbite to PBR》
1 PBR 基础基础理论满足以下条件的光照模型才能称之为 PBR 光照模型(基于物理的材质三大条件):
基于微表面理论的表面模型
能量守恒
使用基于物理的双向反射分布函数 BRDF
微表面理论微表面理论将物体表面建模成无数个微观尺度上有随机朝向的理想镜面反射的微小平面(microfacet)。
[!NOTE] 理想镜面反射理想镜面反射即严格遵循反射定律,反射角等于入射角
光在与非光学平坦表面(Non-Optically-Flat Surfaces)的交互时,非光学平坦表面表现得像一个微小的光学平面表面的大集合。表面上的每个点都会以略微不同的方向对入射光反射,而最终的表面外观 ...
【公式推导03】光照向量
[!NOTE] 约定本文用列向量/右手坐标系进行说明以及推导。若需转换到行向量/左手坐标系,需要对变换矩阵做转置
法线法线分类当一个模型有很多凹凸细节时,模型的顶点网格会很密(数据量大),随之而来的就是性能问题。模型凹凸对应的是顶点的高度偏移,高度偏移影响的是法线方向,因而我们视觉上看到模型的凹凸(光影计算)其实只和模型法线有关,因此可以修改模型原本的法线信息,让模型看起来有凹凸,但是实际却是网格稀疏的低面几何体。
如下图,左侧是一个面很少,并不平滑的几何体,用原法线计算光照后会看到平面(红色线部分),右侧是将几何体原本的法线信息经过插值处理,经光照计算后,就会看起来像是平滑(凸出)过渡的曲面(浅紫色部分)。
四种法线:
顶点法线 (vertex normal):模型自带的信息
面法线 (face normal ) :很容易算,三角形两个边的向量进行叉积即可得到垂直于该面的法向量。
插值法线 (interpolated normal):用 vertex normal 经过插值得到的逐像素的法线
平均顶点法线(averaged normal):实际工作中 ...
CG ShaderLab
CG 语法基础在 Unity Shader 中,ShaderLab 语言只是起到组织代码结构的作用,而真正实现渲染效果的部分是用 CG 语言编写的。CG 程序片段通过指令嵌入在 Pass 中,夹在 Pass 中的指令 CGPROGRAM 和 ENDCG 之间
在 CG 程序片段之前,通常需要先使用 #pragma 声明编译指令
编译指令编译目标等级
编译指令
作用
#pragma vertex name
定义顶点着色器的名称, 通常会使用 vert
#pragma fragment name
定义片段着色器的名称, 通常会使用 frag
#pragma target name
定义 Shader 要编译的目标级别﹐默认 2.5
当编写完 Shader 程序之后,其中的 CG 代码可以被编译到不同的 Shader Models(简称 SM)中,为了能够使用更高级的 GPU 功能,需要对应使用更高等级的编译目标。同时,高等级的编译目标可能会导致 Shader 无法在旧的 GPU 上运行。
声明编译目标的级别可以使用 #pragma target name 指令,或者也 ...
【公式推导02】空间变换
[!NOTE] 约定本文以 GAMES101 课程中的标准进行推导,详情如下:
本文用列向量/所有空间均为右手坐标系进行说明以及推导(若需转换到行向量/左手坐标系,需要对变换矩阵做转置,因为列向量 v 左乘变换矩阵 M 等于行向量 v 右乘变换矩阵 M 的转置)
Camera 的 LookAt (Forward)方向为 $- z$ 轴方向,和 OpenGL 一致。
推导投影矩阵时近、远裁面坐标 $n、f$ 使用矩阵值(带符号),因此 $f<n<0$。和 OpenGL 不一致不一致,OpenGL 使用距离值。
NDC 空间范围 $[-1,1]^3$ ,和 OpenGL 一致
由于 View space 和 NDC space 都是右手系,所以变换后 $z$ 轴方向并没有变化,因此 $n$ 被映射到了 $1$,而 $f$ 被映射到了 $-1$,仍然符合 $f < n$。(这个地方不如 OpenGL 直观,在 OpenGL 中,View Space 是右手坐标系,而 Clip Space 与 NDC Space 是左手坐标系,因此 $n$ 被映射到 ...
Shader Trick
[!NOTE] TitleUnity: UV 坐标原点为左下角UE:UV 坐标原点为左上角
遮罩1 UV 坐标系转换成笛卡尔直角坐标系(0,1)-> (-1, 1)
123456float4 frag(Varyings i) : SV_Target { i.uv = (i.uv - 0.5)*2; //或 i.uv = i.uv * 2 - 1;}
2 圆形遮罩简单方法:笛卡尔坐标系求据中心的距离, 用 length 或 distance 函数即可
方法一:不能用 power 节点代替 multiply,因为 power 节点不支持负数运算,小于 0 的数都会被 clamp 到0。
方法二:UE 材质中的 Sine 函数,取值范围是-1~1,周期为 1(不是 2pai)。
方形遮罩使用这两个节点相乘即可:
方法三:SphereMask
RadialGradientExponential(指数径向渐变)函数UV(矢量 2)(UVs (Vector 2))用于控制渐变所在的位置及其涵盖 0-1 空间的程度。
中心点(矢量 2)(C ...
UV操作
屏幕空间 UV获取屏幕空间 UV
方法一:使用 VPOS 语义获取屏幕空间 uv:[[1 ShaderLab#VPOS 屏幕空间像素位置]]
方法二:顶点着色器计算的顶点齐次裁剪坐标 xy 分量就是屏幕空间的物理像素大小,利用它除以屏幕分辨率 _ScreenParams.xy 即可。_ScreenParams:x 是摄像机目标纹理的宽度(以像素为单位),y 是摄像机目标纹理的高度(以像素为单位),z 是 1.0 + 1.0/x,w 为 1.0 + 1.0/y。1float2 ScreenUV = (input.positionCS.xy/_ScreenParams.xy);
方法三:手动计算(麻烦)123456789101112//在顶点着色器里,计算屏幕 uv,并把 xy 存储为未经透除的 uv 通道,zw 正常存储。 o.ScreenUV.xy = o.positionCS.xy*0.5 + 0.5* float2(o.positionCS.w,o.positionCS.w); o.ScreenUV.zw = o.positionCS.zw;//在片元着色器 ...
溶解特效
溶解的思路
透明度测试+关闭背面剔除1234"RenderType"="TransparentCutout""Queue"="AlphaTest"Cull Off
Frag 阶段,判断是否裁剪像素。
裁剪像素的方法
(常用)通过 clip 函数
通过 discard
通过设置 alpha 变量为 0
clip 函数
clip (x),x 小于 0 就会丢弃该像素。
在片段着色器中,根据当前 uv,采样噪声图,就可以获得一个遮罩值。
clip (遮罩值 - 阈值),就可以看到一个动态溶解效果。12half noise = SAMPLE_TEXTURE2D(_NoiseTex, sampler_NoiseTex, i.uv).r;clip(noise - _DissolveThreshold);
discard 函数:
1234if(noise < _DissolveThreshold){ discard}
ASE 实现
Shader 实现思路一:smoothstep ...
雾
Unity 内置雾缺点:要为场景中所有物体的 shader 添加代码,实现的效果有限。
全局雾效基于屏幕后处理的全局雾效,不需要改 shader 代码。自由度很高
关键:根据深度图来重建每个像素在世界空间下的位置。
在简单的雾效实现中,我们需要计算一个雾效系数$f$,作为混合原始颜色和雾的颜色的混合系数;这个雾效系数 $f$ 有很多计算方法。在 Unity 内置的雾效实现中,支持三种雾的计算方式——线性(Linear)、指数(Exponential)以及指数的平方(Exponential Squared)。
当给定距离 $z$ 后,$f$ 的计算公式分别如下:
Linear:$d_{min}$ 和 $d_{max}$ 分别表示受雾影响的最小距离和最大距离$$f=\frac{d_{max}-\mid z\mid}{d_{max}-d_{min}}$$
Exponential:$d$ 是控制雾的浓度的参数$$f=e^{-d\cdot|z|}$$
Exponential Squared:$d$ 是控制雾的浓度的参数$$f=e^{-(d-|z|)^2}$$
基 ...