Unity Shader - 如何有效地重新着色特定坐标?

2023-12-24

首先,请允许我解释一下我所得到的内容,然后我将讨论接下来我想要弄清楚的内容。

我有什么

我有一个带纹理的自定义网格,其一些边缘与 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 个坐标,每个坐标代表要在运行时着色的图块。这些图块可能会或可能不会突出显示,具体取决于它们在运行时的选择模式值。这是我用来执行此操作的方法:

    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 < coloredTilesArray.Length; i++)
        {
            // Also grab the selection mode as the w value of a float4
            colorizationArray[i] = new Vector4(coloredTilesArray[i].x - Battlemap.HALF_TILE_SIZE, coloredTilesArray[i].y, coloredTilesArray[i].z - Battlemap.HALF_TILE_SIZE, (float)coloredTilesArray[i].selectionMode);            
        }

        // Feed the overwritten array into the shader
        meshRenderer.material.SetVectorArray("_ColorizationArray", colorizationArray);
    }

结果是这组蓝色发光的图块在运行时动态设置和更改:

enter image description here

我所有这一切的目标是突出显示网格上的方块(或图块,如果你愿意的话),作为基于网格的战术游戏的一部分,其中单位可以移动到突出显示区域内的任何图块。每个单位移动后,它就会受到攻击,其中的图块会突出显示为红色,然后轮到下一个单位,依此类推。由于我预计人工智能、运动计算和粒子效果会占用大部分处理时间,因此我需要在运行时动态且非常高效地突出显示图块。

接下来我想做什么

哇,好吧。现在,如果您了解有关着色器的任何信息(我当然不知道,我昨天才开始查看 cg 代码),您可能会想“天哪,真是低效的混乱。你在做什么?!如果语句?!在着色器?”我不会责怪你。

我真正想做的事情几乎是一样的,只是效率更高。使用特定的图块索引,我想告诉着色器“将这些图块内部的表面着色为蓝色,并且仅将这些图块着色”,并以对 GPU 和 CPU 都有效的方式进行操作。

我怎样才能实现这个目标?我已经在 C# 代码中计算图块世界坐标并将坐标提供给着色器,但除此之外我不知所措。我意识到我也许应该切换到顶点/片段着色器,但如果可能的话,我也想避免丢失网格上的任何默认动态照明。

另外,是否有一种类型的变量允许着色器使用本地网格坐标而不是世界坐标将网格着色为蓝色?如果能够移动网格而不必担心着色器代码,那就太好了。

编辑:在发布这个问题后的两周内,我通过传入一个半 Vector4 数组来表示要实际处理的数组量来编辑着色器,_ColorizationArrayLength,它工作得很好,但效率并不高——这会产生 GPU 峰值,在相当现代的显卡上处理需要大约 17 毫秒。我已经更新了上面的着色器代码以及原始问题的部分内容。


由于您的着色只关心大小相同的正方形网格中的 2d 位置,这些正方形都与同一网格对齐,因此我们可以传入一个 2d 纹理,其颜色表示地面的颜色应该是什么样的。

在你的着色器中,添加一个2D _ColorizeMap, and a Vector _WorldSpaceRange。该贴图将用于传递应该对世界的哪些部分进行着色,并且范围将告诉着色器如何在世界空间和 UV(纹理)空间之间进行转换。由于游戏网格与世界 x/y 轴对齐,因此我们可以将坐标从世界空间线性缩放到 UV 空间。

