渲染管线概念:也被称为可编程流水线,渲染管线指的是在给定的3D场景中,根据一架给定的摄像机的视角生成2D图像的一系列步奏。之所以是2D图像,是因为我们的屏幕是2D的,因此我们需要通过2D屏幕来呈现3D场景。在了解渲染管线之前需要了解人体眼睛看到的物体会有以下特性:1、平行线汇集成一个点(向远处望去,目光会在一个点聚集);2、物体的大小会随着距离增加而减小,越远的物体看起来就越小;3、物体会有重叠(景深).光照和阴影是能体现立体感和体积感的因素.三角形是构建物体模型的最基本单位,三角形越多,密度越大,效果越好,但是越耗性能.
写shader的目的就是告诉GPU往屏幕哪里画颜色、怎么画颜色;Shader的作用就是计算物体最终显示的颜色是什么.Shader在渲染管线中可以由程序员控制的部分是Vertex Shader(顶点着色:作用是顶点变换,轮廓信息,把模型正确的、有层次的画在屏幕上)和 Fragment Shader(片元着色:作用是修正每个像素的颜色);
3d坐标转换(矩阵变换,逐顶点雾化、材质属性和光照属性)-->面的组装,面截取,面剔除-->光栅化-->对每一个像素区域进行着色,贴图.光栅化的解释:是把顶点数据转换为片元的过程,具有将图转化为一个个栅格组成的图象的作用.决定窗口坐标中的哪些整型栅格区域被基本图元占用;分配一个颜色值和一个深度值到各个区域,把物体的数学描述以及与物体相关的颜色信息转换为屏幕上用于对应位置的像素及用于填充像素的颜色,使用插值计算方式,这个过程称为光栅化.
简洁来说即是CPU进行模型数据/贴图/色彩的准备,然后将其发送给GPU进行使用,GPU将其一一对应,比如模型数据中每个顶点/每个面上的点对应的每一个像素每一个贴图的像素等等需要大量计算,并且GPU适合于这种计算(CPU不适合).
另外一种GPU渲染管线的流程是:①Vertex Shader(顶点着色器,对每个顶点相关的属性进行创建/修改/忽略,程序员参与编写);②The Geometry Shader(几何着色器,可选阶段,从多边形网格中增删顶点);③Clipping(裁剪,将没有在屏幕中显示出来的部分进行裁剪);④Screen Mapping(屏幕映射,将之前步骤得到的坐标映射到对应的屏幕坐标系上);⑤Triangle Steup(三角形设定,计算三角形表面的差异以及三角形其他相关数据);⑥Triangle Traversal(三角形遍历,找到那些采样点或像素在三角形中);⑦Pixel Shader(像素着色,对每一个像素进行处理);⑧Merger(合并,颜色修改(Color Modifying),Z缓冲(Z-buffer),混合(Blend),模板(Stencil)和相关缓存)等.
RGBA(red,green,blue,Alpha)的计算方式是按照每个值都在0-1(包含0-1)的范围内进行(纯标量)加减乘运算的;为什么白光会被吸收掉一部分,并反射出去,利用运算公式来表示:C0(1,1,1)这是白色,C1(0.75,0.25,0.5)这是粉红色,2个颜色分量相乘即C=(1*1,1*0,1*0)=(0.75,0.25,0.5)得到粉红色,红色吸收25%反射75%,绿色吸收25%反射75%,蓝色吸收50%反射50%;这就是为什么夏天穿黑色的衣服更容易发热的原因,黑色(0,0,0),白色-黑色表示吸收率为100%.
顶点数据:传递给GPU的数据是顶点数据,为了能获取到法线数据,将法线数据存储到顶点数据里面,在GPU处理的阶段,直接获取到法线的数据或者其他数据.将顶点数据进行封装,一般包括①空间坐标信息②法线信息③颜色信息④其他.将这些数据放入 顶点数据/顶点缓冲区 容器等待GPU进行加载.
索引缓存:等待GPU加载之前还有 索引/索引缓冲区 这个概念,是因为如果给GPU 4个顶点,GPU 不知道怎么去绘制图形,绘制成什么样子的图形,就需要索引帮忙完成绘制的样子.①索引顺序不同,构成的三角形也不同,法向量也不同;②DX默认顺时针为正方向,OpenGL默认逆时针为正方向;③背面剔除需要用到索引顺序(关闭三角形背面的渲染,减少模型的面数);④索引可以减小内存(不会把模型的2面都会渲染出来);⑤索引是简单整数数据;
优化:将多个模型的顶点数据/索引数据合并成一个顶点缓冲区/索引缓冲区,然后通知GPU进行渲染,而不是进行多次通知,因为通知是有成本的.这叫做合并缓冲区.模型的顶点/索引数据经常变化,一般情况下不加入合并缓冲区.
顶点数据变换测试:顶点着色,需要我们自己编写函数,传入顶点数据,并且交由GPU执行.
投影:即将3D物体投影到2D窗口上面的过程.
GPU有可能处理的非常快速有可能处理的非常慢,显示器的显示速度就无法跟GPU的绘制速度保持一致.因此就通过双缓冲区来解决这个问题。一个是前台缓冲区一个是后台缓冲区。显卡将绘制好的图像放置于这个两个缓冲区中,然后切换显示就可以避免这个问题。比如:缓冲区1和缓冲区2。假设最初缓冲区1为前台缓冲区,前台缓冲区显示的已经绘制完成的画面。缓冲区2就为后台缓冲区,我们将数据持续写入到后台缓冲区2。当缓冲区2写入完成之后,就交换这两个缓冲区。把缓冲区1当成后台缓冲区,缓冲区2当成前台缓冲区,然后我们继续将数据写入到后台缓冲区1。那么这个缓冲区就保存我们所有的图像信息。GPU会假定平台都会完成NDC规范化设备坐标,即后台缓冲区的屏幕大小在[-1,1]这个区间中,也是3D映射到2D上面算出的比值规范,不然,则不会正确显示模型.
片元着色器:像素着色,顶点插值会被当做像素着色器的输入数据,传入到像素着色器里面。与顶点着色器一样,像素着色器也是一个函数,它处理得是像素片段。因此它也叫片段着色器。像素着色器的任务就是为每个像素片段计算一个颜色值。1、尽量使用常量值 2、尽量在顶点着色器里面计算好需要的数据 3、避免在像素着色器里面做复杂的计算; 输出阶段: 像素片段由像素着色器生成之后,被传送到输出合并器(output merger),在该阶段某些像素被丢弃,未被丢弃的像素被写入到后台缓冲区。混合工作就是在这个阶段完成的,在后面我们会专门讲到混合。
OBJ解析
OBJ文件是一种文本文件,可以直接用记事本打开进行查看和编辑修改。OBJ文件格式支持直线(Line)、多边形(Polygon)、表面(Surface)和自由形态曲线(Free-form Curve)。直线和多边形通过它们的点来描述,曲线和表面则根据它们的控制点和依附于曲线类型的额外信息来定义,这些信息支持规则和不规则的曲线。 (1)OBJ文件是一种3D模型文件。不包含动画、材质特性、粒子等信息。(2)OBJ文件主要支持多边形(Polygons)模型。(3)OBJ文件支持法线和贴图(UV)坐标。关键字
顶点数据(Vertex data):v 几何体顶点(Geometric vertices) vt 贴图坐标点(Texture vertices) vn 顶点法线(Vertex normals)
元素(Elements): p 点(Point) l 线(Line) f 面(Face) curv 曲线(Curve) curv2 2D 曲线(2D curve) surf 表面(Surface)
组(Grouping): g 组名称(Group name) s 光滑组(Smoothing group) mg 合并组(Merging group) o 对象名称(Object name)
显示(Display)/渲染属性(render attributes): usemtl 材质名称(Material name) mtllib 材质库(Material library)
例子:一个模型有顶点,UV坐标,法线,最后定义模型.例子,我们手工编写一个三角模型, 新建一个txt文件然后输入:
将后缀的txt去掉,在unity3d编辑器中obj是可以被识别的,如下变成这种模式:
最后导入unity3d编辑器中:
摄像机照射m模型,然后Scene中从背面看这个模型是透明的,Game中是正面,这个就是plane的特性.这些模型是由美术人员建模并构建,不需要游戏开发人员操心.本章只探究原理.ps:UV映射的意思是将3D物体拆成2D的一个面片,再将2D的图片,根据拆成2D面片的UV坐标进行映射,像素一一对应.
Shader
着色器语言/类型: 1标量: bool #true | false int # 32位有符号整数 half/fixed # 16位浮点数 float # 32位浮点数 double # 64位浮点数
2:向量: float2 # 2D向量 float3 # 3D向量 float4 #4D向量 复合分量: float4 u = {1.0f, 2.0f, 3.0f, 4.0f} u.xyzw u.wyyz u.zzxy u.rgba
3、矩阵类型 float2x2 # 2x2矩阵 mat2 float3x3 # 3x3矩阵 mat3 float4x4 # 4x4矩阵 mat4
4、数组 float m[4] half a[2] float3 v[12]
5、结构体 结构体的定义方法跟c/c++完全一样。但是不能包含函数。 struct Input { float3 pos; float3 normal; float2 uv0; float2 uv1; }
6、typedef关键字 typedef与c/c++功能一样。即给某个类型指定一个别名typedef float2 point; float2 pos = point pos
7、变量修饰符 static #静态类型,程序无法访问这个变量 uniform #常量,程序指定一个值,在着色器中不会在发生变化,也无法改变 const #常量
8、类型转换 转换机制非常灵活 float3 n = float3(1, 0, 0) float3 v = 2.0f * n - 1.0f
9、关键字+保留字 asm bool compile double else extern half if in matrix out pass shared static string true typedef uniform volatile while auto const_cast explicit new reinterpret_cast template unsigned break case continue default friend goto operator private short signed this throw using virtual catch char delete dynamic_cast long mutable protected public sizeof static_cast Try typename class enum namespace register switch union
10、运算符 [] . > < <= >= != == ! && || ?: + += - -= * *= / /= % %= ++ -- = () ,% #取摸,也可能是mod函数。可以用于整数和浮点数
float4 u = float4(1.0f, 2.0f, 3.0f, 4.0f)
float4 v = float4(2.0f, 3.0f, 3.0f, 4.0f)
float4 sum = u + v
sum++
sum = sum * sum
注意:如果是对于矩阵,乘法运算可能在不同的平台有不同意义。
11、程序流程控制 return if (condition) { } else if (condition) { } else { } for (initial;condition;increment) {} while(condition) {} do {} while();
12、函数 函数语法跟c/c++一样。 1、参数为值传递 2、不支持递归 3、都是内联函数
bool foo(in a, int b) { bool ret = false; if (a > b) { ret = true;} else { ret = false; } return ret;}
float square(float x) { return x * x;}
13、内置函数
abs(x) # x绝对值
ceil(x) #返回x最小整数
clamp(x, a, b) #截取x到[a,b]
clip(x) #丢弃当前像素。有些平台是discard指令
cos(x) # x余弦值
cross(u, v) #返回u和v的叉积
degrees(x) #弧度转角度
distance(u,v) # u和v之间的距离
dot(u,v) # u和v的点积
floor(x) #返回x的最大整数
frac(x) #返回小数部分
length(x) #返回x的长度
lerp(u, v, t) #根据参数t在u和v之间线性插值[0,1]
log(x) # ln(x)
log10(x) # log10(x)
max(x,y) #最大
min(x,y) #最小
mul(m,n) #矩阵乘积
normalize(v) #规范化
pow(b,n) #返回b的n次方
radians(x) #角度转弧度
saturate(x) # clamp(x, 0.0, 1.0)
sin(x) # x的正弦值
sqrt(x) #开放
reflect(v,n) #根据入射向量v和法线n计算反射向量
refract(v,n,eta) #根据入射向量v,法线n和折射指数比计算反射向量
tan(x) #正切
transpose #转置矩阵
texture1d|sample1d # 1d纹理采样
texture2d|sample2d # 2d纹理采样
texture3d|sample3d # 3d纹理采样
编写shader前的准备
我在选择的编辑器是VS,需要从github上面下载 ShaderlabVS插件(https://github.com/wudixiaop/ShaderlabVS/releases)下载 ShaderlabVS-2017-0.8.zip 双击添加即可.下面是基本格式,都需要我们去给这些属性赋值之后,我们在下面代码编程中才可以使用这些"材料":
shader里面的属性类型包括:
Range(0,1) 定义浮点数属性,在检视器中可通过一个标注最大最小的滑动条来修改. Color 定义颜色属性 2D 定义2d纹理类型 Rect 定义长方形(非2次方)纹理属性 Cube 定义立方贴图纹理属性 Float 定义浮点数属性 Vector定义一个四元素容器(vector4)属性 备注:对于Range和Float的类型只能是单精度值,对于Color和Vector类型的属性将包含4个由括号围住的数描述.对于纹理(2D,Cube,Rect)缺省值皆可以是一个空字符串也可以是某个内置的缺省纹理:"white" ,"block","gray","bump" ;
纹理后面的大括号{}为纹理属性选项,为可选项,①Texgen texgenmode纹理生成模式,为对应贴图的自动纹理坐标生成模式,为objectLinear,EyeLinear,SphereMap,CubeReflect,CubeNormal之一,这些模式和OpenGL纹理生成模式相对应,如果使用自定义顶点程序,纹理生成将被忽略.②LightmapMode光照贴图模式,纹理将被渲染器的光线贴图属性所影响,不能被使用在材质中,而是取自渲染器的设定.
必须具有一个(或者一个以上的)SubShader{Pass{}} 这个shader文件才可以被使用.FallBack "Diffuse"是指所有的SubShader都不能满足(硬件性能太低),会调用这个Diffuse shader 使用.FallBack Off 明确表示没有后援的shader FallBack "name"制动后援名字.
这里只做个例子,具体的标签值需要去网站查找 https://docs.unity3d.com/Manual/SL-SubShaderTags.html ;
RenderState通用状态: Cull 设置多边形剔除模式 Back 背面剔除 Front 前面剔除 Off都不剔除 ;ZTest 深度测试,默认LEqual ; ColorMask ;ZTest;ZWrite ;AlphaTest ;Blend ;FOG; 查询网址:https://docs.unity3d.com/Manual/SL-CullAndDepth.html
pragma 的格式 https://docs.unity3d.com/Manual/ShaderTut2.html
例子:#pragma vertex vert #pragma 顶点着色器 顶点着色器方法,将数据(系统操作)传入此方法中进行(程序员)加工.
在Properties定义的属性需要在pass通道里面重新定义一样的名字,不然无法使用.文档 https://docs.unity3d.com/Manual/SL-PropertiesInPrograms.html
_NameTex_ST 具有重复贴图的shader
3d建立大型场景常用的shader
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Custom/TilingShader"{Properties{
_Color("Base Color", Color) = (1,1,1,1)
_MainTex("Base(RGB)", 2D) = "white" {}
_ColorU("ColorU", float) = 1.0
_ColorV("ColorV", float) = 1.0
_DetailTex("DetailTex", 2D) = "white" {}
_DetailU("DetailU", float) = 1.0
_DetailV("DetailV", float) = 1.0}
SubShader{
tags{"Queue" = "Transparent" "RenderType" = "Transparent" "IgnoreProjector" = "True"}
Blend SrcAlpha OneMinusSrcAlpha
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
float4 _Color;
sampler2D _MainTex;
float _ColorU;
float _ColorV;
sampler2D _DetailTex;
float _DetailU;
float _DetailV;
struct v2f{float4 pos:POSITION;float2 uv:TEXCOORD0;};
float4 _DetailTex_ST;
v2f vert(appdata_base v){
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord.xy;
//o.uv = TRANSFORM_TEX(v.texcoord, _DetailTex);
//o.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw;
return o;}
half4 frag(v2f i):COLOR
{half4 c = tex2D(_MainTex , i.uv * float2(_ColorU, _ColorV)) * _Color;
half4 d = tex2D(_DetailTex, i.uv * float2(_DetailU, _DetailV));
return c * d;}ENDCG}}}
LightMap:模拟灯光对物体的影响,将这个场景变成一张图片,然后贴在真实场景上面进行模拟.一般对静态物体进行模拟.灯光照射的明暗信息记录下来的过程,叫做烘焙.优点是省去复杂的光照计算,可以对贴图进行二次处理,缺点是多了一层纹理,通常需要额外的UV,静态贴图无法动态改变光的方向(比如游戏场景中的电灯泡,风一吹需要来回晃动,此时使用静态贴图比较假)等.
unity3d shader中RenderType的所有类型:https://blog.csdn.net/u013477973/article/details/80607989
Opaque: 用于大多数着色器(法线着色器、自发光着色器、反射着色器以及地形的着色器)。不透明,图片什么样,就在游戏中展示出什么样
Transparent:用于半透明/全透明着色器(透明着色器、粒子着色器、字体着色器、地形额外通道的着色器)。
TransparentCutout: 蒙皮透明着色器(Transparent Cutout,两个通道的植被着色器)。
Background: Skybox shaders. 天空盒着色器。
Overlay: GUITexture, Halo, Flare shaders. 光晕着色器、闪光着色器。
TreeOpaque: terrain engine tree bark. 地形引擎中的树皮。
TreeTransparentCutout: terrain engine tree leaves. 地形引擎中的树叶。
TreeBillboard: terrain engine billboarded trees. 地形引擎中的广告牌树。
Grass: terrain engine grass. 地形引擎中的草。
GrassBillboard: terrain engine billboarded grass. 地形引擎何中的广告牌草。
Fade:淡入淡出
ps:Draw Call :每次引擎准备数据并通知GPU的过程,通俗讲,就是每帧调用显卡渲染物体的次数.