Monogame:缩放3D网格时实现纹理重复的方法求助
问题:3D网格缩放时纹理拉伸,需实现重复/拉伸模式切换
我正在开发一款以3D网格构建游戏世界的第一人称射击游戏,网格的每个面(前、后、左、右、上、下)均可设置纹理,当前核心实现代码如下:
public Block() { vertexData = new List<VertexPositionNormalTexture>(); //front vertexData.Add(new VertexPositionNormalTexture(new Vector3(-0.5f, -0.5f, 0.5f), new Vector3(0, 0, 1), new Vector2(0.0f, 1.0f))); vertexData.Add(new VertexPositionNormalTexture(new Vector3(-0.5f, 0.5f, 0.5f), new Vector3(0, 0, 1), new Vector2(0.0f, 0.0f))); vertexData.Add(new VertexPositionNormalTexture(new Vector3(0.5f, -0.5f, 0.5f), new Vector3(0, 0, 1), new Vector2(1.0f, 1.0f))); vertexData.Add(new VertexPositionNormalTexture(new Vector3(0.5f, -0.5f, 0.5f), new Vector3(0, 0, 1), new Vector2(1.0f, 1.0f))); vertexData.Add(new VertexPositionNormalTexture(new Vector3(-0.5f, 0.5f, 0.5f), new Vector3(0, 0, 1), new Vector2(0.0f, 0.0f))); vertexData.Add(new VertexPositionNormalTexture(new Vector3(0.5f, 0.5f, 0.5f), new Vector3(0, 0, 1), new Vector2(1.0f, 0.0f))); SetUp(TexType.front); vertexData = new List<VertexPositionNormalTexture>(); //right vertexData.Add(new VertexPositionNormalTexture(new Vector3(0.5f, -0.5f, 0.5f), new Vector3(1, 0, 0), new Vector2(0.0f, 1.0f))); vertexData.Add(new VertexPositionNormalTexture(new Vector3(0.5f, 0.5f, 0.5f), new Vector3(1, 0, 0), new Vector2(0.0f, 0.0f))); vertexData.Add(new VertexPositionNormalTexture(new Vector3(0.5f, -0.5f, -0.5f), new Vector3(1, 0, 0), new Vector2(1.0f, 1.0f))); vertexData.Add(new VertexPositionNormalTexture(new Vector3(0.5f, -0.5f, -0.5f), new Vector3(1, 0, 0), new Vector2(1.0f, 1.0f))); vertexData.Add(new VertexPositionNormalTexture(new Vector3(0.5f, 0.5f, 0.5f), new Vector3(1, 0, 0), new Vector2(0.0f, 0.0f))); vertexData.Add(new VertexPositionNormalTexture(new Vector3(0.5f, 0.5f, -0.5f), new Vector3(1, 0, 0), new Vector2(1.0f, 0.0f))); SetUp(TexType.right); vertexData = new List<VertexPositionNormalTexture>(); //and so on for the remaining faces... } public void SetUp(TexType texType) { vertexBuffer = new VertexBuffer(Basic.gDevice, typeof(VertexPositionNormalTexture), vertexData.Count, BufferUsage.WriteOnly); vertexBuffer.SetData(vertexData.ToArray()); buffers.Add(new Tuple<VertexBuffer, TexType>(vertexBuffer, texType)); } public void Draw(Matrix world) { foreach (var buffer in buffers) { string texName = textures[buffer.Item2.ToString()]; GameScreen.effect.Texture = Textures.textures[texName]; SetEffectsAndDraw(world, buffer.Item1, buffer.Item1.VertexCount); } } private void SetEffectsAndDraw(Matrix world, VertexBuffer buffer, int primitiveCount) { GameScreen.effect.View = GameScreen.camera.view; GameScreen.effect.Projection = GameScreen.camera.projection; GameScreen.effect.EnableDefaultLighting(); GameScreen.effect.World = world; GameScreen.effect.FogEnabled = true; GameScreen.effect.FogColor = Color.Black.ToVector3(); GameScreen.effect.FogStart = 4.0f; GameScreen.effect.FogEnd = 12.0f; //GameScreen.effect.TextureEnabled = true; GameScreen.effect.CurrentTechnique.Passes[0].Apply(); Basic.gDevice.SetVertexBuffer(buffer); Basic.gDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, primitiveCount / 3); }
目前可对网格进行各轴方向的缩放,但缩放后网格表面的纹理会被拉伸(例如scale.Y设为10时效果极差)。希望实现网格缩放时纹理在对应面上重复显示,同时支持在拉伸与重复纹理模式间切换。尝试过以下设置SamplerState的代码但无效果:
sampler = new SamplerState(); sampler.Filter = TextureFilter.Point; sampler.AddressU = TextureAddressMode.Wrap; sampler.AddressV = TextureAddressMode.Wrap; gDevice.SamplerStates[0] = sampler;
核心问题分析
直接设置设备的SamplerStates无效,是因为Effect的Pass.Apply()会重置设备采样器状态到自身默认值,覆盖了你手动设置的状态。此外,当前顶点纹理坐标固定为0-1,缩放后自然会拉伸,需要根据缩放比例调整纹理坐标范围。
解决方案步骤
1. 添加纹理模式控制字段
为Block类添加枚举和字段,用于切换纹理显示模式:
public enum TextureWrapMode { Stretch, Repeat } public TextureWrapMode CurrentWrapMode { get; set; } = TextureWrapMode.Repeat; public Vector3 Scale { get; set; } = Vector3.One;
2. 重构顶点数据生成逻辑
把构造函数中的顶点生成代码抽成独立方法,根据当前Scale调整纹理坐标,确保缩放后纹理能重复:
public void GenerateVertexData() { buffers.Clear(); vertexData = new List<VertexPositionNormalTexture>(); // 前面:X轴对应纹理U,Y轴对应纹理V vertexData.Add(new VertexPositionNormalTexture(new Vector3(-0.5f, -0.5f, 0.5f), new Vector3(0, 0, 1), new Vector2(0.0f, Scale.Y))); vertexData.Add(new VertexPositionNormalTexture(new Vector3(-0.5f, 0.5f, 0.5f), new Vector3(0, 0, 1), new Vector2(0.0f, 0.0f))); vertexData.Add(new VertexPositionNormalTexture(new Vector3(0.5f, -0.5f, 0.5f), new Vector3(0, 0, 1), new Vector2(Scale.X, Scale.Y))); vertexData.Add(new VertexPositionNormalTexture(new Vector3(0.5f, -0.5f, 0.5f), new Vector3(0, 0, 1), new Vector2(Scale.X, Scale.Y))); vertexData.Add(new VertexPositionNormalTexture(new Vector3(-0.5f, 0.5f, 0.5f), new Vector3(0, 0, 1), new Vector2(0.0f, 0.0f))); vertexData.Add(new VertexPositionNormalTexture(new Vector3(0.5f, 0.5f, 0.5f), new Vector3(0, 0, 1), new Vector2(Scale.X, 0.0f))); SetUp(TexType.front); vertexData.Clear(); // 右面:Z轴对应纹理U,Y轴对应纹理V vertexData.Add(new VertexPositionNormalTexture(new Vector3(0.5f, -0.5f, 0.5f), new Vector3(1, 0, 0), new Vector2(0.0f, Scale.Y))); vertexData.Add(new VertexPositionNormalTexture(new Vector3(0.5f, 0.5f, 0.5f), new Vector3(1, 0, 0), new Vector2(0.0f, 0.0f))); vertexData.Add(new VertexPositionNormalTexture(new Vector3(0.5f, -0.5f, -0.5f), new Vector3(1, 0, 0), new Vector2(Scale.Z, Scale.Y))); vertexData.Add(new VertexPositionNormalTexture(new Vector3(0.5f, -0.5f, -0.5f), new Vector3(1, 0, 0), new Vector2(Scale.Z, Scale.Y))); vertexData.Add(new VertexPositionNormalTexture(new Vector3(0.5f, 0.5f, 0.5f), new Vector3(1, 0, 0), new Vector2(0.0f, 0.0f))); vertexData.Add(new VertexPositionNormalTexture(new Vector3(0.5f, 0.5f, -0.5f), new Vector3(1, 0, 0), new Vector2(Scale.Z, 0.0f))); SetUp(TexType.right); vertexData.Clear(); // 剩余面(后、左、上、下)同理,根据面朝向对应缩放轴调整纹理坐标 // ... 补充剩余面的代码 }
3. 修改Effect采样器状态设置
在SetEffectsAndDraw方法中,在Apply()之前设置Effect的采样器状态,避免被覆盖:
private void SetEffectsAndDraw(Matrix world, VertexBuffer buffer, int primitiveCount) { GameScreen.effect.View = GameScreen.camera.view; GameScreen.effect.Projection = GameScreen.camera.projection; GameScreen.effect.EnableDefaultLighting(); GameScreen.effect.World = world; GameScreen.effect.FogEnabled = true; GameScreen.effect.FogColor = Color.Black.ToVector3(); GameScreen.effect.FogStart = 4.0f; GameScreen.effect.FogEnd = 12.0f; // 必须启用纹理才能显示 GameScreen.effect.TextureEnabled = true; // 根据当前模式设置采样器状态 if (CurrentWrapMode == TextureWrapMode.Repeat) { // 线性过滤+重复模式,用PointWrap则是像素化过滤 GameScreen.effect.SamplerStates[0] = SamplerState.LinearWrap; } else { // 线性过滤+拉伸模式,用PointClamp则是像素化过滤 GameScreen.effect.SamplerStates[0] = SamplerState.LinearClamp; } GameScreen.effect.CurrentTechnique.Passes[0].Apply(); Basic.gDevice.SetVertexBuffer(buffer); Basic.gDevice.DrawPrimitives(PrimitiveType.TriangleList, 0, primitiveCount / 3); }
4. 使用方式
- 修改Block缩放后,调用
GenerateVertexData()重新生成顶点缓冲:block.Scale = new Vector3(1, 10, 1); block.GenerateVertexData(); - 切换纹理模式:
// 切换到重复模式 block.CurrentWrapMode = TextureWrapMode.Repeat; // 切换到拉伸模式 block.CurrentWrapMode = TextureWrapMode.Stretch;
内容的提问来源于stack exchange,提问作者Canox
相关产品推荐
相关产品推荐

