Unity中的Shader都是要通过ShaderLab的基本语法进行编写。
一、 下面我们先从Shaderlab基本语法开始入手
ShaderLab基本结构大致3个代码块儿(一、二、三)
(一) 属性 【Properties】
Shader “name”{
【Properties】//属性
}
属性模块允许我们在可视化面板当中为材质添加要使用的原材料。
(二) Subshader算法
SubShader是专门为GPU渲染所编写的Shader的片段,在ShaderLab中至少有一个SubShader,当然也可多个。
但是,显卡每次渲染处理的时候只能选择一个SubShaders执行。那多个SubShader的作用是为了不同硬件的渲染支持,为了Shader能在比较老的图形显卡中也能支持。一般比较越往下的SubShader要简化,运算指令要简单。
(三) 【FallBack】//后退
作用是:如果SubShader里的所有算法都不能执行的话,就会后退回到最初始的状态 可以认为是默认(一般是所有硬件都支持的渲染方式)
比如: FallBack "Diffuse" 返回到漫反射的状态
二、Unity中的三种Shader介绍
(一) surface shaders, 表面着色器(默认创建的Shader类型)
Unity当中被推荐和鼓励使用的Shader,当你在Unity当中创建一个Shader时,默认的就是Surface Shader 。由于图型管线只识别vertex shaders and fragment shader这两种形式。Surface Shader 是在vertex shaders and fragment shader上面的的一种包装,最 终Unity 引擎还是会把Surface Shader 编译成能够被硬件识别和调用的vertex shaders and fragment shader.。Surface Shader 让我们可以不用去关心这些顶点和片段程序的细节,可以直接得到我们想要的着色器。这个着色器处理了很多光照细节。
(二)vertex and fragment shaders 顶点和片元着色器
针对于比较细节上的处理,偏向于底层,可编程图形管线当中能够编写Shader的部分有两个:vertex shaders和 fragment shader。
(三)fixed function shaders.固定功能管线着色器
比如光照、纹理采样。 所有硬件平台都可支持,针对硬件能够执行的基本命令的Shader,当然,功能有限,但是,速度最快。
三、Unity中的三种自定义Shader代码编写
(一)我们以fixed function shaders固定功能管线着色器
首先物体受光照影响表现:
Shader "MyCustom/MyShader" {
Properties {//属性
// 属性名字 界面显示名 类型 值
_Color("MyColor",color) = (1,0,0,1)
_AmbientColor("AmbientColor",color) = (1,0,0,1)
_SpecularColor("SpecularColor",color) = (1,0,0,1)
_Shininess("Shininess",range(0,8)) = 3
_Emission("EmissionColor",color) = (1,1,1,1)
_MainTexture("MainTexture",2d) = ""
}
SubShader {//shader算法
pass//通道(存储图像的色彩)
{
//color(1,0,0,1) //()表示直接赋值,固定值
//color[_Color]//【】中括号表示使用参数值
material
{ //漫反射 必须依照光照,与灯光配合,将光打到物体表面反射光,我们才能看到物体
diffuse[_Color]//漫反射命令,要想受光照影响,必须启用光照 lighting on
ambient[_AmbientColor]//环境光
specular[_SpecularColor]//高光
shininess[_Shininess]//滑动条高光反射系数,配合启用高光的命令separateSpecular on
emission[_Emission]//自发光
}
lighting on//启动光照
separateSpecular on //启用高光
SetTexture[_MainTexture]//设置纹理
{
//与材质效果混合 double 是图片亮度的表现
combine Texture*primary double
}
}
}
// Fallback "Diffuse" //固定功能管线着色器,几乎所有硬件都支持,所以这句话可以不要
}
Properties 属性
Material 材质
Lighting光照
Settexture设置纹理
Pass通道(存储图像的色彩,色彩为什么是RGB,因为RGB是计算机颜色的三原色)
表面着色器surface shaders没有Pass通道,因为它是vertex and fragment shaders 的包装,vertex and fragment shaders顶点片元着色器有Pass通道
(二)表面着色器surface shaders的Shader代码案例
如果想要实现些稍微复杂的效果, 就要用到表面着色器。表面着色器一般会用到CG代码块儿。
代码如下:
Shader "Custom/NewSurfaceShader" {
Properties {
_Color ("Color", Color) = (1,1,1,1)
_MainTex ("Albedo (RGB)", 2D) = "white" {}
///_Normalmap ("Normalmap", 2D) = "Normal" {}
_Cubemap ("Cubemap", CUBE) = "Cube" {}//CUBE unity的立方体贴图
_Glossiness ("Smoothness", Range(0,1)) = 0.5
_Metallic ("Metallic", Range(0,1)) = 0.0
}
SubShader {
Tags { "RenderType"="Opaque" }//描述渲染类型,当前是不透明的物体//Tags {"queue" = "transparent"}//透明的物体
LOD 200 //层级细节
CGPROGRAM//代表当前是CG代码块,开始写CG语言了
// Physically based Standard lighting model, and enable shadows on all light types
#pragma surface surf Standard fullforwardshadows//编译指令
//#pragma编译指令告诉我们surface关键词是一个表面着色器 , //surf是调用的方法 ,下面有一个surf方法
//Standard 光照表现 基于物理的光照模型,就是我们现在所说的全局光照,物体与物体之间的影响,
//(5.0之前是漫反射 Lambert只是受到光照而已,5.0之后实现了次时代的效果)
//fullforwardshadows 阴影表现(平行光、点光、聚光都是有阴影的)
// Use shader model 3.0 target, to get nicer looking lighting
#pragma target 3.0 //表示GPU硬件支持3.0 不写这句话默认是2.0
sampler2D _MainTex;//CG 中的图片类型,sampler2D是CG语言的一个类, //上面 属性Properties 里面声明属性_MainTex时图片表现是2D,
sampler2D _Normalmap; //但是在CG里面,如果想让CG认识_MainTex,必须在CG代码块儿里面重新声明成sampler2D类型
samplerCUBE _Cubemap;
struct Input { // 类似于有一个传入值
float2 uv_MainTex;//记录uv纹理坐标,在CG中使用图片需要记录uv纹理坐标
/// float2 uv_Normalmap;
float3 worldRelfect;//牵扯到反射效果,记录XYZ轴的相关影像效果
};
half _Glossiness;//CG中的浮点型
half _Metallic;
fixed4 _Color;//CG中的四阶向量
//5.0之后光照用的Standard,所以用SurfaceOutputStandard ,如果不是Standard用SurfaceOutput就可以了
void surf (Input IN, inout SurfaceOutputStandard o) {//IN传进来(UV的传入),inout声明的o值传出去
// 下面是一个公式 (主纹理,uv信息),Color表示把颜色应用在uv的所有的点上,有相应的表现
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb; //在表面着色器里使物体受到光照的话用o.Albedo,它表示漫反射, //把拿到的图片的rgb信息应用到漫反射表现效果上
o.Emission=texCUBE(_Cubemap,IN.worldRelfect).rgb; //是立方体贴图反射周围环境的命令,这是没有法线贴图时反射效果的代码写法
o.Normal=UnpackNormal(tex2D (Normalmap, IN.uv_Normalmap)); //是法线贴图
// Metallic and smoothness come from slider variables
o.Metallic = _Metallic;//金属光泽表现
o.Smoothness = _Glossiness;//高光光泽度
o.Alpha = c.a;
}
ENDCG //CG语言代码块儿结束
}
FallBack "Diffuse"//如果SubShader里的所有算法都不能执行的话,就会后退回到最初始的状态
}
注意:
Properties属性里面的所有东西都是Shaderlab结构,Shaderlab知道这些属性你可以在外面调节,但是如果要在表面着色器的CG代码块里进行Shader使用的话都需要重新声明
上面说到Shader中法线贴图与CubeMap贴图(反射)只能用一个。
为什么呢?是因为如果两个都想用到的时候需要引INTERNAL_DATA的命令。
如果没有用到法线贴图,想要实现反射时物体表面把图片直接反射过来就可以了,但是如果牵扯到法线、物体表面、又要牵扯到反射效果的话,就不仅仅是物体表面把图片反射过来就行了,还要考虑到法线贴图里面的凹凸细节里面的像素,反射效果就比普通平面效果稍微复杂一些,这时就要借助WorldReflectionVector。
下面是详细代码:
Shader "Custom/NewSurfaceShader" {
Properties{
_Color("Color", Color) = (1,1,1,1)
_MainTex("Albedo (RGB)", 2D) = "white" {}
_Normalmap ("Normalmap", 2D) = "Normal" {}
_Cubemap("Cubemap", CUBE) = "Cube" {}
_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;
sampler2D _Normalmap;
samplerCUBE _Cubemap;
struct Input {
float2 uv_MainTex;
float2 uv_Normalmap;
float3 worldRefl;
//法线与反射效果配合需要将添加到Input结构中
INTERNAL_DATA
};
half _Glossiness;//CG中的浮点型
half _Metallic;
fixed4 _Color;
void surf(Input IN, inout SurfaceOutputStandard o) {
fixed4 c = tex2D(_MainTex, IN.uv_MainTex) * _Color;
o.Albedo = c.rgb;
//不需要法线的反射效果
// o.Emission=texCUBE(_Cubemap,IN.worldRefl).rgb;
//下面是法线与反射效果配合,编写完法线命令输出后并结合WorldReflectionVector函数将计算像素中的逐个向量
o.Normal = UnpackNormal(tex2D(_Normalmap, IN.uv_Normalmap)); //是法线贴图
o.Emission = texCUBE(_Cubemap, WorldReflectionVector(IN, o.Normal)).rgb;
o.Metallic = _Metallic;//金属光泽表现
o.Smoothness = _Glossiness;//高光光泽度
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}
补充如何创建Cubemap