作者:mydeman 文章来源:http://www.j2medev.com/Article/ShowArticle.asp?ArticleID=661
这篇主要讲述在移动3D图形API中如何使用纹理对象,并且会涉及到多重纹理和纹理变换的应用。本文中的源代码可以在文章的最后下载。
在例程中用到的主要对象是一个棱锥,下面的图片就是棱锥分别在没有应用纹理、应用一种纹理和应用两种纹理情况下的效果。
例程中用到的两种纹理图像如下:
在Java移动3D中用到的纹理图像的大小必须是2的非负指数幂,如(2,4,8,16,32,64,128,256)。
我们首先建立棱锥,并设置将要使用的纹理坐标。尽管棱锥只有五个点组成,但是为了能够使纹理坐标与棱锥顶点正确匹配,我们还是要为棱锥的每一个侧面都指定所需要的三个顶点。
纹理坐标的值必须在从0到1的范围内,但是在程序中我们使用0到255范围内的整数值指定了纹理的坐标,所以我们使用 1.0f / 255.0f 这个因数将这些坐标值等比例缩小。
纹理对象的左上角对应的纹理坐标是(0,0),右下角是(1, 1)。
我们在为棱锥指定纹理坐标时,一定要清楚纹理图像一直是一个正方形,所以正上方中间的纹理坐标应该为0.5,因此我们输入127,127*(1/255) ≈ 0.5。
一定要确保TEXTURES数组和POINTS数组相匹配。点(-1, -1, 1)是棱锥前侧面的左下角,而纹理坐标的左下角是(0,1)。请注意,棱锥的底面是由两个三角形组成。
short []POINTS = new short[] {
-1,-1, 1, 1,-1, 1, 0, 1, 0, //前侧面
1,-1,1, 1,-1,-1, 0, 1, 0, //有侧面
1,-1,-1, -1,-1,-1, 0, 1, 0, //后侧面
-1,-1,-1, -1,-1, 1, 0, 1, 0, //左侧面
-1,-1, 1, 1,-1, 1, 1,-1,-1, //右底面
-1,-1, 1, 1,-1,-1, -1,-1,-1}; //左底面
// 纹理坐标在setTexCoords
short []TEXTURES = new short[] {0,255, 255,255, 127,0,
0,255, 255,255, 127,0,
0,255, 255,255, 127,0,
0,255, 255,255, 127,0,
0,0, 255,0, 255,255,
0,0, 255,255, 0,255};
VertexArray TEXTURE_ARRAY;
TEXTURE_ARRAY = new VertexArray (TEXTURES. length / 2, 2, 2);
TEXTURE_ARRAY.set (0, TEXTURES. length / 2, TEXTURES);
// VertexBuffer保存了对VertexArray对象的引用,而这些VertexArray对象可能包// 含了一系列顶点的位置、法线、颜色和纹理信息。
VertexBuffer vertexBuffer = new VertexBuffer ();
为了使用多重纹理,在具有两个纹理图像的情况下,我们需要为每一个可用的纹理对象指定坐标。setTexCoords方法实现了纹理坐标的等比例缩小。
vertexBuffer.setTexCoords (0, TEXTURE_ARRAY, ( 1.0f / 255.0f ), null);
vertexBuffer.setTexCoords (1, TEXTURE_ARRAY, ( 1.0f / 255.0f ), null);
在创建mesh后,我们创建将要使用的纹理对象。
// 装载图片
Image texImg = Image.createImage (path);
// 利用上面的图片创建纹理对象
Texture2D texture = new Texture2D (new Image2D (Image2D.RGB, texImg)); // 在表面重复纹理
texture.setWrapping (Texture2D.WRAP_REPEAT, Texture2D.WRAP_REPEAT);
// 设置混合模式.
texture.setBlending (Texture2D.FUNC_DECAL);
//设置过滤模式
texture.setFiltering(Texture2D.FILTER_NEAREST, Texture2D.FILTER_NEAREST);
纹理对象创建后,我们就要把它们添加到mesh的外观对象中。如果要移除某个纹理对象,只需要设置相应索引上的对象为null。
// 添加第一个纹理对象
meshAppearance.setTexture(0, brickTexture);
// 添加第二个纹理对象
meshAppearance.setTexture(1, multiTexture);
//移除时
meshAppearance.setTexture(0, null);
meshAppearance.setTexture(1, null);
因为Texture2D继承自Transformable类,所以它可以轻而易举利用转换得到不同的效果。利用下面的方法,我们就可以实现旋转、移动和等比例变换:
postRotate (float x, float y, float z)
translate (float x, float y, float z)
scale (float x, float y, float z)
默认情况下,一个纹理位图必须完整地填充对象的整个表面,所以如果你想使用一个宽度为256的纹理图像对一个宽度为32的表面进行贴图,那么纹理的宽度就会收缩(译者注:也就是说会将整个图片的宽度缩为32,然后贴在本填充的对象表面上)。为了避免这种情况,你可以将纹理的宽度等比例缩小8倍(32 * 8 = 256),scale( 0.125f , 1.0f , 1.0f );现在,如果你希望显示同一个纹理的其他部分,就应该使用translate方法,translate( 0.5f , 1.0f , 1.0f )。
通过这篇文章,相信大家都已经对m3g中的纹理贴图有了一个大致的了解,下面我们再来回顾一下文中的重要内容。
纹理对象Texture2D,它是一个外观(Appearance)对象的一部分,包含了一个纹理图像和一系列的定义如何将纹理图像应用到子网眼对象表面上的属性,这些属性包括纹理图像的填充方式,过滤、混合和纹理的坐标转换。文中创建一个完整的纹理对象步骤为:
1、 创建一个图片对象,利用这个图片对象创建纹理对象。纹理图像是以Image2D对象的引用存储的。这个图像可以是Image2D中所规定的任何格式,需要注意的是,图像的宽度和高度都要为2的非负指数幂,但是二者并不一定相等。如果在程序运行中我们对Image2D对象进行了修改,那么立刻就会反映到纹理对象中,但是这个过程会产生很大的开支,例如重新生成纹理和重新分配内存等,因此在建立纹理对象以后最好不要修改Image2D对象。
2、 使用setWrapping方法设置纹理图像的重复模式。Texture2D中定义了两种纹理图片的重复模式:WRAP_CLAMP和WRAP_REPEAT,前者是指只重复一次,其实也就是没有重复,程序中画的纹理就是这种方式;后者是指无限重复,直到填充这个画面,程序中砖块的纹理就是这种方式。
3、 使用setBlending方法设置混合方式,混合是指过滤后的纹理颜色和引入的片段颜色的结合。Texture2D定义了五种混合方式:FUNC_ADD、FUNC_BLEND、FUNC_DECAL、FUNC_MODULATE和FUNC_REPLACE。程序中分别用到了FUNC_DECAL和FUNC_MODULATE。
4、 使用setFiltering方法设置过滤方式。Texture2D定义三种过滤方式:FILTER_BASE_LEVEL 、FILTER_LINEAR 和FILTER_NEAREST。请注意,这个设置仅仅是一个提示,应用程序在实现时可能忽略这个设置,根据自己的判断选择一种合适的方式。
文章的最后提到了纹理的变换,在例程中通过创建一个平面的对象并进行纹理贴图,在程序运行过程中使用了translate方法进行纹理图像的移动,从而产生了走马灯式的动态效果。下面我们看一下scale方法的使用。程序中的info.png纹理的宽高比为8/1,而创建的平面对象的宽高比也为8/1。下面我们更改组成平面对象的顶点数组为:
short[] POINTS = new short[] { -1, 0, 0, // bottom left point
1, 0, 0, // bottom right
1, 1, 0, // up right
-1, 1, 0 }; // up left
这时宽高比变为2:1,运行程序,就会得到如左下的效果,可以看到纹理图片被压缩了。接着我们使用scale设定缩放比例,在createPlane方法创建纹理的代码中添加scale(
0.25f
,
1.0f
,
1.0f
),再次运行就可以得到右下图所示的效果。
下载源码JSR184Texture