1、Unity 3d官方文档上对WorldNormalVector的解释
float3 worldNormal; INTERNAL_DATA -will contain world normal vector if surface shader writes to o.Normal. Toget the normal vector based on per-pixel normal map, use WorldNormalVector(IN, o.Normal).
float3 worldNormal;INTERNAL_DATA 是U3D提供的内置数据,如果表面shader为SurfaceOutput结构中的Normal赋值了,此它会包含世界空间坐标系中法线的向量,通过调用方法WorldNormalVector(IN,o.Normal) 即可得到此值。
使用方法:
struct Input
{
float3 worldNormal;INTERNAL_DATA
};
void surf(Input IN, inout SurfaceOutput o)
{
o.Normal = UnpackNormal(tex2D(_NormalMap, IN.NormalMap)).rgb;
float3 diffuseVal = texCUBE(_CubeMap, WorldNormalVector(IN, o.Normal)).rgb;
}
2、自定义光照模型
inline float4 LightingCustomDiffuse (SurfaceOutput s, fixed3 lightDir, fixed atten) {
float difLight = max(0, dot (s.Normal, lightDir));
float hLambert = difLight * 0.5 + 0.5;
float4 col;
col.rgb = s.Albedo * _LightColor0.rgb * (hLambert * atten * 2);
col.a = s.Alpha;
return col;
}
inline表示内联的。
CustomDiffuse 表示光照模型,我们自定义的光照模型,unity自动光照模型有lambert和BlinnPhong两种光照模式,由于unity中有严格的书写格式,所以在使用时,其前要有Lighting的注明。
光照具体命名和作用区别
【形式一】
half4 LightingName(SurfaceOutput s, half3lightDir, half atten);
此种形式的函数可以表示在正向渲染路径(forward rendering path)中的光照模式,且此函数不取决于视图方向(view direction)。例如:漫反射(diffuse)。
【形式二】
half4 LightingName (SurfaceOutputs, half3lightDir, half3 viewDir, half atten);
此种形式的函数可以表示在正向渲染路径(forward rendering path)中使用的光照模式,且此函数包含了视图方向(view direction)。
【形式三】
half4LightingName_PrePass (SurfaceOutputs, half4 light);
此种形式的函数可以在延时光照路径(deferred lighting path)中使用的。
【形式四】
half4LightingName_DirLightmap(SurfaceOutput s, fixed4 color, fixed4 scale, boolsurfFuncWritesNormal);
这种形式也是不依赖于视图方向(viewdirection)的光照模式。例如:漫反射(diffuse)。
【形式五】
half4LightingName_DirLightmap(SurfaceOutput s, fixed4 color, fixed4 scale, half3viewDir, bool surfFuncWritesNormal,out half3 specColor);这是使用的依赖于视图方向(view direction)的光照模式(light model)。
比如,一个光照模式(lightingmodel)要么使用视图方向(viewdirection)要么不使用。同样的,如果光照模式(lightingmodel)在延时光照(deferred lighting)中不工作,只要不声明成_PrePass(第三种形式),就是行的。
另外,对于形式四和形式五的选择,主要取决于我们的光照模式(light model)是否依赖视图方向(view direction)。需要注意的是,这两个函数将自动处理正向和延时光照路径(forwardand deferred lighting rendering paths)。
PS: Unity在移动平台中暂时不支持延迟光照渲染。
函数命名:LightingCustomDiffuse
shader中对于方法的名称有着比较严格的约定,想要创建一个光照模型,首先要做的是按照规则声明一个光照计算的函数名字,即Lighting
函数参数:(SurfaceOutputs, fixed3 lightDir, fixed atten)
SurfaceOutput s 表示由surf方法计算出的表面颜色信息
fixed3 lightDir 表示光照方向,由unity内部确定
atten 光的衰减系数,由unity内部确定
函数内部变量:_LightColor0 _SpecColor
_LightColor0 表示光照的颜色,由unity内部确定
_SpecColor 表示镜面反射的颜色,由unity内部确定
3、更改顶点模型
void vert (inout appdata_full v) {
float4 sn = mul(transpose(_Object2World) , _SnowDirection);
if(dot(v.normal, sn.xyz) >= lerp(1,-1, (_Snow * 2) / 3)) {
v.vertex.xyz += (sn.xyz + v.normal) * _SnowDepth * _Snow;
}
}
如何定义片段方法、光照模型和顶点方法
一般格式为 #pragma surface surfaceFunction lightModel[optionalparams]
#pragma surface surf CustomDiffusevertex:vert
//执行顺序 顶点处理函数vert->表面处理函数surf ->光照模型函数CustomDiffuse ->颜色值
surface表示片段着色器其后紧跟片段方法名surf
CustomDiffuse 表示光照模型,我们自定义的光照模型,unity自动光照模型有lambert和BlinnPhong两种光照模式,由于unity中有严格的书写格式,所以在使用时,其前要有Lighting的注明,如LightingLambert、LightingCustomDiffuse。
vertex表示顶点着色器其后使用:紧跟顶点方法vert。
函数内部方法:transpose(矩阵) _Object2World()
transpose()输出原矩阵的逆矩阵,比如模型空间转世界空间,逆矩阵即为世界空间转模型空间
_Object2World()变换矩阵,表示将当前模型空间转换到世界空间中的矩阵
4、surface shader的基本属性和写法
Shader "Custom/Diffuse Texture" {
Properties {
_MainTex ("Base (RGB)", 2D) = "white" {}
}
SubShader {
Tags { "RenderType"="Opaque" }
LOD 200
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
在Properties{}中定义着色器属性,在这里定义的属性将被作为输入提供给所有的子着色器,供外部修改。
一般格式为 _Name("Display Name", type) =defaultValue[{options}]
_Name 属性的名字,简单说就是变量名,在之后整个Shader代码中将使用这个名字来获取该属性的内容
Display Name 这个字符串将显示在Unity的材质编辑器中作为Shader的使用者可读的内容
type 这个属性的类型,可能的type所表示的内容有以下几种:
Color 一种颜色,由RGBA(红绿蓝和透明度)四个量来定义;
2D 一张2的阶数大小(256,512之类)的贴图。这张贴图将在采样后被转为对应基于模型UV的每个像素的颜色,最终被显示出来;
Rect 一个非2阶数大小的贴图;
Cube 即Cube map texture(立方体纹理),简单说就是6张有联系的2D贴图的组合,主要用来做反射效果(比如天空盒和动态反射),也会被转换为对应点的采样;
Range(min, max) 一个介于最小值和最大值之间的浮点数,一般用来当作调整Shader某些特性的参数(比如透明度渲染的截止值可以是从0至1的值等);
Float 任意一个浮点数;
Vector 一个四维数;
defaultValue 定义了这个属性的默认值,通过输入一个符合格式的默认值来指定对应属性的初始值(某些效果可能需要某些特定的参数值来达到需要的效果,虽然这些值可以在之后在进行调整,但是如果默认就指定为想要的值的话就省去了一个个调整的时间,方便很多)。
Color 以0~1定义的rgba颜色,比如(1,1,1,1);
2D/Rect/Cube 对于贴图来说,默认值可以为一个代表默认tint颜色的字符串,可以是空字符串或者"white","black","gray","bump"中的一个
Float,Range 某个指定的浮点数
Vector 一个4维数,写为 (x,y,z,w)
另外还有一个{option},它只对2D,Rect或者Cube贴图有关,在写输入时我们最少要在贴图之后写一对什么都不含的空白的{},当我们需要打开特定选项时可以把其写在这对花括号内。如果需要同时打开多个选项,可以使用空白分隔。可能的选择有ObjectLinear, EyeLinear, SphereMap, CubeReflect, CubeNormal中的一个,这些都是OpenGL中TexGen的模式。
例如:
//Define a color with a default value ofsemi-transparent blue
_MainColor ("Main Color", Color)= (0,0,1,0.5)
//Define a texture with a default of white
_Texture ("Texture", 2D) ="white" {}
着色器标签Tags
表面着色器可以被若干的标签(tags)所修饰,而硬件将通过判定这些标签来决定什么时候调用该着色器
Tags {"RenderType"="Opaque" } 渲染过程中的渲染类型
表示渲染非透明物体时使用此项
Tags {"RenderType"= "Transparent"}
表示存在透明或半透明物体时使用此项
Tags {"IgnoreProjector"="True"}不被Projectors影响
Tags {"ForceNoShadowCasting"="True"}从不产生阴影
Tags {"Queue"="xxx"}指定渲染顺序队列,在深度测试打开的前提下影响,默认深度测试是不打开的。如果你使用Unity做过一些透明和不透明物体的混合的话,很可能已经遇到过不透明物体无法呈现在透明物体之后的情况。这种情况很可能是由于Shader的渲染顺序不正确导致的。Queue指定了物体的渲染顺序,预定义的Queue有:这些预定义的值本质上是一组定义整数,值越大越靠后渲染。
Background - 1000最早被调用的渲染,用来渲染天空盒或者背景
Geometry - 2000这是默认值,用来渲染非透明物体(普通情况下,场景中的绝大多数物体应该是非透明的)
AlphaTest - 2450用来渲染经过AlphaTest的像素,单独为AlphaTest设定一个Queue是出于对效率的考虑
Transparent - 3000以从后往前的顺序渲染透明物体
Overlay - 4000用来渲染叠加的效果,是渲染的最后阶段(比如镜头光晕等特效)
设置管线中显卡状态
比如说alpha混合是否开启,是否允许雾化等等
Material { Material Block } 材质
定义了1个顶点光照管道使用的材质。
Lighting On | Off 开启关闭灯光
关闭或开启顶点光照。
Cull Back | Front | Off 裁剪背面|前面|关闭
设置多边形的裁剪模式。
ZTest (Less | Greater | LEqual | GEqual | Equal |NotEqual | Always)
设置深度测试模式
OpenGL 深度其实就是该象素点在3d世界中距离摄象机的距离,深度缓存中存储着每个象素点(绘制在屏幕上的)的深度值!深度测试决定了是否绘制较远的象素点(或较近的象素点),通常选用较近的,而较远优先能实现透视的效果!!!
基础参考资料:http://www.cx2012.com/Article/ArText129558687429211250ID133.html。
ZWrite On | Off
设置深度写模式类型
Fog { Fog Block }
设置雾化参数
AlphaTest (Less | Greater | LEqual | GEqual | Equal| NotEqual | Always) CutoffValue
开启Alpha测试
AlphaTest comparison AlphaValue(0~1)
_Cutoff ("Alpha cutoff", Range (0,1)) =0.5
AlphaTest Greater [_Cutoff]
Greater只渲染alpha值比AlphaValue大的像素
GEqual只渲染alpha值>=AlphaValue的像素
Less 只渲染alpha值
LEqual 只渲染alpha值<= AlphaValue的像素
Equal只渲染alpha值=AlphaValue的像素
NotEqual只渲染alpha值不等于AlphaValue的像素
Always渲染所有像素相当于关闭alpha测试
Never不渲染任何像素
Blend SourceBlendMode DestBlendMode
设置Alpha混合模式
Zero值为0,使用此设置来删除源或目标值。
SrcColor此阶段的值是和源颜色的值相乘。
SrcAlpha 此阶段的值是和源alpha值相乘。
DstColor 此阶段的值是和源帧缓冲中源颜色的值相乘。
DstAlpha 此阶段的值是和源帧缓冲中源Alpha的值相乘。
OneMinusSrcColor 此阶段的值是和(1-源颜色)的值相乘。
OneMinusSrcAlpha 此阶段的值是和(1-源Alpha)的值相乘。
OneMinusDstColor 此阶段的值是和(1-目标颜色)的值相乘。
OneMinusDstAlpha 此阶段的值是和(1-目标Alpha)的值相乘。这些因子使用alpha通道进行混合
下列是最经常使用的混合类型
Blend Off 关闭混合
Blend SrcAlpha OneMinusSrcAlpha // Alpha blendingalpha混合
Blend One One // Additive 相加混合
Blend One OneMinusDstColor // Soft Additive 柔和相加混合
Blend DstColor Zero // Multiplicative 相乘混合
Blend DstColor SrcColor // 2x Multiplicative 2倍相乘混合。 配置并开启混合,计算产生的颜色和srcFactore相乘,已经在屏幕上的颜色和dstFactor相乘,然后2个颜色相加。
Color Color value
设置颜色,当顶点光照关闭时候有效。
ColorMask RGB | A | 0 | any combination of R, G, B,A
设置颜色遮罩模式。将ColorMask设置为0表示关闭所有渲染的颜色管道。
Offset OffsetFactor , OffsetUnits
设置深度偏移。提示:这个命令仅接受Unity3 3D的常数参数(不是Shader的参数)。用于为图形设置深度的偏移。这个函数一般用于当两图形刚上处于同坐标或两个面在同一坐标重叠时。可以通过这个函数让其中一个图形或面的坐标自动偏移。
SeparateSpecular On | Off
关闭或打开顶点照明单独的镜面反射光的颜色。
ColorMaterial AmbientAndDiffuse | Emission 环境和漫反射|放射
计算顶点照明时使用的每个顶点颜色
SetTexture texture property { [Combine options] } 设置纹理
纹理设置固定功能的多纹理管线,如果使用定制的片断着色器将被忽略。
细节层次LOD
这个数值决定了我们能用什么样的Shader。在Unity的Quality Settings中我们可以设定允许的最大LOD,当设定的LOD小于SubShader所指定的LOD时,这个SubShader将不可用。Unity内建Shader定义了一组LOD的数值,我们在实现自己的Shader的时候可以将其作为参考来设定自己的LOD数值,这样在之后调整根据设备图形性能来调整画质时可以进行比较精确的控制。
VertexLit及其系列 = 100
Decal, Reflective VertexLit = 150
Diffuse = 200
Diffuse Detail, Reflective Bumped Unlit,Reflective Bumped VertexLit = 250
Bumped, Specular = 300
Bumped Specular = 400
Parallax = 500
Parallax Specular = 600
Input结构
Input其实是需要我们去定义的结构,这给我们提供了一个机会,可以把所需要参与计算的数据都放到这个Input结构中,传入surf函数使用(提取数据,包括从内部和外部提取);SurfaceOutput是已经定义好了里面类型输出结构,但是一开始的时候内容暂时是空白的,我们需要向里面填写输出,这样就可以完成着色了。先仔细看看INPUT吧,现在可以跳回来看上面定义的INPUT结构体了:
struct Input {
float2 uv_MainTex;
};
在CG程序中,我们有这样的约定,在一个贴图变量(在我们例子中是_MainTex)之前加上uv两个字母,就代表提取它的uv值(其实就是两个代表贴图上点的二维坐标 )
SurfaceOutput结构
SurfaceOutput结构是预定义的输出结构,我们的surf函数的目标就是根据输入把这个输出结构填上。SurfaceOutput结构体的定义如下
struct SurfaceOutput {
half3 Albedo; //像素的颜色
half3 Normal; //像素的法向值
half3 Emission; //像素的发散颜色
half Specular; //像素的镜面高光
half Gloss; //像素的发光强度
half Alpha; //像素的透明度
};
Albedo,是漫反射的颜色值。
Normal,法线坐标
Emission,自发光颜色
Specular,镜面反射系数
Gloss,光泽系数
Alpha,透明度
5、书写表面着色器
表面着色器书写在 CGPROGRAM..ENDCG语句块中
It must be placedinside SubShader block,not inside Pass.Surface shader will compile into multiple passes itself.
It uses #pragma surface ...
directive toindicate it’s a surface shader.
The #pragmasurface
directive is:
#pragma surface surfaceFunction lightModel [optionalparams]
Optionalparams 为可选参数,可跟如下参数:
Optional parameters:
alpha
- Alpha blendingmode. Use this for semitransparent shaders.
alphatest:VariableName
- Alpha testingmode. Use this for transparent-cutout shaders. Cutoff value is in floatvariable with VariableName.
vertex:VertexFunction
- Custom vertexmodification function. See Tree Bark shader for example.
finalcolor:ColorFunction
- Custom finalcolor modification function. See Surface Shader Examples.
exclude_path:prepass
or exclude_path:forward
- Do not generatepasses for given rendering path.
addshadow
- Add shadowcaster & collector passes. Commonly used with custom vertex modification,so that shadow casting also gets any procedural vertex animation.
dualforward
- Use dual lightmaps in forward renderingpath.
fullforwardshadows
- Support allshadow types in Forward renderingpath.
decal:add
- Additive decalshader (e.g. terrain AddPass).
decal:blend
- Semitransparentdecal shader.
softvegetation
- Makes thesurface shader only be rendered when Soft Vegetation is on.
noambient
- Do not applyany ambient lighting or spherical harmonics lights.
novertexlights
- Do not applyany spherical harmonics or per-vertex lights in Forward rendering.
nolightmap
- Disableslightmap support in this shader (makes a shader smaller).
nodirlightmap
- Disablesdirectional lightmaps support in this shader (makes a shader smaller).
noforwardadd
- Disables Forward renderingadditive pass. This makes the shader support one full directional light, withall other lights computed per-vertex/SH. Makes shaders smaller as well.
approxview
- Computesnormalized view direction per-vertex instead of per-pixel, for shaders thatneed it. This is faster, but view direction is not entirely correct when cameragets close to surface.
halfasview
- Passhalf-direction vector into the lighting function instead of view-direction. Half-directionwill be computed and normalized per vertex. This is faster, but not entirelycorrect.
tessellate:TessFunction
- use DX11 GPUtessellation; the function computes tessellation factors. See Surface Shader Tessellation for details.
部分译文:
exclude_path:prepass或者exclude_path:forward - 使用指定的渲染路径,不需要生成通道。
addshadow - 添加阴影投射 & 收集通道(collector passes)。通常用自定义顶点修改,使阴影也能投射在任何程序的顶点动画上。
dualforward - 在正向(forward)渲染路径中使用 双重光照贴图(duallightmaps)。
fullforwardshadows- 在正向(forward)渲染路径中支持所有阴影类型。
decal:add - 添加贴图着色器(decal shader) (例如: terrain AddPass)。
decal:blend - 混合半透明的贴图着色器(Semitransparent decalshader)。
softvegetation -使表面着色器(surfaceshader)仅能在SoftVegetation打开时渲染。
noambient - 不适用于任何环境光照(ambient lighting)或者球面调和光照(spherical harmonics lights)。
novertexlights -在正向渲染(Forwardrendering)中不适用于球面调和光照(sphericalharmonics lights)或者每个顶点光照(per-vertexlights)。
nolightmap - 在这个着色器上禁用光照贴图(lightmap)。(适合写一些小着色器)
nodirlightmap - 在这个着色器上禁用方向光照贴图(directionallightmaps)。 (适合写一些小着色器)。
noforwardadd - 禁用正向渲染添加通道(Forward renderingadditive pass)。 这会使这个着色器支持一个完整的方向光和所有光照的per-vertex/SH计算。(也是适合写一些小着色器).
approxview - 着色器需要计算标准视图的每个顶点(per-vertex)方向而不是每个像索(per-pixel)方向。 这样更快,但是视图方向不完全是当前摄像机(camera) 所接近的表面。
halfasview - 在光照函数(lighting function)中传递进来的是half-direction向量,而不是视图方向(view-direction)向量。 Half-direction会计算且会把每个顶点(per vertex)标准化。这样做会提高执行效率,但是准确率会打折扣。
表面着色器Input结构体内可用的参数如下:
Surface Shader Inputstructure
The input structure Input
generally hasany texture coordinates needed by the shader. Texture coordinates must be named“uv
”followed by texture name (or start it with “uv2
” to use secondtexture coordinate set).
Additional values that can be put into Input structure:
float3viewDir
- willcontain view direction, for computing Parallax effects, rim lighting etc.
float4
with COLOR
semantic - willcontain interpolated per-vertex color.
float4screenPos
- willcontain screen space position for reflection effects. Used by WetStreet shaderin Dark Unity for example.
float3worldPos
- willcontain world space position.
float3worldRefl
- willcontain world reflection vector if surface shader does not write to o.Normal.See Reflect-Diffuse shader for example.
float3worldNormal
- willcontain world normal vector if surface shader does not write to o.Normal.
float3worldRefl; INTERNAL_DATA
- willcontain world reflection vector if surface shader writes to o.Normal.To get the reflection vector based on per-pixel normal map, use WorldReflectionVector (IN, o.Normal)
. SeeReflect-Bumped shader for example.
float3worldNormal; INTERNAL_DATA
- willcontain world normal vector if surface shader writes to o.Normal.To get the normal vector based on per-pixel normal map, use WorldNormalVector (IN, o.Normal)
.
译文:
float3 viewDir - 视图方向( view direction)值。为了计算视差效果(Parallax effects),边缘光照(rim lighting)等,需要包含视图方向( view direction)值。
float4 with COLOR semantic -每个顶点(per-vertex)颜色的插值。
float4 screenPos - 屏幕空间中的位置。为了反射效果,需要包含屏幕空间中的位置信息。比如在Dark Unity中所使用的 WetStreet着色器。
float3 worldPos - 世界空间中的位置。
float3 worldRefl - 世界空间中的反射向量。如果表面着色器(surface shader)不写入法线(o.Normal)参数,将包含这个参数。 请参考这个例子:Reflect-Diffuse 着色器。
float3 worldNormal - 世界空间中的法线向量(normal vector)。如果表面着色器(surface shader)不写入法线(o.Normal)参数,将包含这个参数。
float3 worldRefl; INTERNAL_DATA - 世界空间中的反射向量。如果表面着色器(surface shader)不写入法线(o.Normal)参数,将包含这个参数。为了获得基于每个顶点法线贴图( per-pixelnormal map)的反射向量(reflectionvector)需要使用世界反射向量(WorldReflectionVector(IN, o.Normal))。请参考这个例子:Reflect-Bumped着色器。
float3 worldNormal; INTERNAL_DATA -世界空间中的法线向量(normal vector)。如果表面着色器(surface shader)不写入法线(o.Normal)参数,将包含这个参数。为了获得基于每个顶点法线贴图( per-pixelnormal map)的法线向量(normalvector)需要使用世界法线向量(WorldNormalVector(IN, o.Normal))。
6、RGB颜色处理
颜色的数学意义
Rgb有基本的数学意义
Rgb三值差距越大,颜色的色彩越强烈,rgb三值差距越小,颜色的色彩越黯淡,这个叫做彩度。又叫饱和度。
Rgb三值数值越高,颜色越明亮,数值越小,颜色越昏暗,这个叫做亮度。
Rgb三值视为一个三角形,值越接近谁,颜色就越接近谁。这个叫色调。
RGB的加法,表示颜色的叠加,颜色越加越亮。
(1,0,0) + (0,1,0)=(1,1,0) 红加绿等于黄
(0.5,0,0)+(0.7,0,0)=(1.2,0,0)=(1,0,0);两个暗红相加就是更亮的红色,超出1.0的就作为1.0,到顶了再加还是红色。
顏色相加是指光的疊加,物理上是光的強度相加。例如多個光源照射到一個表面後反射至攝像機,就可以把各個光照的反射結果相加。
对图片来说,加法会不规则的改变原来的颜色,有不协调感,像是加了一层上去。
一般只有光效才会采用加法混合,就是为了不协调,只有光效适合叠加。
颜色相加的目的是为了给光源制造强光效果,例如你有个灯笼,有一张漫反射纹理,一个发光纹理,那么这两个纹理颜色就要相加,才能产生灯笼发光效果
RGB的乘法,表示对亮度的改变,对三个分量乘以相同的值,则三个分量一起变化亮度,色调不变。正片叠底(multiply):
两个颜色相乘 称为“调制 (Modulate)”。两个相乘的颜色通常是 纹理颜色和光源颜色,相乘后得到的最终渲染的颜色。
颜色相乘法,是为了模拟光照射到物体上的效果,例如你有个地面,地面有一个漫反射纹理,还有一张光照贴图,那么两个颜色需要相乘,才会产生正确光照射物体效果
AlphaBlend透明混合 是加法和乘法的组合运用。
NewColor =SrcColor*(1-Alpha)+Color*Alpha;
将底图的颜色根据当前图的(1-alpha)降低亮度
将当前图的颜色根据当前图的alpha 降低亮度,再合并到一起。
若alpha越低,底图的亮度降低的越少。若alpha越高,当前图的亮度降低的越少。
去色 去色就是把饱和度拉下来,让图片看起来是黑白的
rgb三分量差距越小,则饱和度越低,三分量相等,就是黑白的。
由于人眼对绿色最为敏感,所以有个简单的方法,让rgb全部都等于绿色的值。