【学习链接】
冯乐乐shader https://blog.csdn.net/candycat1992/article/details/44039077
OpenGLShader技巧集合:https://blog.csdn.net/stalendp/article/details/18745663
shader集锦:https://blog.csdn.net/tjw02241035621611/article/details/80038608
超超超牛逼大神主页 http://www.iquilezles.org/www/index.htm
抠脚大佬https://blog.csdn.net/qq_16756235
基础理论:https://www.cnblogs.com/lixiang-share/p/5025662.html
一堆好看的场景浅墨:https://blog.csdn.net/poem_qianmo/article/details/40955607?utm_source=blogxgwz1
unityshader内置函数:https://blog.csdn.net/a6627651/article/details/50680360
shader例子:
为队伍设置不同颜色shader https://www.cnblogs.com/jackmaxwell/p/6220511.html
rayMathing算法:http://www.taikr.com/app.php/article/3880
https://blog.csdn.net/w450468524/article/details/51699086
rayMatching框架:https://blog.csdn.net/tjw02241035621611/article/details/80057928
【Unity Shader的分类】
使用的是ShaderLab编写Unity中的Shader
1.表面着色器 Surface Shader
(Unity独有,对开发人员很方便,对顶点片元做了一层封装 光源较多时,适合使用这个)
2.顶点/片元着色器 Vertex/Fragment Shader
3.固定函数着色器 Fixed Function Shader(很老了)
【基本语法】
Shader "Wen/01 myshader"{ //这里指定shader的名字,不要求跟文件名保持一致
Properties{
//属性
_Color("Wen_Color",Color)=(1,1,1,1) //float4
_Vector("Vector",Vector)=(1,2,3,4) //float4
_Int("Int",Int)=123456 //float
_Float("Float",Float)=3.14159 //float
_Range("Range",Range(-2,2))=0.2 //float
_2D("Texture",2D)="red"{} //若未指定图片,使用该默认的纯色 sampler2D
_Cube("Cube",Cube)="white"{} //立方体贴图(例天空盒)samplerCube
_3D("Texture",3D)="black"{} //3D纹理 samper3D
}
//子Shader模块,可以有很多个 显卡运行效果的时候,从第一个SubShader开始,
//如果第一个SubShader里面的效果都可以实现,那么就使用第一个,否则自动尝试下一个
SubShader{
Pass{
//可以有很多Pass块,每一个都代表一个方法 至少有一个Pass
CGPROGRAM
//使用CG语言编写Shader代码 (HLSLPROGRAM)
//上面声明的变量,需要到这里面重定义一下。
float4 _Color; //可以使用 half fixed代替
//float 32位存储
//half 16位存储 -6万-+6万
//fixed 11位存储 -2-+2 一般颜色都用这个值来存储,节约空间
float4 _Vector;
float _Int;
float _Float;
float _Range;
sampler2D _2D;
samplerCube _Cube;
sampler3D _3D;
ENDCG
}
}
//若全部失败,默认效果
Fallback "Legacy Shaders/VertexLit"
}
【第一个函数 绘制纯色】
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Wen/02 secondshader can run"{
Subshader{
Pass{
CGPROGRAM
//顶点函数 这里只是声明了顶点函数的函数名
//基本作用:完成顶点坐标从模型空间到剪裁空间的转换(从游戏环境转换到视野相机屏幕上)
#pragma vertex vert
//片元函数 这里只是声明了片元函数的函数名
//基本作用 返回模型对应的屏幕上的每一个像素的颜色值
#pragma fragment frag
float4 vert(float4 v : POSITION) :SV_POSITION {//通过语义告诉系统,这个参数是干嘛的
//比如POSITION是告诉系统我需要顶点坐标
//SV_POSITION是解释说明返回值,意思是返回值是裁剪空间下的顶点坐标
float4 pos = UnityObjectToClipPos(v );
return pos;
}
fixed4 frag() :SV_Target{
return fixed4(1,1,1,1);
}
ENDCG
}
}
Fallback "Legacy Shaders/VertexLit"
}
【语义】
(类似C#中的attribute,给变量作说明用的。一般这玩意都写在结构体中,使得结构更清晰)
语义的分类
从应用程序传递到顶点函数的语义:
POSITION 传递顶点坐标(模型空间)
NORMAL 法线(模型空间)
TANGENT 切线(模型空间)
TEXCOORD0~N 纹理坐标
COLOR 顶点颜色
从顶点函数传递到片元函数的语义:
SV_POSITION 裁剪空间中的顶点坐标(一般系统使用)
COLOR0 语义上是传递颜色,但实际上一般都是传递一组值4个
COLOR1 同COLOR0
TEXCOORD0~7 语义上是传递纹理坐标,但不是一定,可以传别的
从片元函数传递给系统:
SV_TARGET 颜色值,表示将要渲染的颜色值
上例通过语义格式修改:
// Upgrade NOTE: replaced 'mul(UNITY_MATRIX_MVP,*)' with 'UnityObjectToClipPos(*)'
Shader "Wen/02 secondshader can run"{
SubShader{
Pass{
CGPROGRAM
//顶点函数 这里只是声明了顶点函数的函数名
//基本作用:完成顶点坐标从模型空间到剪裁空间的转换(从游戏环境转换到视野相机屏幕上)
#pragma vertex vert
//片元函数 这里只是声明了片元函数的函数名
//基本作用 返回模型对应的屏幕上的每一个像素的颜色值
#pragma fragment frag
//application to vertex
struct a2v{
float4 vertex : POSITION; //该语义会让系统把模型空间的顶点的值,自动传给vertex
float3 nomal : NORMAL; //该语义会让系统把模型空间的法线方向的值,自动传给nomal
float4 texcoord : TEXCOORD0; //该语义会让系统把模型的第一套纹理坐标的值传给texcoord
};
//vertex to fragment
struct v2f{
float4 position : SV_POSITION; //裁剪空间中的顶点坐标(一般系统使用)
float3 temp : COLOR0; //返回值,必须有语义 不然程序会报错 这里的语义可以自己定
};
//fragment to application
struct f2a{
fixed4 target : SV_TARGET;
};
//顶点坐标=====>>>> 从程序获取,从顶点传递给片元,片元返回给程序
v2f vert(a2v _v)
{
v2f _f;
_f.position = UnityObjectToClipPos(_v.vertex); //获得模型顶点坐标,转化为裁剪坐标,并将该裁剪坐标赋值给 _f结构中得顶点
_f.temp = _v.nomal; //法线坐标传过来,赋值
return _f;
}
//上一步执行得返回值作为参数传递进来
f2a frag(v2f _f)
{
f2a _a;
_a.target = fixed4(f.temp,1); //顶点传过来的法线坐标,根据此坐标为每个像素的颜色赋值;
//法线坐标中间的像素颜色,会使用插值自动赋值;
//并没什么意义,可以通过颜色判断该点的法线坐标值
return a;
}
ENDCG
}
}
Fallback "Legacy Shaders/VertexLit"
}
【内置函数及导入头文件函数】
pow max dot reflect normalize
【光照模型】
光照模型就是一个公式,使用这个公式来计算某个点的光照效果
标准光照模型
在标准光照模型里面,我们把进入摄像机的光分为下面四个部分:
自发光
高光反射 Specular(镜面反射)
Specular = 直射光 * pow(max(0,cos夹角(反射光和视野方向夹角)),10) cos夹角 = normalize(反射光方向)·normalize(视野方向)
漫反射
兰伯特:Diffuse = 直射光颜色 * max(0,cos夹角(光和法线的夹角)) cos夹角 = normalize(光方向)·normalize(法线方向)
半兰伯特:Diffuse = 直射光颜色 * (cos夹角*0.5+0.5) -0.5~0.5 + 0.5,避免了夹角过大时候全部算0环境光 Ambient
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb; 相加就得到叠加的颜色,相乘是混合的颜色
Blinn-Phong光照模型(上面那个高光Blinn的改进)
反射光和视野方向夹角====》改为法线和x的夹角;x是平行光和视野方向的平分线。
- Tips:
Tags{ "LightMode"="ForwardBase" }
只有定义了正确的LightMode才能得到一些Unity的内置光照变量。
#include "Lighting.cginc"
包含unity的内置文件,才可以使用unity内置的一些变量
漫反射示例
物体本身颜色叠加漫反射颜色,用乘法;添加环境光照,亮度增强,用加法。
高光反射示例
fixed3 specular = _LightColor0.rgb * pow(max(0,dot(specularDir(反射角),camDir)),10);
//添加环境光 (亮度增强,用加)
diffuse = diffuse + ambient + specular;
fixed3 specularDir = 2 * dot(lightDir, normalDir)*normalDir - lightDir;
【镜面反射与phong模型】https://blog.csdn.net/xuexiaokkk/article/details/49999041
Fresnel,反射,折射
基本原理:用一个相机在物体位置拍周围的环境,保存为cubemap;通过视角和法线,获得从视角位置的反射/折射光方向,
菲涅尔和反射颜色处理方式相同:fixed3 color = ambient + lerp(diffuse, reflection, saturate(fresnel)) * atten;
- 反射是直接把菲涅尔强度的计算写成一个固定的数值
菲涅尔是根据视角和法线方向进行计算这个数值:fixed fresnel = _FresnelScale + (1 - _FresnelScale) * pow(1 - dot(worldViewDir, worldNormal), 5);
Grabpass抓取屏幕纹理
如果想要获得透明效果后面的屏幕显示图片,并对其进行操作,可以通过Grabpass方式(也可以通过Rendertexture,以及命令缓冲(Command Buffers))。
Shader "Wen/17-GlassRfraction" {
Properties{
_MainTex("Main Tex",2D) = "white"{}
_BumpMap("Normal Map",2D) = "bump"{}
_Cubemap("Environment Cube Map",cube) = "_Skybox"{}
_Distortion("Distortion", Range(0, 100)) = 10
_RefractAmount("Refract Amount", Range(0.0, 1.0)) = 1.0
}
SubShader{
// We must use Transparent, so other objects are drawn before this one;
Tags { "RenderType" = "Opaque" "Queue" = "Transparent"}
// This pass grabs the screen behind the object into a texture.
// We can access the result in the next pass as _RefractionTex
GrabPass{"_RefractionTex"}
Pass {
Tags { "LightMode" = "ForwardBase" }
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _BumpMap;
float4 _BumpMap_ST;
samplerCUBE _Cubemap;
float _Distortion;
fixed _RefractAmount;
sampler2D _RefractionTex;
float4 _RefractionTex_TexelSize;
struct a2v {
float4 vertex : POSITION;
float3 normal : NORMAL;
float4 tangent : TANGENT;
float2 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float4 scrPos : TEXCOORD0;
float4 uv : TEXCOORD1;
float4 TtoW0 : TEXCOORD2;
float4 TtoW1 : TEXCOORD3;
float4 TtoW2 : TEXCOORD4;
};
v2f vert(a2v v) {
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.scrPos = ComputeGrabScreenPos(o.pos);
o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);
o.uv.zw = TRANSFORM_TEX(v.texcoord, _BumpMap);
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
fixed3 worldNormal = UnityObjectToWorldNormal(v.normal);
fixed3 worldTangent = UnityObjectToWorldDir(v.tangent.xyz);
fixed3 worldBinormal = cross(worldNormal, worldTangent) * v.tangent.w;
o.TtoW0 = float4(worldTangent.x, worldBinormal.x, worldNormal.x, worldPos.x);
o.TtoW1 = float4(worldTangent.y, worldBinormal.y, worldNormal.y, worldPos.y);
o.TtoW2 = float4(worldTangent.z, worldBinormal.z, worldNormal.z, worldPos.z);
return o;
}
fixed4 frag(v2f i) : SV_Target {
float3 worldPos = float3(i.TtoW0.w, i.TtoW1.w, i.TtoW2.w);
fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(worldPos));
// Get the normal in tangent space
fixed3 bump = UnpackNormal(tex2D(_BumpMap, i.uv.zw));
// Compute the offset in tangent space
float2 offset = bump.xy * _Distortion * _RefractionTex_TexelSize.xy;
i.scrPos.xy += offset * i.scrPos.z;
fixed3 refrCol = tex2D(_RefractionTex, i.scrPos.xy / i.scrPos.w).rgb;
// Convert the normal to world space
bump = normalize(half3(dot(i.TtoW0.xyz, bump), dot(i.TtoW1.xyz, bump), dot(i.TtoW2.xyz, bump)));
fixed3 reflDir = reflect(-worldViewDir, bump);
fixed4 texColor = tex2D(_MainTex, i.uv.xy);
fixed3 reflCol = texCUBE(_Cubemap, reflDir).rgb * texColor.rgb;
fixed3 finalColor = reflCol * (1 - _RefractAmount) + refrCol * _RefractAmount;
return fixed4(finalColor, 1);
}
ENDCG
}
}
FallBack "Reflective/VertexLit"
}
上面涉及到的知识点:从切线空间转换到世界空间。这个乘法很迷幻。以后找个人问问吧。
话接上文。从切线空间转世界空间一般都是为了计算法线光照的。存两篇文章。
http://www.cnblogs.com/lancidie/p/7827434.html
https://en.wikibooks.org/wiki/Cg_Programming/Vector_and_Matrix_Operations