普通视图

发现新文章,点击刷新页面。
今天 — 2026年1月24日技术

每日一题-数组中最大数对和的最小值🟡

2026年1月24日 00:00

一个数对 (a,b) 的 数对和 等于 a + b 。最大数对和 是一个数对数组中最大的 数对和 。

  • 比方说,如果我们有数对 (1,5) ,(2,3) 和 (4,4)最大数对和 为 max(1+5, 2+3, 4+4) = max(6, 5, 8) = 8 。

给你一个长度为 偶数 n 的数组 nums ,请你将 nums 中的元素分成 n / 2 个数对,使得:

  • nums 中每个元素 恰好 在 一个 数对中,且
  • 最大数对和 的值 最小 。

请你在最优数对划分的方案下,返回最小的 最大数对和 。

 

示例 1:

输入:nums = [3,5,2,3]
输出:7
解释:数组中的元素可以分为数对 (3,3) 和 (5,2) 。
最大数对和为 max(3+3, 5+2) = max(6, 7) = 7 。

示例 2:

输入:nums = [3,5,4,2,4,6]
输出:8
解释:数组中的元素可以分为数对 (3,5),(4,4) 和 (6,2) 。
最大数对和为 max(3+5, 4+4, 6+2) = max(8, 8, 8) = 8 。

 

提示:

  • n == nums.length
  • 2 <= n <= 105
  • n 是 偶数 。
  • 1 <= nums[i] <= 105

【宫水三叶の相信科学系列】最大数对和的最小值,贪心解的正确性证明

作者 AC_OIer
2021年7月20日 10:39

基本分析 & 证明

直觉上,我们会认为「尽量让“较小数”和“较大数”组成数对,可以有效避免出现“较大数成对”的现象」。

我们来证明一下该猜想是否成立。

假定 $nums$ 本身有序,由于我们要将 $nums$ 拆分成 $n / 2$ 个数对,根据猜想,我们得到的数对序列为:
$$
(nums[0], nums[n - 1]), (nums[1], nums[n - 2]), ... , (nums[(n / 2) - 1], nums[n / 2])
$$

换句话说,构成答案的数对必然是较小数取自有序序列的左边,较大数取自有序序列的右边,且与数组中心对称

假设最大数对是 $(nums[i], nums[j])$,其中 $i < j$,记两者之和为 $ans = nums[i] + nums[j]$。

反证法证明,不存在别的数对组合会比 $(nums[i], nums[j])$ 更优:

假设存在数对 $(nums[p], nums[q])$ 与 $(nums[i], nums[j])$ 进行调整使答案更优。

image.png

接下来分情况讨论:

  • 调整为 $(nums[i], nums[p])$ 和 $(nums[q], nums[j])$:此时最大数对答案为 $nums[q] + nums[j]$,显然 $nums[q] + nums[j] >= nums[i] + nums[j] = ans$。我们要最小化最大数对和,因此该调整方案不会让答案更好;
  • 调整为 $(nums[i], nums[q])$ 和 $(nums[p], nums[j])$:此时最大数对答案为 $\max(nums[i] + nums[q], nums[p] + nums[j]) = nums[p] + nums[j] >= nums[i] + nums[j] = ans$。我们要最小化最大数对和,因此该调整方案不会让答案更好;

上述分析可以归纳推理到每一个“非对称”的数对配对中。

至此我们得证,将原本对称的数对调整为不对称的数对,不会使得答案更优,即贪心解可取得最优解。


贪心

对原数组 $nums$ 进行排序,然后从一头一尾开始往中间组「数对」,取所有数对中的最大值即是答案。

代码:

###Java

class Solution {
    public int minPairSum(int[] nums) {
        Arrays.sort(nums);
        int n = nums.length;
        int ans = nums[0] + nums[n - 1];
        for (int i = 0, j = n - 1; i < j; i++, j--) {
            ans = Math.max(ans, nums[i] + nums[j]);
        }
        return ans;
    }
}
  • 时间复杂度:$O(n\log{n})$
  • 空间复杂度:$O(\log{n})$

答疑

关于「证明」部分,不少小伙伴有一些疑问,觉得挺有代表性的,特意加到题解内。

Q1. 「证明」部分是不是缺少了“非对称”得最优的情况?

A1. 并没有,证明的基本思路如下:

  1. 猜想对称组数对的方式,会得到最优解;

  2. 证明非对称数组不会被对称数对方式更优。

