首先,请允许我解释一下我所拥有的,然后再讨论下一个要解决的问题。
我有什么
我有一个带纹理的自定义网格物体,其某些边缘与Unity中的整数世界坐标完全对齐。在网格中,我添加了自己的粗糙但有效的自定义表面着色器,如下所示:
Shader "Custom/GridHighlightShader" { Properties { [HideInInspector]_SelectionColor("SelectionColor", Color) = (0.1,0.1,0.1,1) [HideInInspector]_MovementColor("MovementColor", Color) = (0,0.205,1,1) [HideInInspector]_AttackColor("AttackColor", Color) = (1,0,0,1) [HideInInspector]_GlowInterval("_GlowInterval", float) = 1 _MainTex("Albedo (RGB)", 2D) = "white" {} _Glossiness("Smoothness", Range(0,1)) = 0.5 _Metallic("Metallic", Range(0,1)) = 0.0 } SubShader { Tags { "RenderType" = "Opaque" } LOD 200 CGPROGRAM // Physically based Standard lighting model, and enable shadows on all light types #pragma surface surf Standard fullforwardshadows // Use shader model 3.0 target, to get nicer looking lighting #pragma target 3.0 struct Input { float2 uv_MainTex; float3 worldNormal; float3 worldPos; }; sampler2D _MainTex; half _Glossiness; half _Metallic; fixed4 _SelectionColor; fixed4 _MovementColor; fixed4 _AttackColor; half _GlowInterval; half _ColorizatiOnArrayLength= 0; float4 _ColorizationArray[600]; half _isPixelInColorizatiOnArray= 0; // Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader. // See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing. // #pragma instancing_options assumeuniformscaling UNITY_INSTANCING_BUFFER_START(Props) // put more per-instance properties here UNITY_INSTANCING_BUFFER_END(Props) void surf(Input IN, inout SurfaceOutputStandard o) { fixed4 c = tex2D(_MainTex, IN.uv_MainTex); // Update only the normals facing up and down if (abs(IN.worldNormal.x) <= 0.5 && (abs(IN.worldNormal.z) <= 0.5)) { // If no colors were passed in, reset all of the colors if (_ColorizationArray[0].w == 0) { _isPixelInColorizatiOnArray= 0; } else { for (int i = 0; i <_ColorizationArrayLength; i++) { if (abs(IN.worldPos.x) >= _ColorizationArray[i].x && abs(IN.worldPos.x) <_ColorizationArray[i].x + 1 && abs(IN.worldPos.z) >= _ColorizationArray[i].z && abs(IN.worldPos.z) <_ColorizationArray[i].z + 1 ) { _isPixelInColorizatiOnArray= _ColorizationArray[i].w; } } } if (_isPixelInColorizationArray > 0) { if (_isPixelInColorizatiOnArray== 1) { c = tex2D(_MainTex, IN.uv_MainTex) + (_SelectionColor * _GlowInterval) - 1; } else if (_isPixelInColorizatiOnArray== 2) { c = tex2D(_MainTex, IN.uv_MainTex) + (_MovementColor * _GlowInterval); } else if (_isPixelInColorizatiOnArray== 3) { c = tex2D(_MainTex, IN.uv_MainTex) + (_AttackColor * _GlowInterval); } } } o.Albedo = c.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }
我向着色器中输入一个浮点数,该浮点数使用一些数学函数随时间在2到3之间波动,这是通过Unity中的一个简单更新函数完成的:
private void Update() { var t = (2 + ((Mathf.Sin(Time.time)))); meshRenderer.material.SetFloat("_GlowInterval", t); }
我还向着色器提供了一个名为_ColorizationArray的Vector4数组,该数组存储0到600个坐标,每个坐标表示在运行时要着色的图块。这些切片可能会或可能不会突出显示,具体取决于它们在运行时的selectionMode值。这是我用来执行此操作的方法:
public void SetColorizationCollectionForShader() { var coloredTilesArray = Battlemap.Instance.tiles.Where(x => x.selectionMode != TileSelectionMode.None).ToArray(); // https://docs.unity3d.com/ScriptReference/Material.SetVectorArray.html // Set the tile count in the shader's own integer variable meshRenderer.material.SetInt("_ColorizationArrayLength", coloredTilesArray.Length); // Loop through the tiles to be colored only and grab their world coordinates for(int i = 0; i结果是在运行时动态设置和更改的一组蓝色发光瓷砖:
我所有这些的目标是作为基于网格的战术游戏的一部分,突出显示网格上的正方形(如果需要的话,也可以显示图块),其中单位可以移动到突出显示区域内的任何图块。每个单位移动之后,它可能会遭受攻击,其中瓷砖会突出显示为红色,然后下一个单位将其转向,依此类推。由于我希望AI,运动计算和粒子效果会占用大部分处理时间,因此我需要在运行时动态且非常高效地突出显示图块。
我接下来要做什么
哦,好的。现在,如果您对着色器一无所知(我当然不知道,我昨天才开始看cg代码),您可能会想:“哦,天哪,这是一种低效的混乱。您在做什么?!如果声明?!在着色器?” 而且我不会怪你。
我真正想做的几乎是同一件事,只是效率更高。使用特定的图块索引,我想告诉着色器“将这些图块内部的表面着色为蓝色,并且仅对这些图块进行着色”,并以对GPU和CPU都有效的方式进行着色。
我该如何实现?我已经在用C#代码计算平铺世界的坐标并将坐标提供给着色器,但是除此之外,我很茫然。我意识到我可能应该切换到顶点/片段着色器,但我也想避免在可能的情况下丢失网格上的任何默认动态光照。
另外,是否存在一种变量类型,该变量将允许着色器使用局部网格坐标而不是世界坐标将网格着色为蓝色?能够移动网格而不必担心着色器代码会很好。
编辑:在发布此问题后的两周内,我通过传入一个Vector4s数组来编辑着色器,该数组代表了实际要处理的数组的多少
_ColorizationArrayLength
,效果很好,但效率不高-这是会在相当现代的图形卡上产生大约17ms的GPU峰值。我已经更新了上面的着色器代码以及原始问题的一部分。
1> Ruzihm..:由于您的着色只关心在均等大小且均对准同一网格的正方形网格中的2d位置,因此我们可以传递2d纹理,其颜色表示地面应如何着色。
在着色器中,添加
2D
_ColorizeMap
和Vector
_WorldSpaceRange
。该地图将用于传递应该对世界的各个部分进行着色的范围,并且范围将告诉着色器如何在世界空间和UV(纹理)空间之间转换。由于游戏网格与世界x / y轴对齐,因此我们可以线性缩放从世界空间到UV空间的坐标。然后,当法线朝上时(您可以检查法线的y是否足够高),获取世界位置的倒数,并从中采样
_ColorizeMap
以获取如何/是否应该着色。Shader "Custom/GridHighlightShader" { Properties { [HideInInspector]_GlowInterval("_GlowInterval", float) = 1 _MainTex("Albedo (RGB)", 2D) = "white" {} _Glossiness("Smoothness", Range(0,1)) = 0.5 _Metallic("Metallic", Range(0,1)) = 0.0 [HideInInspector]_ColorizeMap("Colorize Map", 2D) = "black" {} _WorldSpaceRange("World Space Range", Vector) = (0,0,100,100) } SubShader { Tags { "RenderType" = "Opaque" } LOD 200 CGPROGRAM // Physically based Standard lighting model, // and enable shadows on all light types #pragma surface surf Standard fullforwardshadows // Use shader model 3.0 target, to get nicer looking lighting #pragma target 3.0 struct Input { float2 uv_MainTex; float3 worldNormal; float3 worldPos; }; sampler2D _MainTex; half _Glossiness; half _Metallic; half _GlowInterval; sampler2D _ColorizeMap; fixed4 _WorldSpaceRange; // Add instancing support for this shader. // You need to check 'Enable Instancing' on materials that use the shader. // See https://docs.unity3d.com/Manual/GPUInstancing.html // for more information about instancing. // #pragma instancing_options assumeuniformscaling UNITY_INSTANCING_BUFFER_START(Props) // put more per-instance properties here UNITY_INSTANCING_BUFFER_END(Props) void surf(Input IN, inout SurfaceOutputStandard o) { fixed4 c = tex2D(_MainTex, IN.uv_MainTex); // Update only the normals facing up and down if (abs(IN.worldNormal.y) >= 0.866)) // abs(y) >= sin(60 degrees) { fixed4 colorizedMapUV = (IN.worldPos.xz-_WorldSpaceRange.xy) / (_WorldSpaceRange.zw-_WorldSpaceRange.xy); half4 colorType = tex2D(_ColorizeMap, colorizedMapUV); c = c + (colorType * _GlowInterval); } o.Albedo = c.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }并删除分支:
Shader "Custom/GridHighlightShader" { Properties { [HideInInspector]_GlowInterval("_GlowInterval", float) = 1 _MainTex("Albedo (RGB)", 2D) = "white" {} _Glossiness("Smoothness", Range(0,1)) = 0.5 _Metallic("Metallic", Range(0,1)) = 0.0 [HideInInspector]_ColorizeMap("Colorize Map", 2D) = "black" {} _WorldSpaceRange("World Space Range", Vector) = (0,0,100,100) } SubShader { Tags { "RenderType" = "Opaque" } LOD 200 CGPROGRAM // Physically based Standard lighting model, // and enable shadows on all light types #pragma surface surf Standard fullforwardshadows // Use shader model 3.0 target, to get nicer looking lighting #pragma target 3.0 struct Input { float2 uv_MainTex; float3 worldNormal; float3 worldPos; }; sampler2D _MainTex; half _Glossiness; half _Metallic; half _GlowInterval; sampler2D _ColorizeMap; fixed4 _WorldSpaceRange; // Add instancing support for this shader. // You need to check 'Enable Instancing' on materials that use the shader. // See https://docs.unity3d.com/Manual/GPUInstancing.html // for more information about instancing. // #pragma instancing_options assumeuniformscaling UNITY_INSTANCING_BUFFER_START(Props) // put more per-instance properties here UNITY_INSTANCING_BUFFER_END(Props) void surf(Input IN, inout SurfaceOutputStandard o) { half4 c = tex2D(_MainTex, IN.uv_MainTex); float2 colorizedMapUV = (IN.worldPos.xz - _WorldSpaceRange.xy) / (_WorldSpaceRange.zw - _WorldSpaceRange.xy); half4 colorType = tex2D(_ColorizeMap, colorizedMapUV); // abs(y) >= sin(60 degrees) = 0.866 c = c + step(0.866, abs(IN.worldNormal.y)) * colorType * _GlowInterval; o.Albedo = c.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }然后在您的C#代码中,创建一个无需过滤的纹理。以全黑开始纹理,然后根据突出显示方式向纹理添加颜色。另外,告诉着色器颜色图所表示的世界空间的范围(minX,minZ,maxX,maxZ):
Shader "Custom/GridHighlightShader" { Properties { [HideInInspector]_GlowInterval("_GlowInterval", float) = 1 _MainTex("Albedo (RGB)", 2D) = "white" {} _Glossiness("Smoothness", Range(0,1)) = 0.5 _Metallic("Metallic", Range(0,1)) = 0.0 [HideInInspector]_ColorizeMap("Colorize Map", 2D) = "black" {} _WorldSpaceRange("World Space Range", Vector) = (0,0,100,100) } SubShader { Tags { "RenderType" = "Opaque" } LOD 200 CGPROGRAM // Physically based Standard lighting model, // and enable shadows on all light types #pragma surface surf Standard fullforwardshadows // Use shader model 3.0 target, to get nicer looking lighting #pragma target 3.0 struct Input { float2 uv_MainTex; float3 worldNormal; float3 worldPos; }; sampler2D _MainTex; half _Glossiness; half _Metallic; half _GlowInterval; sampler2D _ColorizeMap; fixed4 _WorldSpaceRange; // Add instancing support for this shader. // You need to check 'Enable Instancing' on materials that use the shader. // See https://docs.unity3d.com/Manual/GPUInstancing.html // for more information about instancing. // #pragma instancing_options assumeuniformscaling UNITY_INSTANCING_BUFFER_START(Props) // put more per-instance properties here UNITY_INSTANCING_BUFFER_END(Props) void surf(Input IN, inout SurfaceOutputStandard o) { fixed4 c = tex2D(_MainTex, IN.uv_MainTex); // Update only the normals facing up and down if (abs(IN.worldNormal.y) >= 0.866)) // abs(y) >= sin(60 degrees) { fixed4 colorizedMapUV = (IN.worldPos.xz-_WorldSpaceRange.xy) / (_WorldSpaceRange.zw-_WorldSpaceRange.xy); half4 colorType = tex2D(_ColorizeMap, colorizedMapUV); c = c + (colorType * _GlowInterval); } o.Albedo = c.rgb; o.Metallic = _Metallic; o.Smoothness = _Glossiness; o.Alpha = c.a; } ENDCG } FallBack "Diffuse" }磁贴的边界可能有些晃动,纹理空间/世界空间之间可能存在对齐问题,但这应该可以帮助您入门。