然后,当法线朝上时(您可以检查法线的 y 是否足够高),获得世界位置的逆 lerp,并从_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):

    public void SetColorizationCollectionForShader()
{   
    Color[] selectionColors = new Color[4] { Color.clear, new Color(0.5f, 0.5f, 0.5f, 0.5f), Color.blue, Color.red };
    float leftMostTileX = 0f + Battlemap.HALF_TILE_SIZE;
    float backMostTileZ = 0f + Battlemap.HALF_TILE_SIZE;

    float rightMostTileX = leftMostTileX + (Battlemap.Instance.GridMaxX - 1)
            * Battlemap.TILE_SIZE;
    float forwardMostTileZ = backMostTileZ + (Battlemap.Instance.GridMaxZ - 1)
            * Battlemap.TILE_SIZE;

    Texture2D colorTex = new Texture2D(Battlemap.Instance.GridMaxX, Battlemap.Instance.GridMaxZ);
    colorTex.filterMode = FilterMode.Point;

    Vector4 worldRange = new Vector4(
            leftMostTileX - Battlemap.HALF_TILE_SIZE,
            backMostTileZ - Battlemap.HALF_TILE_SIZE,
            rightMostTileX + Battlemap.HALF_TILE_SIZE,
            forwardMostTileZ + Battlemap.HALF_TILE_SIZE);

    meshRenderer.material.SetVector("_WorldSpaceRange", worldRange);        

    // Loop through the tiles to be colored only and grab their world coordinates
    for (int i = 0; i < Battlemap.Instance.tiles.Length; i++)
    {
        // determine pixel index from position
        float xT = Mathf.InverseLerp(leftMostTileX, rightMostTileX,
                Battlemap.Instance.tiles[i].x);
        int texXPos = Mathf.RoundToInt(Mathf.Lerp(0f, Battlemap.Instance.GridMaxX - 1.0f, xT));

        float yT = Mathf.InverseLerp(backMostTileZ, forwardMostTileZ,
                Battlemap.Instance.tiles[i].z);
        int texYPos = Mathf.RoundToInt(Mathf.Lerp(0f, Battlemap.Instance.GridMaxZ - 1.0f, yT));

        colorTex.SetPixel(texXPos, texYPos, selectionColors[(int)Battlemap.Instance.tiles[i].selectionMode]);
    }
    colorTex.Apply();

    // Feed the color map into the shader
    meshRenderer.material.SetTexture("_ColorizeMap", colorTex);
}

瓷砖的边界可能存在一些不稳定,并且纹理空间/世界空间之间可能存在一些对齐问题,但这应该可以帮助您入门。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Unity Shader - 如何有效地重新着色特定坐标? 的相关文章

