当前位置: 首页 > news >正文

网格纹理采样算法

我有一个二维纹理数组,需要将它渲染到屏幕上,屏幕上的每个像素来自于数组中不同下标的纹理的对应位置的像素。举个例子,纹理坐标 (0.2,0.3)(0.2, 0.3)(0.2,0.3) 处的像素,它对应的纹理数组的下标是 333,那么就去第333幅纹理里采样 (0.2,0.3)(0.2,0.3)(0.2,0.3)这个纹理坐标的像素。

为了保存屏幕上每个像素(准确来说是子像素)来自纹理数组的哪张纹理,我们将每个像素对应的纹理数组下标也保存到一张纹理里面,暂且叫它索引纹理吧,这样我们只需要对索引纹理进行采样,就能得到每个子像素对应的纹理数组下标了。

虽然 Unity Shader 支持纹理数组(怎么用最后再说),但是我的纹理数组是从相机渲染得到的,我尝试了很多办法,都无法从相机直接渲染到纹理数组,总是只能渲染到纹理数组的第一张纹理上,不知道是真的不行还是我没找到方法。所以我现在只能将纹理数组都渲染到一张大纹理上,相当于把一位数组变成了二维数组。

现在有了网格纹理,也有了索引纹理,只需要在 shader 的片元着色器中完成采样就可以了。思路也很简单,先确定当前像素来自第几幅图,然后将纹理坐标重定位到该子图内,最后用重定位的纹理坐标对网格纹理采样就可以了。首先是纹理坐标重定位,这个过程其实很简单,假设网格纹理有 RRRCCC 列。

  • 首先计算纹理坐标在子图内的坐标。假设网格纹理的大小是 W×HW\times HW×H,则每个子图的大小只有 WC×HR\frac{W}{C}\times\frac{H}{R}CW×RH,相当于 宽高分别缩小了 CCCRRR 倍,因此将纹理坐标 uvuvuv 也分别缩小 CCCRRR 倍就是纹理坐标在子图中的位置。
  • 然后要计算子图在网格纹理中的偏移量,再加上第一步得到的经过缩放的纹理坐标,就是网格纹理的采样坐标。由于纹理坐标是归一化到 0→10\rightarrow 101 的,所以这一步的关键是计算子图在宫格纹理里是第几行,第几列。假设要采样第 iiijjj 列的子图,那么纹理坐标偏移就是 (jC,iR)(\frac{j}{C},\frac{i}{R})(Cj,Ri)

所以现在的问题是计算子图在第几行第几列。我们将纹理数组的下标存到了索引纹理中,但是 shader 中的图像像素也是归一化的,因此在生成索引纹理时,我们就已经对下标进行了归一化,也就是索引纹理里面存放的其实是 i数组长度\frac{i}{数组长度}数组长度i。所以我们用索引纹理的采样结果乘以数组长度(R×CR\times CR×C)就能得到真正的下标了,接下来就是一维数组下标转二维数组下标的问题了,这是个很简单的问题。

float2 redirectUV(float3 uv)
{int count = R * C;int index = floor(uv.z * count)int x = index % C;int y = index / C;return float2((x+uv.x)/C, (y+uv.y)/R)
}float3 rgb = tex2D(_IndexTex, uv)
float2 uv_r = redirectUV(float3(uv, rgb.r));
float2 uv_g = redirectUV(float3(uv, rgb.g));
float2 uv_b = redirectUV(float3(uv, rgb.b));
// sample grid texture

上面我们为了计算子图的横纵坐标,需要先计算子图总数,然后得到子图下标,再转化成行列坐标。那么有没有办法不计算子图总数,也不计算下标,直接得到行列坐标呢?

有的,兄弟!有的!

我们可以换个思路想这个问题,假设有一根长度为 111 的线段,我们先将他分成 RRR 大段,然后再将每一大段分成 CCC 小段,那么其实我们要求解的问题变成了这个归一化的索引下标落在哪一大段,哪一小段。

