如何在SharpDX.Direct3D9中按指定像素尺寸缩放纹理?
问题分析
你的核心问题出在坐标系误解和未考虑纹理实际尺寸两个方面:
- Sprite默认使用归一化设备坐标系(范围-1到1),直接用「目标像素/屏幕分辨率」的比例计算缩放,没有完成像素坐标到该坐标系的正确映射,导致尺寸偏差。
- 加载纹理时使用
D3DX.DefaultNonPowerOf2,Direct3D会自动将非2的幂次尺寸的纹理调整为最近的2的幂次(比如120x120会被转为128x128),你没有基于纹理实际尺寸计算缩放比例,进一步放大了误差。
解决方案
推荐两种简单可靠的实现方式,优先选第一种:
方案1:直接用Sprite.Draw重载指定目标矩形(最省心)
不需要手动构建缩放矩阵,利用Sprite的内置重载直接传入像素坐标的目标矩形,自动处理缩放和旋转:
public static bool DrawTexture(IntPtr device, IntPtr txt, RectangleF rect, float rotation, Color tint) { try { Texture texture = (Texture)txt; // 获取纹理实际尺寸(取第0级mipmap的原生尺寸) SurfaceDescription desc = texture.GetLevelDescription(0); int texWidth = desc.Width; int texHeight = desc.Height; using (Sprite s = new Sprite((Device)device)) { s.Begin(SpriteFlags.None); // 旋转中心设为纹理自身的中心,对应目标矩形的中心 Vector3 center = new Vector3(texWidth / 2f, texHeight / 2f, 0); // 绘制位置设为目标矩形的中心坐标 Vector3 position = new Vector3(rect.X + rect.Width / 2f, rect.Y + rect.Height / 2f, 0); // 传入源矩形(整个纹理)和目标参数,自动完成缩放 s.Draw(texture, new Rectangle(0, 0, texWidth, texHeight), center, position, tint.ToRawColorBGRA()); s.End(); } return true; } catch (Exception ex) { Main.managerInstance.console.PrintError(string.Format("[Direct3D9] An error occured while trying to draw texture. Details: {0}", ex.ToString())); } return false; }
方案2:修正矩阵变换逻辑(适合需要自定义矩阵的场景)
手动构建矩阵时,先切换到像素坐标系,再基于纹理实际尺寸计算缩放比例:
public static bool DrawTexture(IntPtr device, IntPtr txt, RectangleF rect, float rotation, Color tint) { try { Texture texture = (Texture)txt; SurfaceDescription desc = texture.GetLevelDescription(0); float texWidth = desc.Width; float texHeight = desc.Height; Size res = CGame.Resolution; // 计算缩放比例:目标像素尺寸 / 纹理原始尺寸 float scaleX = rect.Width / texWidth; float scaleY = rect.Height / texHeight; using (Sprite s = new Sprite((Device)device)) { // 设置正交投影矩阵,将坐标系转为像素空间:左上角(0,0),右下角(屏幕宽,屏幕高) Matrix orthoMatrix = Matrix.OrthoOffCenterLH(0, res.Width, res.Height, 0, 0, 1); s.SetTransform(SpriteTransformType.Projection, orthoMatrix); s.Begin(SpriteFlags.None); // 矩阵变换顺序:先平移到纹理中心作为旋转原点→缩放→旋转→平移到目标矩形中心 Matrix m = Matrix.Identity * Matrix.Translation(-texWidth/2f, -texHeight/2f, 0) * Matrix.Scaling(scaleX, scaleY, 1) * Matrix.RotationZ(rotation) * Matrix.Translation(rect.X + rect.Width/2f, rect.Y + rect.Height/2f, 0); s.Transform = m; s.Draw(texture, tint.ToRawColorBGRA()); s.End(); } return true; } catch (Exception ex) { Main.managerInstance.console.PrintError(string.Format("[Direct3D9] An error occured while trying to draw texture. Details: {0}", ex.ToString())); } return false; }
额外注意事项
- 永远通过
texture.GetLevelDescription(0)获取纹理实际尺寸,不要依赖原图尺寸,因为D3DX.DefaultNonPowerOf2会自动调整非2的幂次纹理的尺寸。 - 如果不需要纹理自动调整尺寸,可以在加载时直接指定具体宽高(比如原图是128x128就传128),但需要确保设备支持非2的幂次纹理(Direct3D9大部分设备都支持)。
内容的提问来源于stack exchange,提问作者E. S.
相关产品推荐
相关产品推荐