随机推荐

  • Postgres 函数总是返回一行

    我在 Postgres 中编写了以下函数 但遇到了一个问题 它总是返回一行 我的意思是 当没有用户匹配该对时 它会返回所有列为空的行 有没有办法让函数在没有结果时返回 0 零 行 CREATE OR REPLACE FUNCTION fin
  • Angular 2 全局常量提供程序注入器方法

    我有一个全局常量 例如根目录 我希望每个组件都可以访问它 在另一个 stackoverflow 问题中 答案是创建一个常量类并将其导入到每个组件中 有没有一种方法可以引导常量类 以便应用程序中的每个组件都可以访问它而无需任何额外的导入 到目
  • PHP:if (!$val) VS if (empty($val))。有什么区别吗?

    我想知道下面两种情况有什么区别 推荐哪一种 val 0 if val True if empty val It s also True 看看PHP类型对照表 http php net manual en types comparisons
  • @QueryParam 如何将没有值的参数转换为布尔值“false”?

    我想使用像这样的网址http www example com rest foo bar哪里的bar查询参数没有值 它的存在本身就应该表明变量是否是true or false 目前缺失值被假设为 空 并传递给new Boolean 将其视为f
  • XML(带有命名空间)到对象解组

    我从 Web 服务调用得到了以下响应 我尝试使用 JAXB 对其进行解组以将其映射到 java 类 这样做时我遇到了 unmarshal 异常
  • 使用扫描仪时无限循环? [复制]

    这个问题在这里已经有答案了 boolean z false do try a sc nextInt z true catch Exception e while z 尝试这个 如果您第一次尝试使用整数 它会正确执行 但是 如果您输入错误的文
  • Firebase orderByChild 和 equalTo() 查询不起作用

    我正在使用以下 Firebase 数据库表开发 Android 应用程序 posts id 1 author google 111527135678918251124 color 2960686 creationTime 142710414
  • aws cli get 错误“请求中包含的安全令牌无效”

    I did aws configure并测试它之前是否有效 但今天没用 我在尝试获取托管区域时收到错误 aws route53 list hosted zones An error occurred InvalidClientTokenId
  • Hudson 不会从 Git 获取

    我已经打了一天多了 这让我发疯了 我在Win7 PC上安装了Git 并选择 PuttyGen 选项 安装 我已经生成了 ssh 密钥 使用 PuttyGen 我已经添加了ssh 密钥到我的 Git 帐户 并链接 选美比赛中的私钥 我已经设置
  • 为 Bson.M mongodb 创建自定义 mashler/unmashler 时出错

    我收到错误WriteValueBytes can only write while positioned on a Element or Value but is positioned on a TopLevel当尝试为 bson M 创建
  • PyTuple_SetItem 的限制

    我有一个 Python 扩展模块 它创建一个元组作为另一个对象的属性 并在元组中设置项目 每当我在Python中执行这个模块时 我总是收到错误SystemError bad argument to internal function 阅读完
  • 为什么Cppcheck没有发现这个明显的数组越界错误?

    我安装了Cppcheck http cppcheck sourceforge net 我的 C 项目的静态代码分析工具 感觉它的性能很差 例如 谁能告诉我whyCppcheck无法在以下代码中找到数组越界错误 void f int c ch
  • 从 C# 在 PowerShell 6 中运行 PowerShell 脚本

    我有一个与 REST 服务器通信的 PowerShell 脚本 该脚本仅适用于 PowerShell 6 我想从 C 调用它 因为 C 程序需要来自 REST 服务器的信息 并且我不想用 C 重写 REST 代码 基本上 我想从 C 运行
  • 操作系统级别的睡眠是如何实现的?

    我只是感兴趣如何sleep time in ms 是在 C 库中实现的 或者基本上是在操作系统级别实现的 我猜 可能是基于处理器速度 你做了一个 nop 的 while 循环 我不确定睡眠时间是否准确 处理器中的任何特殊寄存器 您在其中写入
  • ASIHTTPRequest 中 POST 中的正文为空

    基本上 我发送一个带有空数据正文的 POST 请求 ASIHTTPRequest request ASIHTTPRequest alloc init request setURL NSURL URLWithString escapedUrl
  • Docker 未知速记标志:-aq 中的“a”)

    我有多个正在运行的 docker 版本 18 09 0 构建 4d60db4 容器 我希望立即停止它们 这篇博文 http blog baudson de blog stop and remove all docker containers
  • 将文件名设置为 Blob 文件

    我想向我的 Blob 文件添加一个文件名 但我真的不知道该怎么做 这是我目前的代码 onClick var myHeader new Headers myHeader append Content Type text plain fetch
  • 如何加密 web.config 中的一项

    ASP NET 4 我用过RSA密钥加密 http msdn microsoft com en us library ff650304 aspx paght000006 webfarmscenarios用于我的网络场的 web config
  • ThreadLocal - 用作带有 spring-boot 的 REST API 的上下文信息

    我有一些spring boot应用程序 它公开了 REST API 提到的 REST API 是由spring security 一切都很好 但是现在我需要设置上下文 用于服务请求 设置上下文是指根据用户上下文选择数据源 关键是Routin
  • Unity Shader - 如何有效地重新着色特定坐标?

    首先 请允许我解释一下我所得到的内容 然后我将讨论接下来我想要弄清楚的内容 我有什么 我有一个带纹理的自定义网格 其一些边缘与 Unity 中的整数世界坐标完全对齐 在网格中 我添加了自己的粗略但有效的自定义表面着色器 如下所示 Shade