叙述网站的设计制作流程,社交媒体营销策略有哪些,怎么设计网络营销方案,零基础学做网站要多久大家好#xff0c;我是阿赵。 这期开始#xff0c;打算介绍一下地面交互的一些做法。 比如#xff1a; Unity引擎制作沙地实时凹陷网格的脚印效果 或者#xff1a; Unity引擎制作雪地效果 这些效果的实现#xff0c;需要基于一些基础的知识。所以这一篇先介绍一下简单… 大家好我是阿赵。 这期开始打算介绍一下地面交互的一些做法。 比如 Unity引擎制作沙地实时凹陷网格的脚印效果 或者 Unity引擎制作雪地效果 这些效果的实现需要基于一些基础的知识。所以这一篇先介绍一下简单的局部UV采样然后映射纹理到地面的做法。 大概需要实现的效果是这个视频的前半部分 Unity曲面细分制作雪地效果 一、轨迹的绘制 看这段视频的前半部分。可以看到球在移动的过程中在地面产生了移动的轨迹 这个效果可能很多朋友都会做一般的做法是计算球的坐标相对于整个地面的位置然后拾像素绘制在地面的遮罩贴图上面。 不过这种做法会有一个问题假如地面很大的时候通过一张和整个地面匹配UV的遮罩贴图来绘制轨迹那么这张遮罩贴图的分辨率需要多大才能显示足够的精度呢比如一个4096米4096米的地面就算我们用一张40964096的贴图做遮罩那么每平方米的面积才占一个像素明显是绘制不出这么清晰的轨迹图形的。 其实我们没有必要去绘制整张贴图只需要局部绘制就好了 绘制这一个小局部然后通过局部UV采样的方式把这个贴图叠加到大贴图上面去。 这时候就需要给Shader传入一个范围让Shader知道这个局部UV最终占整个地面UV的多少。 地面的Shader代码是这样的
Shader azhao/GroundFootStep
{Properties{_MainTex(Texture, 2D) white {}_Color(Color, Color) (1,1,1,1)_centerPos(CenterPos, Vector) (0,0,0,0)_footstepRect(footstepRect,Vector) (0,0,0,0)_footstepTex(footstepTex,2D) gray{}_footstepColor(footstepColor,Color) (1,1,1,1)}SubShader{Tags { RenderTypeOpaque }LOD 100Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include UnityCG.cgincsampler2D _MainTex;float4 _MainTex_ST;fixed4 _Color;uniform float3 _centerPos;float4 _footstepRect;sampler2D _footstepTex;float4 _footstepColor;struct appdata{float4 pos : POSITION;float2 uv : TEXCOORD0;};struct v2f{float4 pos : SV_POSITION;float3 worldPos : TEXCOORD0;float2 uv : TEXCOORD1;float2 footstepUV : TEXCOORD2;};float RemapUV(float min, float max, float val){return (val - min) / (max - min);}v2f vert(appdata i){v2f o;o.pos UnityObjectToClipPos(i.pos);o.worldPos mul(unity_ObjectToWorld,i.pos.xyz);o.uv i.uv*_MainTex_ST.xy _MainTex_ST.zw;o.footstepUV float2(RemapUV(_footstepRect.x, _footstepRect.z, o.worldPos.x), RemapUV(_footstepRect.y, _footstepRect.w, o.worldPos.z));return o;}fixed4 frag (v2f i) : SV_Target{// sample the texturefixed4 col tex2D(_MainTex, i.uv)*_Color;fixed4 footstepCol tex2D(_footstepTex, i.footstepUV);fixed3 footstepRGB _footstepColor.rgb;fixed3 finalRGB col.rgb*(1 - footstepCol.a) footstepRGB * footstepCol.a;fixed4 finalCol fixed4(saturate(finalRGB), 1);return finalCol;return col;}ENDCG}}
}从代码可以看出footstepRect是一个很关键的东西它告诉了Shader需要绘制轨迹的范围在哪里。然后通过RemapUV方法拿这个范围和当前的顶点世界坐标去计算出当前的点该占整体UV的实际位置。 这个footstepRect其实是C#动态算出来的根据角色所在的坐标和半径算出来一个范围。 C#的代码大概是这样
Vector3 pos role.transform.position;
mat.SetVector(_centerPos, pos);
mat.SetFloat(_maxVal, radius);
mat.SetVector(_footstepRect, new Vector4(pos.x - radius, pos.z - radius, pos.x radius, pos.z radius));其实就是中心点加减半径而已。 这个做法的优点是只需要局部绘制一张贴图就能达到比较清晰的轨迹图形 缺点是只能在一定范围内显示超出了footstepRect范围轨迹就会消失了。
二、绘制轨迹的手段 绘制轨迹其实就是连贯的把某个笔刷的像素复制到一张图片上。这个应该不是很难理解的概念。 上面的例子球是一个笔刷它移动的时候它所在的位置会产生一个圆形的笔刷通过连续每帧的覆盖就形成了一个轨迹。 如果绘制的间隔拉大一点看到的情况大概是这样的。 那么问题来了球移动的时候上面说到相对于地表贴图的footstepRect是会变化的所以说我们不能直接把球的笔刷印到之前的那张图去。 比如上一张图的位置是在这里 下一张图的位置就变成了这里 留意看左下角的球它在世界中的位置是一直没有变化的但在这个footstepRect的局部里面它的相对位置是变化了的。 下面来说一下具体的做法。
1、通过摄像机绘制RenderTexture 这里为了渲染一张顶视图我是打了一个摄像机在运动的球的上方然后摄像机跟随这球移动。 需要注意的是摄像机一定要是正交的然后通过控制orthographicSize参数可以准确的绘制符合footstepRect的范围。最后给这个摄像机的targetTexture赋予一张RenderTexture作为输出。
2、通过偏移来叠加上一张图 刚才那个RenderTexture是每帧都会渲染一次的。我们需要2张RenderTexture一张是上一次留下的一张是这一帧渲染出来的。 接下来就是把两张RenderTexture通过Graphics.Blit方法合并在一起。由于Graphics.Blit方法是可以传入一个材质球的所以可以通过写一个Shader来混合2张贴图。具体的方式是计算上一帧和当前帧角色所在位置的偏移然后用偏移来控制上一帧的贴图的UV采样再把两张贴图合并在一起就可以了。
3、合并的Shader
Shader azhao/DrawFootstep
{Properties{_MainTex (Texture, 2D) white {}_lastTex(lastTex,2D) black{}_offset(offset,Vector) (0,0,0,0)}SubShader{Tags { RenderTypeOpaque }LOD 100Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include UnityCG.cgincstruct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _MainTex_ST;sampler2D _lastTex;float2 _offset;v2f vert (appdata v){v2f o;o.vertex UnityObjectToClipPos(v.vertex);o.uv TRANSFORM_TEX(v.uv, _MainTex);return o;}half4 frag (v2f i) : SV_Target{// sample the texturehalf4 col saturate(tex2D(_MainTex, i.uv));half3 curRGB col.rgb * 2 - 1;half4 lastCol saturate(tex2D(_lastTex, i.uv - _offset));float lastAlpha lastCol.a;half3 lastRGB lastCol.rgb*2-1;half mr lastRGB.r*lastAlpha;if (col.a 0){if (curRGB.r 0){if (lastAlpha 0){mr curRGB.r;}}else if (curRGB.r 0){mr min(curRGB.r,mr);}}else{mr lastRGB.r;}mr (mr 1) / 2;float alpha max(col.a, lastAlpha);half3 mixRGB half3(mr, mr, mr);half3 finalRGB mixRGB;return half4(finalRGB, alpha);}ENDCG}}
}三、细节问题 第一步绘制轨迹通过局部UV坐标采样和地表的贴图纹理混合。这里会存在一个问题。通过第二步绘制出来的轨迹贴图是Clamp平铺方式的 这意味着超出了UV的0到1范围的坐标会直接采样了0或者1的UV。具体的表现是这样的 这个黑线其实就是到边缘了所以超出的部分都会是黑的 为了解决这个问题可以加一个渐变的遮罩叠加 把UV接近0和1的地方都变成纯黑色这样就不会出现Clamp平铺的问题也可以让接近边缘的地方不会有一个很硬的消失而是稍微柔软的过渡。 所以用于绘制轨迹混合的shader会变成这样
Shader azhao/DrawFootstep
{Properties{_MainTex (Texture, 2D) white {}_lastTex(lastTex,2D) black{}_offset(offset,Vector) (0,0,0,0)_maskTex(maskTex,2D) white{}_reduceVal(reduceVal,float) 0.001}SubShader{Tags { RenderTypeOpaque }LOD 100Pass{CGPROGRAM#pragma vertex vert#pragma fragment frag#include UnityCG.cgincstruct appdata{float4 vertex : POSITION;float2 uv : TEXCOORD0;};struct v2f{float2 uv : TEXCOORD0;float4 vertex : SV_POSITION;};sampler2D _MainTex;float4 _MainTex_ST;sampler2D _lastTex;float2 _offset;sampler2D _maskTex;float _reduceVal;v2f vert (appdata v){v2f o;o.vertex UnityObjectToClipPos(v.vertex);o.uv TRANSFORM_TEX(v.uv, _MainTex);return o;}half4 frag (v2f i) : SV_Target{// sample the texturehalf4 col saturate(tex2D(_MainTex, i.uv));half3 curRGB col.rgb * 2 - 1;half4 lastCol saturate(tex2D(_lastTex, i.uv - _offset));float lastAlpha saturate(lastCol.a - _reduceVal);half4 maskCol tex2D(_maskTex, i.uv);half3 lastRGB lastCol.rgb*2-1;half mr lastRGB.r*lastAlpha;if (col.a 0){if (curRGB.r 0){if (lastAlpha 0){mr curRGB.r;}}else if (curRGB.r 0){mr min(curRGB.r,mr);}}else{mr lastRGB.r;}mr (mr 1) / 2;float alpha max(col.a, lastAlpha)*maskCol.r;half3 mixRGB half3(mr, mr, mr);half3 finalRGB mixRGB * maskCol.rgb;return half4(finalRGB, alpha);}ENDCG}}
}