Created
March 10, 2020 04:07
-
-
Save stilllisisi/78a21dbe1a53c47ea02b4c5818c05f5d to your computer and use it in GitHub Desktop.
【游戏-渲染】移动端大规模草渲染的实现
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| //首先用最简单的方式,用程序生成了一个大网格。 | |
| //考虑到自然风,打算模仿一个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; | |
| } | |
Author
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://gameinstitute.qq.com/community/detail/128336