提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
目录
前言
一、ShaderLab
二、Unity Shader结构
三、GPU流水线
四、着色器
五、坐标空间
六、编写及实现
总结
Unity shader是计算机图形渲染管线的一部分,是一小段应用程序,是GPU渲染流水线的
一些可高度编程的阶段,而编译出来的最终代码是会在GPU上运行的。Unity Shader告诉计算机在场景中怎样对物体渲染和着色,这个过程包括计算颜色和光照,并将其赋予对象,使其按照我们的想法显示在屏幕上。
在unity中需要配合材质才能达到需要的效果。常见流程是1.创建一个材质,2.创建一个Unity Shader,并把它赋给上步创建的材质,3.把材质赋给要渲染的对象,4.在材质面板中调整Unity Shader的属性得到满意的结果。
ShaderLab作为一种专门为Unity Shader服务的语言。即我们可以编写ShaderLab来生成Unity Shader。Cg(C for Graphic)/ HLSL(High Level Shading Language)/ GLSL(OnpenGL Shading Language)都是着色语言,作为中间语言,即交给GPU可以理解的语言。
因为Microsoft和NVIDIA合作,所以Cg/HLSL实际上是同一种语言。而ShaderLab内部可以嵌套Cg/HLSL语言编写着色代码,需要嵌套在命令CGPROGRAM和ENDCG之间。这里的Cg/HLSL是Unity经封装后提供的,有些原生的函数和用法Unity并没有提供支持。
也可以用GLSL来编写,但是目标平台只有Masc OS X、OpenGL ES 2.0、Linux。代码嵌套在GLSLPROGRAM和ENDGLSL之间。
名字:Shader “MyShader”{}
属性:Properties{
Name {“display name”, PropertyType} = DefaultValue
}//材质和Unity Shader交互
重要成员:SubShader{//不少于一个
[Tags]//标签设置,告诉渲染引擎希望怎样以及何时渲染这个对象,可选
[RenderSetup]//状态设置,设置显卡的各种状态,如是否开启深度测试等,可选
Pass{
[Name]//通过这个名称,可以使用UsePass命令直接使用其他Unity Shader的Pass
[Tags]
[RenderSetup]
//other code
//一次完整的渲染流程,如果Pass数目过多,往往会导致渲染性能下降。
//状态和标签同样可以在Pass中声明,不同的是,SubShader中的一些标签设置是 //特定的,如果在SubShader进行了这些设置,将会用于所有的Pass。
}
//Other Passes
}
后路:Fallback ”name”//如果上面所有的SubShader在这块显卡中都不能运行,就用这个最低级的Shader
顶点数据->顶点着色器->装配图元->光栅化->片段着色器->测试和混合->帧缓存
GPU获取到CPU传递的顶点数据之后,流水线开始运作,在顶点着色器中,顶点坐标从模型空间变换到裁切空间,并可以通过shader程序对顶点进行处理,以实现一些特殊的效果。
装配图元阶段将顶点着色器输出的顶点数据装配成指定的几何图元,基本图元包括点线面。光栅化阶段将几何图元转变为片段,确定屏幕坐标中的哪些整形栅格区域被基本图元占用,再分配颜色值和深度值到各个区域。片段在经过视锥体裁切之后被传递到片元着色器,在这个阶段,片元着色器会计算光照、阴影、纹理等所有颜色数据,最终计算出像素的颜色。确定了像素的颜色之后会进入测试和混合阶段,在这个阶段会检测所有像素的深度值,将当前片段的深度值与深度缓存中的数值进行比较,从而判断这个像素前面是否有物体对它进行遮挡,进而决定这个像素是否应该被丢弃,通过测试的像素会与已经绘制好的图像进行混合,从而得到最终的颜色。帧缓存是流水线的最后一站,用于存储将要渲染到屏幕上的像素,等待下一步输出到屏幕上。
Shader一般有顶点/片元着色器、表面着色器、固定管线着色器(正在被淘汰)
顶点/片元着色器:用顶点着色器进行坐标变换、逐顶点光照以及传递数据(模型空间->齐次裁剪空间->计算顶点颜色),用片元着色器进行逐像素的渲染(输入上一阶段对顶点信息的插值,输出颜色值),一般都是同时出现。Cg代码写在Pass语义块中,定义在CGPROGRAM和ENDCG之间。复杂但灵活性更高。通过#pragma vertex vert #pragma fragment frag编译Cg代码片段。
表面着色器:对顶点/片元着色器的更进一层封装,Unity自己创造的一种着色器代码类型,在背后做了很多工作,为我们处理了很多光照细节,使代码量有明显的降低。Cg代码写在SubShader语义块中,定义在CGPROGRAM和ENDCG之间。对细节的把控不如顶点/片元着色器。背后仍旧将其转换成对应的顶点/片元着色器,渲染代价比较大。通过#pragma surface编译Cg代码片段。
shader具有很强的封装性,在实际编写shader的过程中,除了调用必须的函数外,还需要进行坐标的转换(如:o.vertex = UnityObjectToClipPos(v.vertex)),在不同的空间中有不同的坐标系:
模型空间(Object Space):float4
定义:以模型(通常为模型的重心)为原点的空间,一般默认物体前方为z轴,右边为x轴,上方为y轴。
世界空间 WS(World Space):float4
定义:一般来说世界空间就是游戏空间,其原点放置在游戏空间的中心。
观察空间 VS(View Space):float4
定义:以相机为原点的坐标空间。(观察空间与屏幕空间不一样,观察空间是一个三维空间,屏幕空间是二维空间)。
裁剪空间 CS(Clip Space):float4
定义:与投影矩阵计算后也被称为齐次裁剪空间(HCS Homogeneous Clip Space),这个用于变换的矩阵叫做裁剪矩阵(Clip Matrix),也被称为投影矩阵(Projection Matrix)。
屏幕空间 SS(Screen Space):float2
定义:当裁剪工作完毕后,就需要进行真正的投影,也就是说需要把视锥体投影到屏幕空间(Screen Space)。经过这一步变换就会得到真正的像素位置,而不是虚拟的三维坐标。
视口空间 VP(ViewPort Space):float2
定义:屏幕空间除以屏幕分辨率得到的就是视口空间(viewport space)中的坐标。
模型空间(本地空间)经模型变换矩阵变换为世界空间
世界空间经视变换矩阵变换为摄像机空间(观察空间)
摄像机空间(观察空间)经裁切矩阵变换为裁切空间(裁切几何体)
裁切空间(裁切几何体)经投影矩阵变换为屏幕空间(得到屏幕像素坐标)
通过编写Unity shader最终实现包括但不限于:光照,阴影,纹理,透明,流光,描边,消融,液体,序列帧动画,后期处理等效果。
而在用visua studio编写Unity shader的过程中会发现,没有代码补全,没有报错提示,编写完成运行的时候,在console中报的错误也很模糊,不易找出哪里出现了bug。
ASE是一款Unity提供的付费插件,下载并导入之后,可以在此进行shader的编写,只需进行节点的创建以及设置并进行连线即可,连线完成就意味着代码生成完毕,可当做手写的shader直接赋予材质。不仅如此,ASE还可以在编写过程中,查看中间节点的运行效果。
此外,还可以在C#脚本中创建材质,并找到指定的Unity shader赋予给该材质(避免创建材质并拖动),同时,可在脚本中对材质的各个属性进行设置。
以上就是要讲的内容,本文仅仅简单介绍了unity shader的简单概念,而在实际的编写及应用中还需要更深一步的学习。