Skip to content

Instantly share code, notes, and snippets.

@stilllisisi
Created March 10, 2020 04:07
Show Gist options
  • Select an option

  • Save stilllisisi/78a21dbe1a53c47ea02b4c5818c05f5d to your computer and use it in GitHub Desktop.

Select an option

Save stilllisisi/78a21dbe1a53c47ea02b4c5818c05f5d to your computer and use it in GitHub Desktop.
【游戏-渲染】移动端大规模草渲染的实现
//首先用最简单的方式,用程序生成了一个大网格。
//考虑到自然风,打算模仿一个dx 草下面的一个算法:
//用位置信息作为随机种子
#ifdef GRASS_OBJECT_MODE
float3 randCalcPos = p[0].objectSpacePos;
#else
float3 randCalcPos = oPos;
#endif
//进一步获得xz的随机
fixed randX = rand(randCalcPos.xz + 1000) * _Disorder * 2 - _Disorder;
fixed randZ = rand(randCalcPos.xz - 1000) * _Disorder * 2 - _Disorder;
//Random value from 2D value between 0 and 1
inline float rand(float2 co){
return frac(sin(dot(co.xy, float2(12.9898,78.233))) * 43758.5453);
}
//If grass is looked at from the top, it should still look like grass
#ifdef GRASS_TOP_VIEW_COMPENSATION
fixed topViewCompensation = 1 + pow(max(0, dot(viewDir, up)), 20) * 0.8;
width *= topViewCompensation;
fixed2 windDir = wind(randCalcPos, fixed2(randX, randZ) * (topViewCompensation));
#else
fixed2 windDir = wind(randCalcPos, fixed2(randX, randZ));
#endif
inline fixed2 wind(float3 pos, fixed2 offset)
{
float3 realPos = float3(pos.x * cos(_WindRotation) - pos.z * sin(_WindRotation), pos.y, pos.x * sin(_WindRotation) + pos.z * cos(_WindRotation));
//这个是绕原点旋转之后的新坐标,具体推倒可以参考https://blog.csdn.net/u012138730/article/details/80320162
fixed2 windWaveStrength = _WindParams.x * sin(0.7f*windStrength(realPos)) * cos(0.15f*windStrength(realPos));
windWaveStrength += windRipple(realPos);
fixed2 wind = fixed2(windWaveStrength.x + offset.x, windWaveStrength.y + offset.y);
return fixed2(wind.x * cos(_WindRotation) - wind.y * sin(_WindRotation), wind.x * sin(_WindRotation) + wind.y * cos(_WindRotation));
}
//这个风力函数比较复杂,用四个正余弦函数弄出的一个波动效果
inline fixed windStrength(float3 pos)
{
return pos.x + _Time.w*_WindParams.y + 5*cos(0.01f*pos.z + _Time.y*_WindParams.y * 0.2f) + 4*sin(0.05f*pos.z - _Time.y*_WindParams.y*0.15f) + 4*sin(0.2f*pos.z + _Time.y*_WindParams.y * 0.2f) + 2*cos(0.6f*pos.z - _Time.y*_WindParams.y*0.4f);
}
inline fixed windRippleStrength(float3 pos)
{
return sin(100*pos.x + _Time.y*_WindParams.w*3 + pos.z)*cos(10*pos.x + _Time.y*_WindParams.w*2 + pos.z*0.5f);
}
inline fixed2 windRipple(float3 pos)
{
return _WindParams.z * fixed2(windRippleStrength(pos), windRippleStrength(pos + float3(452, 0, 987)));
}
//lod是这个草要分成几段,是曲面细分那边的内容
for(fixed i = 1; i <= lod; i++)
{
fixed segment = i*invLod;
fixed sqrSegment = segment*segment;
//segment是草的长度的百分比,pos就是最终的实际高度
float3 pos = float3(up*segment*realHeight);
//xz要加上风所带来的影响,
pos.xz += windDir.xy * sqrSegment * stiffnessFactor;
//高度也要矫正
pos.y -= length(windDir) * sqrSegment * 0.5f * stiffnessFactor;
fixed uvHeight = segment;
viewDir = normalize(rendererPos - pos);
fixed3 localUp = pos - lastPos;
//Simple grass has no texture, so the mesh has to look like a blade of grass
pIn.vertex = float4((pos - width * groundRight * (1 - sqrSegment)).xyz, 1);
getNormals(localUp, lightDir, groundRight, /*out*/ pIn.normal, /*out*/ pIn.reflectionNormal);
triStream.Append(geomToFrag(pIn));
//Simple grass has no texture, so the mesh has to look like a blade of grass
pIn.vertex = float4((pos + width * groundRight * (1 - sqrSegment)).xyz, 1);
getNormals(localUp, lightDir, groundRight, /*out*/ pIn.normal, /*out*/ pIn.reflectionNormal);
triStream.Append(geomToFrag(pIn));
lastPos = pos;
}
//最终我只采纳了windStrength的部分,ripple部分在面数不够的情况下,草扭曲的样子不是很好看。
//我就换了一种做法,堆了两层草。用十字星的形式,这样的坏处是增加了面数,但好处也很明显,让整个草看上去更加自然。
//首先草根据对应rt里的坐标读取像素,根据像素确定歪的方向,并且做歪曲。那么如何得到这张rt呢?
//第一步是球的正面下压,我的想法是根据不同朝向,分别转成颜色值并显示出来,最终渲染到rt中。
//shader代码如下:
v2f o;
float4 vertex = v.vertex;
o.vertex = UnityObjectToClipPos(v.vertex);
o.pos = mul(unity_ObjectToWorld, v.vertex);
float r = v.vertex.x + 0.5;
float b = v.vertex.z + 0.5;
float len = length(v.vertex.xz * 2);
o.color.r = r;
o.color.g = pow(1 - len, 1);
o.color.b = b;
o.color.a = 1;
return o;
//然后是运动轨迹,原理一样,只不过需要通过运动动态创建网格
//如果草只有一部分顶点被压住,那么草会扭曲成很难看的样子,解决方案是把草的位置浓缩成一个点,草上面的所有顶点都用浓缩的这个点来判断,这样草的位置就一致了。
v2f vert(appdata v, uint vid : SV_VertexID)
{
v2f o;
float index = floor(vid / ((_GrassSeg + 1) * 2));
float grid = _GrassRange / _GrassNum;
float row = floor(index / _GrassNum);
float col = index % _GrassNum;
float4 objectPos = float4(-_GrassRange / 2 + row * grid, 0, -_GrassRange / 2 + col * grid, 1);
float4 worldPos1 = mul(unity_ObjectToWorld, objectPos);
float4 vertex = v.vertex;
float3 worldPos = mul(unity_ObjectToWorld, v.vertex).xyz;
float3 randCalcPos = worldPos;
float2 windDir = wind(randCalcPos);
float4 grassuv1 = mul(GrassMatrix, worldPos1);
float2 grassuv2 = grassuv1.xy / grassuv1.w * 0.5 + 0.5;
#if UNITY_UV_STARTS_AT_TOP
grassuv2.y = 1 - grassuv2.y;
#endif
float4 n = tex2Dlod(_GrassTex, float4(grassuv2, 0, 0));
n.xz = (n.xz - 0.5) * 2;
float2 off = (windDir.xy) * pow(v.vertex.y * 2, 2);// +n.xyz;
worldPos.y -= 10 * dot(off, off);
float2 newOff = off * (1 - n.g) + normalize(n.xz) * v.vertex.y * n.g * 0.6;
worldPos.y *= (1 - n.g);
//off = float2(-0.3, 0) * v.vertex.y;
worldPos.xz += newOff;
o.pos = mul(UNITY_MATRIX_VP, float4(worldPos, 1));
//o.pos = UnityObjectToClipPos(vertex);
o.uv = v.uv;
half3 worldNormal = UnityObjectToWorldNormal(v.normal);
half nl = max(0, dot(worldNormal, _WorldSpaceLightPos0.xyz));
o.diff = nl * _LightColor0;
o.diff *= v.color;
o.diff.rgb += +ShadeSH9(half4(worldNormal, 1));
TRANSFER_SHADOW(o)
return o;
}
@stilllisisi
Copy link
Author

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment