TEXTURE_3D
撰写时间:2026-05-16
修订时间:2026-05-16
TEXTURE_3D (3D纹理)在图形编程中主要用于需要在三维空间内访问体数据 (volumn data) 的场合,其典型应用场合诸如体积渲染、流体烟雾模拟、动画变形及蒙皮等纹理插值,等等。
这些应用都依赖于TEXTURE_3D的两大特性:随机访问任意空间位置和GPU硬件三线性插值,使得体数据处理高效且代码简洁。
因此,TEXTURE_3D典型应用已经超出纹理贴图的常规范畴。本章中,我们将实现最基本的TEXTURE_3D应用。
着色器
顶点着色器
源代码
aTexCoords的数据类型为vec3。即对于每个顶点的纹理坐标值,我们将传入诸如:
的数据。
关于纹理坐标系的字母缩写
在早期的图形学中,纹理坐标常用(u, v)来表示。其中u代表纹理坐标系中的X轴,v代表纹理坐标系中的Y轴。
后面为支持3维纹理坐标系,又使用(u, v, w)来表示纹理坐标值。至今一些应用软件的界面中仍可看到这种表示方式。
而齐次坐标系中的顶点坐标一般使用(x, y, z, w)来表示其坐标值。可以看出,w在两种坐标系中都出现了,容易引起混淆。
为避免混淆,OpenGL, WebGL均在纹理坐标系中引入了(s, t, r, q)的表示方法。故此,在下面,我们将看到如下代码:
即分别设置纹理坐标系的S轴, T轴及R轴的重复贴图方式。
片断着色器
源代码
代码解析
代码:
对于类型为sampler3D的通用顶点属性,我们必须显式地指定其精度,否则将编译出错。
也可以统一设置:
之前我们在片断着色器中判断是否使用纹理坐标的代码为:
而代码:
可将相应代码浓缩为一行。GLSL的mix常用于生成中间的线性插值。若uIsUseTexture的值为false,则float(uIsUseTexture)的值为0.0,则mix函数返回vColor;否则,mix函数返回texture函数的采样值。
调试技巧
要让一个纹理应用程序正确地运行起来,主要涉及两大方面,一是纹理图像内容的正确设置,二是纹理坐标的正确设置。两者必须同时准确无误才行。而3D纹理应用的调试又比2D纹理应用增加了额外的难度。
我们可以采用分而治之
的策略。例如,下面的片断着色器的代码先使用一个固定的纹理坐标值:
这样可将重心放在纹理图像内容的正确设置上面。而当应用程序运行起来,确认纹理图像内容已没有任何问题后,再恢复为:
从而将重心转向纹理坐标的设置上面。
客户端主程序
其主要代码如下所示。
对于vertices的每一个顶点,都向它们分别指定一组三维的纹理坐标值。
为易于理解,我们可将三维纹理图像视为具有多层的二维纹理图像。上面tex3DCoords每个纹理坐标值(s, t, r)分量中,r的分量均为0.0,表示这是第0层的二维纹理图像的纹理坐标值。而一个2维纹理坐标系如下所示:
则tex3DCoords代码中的每个纹理坐标位置从上到下,分别映射至四边形的左上角、左下角、右下角及右上角。
运行应用。
因为只有一层的二维纹理图像,因此其效果与前面的二维纹理贴图完全一样。
而不同的是,这次我们的代码是完全基于TEXTURE_3D的纹理应用程序。我们将其降级为二维纹理贴图,先让应用程序跑起来,从而掌握其基本规律及原理后,下面我们再实现多层的三维纹理贴图。
这其中的理念类似于如何理解现实世界中的四维世界。对于已经牢牢被三维世界所束缚的我们,很难一下子理解何为四维世界。但我们可以先降级为一维坐标系,找出一维坐标系如何升格为二维坐标系的规律;再找出二维坐标系如何升格为三维坐标系的规律。最后应用类似的规律将三维坐标系升格为四维坐标系。
Texture3DMesh的实现
构造器
Texture3DMesh继承于SoleColorMesh,先将其抽象为一个单色的Mesh,而又能充分利用应用框架对Mesh类的各种支持。
initTexVAO方法
WebGLUtils-v12.js中增加了对TEXTURE_3D的纹理支持:
并且loadShader函数中可自动感知sampler3D的采样器:
initTexture方法
gl的texImage3D方法是WebGL 2.0才新增的方法,用于为三维纹理图像填充内容。它有多个版本,这里使用类型化数组的版本,并且最后一个参数srcOffset可用以灵活、自由地指定类型化数组中的起始位置。从而让我们更充分地理解texImage3D各参数的具体含义。
例如,对于srcData:
这是一个具有4个颜色值的类型数组,每4个数组元素代表一个颜色值。
如果我们希望将纹理图像的内容初始化为1 × 1 × 1的内容时,无需改动srcData的值:
上面使用了C0
颜色值作为仅有的像素颜色值。同理,将pixelIndex的值改为(0, 3)之间的任意值,可使用其它对应的颜色值。
而原来的代码为:
则要求使用2行、2列(只有1层)的纹理图像,此时必须完全使用srcData的所有数据,因此此时pixelIndex的值必须为0,否则将抛出:
的异常,意为,srcData的容量不够大。
以后当需要使用2层的纹理图像数据时:
通过这种方式,可以增加代码的灵活性与多样性。
renderTexture方法
激活第0个纹理单元,将该纹理单元内的TEXTURE_3D绑定至上节所创建的纹理对象,渲染图像。
运行应用。