然后我们证明了“非对称方式”不会比“对称方式”更优,因此“对称方式”可以取得最优解。

至于非对称和非对称之间怎么调整,会更优还是更差,我不关心,也不需要证明,因为已经证明了非对称不会比对称更优。

Q2. 证明部分的图 $p$、$q$ 是在 $i$、$j$ 内部,那么其他 $p$、$q$、$i$、$j$ 大小关系的情况呢?

A2. 有这个疑问,说明没有重点理解「证明」中的加粗部分(原话):

上述分析可以归纳推理到每一个“非对称”的数对配对中。

也就是说,上述的分析是可以推广到每一步都成立的,包括第一步,当 $i$ 为排序数组的第一位原始,$j$ 为排序数组中最后一位时,任意 $p$ 和 $q$ 都是在 $i$、$j$ 内部的。

因此,「证明」对边界情况成立,同时对任意不成“对称”关系的数对也成立(其实也就是「证明」部分中的原话)。

更大白话一点是:对于任意“非对称”的数对组合,将其调整为“对称”数对组合,结果不会变差。


最后

如果有帮助到你,请给题解点个赞和收藏,让更多的人看到 ~ ("▔□▔)/

也欢迎你 关注我 和 加入我们的「组队打卡」小群 ,提供写「证明」&「思路」的高质量题解。

所有题解已经加入 刷题指南,欢迎 star 哦 ~

数组中最大数对和的最小值

2021年5月30日 00:16

方法一:排序 + 贪心

提示 $1$

数组内只有两个数的情况是平凡的。我们可以考虑数组中只有四个数 $x_1 \le x_2 \le x_3 \le x_4$ 的情况。此时 $(x_1, x_4), (x_2, x_3)$ 的拆分方法对应的最大数对和一定是最小的。

提示 $1$ 解释

我们可以枚举所有的拆分方法。除了上文的拆分方法外还有两种拆分方法:

  • $(x_1, x_3), (x_2, x_4)$

    此时 $x_2 + x_4 \ge x_1 + x_4$ 且 $x_2 + x_4 \ge x_2 + x_3$。

    那么 $\max(x_1+x_3,x_2+x_4) \ge x_2 + x_4 \ge \max(x_1+x_4,x_2+x_3)$。

  • $(x_1, x_2), (x_3, x_4)$

    同样地,$\max(x_1+x_2,x_3+x_4) \ge x_3 + x_4 \ge \max(x_1+x_4,x_2+x_3)$。

提示 $2$

对于 $n$ 个数($n$ 为偶数)的情况,上述的条件对应的拆分方法,即第 $k$ 大与第 $k$ 小组成的 $n / 2$ 个数对,同样可以使得最大数对和最小。

提示 $2$ 解释

我们可以类似 提示 $1$ 对所有数建立全序关系,即 $x_1 \le \cdots \le x_n$。我们需要证明,任意的拆分方法得到的最大数对和一定大于等于给定的拆分方法得到的最大数对和。

我们可以考虑上述命题的充分条件:假设给定拆分方法中的数对和 $x_k + x_{n+1-k}$ 在 $k = k'$ 时最大,那么对于任意的拆分方法,都存在一组 $u, v$ 使得 $x_u + x_v \ge x_{k'} + x_{n+1-k'}$。

我们可以用反证法证明。

同样,我们假设 $u < v$,那么使得 $x_v \ge x_{n+1-k'}$ 的 $v$ 的取值一共有 $k'$ 种。即闭区间 $[n+1-k',n]$ 中的所有整数。对于这些 $v$ 组成的数对,如果想使得 $x_u + x_v < x_{k'} + x_{n+1-k'}$ 恒成立,必须要 $x_u < x_{k'}$。此时需要有 $k'$ 个不同的 $u$ 的取值,但只有闭区间 $[1,k'-1]$ 中的 $k'-1$ 个整数满足 $x_u < x_{k'}$ 的条件,这就产生了矛盾。

因此,一定存在一组 $u, v$ 使得 $x_u + x_v \ge x_{k'} + x_{n+1-k'}$。

思路与算法

根据 提示 $2$,我们需要将 $\textit{nums}$ 排序。排序后,我们遍历每一个第 $k$ 大与第 $k$ 小组成的数对,计算它们的和,并维护这些和的最大值。同样根据 提示 $2$,遍历完成后求得的最大数对和就是满足要求的最小值。

