在三角形上添加纹理
来源:互联网 发布:负载均衡网络拓扑图 编辑:程序博客网 时间:2024/06/06 03:43
5.2 在三角形上添加纹理
问题
你想绘制一个颜色漂亮的三角形并可以完全控制三角形上的颜色。
解决方案
你的显卡允许你指定一张图像,从这张图像可以采样想要的颜色。
这意味着你需要将一张2D图像导入到XNA项目中,并在绘制三角形之前将它传递到显卡。对每个顶点,你要指定2D图像的哪个位置对应顶点。
工作原理
首先将一张2D图像导入到项目中,如教程3-1所示。在LoadContent方法中将它链接到一个变量:
myTexture = content.Load<Texture2D>("texture");
然后你要定义三角形的顶点。如前面的教程所述,你需要指定每个顶点的3D位置。这次你无需定义颜色而是定义在2D纹理中的对应位置,这一步需要在每个顶点中存储2D纹理坐标。如果三角形的每个顶点对应一个2D纹理坐标,显卡就会将你指定的2D图像的某一部分填充到三角形内部。
当指定纹理坐标时,要记住纹理的左上为坐标(0,0),右上为(1,0)。这意味着第二个坐标值表示垂直位置,所以右下坐标为(1,1)。图5-4显示了对应不同纹理坐标的纹理区域。
图5-4 为顶点指定纹理坐标
你需要存储一个2D纹理坐标而不是每个顶点的颜色。不是存储一个包含VertexPositionColor元素的数组,而是创建一个包含VertexPositionTexture元素的数组,如这个名称所述,它将存储纹理坐标和一个3D位置:
private void InitVertices() { vertices = new VertexPositionTexture[9]; int i = 0; vertices[i++] = new VertexPositionTexture(new Vector3(-10, 1, -5), new Vector2(0,0)); vertices[i++] = new VertexPositionTexture(new Vector3(-7, 5, -5), new Vector2(0.5f, 1)); vertices[i++] = new VertexPositionTexture(new Vector3(-4, 1, -5), new Vector2(1,0)); vertices[i++] = new VertexPositionTexture(new Vector3(-3, 1, -5), new Vector2(0, 0.5f)); vertices[i++] = new VertexPositionTexture(new Vector3(0, 5, -5), new Vector2(1, 0)); vertices[i++] = new VertexPositionTexture(new Vector3(3, 1, -5),new Vector2(1, 1)); vertices[i++] = new VertexPositionTexture(new Vector3(4, 1, -5), new Vector2(0.25f, 0.5f)); vertices[i++] = new VertexPositionTexture(new Vector3(7, 5, -5), new Vector2(0.5f, 0)); vertices[i++] = new VertexPositionTexture(new Vector3(10, 1, -5), new Vector2(1, 1)); myVertexDeclaration=new VertexDeclaration(device, VertexPositionTexture.VertexElements); }
这里定义的九个顶点拥有对应图5-4中的三个三角形的纹理坐标。通过指定纹理坐标,你可以显示如图5-4所示的三角形。
注意: 三角形在3D空间中的实际位置定义为一个Vector3。例如,图5-4中的第一个三角形是倒置的,因为两个顶点的Y坐标是1,中间一个顶点的Y坐标为5,这使得三角形中间的字母XNA也是颠倒绘制的(译者注:图5-4中的第一个三角形中的XNA并没有颠倒,但在源代码中是颠倒的,怀疑作者截图有误)。
你还需要告知显卡顶点现在包含不同的信息。所以需要基于VertexPositionTexture的VertexElement创建一个VertexDeclaration,这样可以在绘制三角形前将它传递到显卡。
定义了顶点后,你就做好了绘制三个三角形的准备:
device.RenderState.CullMode = CullMode.None; basicEffect.World = Matrix.Identity;basicEffect.View = fpsCam.ViewMatrix; basicEffect.Projection = fpsCam.ProjectionMatrix; basicEffect.Texture = myTexture; basicEffect.TextureEnabled = true; basicEffect.Begin(); foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes) { pass.Begin(); device.VertexDeclaration=myVertexDeclaration; device.DrawUserPrimitives<VertexPositionTexture>(PrimitiveType.TriangleList, vertices, 0, 3); pass.End(); }basicEffect.End();
你需要将纹理传递到显卡,这一步需要将这个纹理设置为BasicEffect的当前纹理。然后,不像前一个教程那样使用定义在顶点中的颜色,本教程通过将 TextureEnabled设置为true指定BasicEffect从纹理采样颜色。因为你使用了一个不同的顶点格式,所以需要将新的VertexDeclaration传递到显卡。
最后绘制三个三角形。
纹理寻址模式
如前所述,纹理坐标(0,0)对应纹理的左上点的像素,(1,1)对应右下点。
但这并没有限定纹理坐标的值必须在[0,1]范围内,例如设定为(1.5f, –0.5f)也可以。在这种情况中,你需要通过设置U和V(U是第一个纹理坐标,V是第二个)的纹理模式让XNA知道如何处理这样的坐标。下面的代码定义了一个U和V坐标超过[0,1]范围的三角形:
private void InitVertices() { vertices = new VertexPositionTexture[3]; int i = 0; vertices[i++] = new VertexPositionTexture(new Vector3(-3, -3, -1), new Vector2(-0.5f,1.5f)); vertices[i++] = new VertexPositionTexture(new Vector3(0, 5, -1), new Vector2(0.5f, -1.5f)); vertices[i++] = new VertexPositionTexture(new Vector3(3, -3, -1), new Vector2(1.5f,1.5f)); myVertexDeclaration=new VertexDeclaration(device, VertexPositionTexture.VertexElements); }
然后你要通过设置纹理采样器的寻址模式让XNA知道如何处理这些坐标,这用来从纹理采样颜色。
TextureAddressMode.Clamp
使用下列代码将UV模式设置为Clamp:
device.SamplerStates[0].AddressU = TextureAddressMode.Clamp; device.SamplerStates[0].AddressV = TextureAddressMode.Clamp;
这个寻址模式让所有的纹理坐标截取到[0,1]范围内,所有小于0的坐标截取到0,大于1的截取到1。
结果是,超出[0,1] 范围的纹理坐标的所有像素的颜色为纹理边缘的颜色,如图5-5所示。图中显示的三角形是前面已经定义的。
下面是显卡在从图像采样颜色前首先映射的纹理坐标的例子:
- (-0.5f, 1.5f) → (0, 1)
- (0.5f, -1.5f) → (0.5f, 0)
- (1.5f, 1.5f) → (1,1)
图5-5 Clamp (左)和Wrap (右)纹理寻址模式
TextureAddressMode.Wrap
这是默认的纹理寻址模式,在使用了一个不同的模式后你可以使用下列代码重新选择使用wrap模式:
device.SamplerStates[0].AddressU = TextureAddressMode.Wrap; device.SamplerStates[0].AddressV = TextureAddressMode.Wrap;
使用这个模式,显卡会从坐标加上或减去1直到坐标仍回到[0,1]范围。
这会导致原始纹理被复制,如图5-5右边所示。
下面是一些纹理坐标映射的例子:
- (-0.5f, 1.5f) → (0.5f, 0.5f)
- (1.2f, -0.2f) → (0.2f,0.8f)
- (-0.7f, -1.2f) → (0.3f,0.8f)
TextureAddressMode.Mirror
使用下列代码将纹理寻址模式设置为:
device.SamplerStates[0].AddressU = TextureAddressMode.Mirror; device.SamplerStates[0].AddressV = TextureAddressMode.Mirror;
这个模式导致原始纹理被复制,但与Wrapping模式不同的是,复制的图像与相邻的原始图像是互为镜像的。复制的图像在原始图像的上方和下方是垂直镜像的,左方和右方是水平映射的。对角线上的复制沿着两边,翻转180度,如图5-6左图所示。
图5-6 Mirror (左)和MirrorOne (右)纹理寻址模式
这个模式很有用,因为可以在放大图像避免边缘锐化。
下面是一些坐标映射的例子:
- (-0.5f, 1.5f) → (0.5f, 0.5f)
- (0.5f, -1.5f) → (0.5f, 0.5f)
- (1.2f, 1.7f) → (0.8f, 0.3f)
TextureAddressMode.MirrorOnce
你可以选择MirrorOnce模式:
device.SamplerStates[0].AddressU = TextureAddressMode.MirrorOnce; device.SamplerStates[0].AddressV = TextureAddressMode.MirrorOnce;
这个模式将[-1,1] 区域内的纹理坐标镜像到[0,1]区间,而所有超出[-1,1]区间的坐标会截取到–1 (小于–1的值)或1 (大于1的值),如图5-5右边所示。
下面是采样坐标的映射例子:
- (-0.5f, 1.5f) → (0.5f, 1)
- (0.5f, -1.5f) → (0.5f, -1)
- (1.2f, 1.7f) →(1, 1)
TextureAddressMode.Border
你可以给纹理坐标超出[0,1]区间的像素设置一个指定的颜色,这个颜色叫做 BorderColor,可以使用下列代码存储在SamplerState中:
device.SamplerStates[0].BorderColor = Color.LightSeaGreen; device.SamplerStates[0].AddressU = TextureAddressMode.Border; device.SamplerStates[0].AddressV = TextureAddressMode.Border;
当调试时这个模式很有用,因为它清晰地显示了所有超出[0,1]区间的纹理坐标,所有超出此区间的像素都使用BorderColor绘制。
当心:在XBox360平台上只能使用白色作为边界颜色。
代码
首先需要定义顶点,因为需要保护3D位置和对应的纹理位置,你需要使用VertexPositionTexture elements。别忘了初始化对应的VertexDeclaration。
private void InitVertices() { vertices = new VertexPositionTexture[3]; int i = 0; vertices[i++] = new VertexPositionTexture(new Vector3(-3, -3, -1), new Vector2(-0.5f, 1.5f)); vertices[i++] = new VertexPositionTexture(new Vector3(0, 5, -1), new Vector2(0.5f, -1.5f)); vertices[i++] = new VertexPositionTexture(new Vector3(3, -3, -1), new Vector2(1.5f, 1.5f)); myVertexDeclaration= new VertexDeclaration(device,VertexPositionTexture.VertexElements); }
有了存储在数组中的顶点和一个有效的VertexDeclaration,你可以设置一个纹理寻址模式并绘制三角形:
protected override void Draw(GameTime gameTime) { device.Clear(ClearOptions.Target | ClearOptions.DepthBuffer, Color.CornflowerBlue, 1, 0); //draw triangles device.RenderState.CullMode = CullMode.None; basicEffect.World = Matrix.Identity; basicEffect.View = fpsCam.ViewMatrix; basicEffect.Projection = fpsCam.ProjectionMatrix; basicEffect.Texture = myTexture; basicEffect.TextureEnabled = true; device.SamplerStates[0].AddressU=TextureAddressMode.Mirror; device.SamplerStates[0].AddressV = TextureAddressMode.Mirror; basicEffect.Begin(); foreach (EffectPass pass in basicEffect.CurrentTechnique.Passes) { pass.Begin(); device.VertexDeclaration = myVertexDeclaration; device.DrawUserPrimitives<VertexPositionTexture>(PrimitiveType.TriangleList, vertices, 0, 1); pass.End(); } basicEffect.End(); base.Draw(gameTime); }
- 在三角形上添加纹理
- 在纹理上写文本
- Android3D游戏开发之为三角形添加纹理
- Unity3D Mesh小课堂(二)为三角形添加纹理
- DirectShow在DirectX9 3D上的纹理播放
- 在BlackBerry上使用OpenGL绘图(十一):纹理数组
- 给立方体添加纹理
- Surface添加纹理失败:
- opengl中添加纹理
- Opengl 添加纹理
- osg 1 添加纹理
- Android OpenGL添加纹理
- osg 添加纹理
- 在对话框上添加视图
- 在对话框上添加状态栏
- 在图片上添加文字
- 在UIBarButtonItem上添加UISegmentedControl
- 在Dialog上添加菜单
- 随机数不是事!^~^
- 寒小阳
- 笔试 - 2014创新工场涂鸦移动校园招聘测试题 及 答案
- Eclipse不能识别手机或模拟器的解决方案!
- KMP的简单应用(SDUT 2772)
- 在三角形上添加纹理
- iOS开发- 蓝牙后台接收数据(BLE4.0)
- 自动分配IP地址让局域网更高效的管理--DHCP
- 【SSH2(实践篇)】--Struts2文件上传下载实例
- android 单位换算
- poj-2109 Power of Cryptography
- LeetCodeOJ--Reverse Words in a String
- 强烈推荐《深入浅出JavaScript》(beginning JavaScript with DOM Scripting and Ajax
- 忆往昔