我们将归一化的索引乘以 RRR,它的整数部分就是行数;而它的小数部分乘以 CCC 就是列数,在 shader 里我们可以使用 modf 函数来拆分浮点数。

float2 redirectUV(float3 uv)
{float2 ov;ov.x = floor(modf(uv.z * R, ov.y) * C);return (ov + uv.xy) / float2(C, R);	
}float3 rgb = tex2D(_IndexTex, uv)
float2 uv_r = redirectUV(float3(uv, rgb.r));
float2 uv_g = redirectUV(float3(uv, rgb.g));
float2 uv_b = redirectUV(float3(uv, rgb.b));
// sample grid texture

这样只用一行代码就能把行列坐标同时计算出来了,这个公式可以从前面的方法中推导出来,感兴趣的朋友可以试一试。

unity shader 中使用纹理数组

最后说一说怎么在 unity shader 中使用纹理数组。

首先声明纹理的时候要使用 2DArray 类型。

Properties
{_MainTex ("Tex", 2DArray) = "" {}
}

然后是在 Pass 里面声明采样器,普通2D纹理通常是:

sampler2D _MainTex;
float4 _MainTex_ST;

而纹理数组使用的是:

UNITY_DECLARE_TEX2DARRAY(_MyArr);

最后是纹理采样,需要使用下面的函数:

UNITY_SAMPLE_TEX2DARRAY(_MyArr, i.uv);

注意这里的 uvfloat3z 分量就是纹理数组的下标,这里不是归一化下标,是真实下标。

在 C# 中创建 RenderTexture 纹理数组需要在纹理真正创建前设置下面的属性:

renderTexture.dimension = UnityEngine.Rendering.TextureDimension.Tex2DArray;
renderTexture.volumeDepth = arrayLength;
http://www.dtcms.com/a/358096.html

相关文章:

  • U盘/移动硬盘里可清理的那些跨系统遗留文件
  • 使用JAVA制作minecraft红石和创造模式插件
  • 理解JVM
  • 蓝牙5.3核心技术架构解析:从控制器到主机的无线通信设计
  • 广东省省考备考(第九十天8.30)——判断推理(强化训练)
  • 项目管理的五个阶段是什么
  • 在线简历生成工具,免费好用
  • 【MLLM】从BLIP3o到BLIP3o-NEXT:统一生成与理解
  • 【Docker】Docker初识
  • AI工具营销落地方案:工业产品营销
  • python pyqt5开发DoIP上位机【诊断回复的函数都是怎么调用的?】
  • 计算机毕设 java 阿歹果园养鸡场管理系统 基于 SSM 框架的果园养鸡场全流程管理系统设计与实现 Java+MySQL 的养殖生产与进销存一体化平台开发
  • SQLSugar 封装原理详解:从架构到核心模块的底层实现
  • uniapp解析富文本,视频无法显示问题
  • HTTP的概念、原理、工作机制、数据格式和REST
  • 卫星信号和无线信号的设备厂商
  • Linux(1)|入门的开始:Linux基本指令
  • 【C++】C++ const成员函数与取地址操作符重载
  • 数据结构 03(线性:单链表)
  • 强化学习-CH6 随机近似
  • Ansible 核心运维场景落地:YUM 仓库、SSH 公钥、固定 IP 配置技巧
  • 2025年9月计算机二级C++语言程序设计——选择题打卡Day11
  • 如何取得专案/设计/设定/物件的属性
  • mysql权限user表赋权操作修改
  • 《潮汐调和分析原理和应用》之四S_Tide使用2
  • 整体设计 的语言设计:通用模型语言的标准模板语言的规格模式语言(搜狗汪仔答问)
  • 【计算机网络】生产问题排查:如何使用Wireshark抓包/读取抓包文件进行网络分析
  • 使用AI大模型Seed1.5-VL精准识别开车接打电话等交通违法行为
  • TensorFlow深度学习实战(35)——概率神经网络
  • k8s(自写)