一、流动河流
Shader "Unity Shaders Book/Chapter 11/Water" {
Properties {
_MainTex ("Main Tex", 2D) = "white" {} //主纹理
_Color ("Color Tint", Color) = (1, 1, 1, 1) //整体颜色
_Magnitude ("Distortion Magnitude", Float) = 1 //控制水流波动幅度
_Frequency ("Distortion Frequency", Float) = 1 //控制波动频率
_InvWaveLength ("Distortion Inverse Wave Length", Float) = 10 //控制波长的倒数
_Speed ("Speed", Float) = 0.5 //河流纹理的移动速度
}
SubShader {
// Need to disable batching because of the vertex animation
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"}
//DisableBatching:批处理合并所有模型,会使各自的模型空间丢失,我们在进行顶点动画时,需要使用到模型空间的变换,所以需要关闭批处理。
Pass {
Tags { "LightMode"="ForwardBase" }
ZWrite Off //关闭深度写入
Blend SrcAlpha OneMinusSrcAlpha //开启混合模式
Cull Off //关闭了剔除功能
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
float _Magnitude;
float _Frequency;
float _InvWaveLength;
float _Speed;
struct a2v {
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert(a2v v) {
v2f o;
float4 offset; //顶点的位移量
offset.yzw = float3(0.0, 0.0, 0.0); //yzw都为0,
offset.x = sin(_Frequency * _Time.y + v.vertex.x * _InvWaveLength + v.vertex.y * _InvWaveLength + v.vertex.z * _InvWaveLength) * _Magnitude;
//书上说,用_Frequency乘时间来模拟频率,为了让不同位置有不同位移,我们对上述结果加上了模型空间的位置分量,×_InvWaveLength来控制波长。_Magnitude来控制幅度。
//可以看下物体模型的坐标系,上下运动对应着模型空间的x轴,所以我们才更改x轴。
o.pos = UnityObjectToClipPos(v.vertex + offset); //加上偏移
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex); //计算偏移和缩放
o.uv += float2(0.0, _Time.y * _Speed); //水平移动,如果不是河流只是飘动效果,那这个可以去掉
return o;
}
fixed4 frag(v2f i) : SV_Target {
fixed4 c = tex2D(_MainTex, i.uv);
c.rgb *= _Color.rgb;
return c;
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}
二、广告牌技术
广告牌技术 : 会根据视角方向旋转一个被纹理着色的多边形(通常为简单的四边形,这个多边形就是广告牌),使得多边形看起来好像总是面对着摄像机。
应用:渲染一些烟雾、云朵、闪光效果等等。
广告牌技术的本质就是构造旋转矩阵,广告牌技术使用的三个基向量是表面法线、向上的方向、向右的方向,除此之外还需要一个锚点。锚点不变用来确定位置。
广告牌要按照需求来构建三个相互正交的基向量:
我们首先可以确定一个方向,比如草丛的上方向是不变的(0,1,0),法线方向不变,比如粒子效果,法线方向固定一直指向视角方向,指向上的方向是变化的。
我们假定法线方向固定,用法线方向和向上的方向叉乘计算right方向(此时法线和向上的方向并不垂直,但right方向和这两个其中之一肯定是垂直的),然后我们再用right和法线来计算up方向,这样确定的法线、right、up两两之间都垂直,就构建出了基向量。
感觉书作者的代码有些难理解的地方,研究了好久才懂,在代码中注释了。
Shader "Unity Shaders Book/Chapter 11/Billboard" {
Properties {
_MainTex ("Main Tex", 2D) = "white" {} //广告牌的透明纹理
_Color ("Color Tint", Color) = (1, 1, 1, 1) //整体颜色
_VerticalBillboarding ("Vertical Restraints", Range(0, 1)) = 1 //调整是固定法线还是固定指向上方向,就是控制约束垂直方向的程度。
}
SubShader {
// Need to disable batching because of the vertex animation
Tags {"Queue"="Transparent" "IgnoreProjector"="True" "RenderType"="Transparent" "DisableBatching"="True"}
//一样还是取消批处理,因为我们需要模型空间来操作(需要得到模型空间的中心锚点)。
Pass {
Tags { "LightMode"="ForwardBase" }
ZWrite Off //这三个没变化
Blend SrcAlpha OneMinusSrcAlpha
Cull Off
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Lighting.cginc"
sampler2D _MainTex;
float4 _MainTex_ST;
fixed4 _Color;
fixed _VerticalBillboarding;
struct a2v {
float4 vertex : POSITION;
float4 texcoord : TEXCOORD0;
};
struct v2f {
float4 pos : SV_POSITION;
float2 uv : TEXCOORD0;
};
v2f vert (a2v v) {
v2f o;
float3 center = float3(0, 0, 0); //确定锚点
float3 viewer = mul(unity_WorldToObject,float4(_WorldSpaceCameraPos, 1)); //获得模型空间中的视角方向
float3 normalDir = viewer - center; //法线方向确定
normalDir.y =normalDir.y * _VerticalBillboarding; //使用_VerticalBillboarding控制垂直方向上的约束度,
//当_VerticalBillboarding为1时,意味着法线方向固定为视角方向。
//当_VerticalBillboarding为0时,意味着法线y方向为0,也就是说广告牌的上方向为(0,1,0)
normalDir = normalize(normalDir); //进行归一化
float3 upDir = abs(normalDir.y) > 0.999 ? float3(0, 0, 1) : float3(0, 1, 0);
//用于防止法线的方向和向上方向平行,
float3 rightDir = normalize(cross(upDir, normalDir)); //这里是粗略的upDir
upDir = normalize(cross(normalDir, rightDir)); //重新计算精确的
float3 centerOffs = v.vertex.xyz - center; //获得原来的偏移
float3 localPos = center + rightDir * centerOffs.x + upDir * centerOffs.y + normalDir * centerOffs.z;
//也就是说进行一个坐标的转换,原来是普通的x、y、z轴,
//现在呢,我们把原来的z轴当成了法线方向,y轴当成了upDir,x轴当成了 负的rightDir
//这里我没有理解错的话,应该是:
//float3 localPos = center - rightDir * centerOffs.x + upDir * centerOffs.y + normalDir * centerOffs.z;
//因为我们构建的是右手坐标系,所以x轴进行翻转后,才能模拟出坐标轴变换的效果,这样才能根据顶点和锚点的偏移量进行转换。
//举个例子原来 坐标轴 顶点和锚点的偏移是(1,1,1),那么我们在新的坐标轴就也要有(1,1,1)的偏移,
//所以就要求我们要使用相同类型的坐标系(指左手右手),计算就是在新的坐标轴方向上加上偏移。
//之所以书中作者在这里使用的是加法,因为项目中的纹理,x轴的正负并不能影响实际效果,是对称的。
//我觉得还是要使用减法更加严谨。
//或许我们可以直接建立左手坐标系的基向量,这样会更好理解。。
o.pos = UnityObjectToClipPos(float4(localPos, 1)); //转换到裁剪空间
o.uv = TRANSFORM_TEX(v.texcoord,_MainTex); //进行便宜缩放的计算
return o;
}
fixed4 frag (v2f i) : SV_Target {
fixed4 c = tex2D (_MainTex, i.uv); //采样
c.rgb *= _Color.rgb;
return c;
}
ENDCG
}
}
FallBack "Transparent/VertexLit"
}
我们使用的是(Quad),而不可以是(Plane),因为我们的代码是建立在数值摆放的多边形基础上,
我们使用的多边形的定点结构要满足在模型空间下竖直排列。