1.什么是OpenGL、DirectX
Shader可以认为是一种渲染命令,有OpenGL或者DirectX进行解析,来控制渲染图形
GLSL shader语言用于OpenGL
HLSL shader语言用于DirectX
使用CG编写的shader可以跨平台,既可以在OpenGL上解析,也可以在DirectX上解析。
2.unity shader的分类
使用ShaderLab编写Unity中的Shader,
分类1:表面着色器 Surface Shader,相当于对顶点/片元着色器进行了一层封装
分类2:顶点/片元着色器 Vertex/Fragment Shader
分类3:固定函数着色器 Fixed Function Shader(使用不多,几乎被弃用)
3.unity Shader的属性类型有哪些
Properties//属性,属性定义好后不能直接使用,需要在Pass块中重新定义
{
// 属性名 类型 默认值
_Color("Color", Color) = (1,1,1,1) //颜色(float4)
_Vector("Vector", Vector) = (1,1,1,1) //四维向量 与Color本质上是一种类型,只 是使用上不同(float4)
_Int("Int", Int) = 12 //整数(float)
_Float("Float", Float) = 5.4 //小数(float)
_Range("Range", Range(0,1)) = 0.5 //Range类型(float)
_2D("Texture", 2D) = "white"{} //图片类型,引号内为颜色,若不使用图片, 则认为是指定颜色的图片(sampler2D)
_Cube("Cube", Cube) = "white"{} //立方体纹理,比如天空盒就会用到该 属性(samplerCube)
_3D("3D", 3D) = "black"{} //3D纹理(sampler3D)
}
4.untiy Shader属性的使用
Properties属性定义好后不能直接使用,需要在Pass块中重新定义
重新定义的名字需要与Properties属性中保持一致
注意,使用float时可以使用half和fixed进行代替
例如float4类型可以用half4和fixed4,float3可以用half3和fixed3
float可以用half和fixed代替
float4 _Color;
float4 _Vector;
float _Int; //其中int,float,range都可以使用float
float _Float;
float _Range;
sampler2D _2D;
samplerCube _Cube;
sampler3D _3D;
5.float、half、fixed类型的区别
float使用32位进行存储
half使用16位进行存储,在-60000到60000间,超出范围只能用float
fixed使用11位进行存储,在-2到2间,一般颜色使用fixed
6.顶点函数vert和片元函数frag
//顶点函数 这里只是声明了顶点函数的函数名
//作用,将模型顶点的模型空间坐标转化为剪裁空间坐标,从游戏环境转换到视野相机屏幕(裁剪坐标不完全为屏幕坐标)
#pragma vertex vert
//片元函数 这里只是声明了片元函数的函数名
//基本作用 返回模型对应的屏幕上的每一个像素的颜色值
#pragma fragment frag
//Shader中函数的返回值与参数不是固定的,但是有一定的要求
//shader中坐标一般为4位而不是3位,因为可以使用矩阵计算,第4位不会用可不用
//通过语义告诉系统,参数的作用,比如POSITION是告诉系统此处需要顶点坐标,SV_POSITION这个语义用来解释说明返回值,意思是返回值是剪裁空间下的顶点坐标
float4 vert(float4 v : POSITION) : SV_POSITION //float4 v : POSITION此为语义说明,POSITION代表将顶点坐标传递给v, SV_POSITION是对返回值进行语义说明,代表返回值为剪裁空间的坐标
{
return UnityObjectToClipPos(v);
//float4 pos = mul(UNITY_MATRIX_MVP,v); //mul(UNITY_MATRIX_MVP,v)进行矩阵计算,UNITY_MATRIX_MVP为系统的矩阵,计算结果为剪裁空间的坐标
//return pos;
}
fixed4 frag() : SV_Target
{
return _Color;
}
7.Shader中NORMAL与TEXCOORD0的语义说明
//使用结构体封装好语义说明,实现函数时就不需要进行语义说明了
struct a2v
{
float4 vertex : POSITION; //告诉unity将模型空间的顶点坐标填充给vertex
float3 normal : NORMAL; //告诉unity将模型空间的法线向量填充给normal
float4 texcoord : TEXCOORD0; //告诉unity把第一套纹理坐标填充给texcoord
};
8.从应用程序传递到顶点函数的语义有哪些(a2v结构体内的语义)
POSITION 模型空间下的顶点坐标
NORMAL 模型空间下的法向量
TANGENT 模型空间下的切线
TEXCOORD0~N 纹理坐标
COLOR 顶点颜色
9.从顶点函数传递给片元函数的时候可以使用的语义(v2f结构体内的语义)
SV_POSITION 剪裁空间下的顶点坐标(一般是系统直接使用)
COLOR0 可以传递一组值 如float4类型
COLOR1 可以传递一组值 如float4类型
TEXCOORD0~7 传递纹理坐标
10.片元函数传递给系统的语义
SV_Target 颜色值,显示到屏幕上的颜色、
11.什么是光照模型
光照模型就是一个公式,用来计算光 照射在某个点上呈现的效果
标准光照模型
在标准光照模型里面,我们把进入摄像机的光分为下面4个部分
自发光
高光反射 Specular = 直射光 * pow(max(cos(夹角),0), 高光参数),其中夹角为反射光 方向与视野方向的夹角
漫反射 Diffuse = 直射光颜色*cos(夹角)表示光和法线的夹角,当夹角小于0时, 使用0,大于0时使用该夹角
环境光 UNITY_LIGHTMODEL_AMBIENT.rgb
12.实现漫反射时的注意点
只有定义了正确的LightMode才能得到一些Unity的内置光照变量
Tags{"LightMode"="ForwardBase"}
可以取到第一个直射光的颜色 _LoghtColor0,以及第一个直射光的位置_WorldSpaceLightPos0;
包含unity的内置文件,才可以使用unity内置的一些变量
#include "Lighting.cginc"
normalize()方法用来将一个向量进行归一化
max()方法用来取得最大值
dot()用来取得两个向量的点积
_WorldSpaceLightPos0 取得平行光的方向(位置)
_LoghtColor0 取到平行光的颜色
UNITY_MATRIX_MVP 这个矩阵将一个坐标从模型空间转换到剪裁空间
_World2Object这个矩阵用来把一个向量总世界空间转换到模型空间
UNITY_LIGHTMODEL_AMBIENT用来获取系统内置的环境光
13.兰伯特光照模型与半兰伯特光照模型
兰伯特光照模型就是漫反射光照模型,公式为Diffuse = 直射光颜色*cos(夹角)表示光和法线的夹角,当夹角小于0时, 使用0,大于0时使用该夹角
半兰伯特光照模型,公式为:Diffuse = 直射光颜色*(cos(夹角) * 0.5 + 0.5)
14.高光反射的实现
公式为:Specular = 直射光 * pow(max(cos(夹角),0), 高光参数),其中夹角为反射光方向与视野方向的夹角
/计算得到视野方向,其中_WorldSpaceCameraPos为相机在世界空间的位置
normalize(_WorldSpaceCameraPos.xyz - mul(v.vertex, unity_WorldToObject).xyz);
//此处计算得到光的反射向量,reflext方法是计算光的反射方向的,其中-lightDir是光照方向,normal是在世界空间下的法线方向
fixed3 reflectDir = normalize(reflect(-lightDir, normal));
15.高光反射也叫作Blinn光照模型,他存在一个优化的模型叫做Blinn-Phong光照模型
Blinn-Phong光照模型的计算公式为:
Specular = 直射光 * pow(max(cos(夹角),0), 高光参数),其中夹角为法线与向量X的夹角,
其中X为平行光与视野方向的平分线
16.UnityCG.cginc中一些常用的函数
摄像机方向:
float3 WorldSpaceViewDir(float4 v) 根据模型空间中的顶点左边得到(世界空间)从这 个点到摄像机的观察方向
float3 UnityWorldSpaceViewDir(float4 v) 世界空间中的顶点坐标-->世界空间从这个点到摄 像机的观察方向
float3 ObjSpaceViewDir(float4 v) 模型空间中的顶点坐标-->模型空间从这个点到摄 像机的观察方向
光源方向:
float3 WorldSpaceLightDir(float4 v) 模型空间中的顶点坐标-->世界空间中从这个点到 光源的方向
float3 UnityWorldSpaceLightDir(float4 v) 世界空间中的顶点坐标-->世界空间中从这个点到 光源的方向
float3 ObjSpaceLightDir(float4 v) 模型空间中的顶点坐标-->模型空间中从这个点到 光源的方向
方向转换
float3 UnityObjectToWorldNormal(float3 norm) 把法线方向由模型空间-->世界空间
float3 UnityObjectToWorldDir(float3 dir) 把方向由模型空间-->世界空间
float3 UnityWorldToObjectDir(float3 dir) 把方向由世界空间-->模型空间
17.关于纹理贴图的实现
首先在a2v中定义纹理float4 texcoord : TEXCOORD0;
然后在v2f中定义一个uv,贴图其实就是uv,float2 uv : TEXCOORD1;
然后在顶点函数中赋值f.uv = v.texcoord.xy * _MainTex_ST.xy + _MainTex_ST.zw; //添加贴图的旋转与缩放,使Tiling与Offest参数可用,其中_MainTex_ST是贴图的旋转缩放数据
float4 _MainTex_ST; //固定格式,使用贴图变量的名称加上"_ST",_MainTex_ST.xy 为x和y轴的缩放,_MainTex_ST.zw为x和y轴上得到旋转
fixed3 texColor = tex2D(_MainTex, f.uv.xy);tex2D方法为获取贴图在uv上各点的颜色,
然后使用该颜色代替漫反射的颜色,是物体表面显示图片
fixed3 diffuse = _LightColor0.rgb * (dot(normalDir, lightDir) * 0.5 + 0.5) * texColor;
18.法线映射的前置知识
法线normal = (法线贴图的rgb)pixel * 2 - 1;
使用 UnpackNormal(法线贴图颜色)可得到切线空间的法线
//得到法线贴图的颜色
fixed4 normalColor = tex2D(_NormalMap, f.uv.zw);
//得到切线空间的法线
fixed3 normalDir = normalize(UnpackNormal(normalColor));
19.修改透明度的前置条件
首先添加Tags,
Tags{"Queue"="Transparent" "IngnoreProjector"="True" "RenderType"="Transparent"}
然后
ZWrite off
Blend SrcAlpha OneMinusSrcAlpha
最后在片元函数中返回的颜色中修改Alpha值 return fixed4(value, _AlphaScale);