视频
色位置脚本
private void Update()
{
grassMateral.SetVector("_PlayerPos", this.transform.position + Vector3.up * 0.5f);
}
————————————————————
Shader "Unlit/Grass+Wind+Cashing"
{
Properties
{
[HDR]_GrassColor("GrassColor", Color) = (1,1,1,1) //控制草的颜色
_SpecualarColor("SpecularColor", Color) = (1,1,1,1) //控制草的高光颜色
_Specular("_Specular", Range(0, 1)) = 1 //控制草的高光程度
_Gloss("Gloss", Range(0,20)) = 1 //控制草的高光
[HDR]_FresnelColor("FresnelColor", Color) = (1,1,1,1) //控制草的边缘高光颜色
_FresnelPower("FresnelPower", Range(0, 5)) = 1 //控制草的边缘高光程度
_MainTex ("Texture", 2D) = "white" {} //草的颜色贴图
_AlphaTex("AlphaTexture", 2D) = "white" {} //草的透明贴图
_GrassHeight("GrassHeight", Range(0.5, 5)) = 2.5 //控制草的高度
_GrassWidth("GrassWidth", Range(0.001, 0.5)) = 0.05 //控制草的宽度
_BladeForward("BladeForward", Range(0, 2)) = 1 //控制草的弯曲程度
_WindTex("WindTex", 2D) = "white" {} //风的采样贴图
_WindVector("WindVector", Vector) = (1,1,1,0) //控制风的方向
_WindTimeScale("WindTimeScale", float) = 1 //控制风的速度
_WindTexMapSize("WindTexMapSize", float) = 80 //控制风的采样贴图大小
_WindXZStrength("WindXZStrength", float) = 10 //控制风在草的XZ轴上的偏移
_WindYStrength("WindYStrength", float) = 10 //控制风在草的Y轴上的偏移 */
//交互
_InteracRadius("InteracRadius",Range(0.5,20))=1
_InteracStrength("InteracStrength",Range(0.5,20))=1
}
SubShader
{
//存在顶点动画,所以要关闭批处理,DisableBatching 设置为 True
Tags {
"RenderType" = "TransparentCutout"
"IgnoreProjector" = "True"
"Queue" = "AlphaTest"
"DisableBatching" = "True"
}
//设置为双面渲染,关闭背面剔除
Cull Off
LOD 100
Pass
{
//设置为前向渲染模式
Tags { "LightMode" = "ForwardBase" }
Cull Off
AlphaToMask On
CGPROGRAM
//引入头文件
#include "UnityCG.cginc"
#include "Lighting.cginc"
#include "AutoLight.cginc"
//要应用几何着色器必须要将编译目标设置为 4.0
#pragma target 4.0
#pragma multi_compile_fwdbase
#pragma vertex vert
#pragma fragment frag
//定义几何着色器
#pragma geometry geom
fixed4 _GrassColor;
fixed4 _SpecualarColor;
fixed _Specular;
float _Gloss;
fixed4 _FresnelColor;
half _FresnelPower;
sampler2D _MainTex;
float4 _MainTex_ST;
sampler2D _AlphaTex;
fixed _GrassHeight;
fixed _GrassWidth;
fixed _BladeForward;
sampler2D _WindTex;
float4 _WindTex_ST;
half4 _WindVector;
half _WindTimeScale;
float _WindTexMapSize;
half _WindXZStrength;
half _WindYStrength;
float4 _PlayerPos;
half _InteracRadius;
half _InteracStrength;
struct a2v {
float4 pos : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
};
//顶点着色器传给几何着色器的数据结构
struct v2g {
float4 pos : POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
};
//几何着色器传给片元着色器的数据结构
struct g2f {
float4 pos : SV_POSITION;
float3 normal : NORMAL;
float2 uv : TEXCOORD0;
float3 worldPos : TEXCOORD1;
};
static const float oscillateDelta = 0.05;
//顶点着色器
//直接将从网格得到的数据传给传入几何着色器的结构体 v2g
v2g vert(a2v v) {
v2g o;
o.pos = v.pos;
o.normal = v.normal;
o.uv = v.uv;
return o;
}
//创建 CreatG2fOut() 函数
//初始化从几何着色器传入片元着色器的结构体 g2f
g2f CreatG2fOut() {
g2f output;
output.pos = float4(0, 0, 0, 0);
output.normal = float3(0, 0, 0);
output.uv = float2(0, 0);
output.worldPos = float3(0, 0, 0);
return output;
}
g2f GetVertex(float4 pos, float3 normal, float2 uv) {
g2f output;
output.pos = UnityObjectToClipPos(pos);
output.normal = UnityObjectToWorldNormal(normal);
output.uv = uv;
output.worldPos = UnityObjectToWorldDir(pos);
return output;
}
//几何着色器
[maxvertexcount(30)]//限制几何着色器输出的最大顶点数目。每当输入一个图元,几何着色器可以输出 0~N 个图元。不论是什么结构的图元都是由顶点构成的,而这个语句就是用来限制输出最大的顶点数量(只要小于等于这个数量就可以,多余的顶点会被剔除)
void geom(point v2g points[1], inout TriangleStream<g2f> triStream) {
//顶点着色器输入的顶点位置
float4 root = points[0].pos;
//生成一个伪随机数
float random = sin(UNITY_HALF_PI * frac(root.x) + UNITY_HALF_PI * frac(root.z));
//给每根草的长宽加上这个随机值,我们希望草的宽度不要太宽或者太窄
_GrassWidth = _GrassWidth + (random / 50);
_GrassHeight = _GrassHeight + (random / 5);
//设置草的网格顶点一共有12个
const int vertexCount = 12;
//创建12个 g2f 输出数组
g2f v[vertexCount] = {
CreatG2fOut(), CreatG2fOut(), CreatG2fOut(), CreatG2fOut(),
CreatG2fOut(), CreatG2fOut(), CreatG2fOut(), CreatG2fOut(),
CreatG2fOut(), CreatG2fOut(), CreatG2fOut(), CreatG2fOut()
};
//初始化每个顶点的位置 pos 和 uv
float4 pos = float4(0, 0, 0, 0);
float2 uv = float2(0, 0);
//顶点的 UV 在竖直方向上的当前值和偏移值
float currentV = 0;
float offsetV = 1.0 / (vertexCount / 2 - 1);
//顶点的 y 坐标在竖直方向上的当前值的偏移值
float currentVertexHeight = 0;
float currentHeightOffset = 0;
float verticalEff = 0;
//BlendCood
//让草绕着自身的 y 轴进行旋转
//生成一个随机角度
fixed randomAngle = frac(sin(root.x)*10000.0) * UNITY_HALF_PI;
//根据矩阵旋转的定理,分别创建旋转矩阵
//平移矩阵,先将所有点平移到原点
float4x4 firstTransformMatrix = float4x4(
1.0, 0.0, 0.0, -root.x,
0.0, 1.0, 0.0, -root.y,
0.0, 0.0, 1.0, -root.z,
0.0, 0.0, 0.0, 1.0
);
//旋转矩阵
float4x4 rotateMatrix = float4x4(
cos(randomAngle), 0, sin(randomAngle), 0,
0, 1, 0, 0,
-sin(randomAngle), 0, cos(randomAngle), 0,
0, 0, 0, 1
);
//再平移回去
float4x4 lastTransformMatrix = float4x4(
1.0, 0.0, 0.0, root.x,
0.0, 1.0, 0.0, root.y,
0.0, 0.0, 1.0, root.z,
0.0, 0.0, 0.0, 1.0
);
//BlendCood
//进行生成全部草顶点的循环
for (int i = 0; i < vertexCount; i++)
{
//fmod(a,b) 返回 a 除 b 的余数
//如果返回值为偶数,顶点 UV 坐标均为(0,V)
if (fmod(i, 2) == 0) {
pos = float4(root.x - _GrassWidth, root.y + currentVertexHeight, root.z, 1);
uv = fixed2(0, currentV) * _MainTex_ST.xy + _MainTex_ST.zw;
}
else {
pos = float4(root.x + _GrassWidth, root.y + currentVertexHeight, root.z, 1);
uv = fixed2(1, currentV) * _MainTex_ST.xy + _MainTex_ST.zw;
currentV += offsetV;
currentVertexHeight += currentV * _GrassHeight;
}
//对顶点 XZ 轴进行偏移
float2 randomDir = float2(sin((random * 15)), sin((random * 10)));
float2 forward = (sin((root.x * 10 + root.z / 5) * random)* verticalEff + randomDir * sin((random * 15)))* verticalEff;
pos.xz += forward * _BladeForward;
if (fmod(i, 2) == 1) {
verticalEff += offsetV;
}
//对顶点 Y 轴进行旋转
//pos = mul(lastTransformMatrix, mul(rotateMatrix, mul(firstTransformMatrix, pos)));
//对顶点 Y 轴进行旋转
pos = mul(lastTransformMatrix, mul(rotateMatrix, mul(firstTransformMatrix, pos)));
//交互
//--与玩家的交互
float3 worldPos = mul(unity_ObjectToWorld, pos).xyz;
//每根草的顶点与玩家的距离
float3 dis = distance(_PlayerPos, worldPos);
//将这段距离由进到远的范围缩小为 0~1
float3 circle = 1 - saturate(dis / _InteracRadius);
//每根草的顶点倒下的方向
float3 dir = normalize(worldPos - _PlayerPos);
float3 strength = dir * circle;
//XZ 轴上的移动
pos.xz += strength * _InteracStrength * uv.y;
//Y 轴上的移动
float2 InterOffsetXZ = strength * _InteracStrength * uv.y;
pos.y -= pos.y - sqrt(pos.y * pos.y - (InterOffsetXZ.x * InterOffsetXZ.x + InterOffsetXZ.y * InterOffsetXZ.y));
/*/风随机偏移
float2 wind = float2(sin(_Time.x * UNITY_PI * 5), sin(_Time.x * UNITY_PI * 5));
wind.x += (sin(_Time.x + root.x / 25) + sin((_Time.x + root.x / 15) + 50)) * 0.5;
wind.y += cos(_Time.x + root.z / 80);
wind *= lerp(0.7, 1.0, 1.0 - random);
float oscillationStrength = 2.5f;
float sinSkewCoeff = random;
float lerpCoeff = (sin(oscillationStrength * _Time.x + sinSkewCoeff) + 1.0) / 2;
float2 leftWindBound = wind * (1.0 - oscillateDelta);
float2 rightWindBound = wind * (1.0 + oscillateDelta);
wind = lerp(leftWindBound, rightWindBound, lerpCoeff);
float randomAngle = lerp(-UNITY_PI, UNITY_PI, random);
float randomMagnitude = lerp(0, 1., random);
float2 randomWindDir = float2(sin(randomAngle), cos(randomAngle));
wind += randomWindDir * randomMagnitude;
float windForce = length(wind);
pos.xz += wind.xy * verticalEff;
pos.y -= windForce * verticalEff * 0.8;
pos = UnityObjectToClipPos(pos);
if (fmod(i, 2) == 1) {
verticalEff += offsetV;
}*/
//--风
//将世界坐标下的风的方向转化为局部坐标
float4 localWindDir = normalize(mul(unity_WorldToObject, _WindVector));
//控制风速(实际上为采样 uv 的移动速度)
float time = (_Time.y)*(_WindTimeScale);
//对风贴图进行采样
half4 rootWorldPos = mul(unity_ObjectToWorld, root);
//windmutation 一直在 0~1 之间变化
float windmutation = 1 - tex2Dlod(_WindTex, float4(rootWorldPos.x / _WindTexMapSize + time, rootWorldPos.z / _WindTexMapSize, 0, 0)).g;
//sin(time + windmutation * 10) * cos(time * 2 / 3 + 1 + windmutation * 10) -1~1
//localWindDir.xz 控制风的方向
//clamp(uv.y - 0.1, 0, 1) UV.y 范围在 0.1 以下的顶点不发生移动
//xz 轴上的偏移
half2 xzOffset = sin(time + windmutation * 10) * cos(time * 2 / 3 + 1 + windmutation * 10) * localWindDir.xz * clamp(uv.y - 0.1, 0, 1);
pos.xz += xzOffset * _WindXZStrength;
//根据 XZ 轴上的偏移算出在 Y 轴上
//直角三角形定理
//Y轴的偏移
half yOffset = pos.y - sqrt(pos.y * pos.y - (xzOffset.x * xzOffset.x + xzOffset.y * xzOffset.y));
pos.y -= yOffset * _WindYStrength * clamp(uv.y - 0.35, 0, 1);
//pos.xz += sin(_Time.y * _WindTimeScale) * uv.y;
v[i] = GetVertex(pos, points[0].normal, uv);
}
//inout TriangleStream<g2f> triStream 输出三角形,即三个顶点数据
//将每三个顶点转化为三角形输出到片元着色器
for (int p = 0; p < (vertexCount - 2); p++)
{
//triStream.Append(vertex); 该方法将输入的三个顶点自动构建成三角形
triStream.Append(v[p]);
triStream.Append(v[p + 2]);
triStream.Append(v[p + 1]);
}
}
//片元着色器
//简单的 Blin-Phong 光照模型
float4 frag(g2f i) : SV_Target{
//对_MainTex纹理和_AlphaTex纹理进行采样
fixed4 texColor = tex2D(_MainTex, i.uv);
fixed alpha = tex2D(_AlphaTex, i.uv).a;
//将法线归一化
float3 worldNormal = normalize(i.normal);
float3 worldSpecNormal = worldNormal;
worldNormal = worldNormal * 0.5 + 0.5;
//得到环境光
fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
//得到世界空间下光照方向
fixed3 worldLightDir = normalize(UnityWorldSpaceLightDir(i.worldPos));
//Diffuse 漫反射颜色
fixed NdotL = saturate(dot(worldNormal, worldLightDir));
fixed3 diffuse = _LightColor0.rgb * NdotL;
//Specular 高光颜色
//得到世界空间下的视线方向
fixed3 worldViewDir = normalize(UnityWorldSpaceViewDir(i.worldPos));
//得到半角向量
fixed3 halfDir = normalize(worldLightDir + worldViewDir);
fixed3 NdotH = saturate(dot(worldSpecNormal, halfDir));
float spec = pow(NdotH, _Specular * 128.0) * _Gloss;
fixed3 specular = _SpecualarColor * _LightColor0.rgb * spec;
//Fresnel
fixed fresnel = saturate(1 - dot(worldSpecNormal, worldViewDir));
//fresnel = clamp(fresnel - 0.2, 0, 1);
fresnel = pow(fresnel, _FresnelPower) * clamp(i.uv.y - 0.5, 0, i.uv.y);
fixed3 fresnelColor = fresnel * _FresnelColor;
//得到并输出最终颜色
fixed3 finalColor = ambient + diffuse + specular;
return fixed4(texColor * _GrassColor.rgb * finalColor, alpha);
//return fixed4(worldNormal, alpha);
}
ENDCG
}
}
FallBack "Diffuse"
}