代码

###C++

class Solution {
public:
    int minPairSum(vector<int>& nums) {
        int n = nums.size();
        int res = 0;
        sort(nums.begin(), nums.end());
        for (int i = 0; i < n / 2; ++i) {
            res = max(res, nums[i] + nums[n - 1 - i]);
        }
        return res;
    }
};

###Java

class Solution {
    public int minPairSum(int[] nums) {
        int n = nums.length;
        int res = 0;
        Arrays.sort(nums);
        for (int i = 0; i < n / 2; ++i) {
            res = Math.max(res, nums[i] + nums[n - 1 - i]);
        }
        return res;
    }
}

###C#

public class Solution {
    public int MinPairSum(int[] nums) {
        int n = nums.Length;
        int res = 0;
        Array.Sort(nums);
        for (int i = 0; i < n / 2; ++i) {
            res = Math.Max(res, nums[i] + nums[n - 1 - i]);
        }
        return res;
    }
}

###Python

class Solution:
    def minPairSum(self, nums: List[int]) -> int:
        n = len(nums)
        res = 0
        nums.sort()
        for i in range(n // 2):
            res = max(res, nums[i] + nums[n-1-i])
        return res

###JavaScript

var minPairSum = function(nums) {
    const n = nums.length;
    let res = 0;
    nums.sort((a, b) => a - b);
    for (let i = 0; i < Math.floor(n / 2); i++) {
        res = Math.max(res, nums[i] + nums[n - 1 - i]);
    }
    return res;
};

###go

func minPairSum(nums []int) (ans int) {
    sort.Ints(nums)
    n := len(nums)
    for i, val := range nums[:n/2] {
        ans = max(ans, val+nums[n-1-i])
    }
    return
}

func max(a, b int) int {
    if a > b {
        return a
    }
    return b
}

###C

int cmp(int *a, int *b) {
    return *a - *b;
}

int minPairSum(int *nums, int numsSize) {
    int res = 0;
    qsort(nums, numsSize, sizeof(int), cmp);
    for (int i = 0; i < numsSize / 2; ++i) {
        res = fmax(res, nums[i] + nums[numsSize - 1 - i]);
    }
    return res;
}

复杂度分析

  • 时间复杂度:$O(n\log n)$,其中 $n$ 为数组 $\textit{nums}$ 的长度。排序 $\textit{nums}$ 的时间复杂度为 $O(n\log n)$,遍历维护最大数对和的时间复杂度为 $O(n)$。

  • 空间复杂度:$O(\log n)$,即为排序的栈空间开销。

证明,交换论证法(Python/Java/C++/C/Go/JS/Rust)

作者 endlesscheng
2021年5月30日 00:10

你可能猜了一个结论:把最小的数和最大的数配对,把第二小的数和第二大的数配对,依此类推。注意题目保证 $n$ 是偶数。

但是,如何证明这样做是对的?

设 $\textit{nums}$ 排序后的结果为 $a_1\le a_2\le \cdots \le a_n$。

首先证明,$a_1$ 与 $a_n$ 配对是最优的。

设 $a_i$ 和 $a_j$ 是数组中的另外两个数,考虑如下两种配对方案:

  • $(a_1,a_i)$ 和 $(a_j,a_n)$。最大数对和为 $M_1 = \max(a_1+a_i,a_j+a_n,其他数对和)$。
  • $(a_1,a_n)$ 和 $(a_i,a_j)$。最大数对和为 $M_2 = \max(a_1+a_n,a_i+a_j,其他数对和)$。

由于 $a_1\le a_j$,$a_i\le a_n$,所以 $a_1+a_i\le a_j+a_n$,所以 $M_1 = \max(a_j+a_n,其他数对和)$。

由于 $a_1+a_n\le a_j+a_n$ 且 $a_i+a_j\le a_j+a_n$,所以 $\max(a_1+a_n,a_i+a_j)\le a_j+a_n$,所以 $M_2\le M_1$。

这意味着,对于任意最优配对方案,将其调整为 $a_1$ 和 $a_n$ 配对,不会让最大数对和变得更大。所以存在最优配对方案,其中 $a_1$ 和 $a_n$ 是配对的。

去掉 $a_1$ 和 $a_n$(已配对),问题变成一个规模更小的子问题($n-2$ 个数),同理可得其他数的配对方式。

class Solution:
    def minPairSum(self, nums: List[int]) -> int:
        nums.sort()
        n = len(nums)
        return max(nums[i] + nums[-1 - i] for i in range(n // 2))
class Solution {
    public int minPairSum(int[] nums) {
        Arrays.sort(nums);
        int n = nums.length;
        int ans = 0;
        for (int i = 0; i < n / 2; i++) {
            ans = Math.max(ans, nums[i] + nums[n - 1 - i]);
        }
        return ans;
    }
}
class Solution {
public:
    int minPairSum(vector<int>& nums) {
        ranges::sort(nums);
        int n = nums.size();
        int ans = 0;
        for (int i = 0; i < n / 2; i++) {
            ans = max(ans, nums[i] + nums[n - 1 - i]);
        }
        return ans;
    }
};
#define MAX(a, b) ((b) > (a) ? (b) : (a))

int cmp(const void* a, const void* b) {
    return *(int*)a - *(int*)b;
}

int minPairSum(int* nums, int numsSize) {
    qsort(nums, numsSize, sizeof(int), cmp);
    int ans = 0;
    for (int i = 0; i < numsSize / 2; i++) {
        ans = MAX(ans, nums[i] + nums[numsSize - 1 - i]);
    }
    return ans;
}
func minPairSum(nums []int) (ans int) {
slices.Sort(nums)
n := len(nums)
for i, x := range nums[:n/2] {
ans = max(ans, x+nums[n-1-i])
}
return
}
var minPairSum = function(nums) {
    nums.sort((a, b) => a - b);
    const n = nums.length;
    let ans = 0;
    for (let i = 0; i < n / 2; i++) {
        ans = Math.max(ans, nums[i] + nums[n - 1 - i]);
    }
    return ans;
};
impl Solution {
    pub fn min_pair_sum(mut nums: Vec<i32>) -> i32 {
        nums.sort_unstable();
        let n = nums.len();
        let mut ans = 0;
        for i in 0..n / 2 {
            ans = ans.max(nums[i] + nums[n - 1 - i]);
        }
        ans
    }
}

复杂度分析

  • 时间复杂度:$\mathcal{O}(n\log n)$,其中 $n$ 是 $\textit{nums}$ 的长度。瓶颈在排序上。
  • 空间复杂度:$\mathcal{O}(1)$。忽略排序的栈开销。

专题训练

见下面贪心题单的「§1.2 单序列配对」和「§1.7 交换论证法」。

分类题单

如何科学刷题?

  1. 滑动窗口与双指针(定长/不定长/单序列/双序列/三指针/分组循环)
  2. 二分算法(二分答案/最小化最大值/最大化最小值/第K小)
  3. 单调栈(基础/矩形面积/贡献法/最小字典序)
  4. 网格图(DFS/BFS/综合应用)
  5. 位运算(基础/性质/拆位/试填/恒等式/思维)
  6. 图论算法(DFS/BFS/拓扑排序/基环树/最短路/最小生成树/网络流)
  7. 动态规划(入门/背包/划分/状态机/区间/状压/数位/数据结构优化/树形/博弈/概率期望)
  8. 常用数据结构(前缀和/差分/栈/队列/堆/字典树/并查集/树状数组/线段树)
  9. 数学算法(数论/组合/概率期望/博弈/计算几何/随机算法)
  10. 贪心与思维(基本贪心策略/反悔/区间/字典序/数学/思维/脑筋急转弯/构造)
  11. 链表、树与回溯(前后指针/快慢指针/DFS/BFS/直径/LCA)
  12. 字符串(KMP/Z函数/Manacher/字符串哈希/AC自动机/后缀数组/子序列自动机)

我的题解精选(已分类)

欢迎关注 B站@灵茶山艾府

前端性能优化:深入解析防抖(Debounce)与节流(Throttle)

作者 San30
2026年1月23日 22:32
在前端开发中,用户体验至关重要。然而,许多高频触发的用户交互——例如快速输入搜索关键词、调整浏览器窗口大小、或者快速滚动页面——往往会产生大量的事件。如果我们直接将复杂的任务(如 AJAX 请求、DO

【节点】[InstanceID节点]原理解析与实际应用

作者 SmalBox
2026年1月24日 08:54

【Unity Shader Graph 使用与特效实现】专栏-直达

在Unity引擎的渲染管线中,GPU实例化是一项重要的性能优化技术,它允许在单个绘制调用中渲染多个相同的网格,显著减少了CPU到GPU的数据传输开销。在URP(Universal Render Pipeline)的Shader Graph中,InstanceID节点扮演着关键角色,它为开发者提供了访问每个实例唯一标识符的能力。本文将深入探讨InstanceID节点的各个方面,包括其工作原理、应用场景、使用技巧以及实际示例。

InstanceID节点的基本概念

InstanceID节点是Shader Graph中的一个特殊功能节点,它返回当前正在渲染的几何体实例的唯一标识符。这个标识符在GPU实例化过程中由Unity引擎自动分配和管理。

当Unity使用GPU实例化技术渲染多个相同网格时,每个实例都会获得一个独一无二的Instance ID。这个ID从0开始递增,对应于绘制调用中每个实例的索引位置。理解这一点对于正确使用InstanceID节点至关重要。

InstanceID节点的输出是一个浮点数值,代表当前实例的标识符。在非实例化渲染情况下,这个值始终为0。需要注意的是,当使用动态实例化时,实例ID在不同帧之间可能不会保持一致,这意味着开发者不应该依赖实例ID在多个帧之间的持久性。

InstanceID节点的工作原理

要深入理解InstanceID节点,首先需要了解GPU实例化的基本机制。GPU实例化允许在单个绘制调用中渲染多个相同的网格,每个实例可以有不同的变换矩阵、颜色和其他属性。Unity通过实例缓冲区(Instance Buffer)将这些每实例数据传递给着色器。

在着色器执行过程中,系统会为每个实例分配一个唯一的索引,这就是Instance ID的来源。InstanceID节点本质上就是访问这个内置的着色器变量unity_InstanceID

在HLSL代码中,InstanceID节点的实现大致如下:

HLSL

void Unity_InstanceID_float(out float Out)
{
    Out = unity_InstanceID;
}

当在Shader Graph中使用InstanceID节点时,这个内置变量会被自动包含在生成的着色器代码中。Unity的渲染管线负责在实例化绘制调用时正确设置这个值。

InstanceID节点的端口配置

InstanceID节点的端口配置相对简单,只有一个输出端口:

输出端口(Out)提供当前实例的ID值,数据类型为浮点数(Float)。这个端口不需要任何绑定,因为它的值是由渲染管线自动提供的。

虽然输出类型显示为浮点数,但实际使用时,实例ID通常是整数值。在Shader Graph中,我们可以通过适当的节点将其转换为整数,或者直接作为浮点数使用,具体取决于应用场景。

InstanceID节点的应用场景

InstanceID节点在Shader Graph中有多种应用场景,以下是几个常见的用例:

  • 实例差异化:通过Instance ID为每个实例生成不同的颜色、大小或外观,即使它们使用相同的网格和材质。例如,在渲染一片森林时,可以使用Instance ID为每棵树生成略微不同的颜色和大小,增加场景的自然感。
  • 程序化动画:利用Instance ID为每个实例创建不同的动画效果。例如,在渲染一群鱼时,可以使用Instance ID控制每条鱼的游动相位,使鱼群运动更加自然。
  • 数据索引:使用Instance ID作为索引,从纹理或数组中查找对应的数据。这在需要为每个实例应用不同贴图或参数时特别有用。
  • 随机值生成:将Instance ID作为随机数生成的种子,为每个实例创建可预测的随机值。这种方法确保同一实例在不同帧中保持一致的随机行为。
  • 调试和可视化:在开发过程中,使用Instance ID可视化不同的实例,帮助调试实例化相关的问题。

使用InstanceID节点的注意事项

在使用InstanceID节点时,有几个重要事项需要注意:

  • 实例化启用条件:InstanceID节点只有在真正使用GPU实例化时才会返回有意义的非零值。确保材质和渲染设置正确启用了GPU实例化。
  • 动态实例化的不稳定性:当使用动态实例化时,实例ID在不同帧之间可能不一致。避免依赖实例ID的持久性进行跨帧的状态管理。
  • 性能考虑:虽然访问InstanceID本身开销很小,但基于它的复杂计算可能会影响性能。在移动平台等性能受限的环境中要特别小心。
  • 最大值限制:实例ID的取值范围受限于绘制调用中的实例数量。在极少数情况下,如果实例数量超过一定限制,可能需要考虑替代方案。
  • 与非实例化渲染的兼容性:在非实例化渲染情况下,InstanceID始终返回0。确保着色器在这种情况下也能正确工作。

InstanceID节点与其他节点的配合使用

InstanceID节点通常需要与其他Shader Graph节点配合使用才能发挥最大效用:

与数学节点结合,可以通过简单的运算将Instance ID转换为更有用的值范围。例如,使用取模运算将Instance ID限制在特定范围内。

与纹理节点配合,可以使用Instance ID作为UV坐标或纹理数组的索引,实现每个实例使用不同纹理的效果。

与随机节点结合,可以将Instance ID作为随机种子,生成每个实例特有的随机值,同时保证这些值在帧间保持一致。

与时间节点配合,可以创建基于Instance ID的相位偏移动画,使多个实例的动画效果错开,增加视觉多样性。

实际示例:创建实例化颜色变化效果

下面通过一个具体示例演示如何使用InstanceID节点为实例化对象创建颜色变化效果:

首先在Shader Graph中创建InstanceID节点,然后将其连接到Color节点的各个通道。通过适当的数学运算,可以将Instance ID映射到不同的颜色范围。

一个常见的做法是使用正弦函数基于Instance ID生成平滑的颜色变化:

HLSL

// 伪代码示例
float hue = (InstanceID * 0.1) % 1.0;
float3 color = HSLtoRGB(hue, 1.0, 0.5);

在Shader Graph中,可以通过以下节点连接实现类似效果:

  1. 将InstanceID节点连接到Multiply节点,乘以一个小数(如0.1)控制颜色变化速率
  2. 将结果连接到Fraction节点,取小数部分确保值在0-1范围内
  3. 使用这个值作为HDR Color节点的Hue输入

这种方法可以为每个实例生成独特但协调的颜色,非常适合用于创建大规模的对象群组,如草地、人群或星空。

实际示例:实例化动画偏移

另一个实用示例是使用InstanceID节点为实例化对象创建动画偏移:

假设我们有一组使用相同动画的实例,但希望它们的动画相位不同,避免所有实例完全同步运动。可以通过以下步骤实现:

将InstanceID节点连接到Multiply节点,乘以一个控制相位间隔的值。然后将结果添加到时间变量上,作为每个实例的个性化时间输入。

在Shader Graph中的具体实现:

  1. 创建Time节点获取着色器时间
  2. 创建InstanceID节点获取实例标识
  3. 使用Multiply节点将InstanceID乘以一个小的相位值(如0.2)
  4. 使用Add节点将时间与相位偏移相加
  5. 将这个个性化时间用于驱动动画计算

这种方法可以创建更加自然和有机的动画效果,特别适用于群体动画,如鸟群、鱼群或摇摆的植物。

性能优化与最佳实践

为了确保使用InstanceID节点的着色器具有良好的性能,可以遵循以下最佳实践:

  • 尽量减少基于InstanceID的复杂计算,特别是在片段着色器中
  • 尽可能在顶点着色阶段完成基于InstanceID的计算,而不是在片段着色阶段
  • 使用适当的精度修饰符,在保证质量的同时减少计算开销
  • 在移动平台上,测试使用InstanceID的着色器性能,确保不会造成帧率下降
  • 考虑使用其他实例化技术,如GPU实例化属性块,对于大量不同的实例数据

常见问题与解决方案

在使用InstanceID节点时,可能会遇到一些常见问题:

如果InstanceID始终返回0,首先确认是否真正启用了GPU实例化。检查材质的Inspector窗口,确保"Enable GPU Instancing"选项已勾选。同时确认是通过Graphics.DrawMeshInstanced或类似的实例化API进行渲染。

如果实例化对象的颜色或行为不符合预期,检查基于InstanceID的计算是否正确。特别注意数值范围和精度问题,确保计算不会导致意外的截断或溢出。

当遇到性能问题时,使用Unity的Frame Debugger分析绘制调用和实例化情况。确认实例化确实按预期工作,并且没有意外的批处理中断。


【Unity Shader Graph 使用与特效实现】专栏-直达 (欢迎点赞留言探讨,更多人加入进来能更加完善这个探索的过程,🙏)

❌
❌