Unity3D中的Shader
简单的说,Shader是为渲染管线中的特定处理阶段提供算法的一段代码。Shader是伴随着可编程渲染管线出现的,从而可以对渲染过程加以控制。
1. Unity提供了很多内建的Shader,这些可以从官网下载,打开looking for older version的链接就能看到Build-in shaders。选择合适的Shader很重要,以下是开销从低到高的排序:
(1)Unlit:仅使用纹理颜色,不受光照影响
(2)VertexLit:顶点光照
(3)Diffuse:漫反射
(4)Specular:在漫反射基础上增加高光计算
(5)Normal mapped:法线贴图,增加了一张法线贴图和几个着色器指令
(6)Normal Mapped Specular:带高光法线贴图
(7)Parallax Normal Mapped:视差法线贴图,增加了视差贴图的计算开销
(8)Parallax Normal Mapped Specular:带高光视差法线贴图
对于现在流行的移动平台游戏,Unity提供了几种专门的着色器放在Shader->Mobile下,它们是专门优化过的。
2. 在Unity中,可以编写3种类型的Shader:
表面着色器(Surface Shaders):最常用的Shader,可以与灯光、阴影、投影器交互,以Cg/HLSL语言进行编写,不涉及光照时尽量不要使用。
顶点和片段着色器(Vertex and Fragment Shaders):全屏图像效果,代码比较多,以Cg/HLSL编写,难以和光照交互。
固定功能管线着色器(Fixed Function Shaders):游戏要运行在不支持可编程管线的老旧机器上时,需要用ShaderLab语言来编写。
无论编写哪种Shader,实际的Shader代码都需要嵌入ShaderLab代码中,Unity通过ShaderLab代码来组织Shader结构。
下面是我新建的一个Shader的默认内容:
Shader "Custom/TestShader" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD CGPROGRAM
#pragma surface surf Lambert sampler2D _MainTex; struct Input {
float2 uv_MainTex;
}; void surf (Input IN, inout SurfaceOutput o) {
half4 c = tex2D (_MainTex, IN.uv_MainTex);
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
Properties:用来定义着色器中使用的贴图资源或者数值参数等,这里定义了一个Base (RGB)的2D纹理。
SubShader:一个着色器包含的一个或多个子着色器。Unity从上到下遍历子着色器,找到第一个能被设备支持的着色器。
FallBack:备用着色器,一个对硬件要求最低的Shader名字。
(1)Properties定义的属性
名称("显示名称", Vector) = 默认向量值,一个四维向量
名称("显示名称", Color) = 默认颜色值,一个颜色(取值0~1的四维向量)属性
名称("显示名称", Float) = 默认浮点数值,一个浮点数
名称("显示名称", Range(min,max)) = 默认浮点数值,一个浮点数,取值min~max
名称("显示名称", 2D) = 默认贴图名称{选项},一个2D纹理属性
名称("显示名称", Rect) = 默认贴图名称{选项},一个矩形纹理属性(非2的n次幂)
名称("显示名称", Cube) = 默认贴图名称{选项},一个立方体纹理属性
选项指的是一些纹理的可选参数,包括:
TexGen:纹理生成模式,可以是ObjectLinear、EyeLinear、SphereMap、CubeReflect、CubeNormal中的一种。如果使用了自定义的顶点程序,这些参数会被忽略。
LightmapMode:纹理将受渲染器的光照贴图参数影响。纹理将不会从材质中获取,而是取自渲染器的设置。
示例如下:
Properties {
_RefDis ("Reflect Distance", Range(0, 1)) = 0.3 //范围数值
_Color ("Reflect Color", Color) = (.34, .85, .92, 1) //颜色
_MainTex ("Reflect Color", 2D) = "white"{} //纹理
}
常用的变量类型如下:
颜色和向量:float4, half4, fixed4
范围和浮点数:float, half, fixed
2D纹理贴图:sampler2D
Cubemap:samplerCUBE
3D纹理贴图:sampler3D
(2)SubShader,子着色器由标签(可选)、通用状态(可选)、Pass列表组成。使用子着色器渲染时,每个pass都会渲染一次对象,所以应尽量减少Pass数量。
(3)Category,分类用于提供让子着色器继承的命令。
3. 表面着色器,使用Cg/HLSL编写,然后嵌在ShaderLab的结构代码中使用。仅需编写最关键的表面函数,其余代码由Unity生成,包括适配各种光源类型、渲染实时阴影以及集成到前向/延迟渲染管线中。如果你需要的效果与光照无关,最好不要使用表面着色器,否则会进行很多不必要的光照计算。使用#pragma surface...来指明是一个表面着色器。输入结构体Input一般包含必须的纹理坐标,还可以在输入结构中加入一些附加数据。
CGPROGRAM
#pragma surface surf Lambert sampler2D _MainTex;
fixed4 _Color; struct Input {
float2 uv_MainTex;
}; void surf (Input IN, inout SurfaceOutput o) {
half4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
o.Alpha = c.a;
}
ENDCG
SurfaceOutpu描述了表面的各种参数,它的标准结构如下:
struct SurfaceOutput{
half3 Albedo; //反射光
half3 Normal; //法线
half3 Emission; //自发光
half Specular; //高光
half Gloss; //光泽度
half Alpha; //透明度
}
4. 顶点和片段着色器,运行于具有可编程渲染管线的硬件上,它包括顶点程序和片段程序。使用该着色器渲染时,固定功能管线将会关闭,即编写好的顶点程序替代原有的3D变换、光照、纹理坐标生成等功能,片段程序会替换掉SetTexture命令中的纹理混合模式。代码使用Cg/HLSL编写,放在Pass命令中,格式如下:
SubShader{
Pass{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag //cg code ENDCG
}
}
编译命令说明如下:
#pragma vertex name-----------------------------将函数name的代码编译成顶点程序
#pragma fragment name---------------------------将函数name的代码编译成片段程序
#pragma geometry name---------------------------将函数name的代码编译成DX10的几何着色器
#pragma hull name-------------------------------将函数name的代码编译成DX11的hull着色器
#pragma domain name-----------------------------将函数name的代码编译成DX11的domain着色器
#pragma fragmentoption option-------------------添加选项到编译的OpenGL片段程序,对于顶点程序或编译目标不是OpenGL的无效
#pragma target name-----------------------------设置着色器的编译目标
#pragma only_renderers space separated names----仅编译到指定的渲染平台
#pragma exclude_renderers space separated names-不编译到指定的渲染平台
#pragma glsl------------------------------------为桌面系统的OpenGL进行编译时,将Cg/HLSL代码转换成GLSL代码
#pragma glsl_no_auto_normalization--------------编译到移动平台GLSL时,关闭顶点着色器中对法线和切线进行自动规范化
示例代码,使用命令:
Shader "Custom/Shader1" {
Properties {
_Color("Main Color", Color) = (,,, 0.5)
_SpecColor("Spec Color", Color) = (,,,)
_Emission("Emmisive Color", Color) = (,,,)
_Shininess("Shininess", Range(0.01, )) = 0.7
_MainTex("Base (RGB)", 2D) = "white" {}
} SubShader {
Pass{
Material{
Diffuse[_Color]
Ambient[_Color]
Shininess[_Shininess]
Specular[_SpecColor]
Emission[_Emission]
}
Lighting On
SeparateSpecular On
SetTexture[_MainTex]{
constantColor[_Color]
Combine texture * primary DOUBLE, texture * constant
}
}
} FallBack "Diffuse"
}
示例代码,使用Cg。其中的包含文件可以在/Data/CGIncludes/目录下找到。
Shader "Custom/Shader2" { //定义属性(变量)
Properties {
_MainTex ("Texture", 2D) = "white" {} //纹理
_Color ("Main Color", Color) = (,,,0.5) //颜色
} //子着色器
SubShader { //每个Pass中,对象几何体都被渲染一次
Pass{ CGPROGRAM //Cg代码开始
#pragma vertex vert //将函数vert编译为顶点程序
#pragma fragment frag //将函数frag编译为片段程序 //包含一个内置的cg文件,提供了常用的声明和函数,比如appdata_base结构
#include "UnityCG.cginc" float4 _Color; //变量,颜色的向量表示
sampler2D _MainTex;
float4 _MainTex_ST; //定义一个结构体v2f
struct v2f{
float4 pos:SV_POSITION;
float2 uv:TEXCOORD0;
}; //顶点处理程序
v2f vert(appdata_base v)
{
v2f o;
//3D坐标被投影到2D窗口中,与矩阵Model-View-Projection相乘
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
} //片段处理程序
half4 frag(v2f i):COLOR
{
half4 texcol = tex2D(_MainTex, i.uv);
//自定义颜色_Color与纹理的融合
return texcol * _Color;
} ENDCG //Cg代码结束
} } //备用着色器
FallBack "VertexLit"
}
上面的例子用到了一些内置的变量,有下面这些:
UNITY_MATRIX_MVP--------------------------当前的model*view*projection矩阵
UNITY_MATRIX_MV---------------------------当前的model*view矩阵
UNITY_MATRIX_V----------------------------当前的view矩阵
UNITY_MATRIX_P----------------------------当前的projection矩阵
UNITY_MATRIX_VP---------------------------当前的view*projection矩阵
UNITY_MATRIX_T_MV-------------------------model*view矩阵的转置矩阵
UNITY_MATRIX_IT_MV------------------------model*view矩阵的转置逆矩阵
UNITY_MATRIX_TEXTURE0
UNITY_MATRIX_TEXTURE1
UNITY_MATRIX_TEXTURE2
UNITY_MATRIX_TEXTURE3---------------------纹理变换矩阵
UNITY_LIGHTMODEL_AMBIENT------------------当前的环境光颜色
下面是官方文档中的一个例子,可以产生不同颜色交错的效果:
Shader "Custom/Bars" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag #include "UnityCG.cginc" struct vertOut {
float4 pos:SV_POSITION;
float4 scrPos;
}; vertOut vert(appdata_base v) {
vertOut o;
o.pos = mul (UNITY_MATRIX_MVP, v.vertex);
//ComputeScreenPos将返回片段着色器的屏幕位置scrPos
o.scrPos = ComputeScreenPos(o.pos);
return o;
} fixed4 frag(vertOut i) : COLOR0 {
float2 wcoord = (i.scrPos.xy/i.scrPos.w);
fixed4 color; //改变50可以调整间距
if (fmod(50.0*wcoord.x,2.0)<1.0) {
color = fixed4(wcoord.xy,0.6,1.0);//这里可以改变颜色
} else {
color = fixed4(0.1,0.3,0.7,1.0);//这里可以改变颜色
}
return color;
} ENDCG
}
}
}
下面的例子来自官方手册,棋盘格效果:
Shader "Custom/Chess" { SubShader { Pass {
CGPROGRAM
#pragma vertex vert
#pragma fragment frag #include "UnityCG.cginc" //输入顶点结构体,包含位置和颜色
struct vertexInput {
float4 vertex : POSITION;
float4 texcoord0 : TEXCOORD0;
}; //片段结构体,包含位置和颜色
struct fragmentInput{
float4 position : SV_POSITION;
float4 texcoord0 : TEXCOORD0;
}; //顶点处理
fragmentInput vert(vertexInput i){
fragmentInput o;
o.position = mul (UNITY_MATRIX_MVP, i.vertex);
o.texcoord0 = i.texcoord0;
return o;
} //片段处理
float4 frag(fragmentInput i) : COLOR {
float4 color;
//fmod用来取余数,物体表面X方向被分成了8/2=4个区间
//X坐标对2求余,所以这里用1来作为比较,黑、白各占一半
if ( fmod(i.texcoord0.x*8.0,2.0) < 1.0 ){
if ( fmod(i.texcoord0.y*8.0,2.0) < 1.0 )
{
color = float4(1.0,1.0,1.0,1.0);//白色
} else {
color = float4(0.0,0.0,0.0,1.0);//黑色
}
} else {
if ( fmod(i.texcoord0.y*8.0,2.0) > 1.0 )
{
color = float4(1.0,1.0,1.0,1.0);//白色
} else {
color = float4(0.0,0.0,0.0,1.0);//黑色
}
}
return color;
} ENDCG
}
} FallBack "Diffuse"
}
相同效果的简化代码:
Shader "Custom/ChessOpt" {
SubShader {
Pass {
CGPROGRAM
#pragma vertex vert_img
#pragma fragment frag #include "UnityCG.cginc" float4 frag(v2f_img i) : COLOR {
bool p = fmod(i.uv.x*8.0,2.0) < 1.0;
bool q = fmod(i.uv.y*8.0,2.0) > 1.0; return float4(float3((p && q) || !(p || q)),1.0);
}
ENDCG
}
}
}
上一个例子中有个texcoord0变量,它的x和y的值都是从0到1的,刚好映射到一张特殊的纹理上。
Unity3D中的Shader的更多相关文章
- Unity3D中的shader基础知识
1.Unity中配备了强大的阴影和材料的语言工具称为ShaderLab,以程式语言来看,它类似于CgFX和Direct3D的效果框架语法,它描述了材质所必须要的一切咨询,而不仅仅局限于平面顶点/像素着 ...
- [Unity3D]降低向Shader中传值的开销
Unity3D中提供了很多API用于向shader传值,这篇文章对比测试了两类不同的使用方法的性能. 正文 Unity3D中,通过C#代码向shader传值有两种方式. 一种是面向具体的materia ...
- 【unity shaders】:Unity中的Shader及其基本框架
shader和Material的基本关系 Shader(着色器)实际上就是一小段程序,它负责将输入的Mesh(网格)以指定的方式和输入的贴图或者颜色等组合作用,然后输出.绘图单元可以依据这个输出来将图 ...
- Unity3D中灵活绘制进度条
有时我们需要在Unity3D中绘制进度条,如: 或 如果使用4.6版本以下的unity绘制环形的进度条可能需要费点劲.我搜到的大多数方法都是用NGUI插件,但有时只是 ...
- Unity3D中事件函数的运行顺序
Unity3D中脚本的生命周期是依照预先定义好的事件函数的运行流程来演化的,详细流程例如以下: Editor模式下Reset: 当脚本第一次被挂到GameObject上或用户点击Resetbutton ...
- Unity3D 中材质球(Material)预制体打包成AB(AssetBundle)出现材质丢失问题的解决方案
关于Unity3D中打AB(AssetBundle)资源包,默认是不连Shader一同打包进去的,所以得自行添加,添加方式比较简单,不需要往项目中添加Shader,只是点选一下就可以了,具体实现方式如 ...
- UE4/Unity3D中同时捕获多高清摄像头的高效插件
本文主要讲实现过程的一些坑. 先说下要实现的目标,主要功能在UE4/Unity中都要用,能同时捕获多个摄像头,并且捕获的图片要达到1080p25桢上,并且需要经过复杂的图片处理后丢给UE4/Unity ...
- unity3d 材质概述 ---- shader
学习笔记: 材质概述: 物体呈现在我们前面除了形体外,还包括“固有颜色”和“质地”(质感与光学性质).固有颜色让物体的表面看起来是什么颜色,而质感决定了该物质是使用什么材质的.在三维建模软 ...
- Unity3d中使用摄像机制作实时显示小地图
Unity3d中使用摄像机制作实时显示小地图,以之前的tank为例.开始制作之前场景中物体如图. 开始制作,步骤1:新建一个camera及一个plane.对齐位置,将camera改名为camera_U ...
随机推荐
- 【Spring】如何在单个Boot应用中配置多数据库?
原创 BOOT 为什么需要多数据库? 默认情况下,Spring Boot使用的是单数据库配置(通过spring.datasource.*配置具体数据库连接信息).对于绝大多数Spring Boot应用 ...
- flex布局全解析
前言 很长一段时间, 我知道有flex这个布局方式, 但是始终没有去学它. 3点原因: 感觉还比较新, 担心兼容性不好. 普通的布局方式能满足我的绝大多数需求. 好像蛮复杂的. 最近由于开发需要, 学 ...
- Hibernate学习笔记(1)
1 使用Hibernate (1)创建User Library,命名为HIBERNATE3,加入需要的jar (2)创建hibernate配置文件hibernate.cfg.xml, 为了便于调试最好 ...
- c语言 快排排序
快速排序(Quick Sort): 这个算法的霸气程度从它的名字就可以看出来了.快速排序的应用也是非常广的的,各种类库都可以看到他的身影.这当然与它的“快”是有联系的,正所谓天下武功唯快不破. 快速排 ...
- VS2012安装英文的语言包后,调试的时候提示Unknown error:0x80040d10
https://social.msdn.microsoft.com/Forums/en-US/e11a86ef-3be2-4256-92e9-d12809f2a6ca/error-0x80040d10 ...
- Error Curves(2010成都现场赛题)
F - Error Curves Time Limit:3000MS Memory Limit:0KB 64bit IO Format:%lld & %llu Descript ...
- bzoj4080
分组赛时wy大神讲的题,网上都是随机化的题解 我来讲一下正解吧,我们穷举两个点,这两点距离要小于限制 然后我们分别以这两个点为圆心,两点距离为半径画圆 圆圆相交的部分被两点练成线段划分成两部分,不难发 ...
- iOS开发:UINavigationController常用操作
NavigationController常用操作: 更改bar的背景颜色:self.navigationController?.navigationBar.barTintColor =UIColor. ...
- highcharts 设置标题不显示
设置标题不显示:title:false 用法: title: { text: false },
- 安装IIS之后运行aspx 显示“服务器应用程序不可用” 解决办法
引起这个的原因大概是现安装了.Net Framework,后装的IIS导致.Net没有在IIS里注册. 另外,还有可能是ASPNET账户没有IIS所指定服务器目录的权限.在资源管理器中找到“工具-文 ...