python编程示例系列
python编程示例系列二
python的Web神器Streamlit
如何应聘高薪职位
C#视觉应用开发问题系列
c#串口应用开发问题系列
microPython Python最小内核源码解析
NI-motion运动控制c语言示例代码解析
OpenRA开源红色警戒游戏RPG源码解读
# OpenRA渲染器代码详解
以下是对OpenRA游戏引擎中Renderer类的详细分析,包括中文注释、逻辑解读、流程图和特殊语法技巧的解析。
代码详解(带中文注释)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using OpenRA.FileFormats;
using OpenRA.Graphics;
using OpenRA.Primitives;
using OpenRA.Support;
namespace OpenRA
{
public sealed class Renderer : IDisposable
{
// 渲染类型枚举:无渲染、世界渲染、UI渲染
enum RenderType { None, World, UI }
// 世界渲染相关的渲染器
public SpriteRenderer WorldSpriteRenderer { get; } // 世界精灵渲染器
public RgbaSpriteRenderer WorldRgbaSpriteRenderer { get; } // 世界RGBA精灵渲染器
public RgbaColorRenderer WorldRgbaColorRenderer { get; } // 世界RGBA颜色渲染器
public IRenderer[] WorldRenderers = Array.Empty<IRenderer>(); // 世界渲染器数组,默认为空
// UI渲染相关的渲染器
public RgbaColorRenderer RgbaColorRenderer { get; } // RGBA颜色渲染器
public SpriteRenderer SpriteRenderer { get; } // 精灵渲染器
public RgbaSpriteRenderer RgbaSpriteRenderer { get; } // RGBA精灵渲染器
// 窗口状态属性
public bool WindowHasInputFocus => Window.HasInputFocus; // 窗口是否有输入焦点
public bool WindowIsSuspended => Window.IsSuspended; // 窗口是否被挂起
// 字体字典
public IReadOnlyDictionary<string, SpriteFont> Fonts;
// 平台相关接口
internal IPlatformWindow Window { get; } // 平台窗口接口
internal IGraphicsContext Context { get; } // 图形上下文接口
// 纹理和缓冲区大小
internal int SheetSize { get; } // 纹理表大小
internal int TempVertexBufferSize { get; } // 临时顶点缓冲区大小
internal int TempIndexBufferSize { get; } // 临时索引缓冲区大小
// 渲染缓冲区
readonly IVertexBuffer<Vertex> tempVertexBuffer; // 临时顶点缓冲区
readonly IIndexBuffer quadIndexBuffer; // 四边形索引缓冲区
readonly Stack<Rectangle> scissorState = new(); // 裁剪区域状态栈
readonly ITexture worldBufferSnapshot; // 世界缓冲区快照纹理
// 屏幕帧缓冲区
IFrameBuffer screenBuffer; // 屏幕帧缓冲区
Sprite screenSprite; // 屏幕精灵
// 世界帧缓冲区
IFrameBuffer worldBuffer; // 世界帧缓冲区
Sheet worldSheet; // 世界纹理表
Sprite worldSprite; // 世界精灵
Size lastMaximumViewportSize; // 上次最大视口大小
Size lastWorldViewportSize; // 上次世界视口大小
// 世界帧缓冲区属性
public Size WorldFrameBufferSize => worldSheet.Size; // 世界帧缓冲区大小
public int WorldDownscaleFactor { get; private set; } = 1; // 世界下采样因子,默认为1
/// <summary>
/// 复制并返回当前渲染的世界状态作为临时纹理
/// </summary>
public ITexture WorldBufferSnapshot()
{
worldBufferSnapshot.SetDataFromReadBuffer(new Rectangle(int2.Zero, worldSheet.Size));
return worldBufferSnapshot;
}
// 字体相关
SheetBuilder fontSheetBuilder; // 字体纹理表构建器
readonly IPlatform platform; // 平台接口
// 深度缓冲区相关
float depthMargin; // 深度边距
// 缓存的状态
Size lastBufferSize = new(-1, -1); // 上次缓冲区大小
Rectangle lastWorldViewport = Rectangle.Empty; // 上次世界视口
ITexture currentPaletteTexture; // 当前调色板纹理
int currentPaletteHeight = 0; // 当前调色板高度
IBatchRenderer currentBatchRenderer; // 当前批处理渲染器
RenderType renderType = RenderType.None; // 当前渲染类型,默认为无
/// <summary>
/// 渲染器构造函数
/// </summary>
/// <param name="platform">平台接口</param>
/// <param name="graphicSettings">图形设置</param>
public Renderer(IPlatform platform, GraphicSettings graphicSettings)
{
this.platform = platform;
var resolution = GetResolution(graphicSettings); // 获取分辨率
// 计算临时缓冲区大小
TempVertexBufferSize = graphicSettings.BatchSize - graphicSettings.BatchSize % 4;
TempIndexBufferSize = TempVertexBufferSize / 4 * 6;
// 创建窗口
Window = platform.CreateWindow(new Size(resolution.Width, resolution.Height),
graphicSettings.Mode, graphicSettings.UIScale, TempVertexBufferSize, TempIndexBufferSize,
graphicSettings.VideoDisplay, graphicSettings.GLProfile);
Context = Window.Context; // 获取图形上下文
SheetSize = graphicSettings.SheetSize; // 设置纹理表大小
// 创建各种渲染器
var combinedBindings = new CombinedShaderBindings();
WorldSpriteRenderer = new SpriteRenderer(this, Context.CreateShader(combinedBindings));
WorldRgbaSpriteRenderer = new RgbaSpriteRenderer(WorldSpriteRenderer);
WorldRgbaColorRenderer = new RgbaColorRenderer(WorldSpriteRenderer);
SpriteRenderer = new SpriteRenderer(this, Context.CreateShader(combinedBindings));
RgbaSpriteRenderer = new RgbaSpriteRenderer(SpriteRenderer);
RgbaColorRenderer = new RgbaColorRenderer(SpriteRenderer);
// 创建缓冲区
tempVertexBuffer = Context.CreateVertexBuffer<Vertex>(TempVertexBufferSize);
quadIndexBuffer = Context.CreateIndexBuffer(Util.CreateQuadIndices(TempIndexBufferSize / 6));
worldBufferSnapshot = Context.CreateTexture();
}
/// <summary>
/// 根据图形设置获取分辨率
/// </summary>
static Size GetResolution(GraphicSettings graphicsSettings)
{
var size = (graphicsSettings.Mode == WindowMode.Windowed)
? graphicsSettings.WindowedSize // 窗口模式
: graphicsSettings.FullscreenSize; // 全屏模式
return new Size(size.X, size.Y);
}
/// <summary>
/// 设置UI缩放
/// </summary>
public void SetUIScale(float scale)
{
Window.SetScaleModifier(scale);
}
/// <summary>
/// 初始化字体
/// </summary>
public void InitializeFonts(ModData modData)
{
// 释放旧字体资源
if (Fonts != null)
foreach (var font in Fonts.Values)
font.Dispose();
using (new PerfTimer("SpriteFonts")) // 性能计时器
{
fontSheetBuilder?.Dispose(); // 释放旧的字体纹理表构建器
fontSheetBuilder = new SheetBuilder(SheetType.BGRA, modData.Manifest.FontSheetSize);
// 创建字体字典
Fonts = modData.Manifest.Get<Fonts>().FontList.ToDictionary(x => x.Key,
x => new SpriteFont(
platform, x.Value.Font, modData.DefaultFileSystem.Open(x.Value.Font).ReadAllBytes(),
x.Value.Size, x.Value.Ascender, Window.EffectiveWindowScale, fontSheetBuilder));
}
// 注册窗口缩放变化事件处理器
Window.OnWindowScaleChanged += (oldNative, oldEffective, newNative, newEffective) =>
{
Game.RunAfterTick(() =>
{
// 重新计算新窗口缩放的下采样因子
SetMaximumViewportSize(lastMaximumViewportSize);
// 更新Chrome提供者的DPI缩放
ChromeProvider.SetDPIScale(newEffective);
// 更新所有字体的缩放
foreach (var f in Fonts)
f.Value.SetScale(newEffective);
});
};
}
/// <summary>
/// 初始化深度缓冲区
/// </summary>
public void InitializeDepthBuffer(MapGrid mapGrid)
{
// 深度缓冲区需要初始化足够的范围来覆盖:
// - 屏幕的高度
// - 从屏幕底部下方的MaxTerrainHeight的瓦片的z偏移(推入视图)
// - 来自MaxTerrainHeight地形上方的actor的额外z偏移
// - 一个小边距,使得部分渲染在屏幕顶部边缘上方的瓦片不会被推到裁剪平面后面
// 我们需要mapGrid.MaximumTerrainHeight * mapGrid.TileSize.Height / 2的偏移来覆盖地形高度
// 并选择使用mapGrid.MaximumTerrainHeight * mapGrid.TileSize.Height / 4来处理actor和顶部边缘情况
depthMargin = mapGrid == null || !mapGrid.EnableDepthBuffer ? 0 : mapGrid.TileSize.Height * mapGrid.MaximumTerrainHeight;
}
/// <summary>
/// 开始一帧的渲染
/// </summary>
void BeginFrame()
{
Context.Clear(); // 清除上下文
var surfaceSize = Window.SurfaceSize; // 获取表面大小
var surfaceBufferSize = surfaceSize.NextPowerOf2(); // 计算下一个2的幂大小
// 如果屏幕精灵为空或大小不匹配,重新创建屏幕缓冲区
if (screenSprite == null || screenSprite.Sheet.Size != surfaceBufferSize)
{
screenBuffer?.Dispose(); // 释放旧的屏幕缓冲区
// 将屏幕渲染到帧缓冲区以简化截图回读
screenBuffer = Context.CreateFrameBuffer(surfaceBufferSize, Color.FromArgb(0xFF, 0, 0, 0));
}
// 如果屏幕精灵为空或尺寸不匹配,重新创建屏幕精灵
if (screenSprite == null || surfaceSize.Width != screenSprite.Bounds.Width || -surfaceSize.Height != screenSprite.Bounds.Height)
{
var screenSheet = new Sheet(SheetType.BGRA, screenBuffer.Texture);
// 在Y轴翻转精灵以匹配OpenGL的左下角原点
var screenBounds = Rectangle.FromLTRB(0, surfaceSize.Height, surfaceSize.Width, 0);
screenSprite = new Sprite(screenSheet, screenBounds, TextureChannel.RGBA);
}
// 在HiDPI窗口中,我们遵循Apple的约定,将窗口坐标定义为标准分辨率窗口
// 但有一个更高分辨率的后备表面,每个视口像素有多个纹理像素
// 我们必须将表面缓冲区大小转换为视口大小 - 通常这不仅仅是窗口大小
// 四舍五入到下一个2的幂,因为NextPowerOf2计算是在表面像素坐标中完成的
var scale = Window.EffectiveWindowScale;
var bufferSize = new Size((int)(surfaceBufferSize.Width / scale), (int)(surfaceBufferSize.Height / scale));
// 如果缓冲区大小发生变化,更新视口参数
if (lastBufferSize != bufferSize)
{
SpriteRenderer.SetViewportParams(bufferSize, 1, 0f, int2.Zero);
lastBufferSize = bufferSize;
}
}
/// <summary>
/// 设置最大视口大小
/// </summary>
public void SetMaximumViewportSize(Size size)
{
// 目标是以1:1缩放将世界渲染到帧缓冲区,然后使用自定义
// 过滤器进行上/下采样,以提供清晰的缩放并避免使用深度缓冲区时的渲染故障
// 这种方法对大尺寸缩放不好,首先会饱和GPU填充率,然后在
// 达到帧缓冲区大小限制(通常为16k)时崩溃。因此,我们将最大帧缓冲区大小限制为
// 窗口表面大小的两倍,这在渲染质量和性能之间取得了合理的平衡。
// 使用深度缓冲区的模组必须限制其艺术品分辨率或最大缩小级别。
Size worldBufferSize;
if (depthMargin == 0)
{
var surfaceSize = Window.SurfaceSize;
worldBufferSize = new Size(Math.Min(size.Width, 2 * surfaceSize.Width), Math.Min(size.Height, 2 * surfaceSize.Height)).NextPowerOf2();
}
else
worldBufferSize = size.NextPowerOf2();
// 如果世界精灵为空或世界表大小不匹配,重新创建世界缓冲区
if (worldSprite == null || worldSheet.Size != worldBufferSize)
{
worldBuffer?.Dispose(); // 释放旧的世界缓冲区
// 如果启用世界帧缓冲区下采样且世界大小超过最终输出大小的两倍,我们允许它被下采样!
worldBuffer = Context.CreateFrameBuffer(worldBufferSize);
// 像素艺术缩放模式是自定义的双线性采样
worldBuffer.Texture.ScaleFilter = TextureScaleFilter.Linear;
worldSheet = new Sheet(SheetType.BGRA, worldBuffer.Texture);
// 使缓存的状态无效以强制着色器更新
lastWorldViewport = Rectangle.Empty;
worldSprite = null;
}
lastMaximumViewportSize = size;
}
/// <summary>
/// 开始世界渲染
/// </summary>
public void BeginWorld(Rectangle worldViewport)
{
// 确保当前没有进行渲染
if (renderType != RenderType.None)
throw new InvalidOperationException($"BeginWorld called with renderType = {renderType}, expected RenderType.None.");
BeginFrame(); // 开始一帧
// 确保已设置最大视口大小
if (worldSheet == null)
throw new InvalidOperationException("BeginWorld called before SetMaximumViewportSize has been set.");
// 如果世界精灵为空或视口大小发生变化,重新创建世界精灵
if (worldSprite == null || worldViewport.Size != lastWorldViewportSize)
{
// 如果需要,下采样世界渲染以适应帧缓冲区
var vw = worldViewport.Size.Width;
var vh = worldViewport.Size.Height;
var bw = worldSheet.Size.Width;
var bh = worldSheet.Size.Height;
WorldDownscaleFactor = 1;
while (vw / WorldDownscaleFactor > bw || vh / WorldDownscaleFactor > bh)
WorldDownscaleFactor++;
var s = new Size(vw / WorldDownscaleFactor, vh / WorldDownscaleFactor);
worldSprite = new Sprite(worldSheet, new Rectangle(int2.Zero, s), TextureChannel.RGBA);
lastWorldViewportSize = worldViewport.Size;
}
worldBuffer.Bind(); // 绑定世界缓冲区
// 如果世界视口发生变化,更新视口参数
if (lastWorldViewport != worldViewport)
{
WorldSpriteRenderer.SetViewportParams(worldSheet.Size, WorldDownscaleFactor, depthMargin, worldViewport.Location);
lastWorldViewport = worldViewport;
}
renderType = RenderType.World; // 设置渲染类型为世界
}
/// <summary>
/// 开始UI渲染
/// </summary>
public void BeginUI()
{
if (renderType == RenderType.World)
{
// 完成世界渲染
Flush();
worldBuffer.Unbind();
// 将世界缓冲区渲染到UI缓冲区
screenBuffer.Bind();
var scale = Window.EffectiveWindowScale;
var bufferScale = new float3(
(int)(screenSprite.Bounds.Width / scale) / worldSprite.Size.X,
(int)(-screenSprite.Bounds.Height / scale) / worldSprite.Size.Y,
1f);
// 启用像素艺术缩放,绘制世界精灵,然后禁用像素艺术缩放
SpriteRenderer.EnablePixelArtScaling(true);
RgbaSpriteRenderer.DrawSprite(worldSprite, float3.Zero, bufferScale);
Flush();
SpriteRenderer.EnablePixelArtScaling(false);
}
else
{
// 跳过了世界渲染
BeginFrame();
screenBuffer.Bind();
}
renderType = RenderType.UI; // 设置渲染类型为UI
}
/// <summary>
/// 设置调色板
/// </summary>
public void SetPalette(HardwarePalette palette)
{
// 注意:palette.Texture和palette.ColorShifts同时更新
// 所以我们只需要检查其中一个来知道是否必须更新纹理
// 同时比较高度,以防添加了新的调色板
if (palette.Texture == currentPaletteTexture && palette.Height == currentPaletteHeight)
return;
Flush(); // 刷新渲染
currentPaletteTexture = palette.Texture;
currentPaletteHeight = palette.Height;
// 为所有渲染器设置调色板
SpriteRenderer.SetPalette(palette);
WorldSpriteRenderer.SetPalette(palette);
foreach (var r in WorldRenderers)
r.SetPalette(palette);
}
/// <summary>
/// 结束一帧的渲染
/// </summary>
public void EndFrame(IInputHandler inputHandler)
{
// 确保当前正在进行UI渲染
if (renderType != RenderType.UI)
throw new InvalidOperationException($"EndFrame called with renderType = {renderType}, expected RenderType.UI.");
Flush(); // 刷新渲染
screenBuffer.Unbind(); // 解绑屏幕缓冲区
// 将合成器缓冲区渲染到屏幕
// HACK / PERF:调整坐标以覆盖实际窗口,同时保持缓冲区视口参数
// 这样可以节省每帧两次冗余(且昂贵)的SetViewportParams调用
RgbaSpriteRenderer.DrawSprite(screenSprite, new float3(0, lastBufferSize.Height, 0),
new float3(lastBufferSize.Width / screenSprite.Size.X, -lastBufferSize.Height / screenSprite.Size.Y, 1f));
Flush(); // 再次刷新渲染
Window.PumpInput(inputHandler); // 处理输入
Context.Present(); // 提交渲染结果
renderType = RenderType.None; // 重置渲染类型
}
/// <summary>
/// 绘制批处理
/// </summary>
public void DrawBatch<T>(IVertexBuffer<T> vertices, IShader shader,
int firstVertex, int numVertices, PrimitiveType type)
where T : struct
{
vertices.Bind(); // 绑定顶点缓冲区
shader.Bind(); // 绑定着色器
Context.DrawPrimitives(type, firstVertex, numVertices); // 绘制图元
PerfHistory.Increment("batches", 1); // 增加批处理计数
}
/// <summary>
/// 绘制四边形批处理
/// </summary>
public void DrawQuadBatch(ref Vertex[] vertices, IShader shader, int numVertices)
{
tempVertexBuffer.SetData(ref vertices, numVertices); // 设置临时顶点缓冲区数据
DrawQuadBatch(tempVertexBuffer, quadIndexBuffer, shader, numVertices / 4 * 6, 0);
}
/// <summary>
/// 绘制四边形批处理(泛型版本)
/// </summary>
public void DrawQuadBatch<T>(IVertexBuffer<T> vertices, IIndexBuffer indices, IShader shader, int numIndices, int start)
where T : struct
{
vertices.Bind(); // 绑定顶点缓冲区
indices.Bind(); // 绑定索引缓冲区
shader.Bind(); // 绑定着色器
Context.DrawElements(numIndices, start); // 绘制元素
PerfHistory.Increment("batches", 1); // 增加批处理计数
}
/// <summary>
/// 刷新渲染
/// </summary>
public void Flush()
{
CurrentBatchRenderer = null; // 设置当前批处理渲染器为null,触发刷新
}
// 各种窗口和渲染属性
public Size Resolution => Window.EffectiveWindowSize; // 有效窗口大小
public Size NativeResolution => Window.NativeWindowSize; // 原生窗口大小
public float WindowScale => Window.EffectiveWindowScale; // 有效窗口缩放
public float NativeWindowScale => Window.NativeWindowScale; // 原生窗口缩放
public GLProfile GLProfile => Window.GLProfile; // GL配置文件
public GLProfile[] SupportedGLProfiles => Window.SupportedGLProfiles; // 支持的GL配置文件
/// <summary>
/// 批处理渲染器接口
/// </summary>
public interface IBatchRenderer { void Flush(); }
/// <summary>
/// 当前批处理渲染器
/// </summary>
public IBatchRenderer CurrentBatchRenderer
{
get => currentBatchRenderer;
set
{
if (currentBatchRenderer == value)
return;
currentBatchRenderer?.Flush(); // 如果当前批处理渲染器不为null,刷新它
currentBatchRenderer = value; // 设置新的批处理渲染器
}
}
/// <summary>
/// 创建帧缓冲区
/// </summary>
public IFrameBuffer CreateFrameBuffer(Size s)
{
return Context.CreateFrameBuffer(s);
}
/// <summary>
/// 创建着色器
/// </summary>
public IShader CreateShader(IShaderBindings bindings)
{
return Context.CreateShader(bindings);
}
/// <summary>
/// 创建顶点缓冲区
/// </summary>
public IVertexBuffer<T> CreateVertexBuffer<T>(int length) where T : struct
{
return Context.CreateVertexBuffer<T>(length);
}
/// <summary>
/// 启用裁剪
/// </summary>
public void EnableScissor(Rectangle rect)
{
// 必须保持在当前裁剪矩形内
if (scissorState.Count > 0)
rect = Rectangle.Intersect(rect, scissorState.Peek());
Flush(); // 刷新渲染
if (renderType == RenderType.World)
{
// 为世界渲染计算裁剪矩形
var r = Rectangle.FromLTRB(
rect.Left / WorldDownscaleFactor,
rect.Top / WorldDownscaleFactor,
(rect.Right + WorldDownscaleFactor - 1) / WorldDownscaleFactor,
(rect.Bottom + WorldDownscaleFactor - 1) / WorldDownscaleFactor);
worldBuffer.EnableScissor(r);
}
else
Context.EnableScissor(rect.X, rect.Y, rect.Width, rect.Height);
scissorState.Push(rect); // 将裁剪矩形压入栈
}
/// <summary>
/// 禁用裁剪
/// </summary>
public void DisableScissor()
{
scissorState.Pop(); // 弹出栈顶的裁剪矩形
Flush(); // 刷新渲染
if (renderType == RenderType.World)
{
// 恢复之前的裁剪矩形
if (scissorState.Count > 0)
{
var rect = scissorState.Peek();
var r = Rectangle.FromLTRB(
rect.Left / WorldDownscaleFactor,
rect.Top / WorldDownscaleFactor,
(rect.Right + WorldDownscaleFactor - 1) / WorldDownscaleFactor,
(rect.Bottom + WorldDownscaleFactor - 1) / WorldDownscaleFactor);
worldBuffer.EnableScissor(r);
}
else
worldBuffer.DisableScissor();
}
else
{
// 恢复之前的裁剪矩形
if (scissorState.Count > 0)
{
var rect = scissorState.Peek();
Context.EnableScissor(rect.X, rect.Y, rect.Width, rect.Height);
}
else
Context.DisableScissor();
}
}
/// <summary>
/// 启用深度缓冲区
/// </summary>
public void EnableDepthBuffer()
{
Flush(); // 刷新渲染
Context.EnableDepthBuffer();
}
/// <summary>
/// 禁用深度缓冲区
/// </summary>
public void DisableDepthBuffer()
{
Flush(); // 刷新渲染
Context.DisableDepthBuffer();
}
/// <summary>
/// 清除深度缓冲区
/// </summary>
public void ClearDepthBuffer()
{
Flush(); // 刷新渲染
Context.ClearDepthBuffer();
}
/// <summary>
/// 启用抗锯齿过滤器
/// </summary>
public void EnableAntialiasingFilter()
{
// 确保当前正在进行UI渲染
if (renderType != RenderType.UI)
throw new InvalidOperationException($"EndFrame called with renderType = {renderType}, expected RenderType.UI.");
Flush(); // 刷新渲染
SpriteRenderer.EnablePixelArtScaling(true);
}
/// <summary>
/// 禁用抗锯齿过滤器
/// </summary>
public void DisableAntialiasingFilter()
{
// 确保当前正在进行UI渲染
if (renderType != RenderType.UI)
throw new InvalidOperationException($"EndFrame called with renderType = {renderType}, expected RenderType.UI.");
Flush(); // 刷新渲染
SpriteRenderer.EnablePixelArtScaling(false);
}
/// <summary>
/// 抓取窗口鼠标焦点
/// </summary>
public void GrabWindowMouseFocus()
{
Window.GrabWindowMouseFocus();
}
/// <summary>
/// 释放窗口鼠标焦点
/// </summary>
public void ReleaseWindowMouseFocus()
{
Window.ReleaseWindowMouseFocus();
}
/// <summary>
/// 保存屏幕截图
/// </summary>
public void SaveScreenshot(string path)
{
// 直接从纹理获取数据以防止表格缓冲它
var src = screenBuffer.Texture.GetData();
var srcWidth = screenSprite.Sheet.Size.Width;
var destWidth = screenSprite.Bounds.Width;
var destHeight = -screenSprite.Bounds.Height;
// 在线程池中处理截图保存
ThreadPool.QueueUserWorkItem(_ =>
{
// 从(较大的)后备表面提取屏幕矩形
var dest = new byte[4 * destWidth * destHeight];
for (var y = 0; y < destHeight; y++)
Array.Copy(src, 4 * y * srcWidth, dest, 4 * y * destWidth, 4 * destWidth);
// 保存为PNG格式
new Png(dest, SpriteFrameType.Bgra32, destWidth, destHeight).Save(path);
});
}
/// <summary>
/// 释放资源
/// </summary>
public void Dispose()
{
worldBuffer?.Dispose();
screenBuffer.Dispose();
worldBufferSnapshot.Dispose();
tempVertexBuffer.Dispose();
quadIndexBuffer.Dispose();
fontSheetBuilder?.Dispose();
if (Fonts != null)
foreach (var font in Fonts.Values)
font.Dispose();
Window.Dispose();
}
/// <summary>
/// 设置垂直同步
/// </summary>
public void SetVSyncEnabled(bool enabled)
{
Window.Context.SetVSyncEnabled(enabled);
}
/// <summary>
/// 获取剪贴板文本
/// </summary>
public string GetClipboardText()
{
return Window.GetClipboardText();
}
/// <summary>
/// 设置剪贴板文本
/// </summary>
public bool SetClipboardText(string text)
{
return Window.SetClipboardText(text);
}
// 其他属性
public string GLVersion => Context.GLVersion; // GL版本
public int DisplayCount => Window.DisplayCount; // 显示器数量
public int CurrentDisplay => Window.CurrentDisplay; // 当前显示器
}
}
逻辑解读
Renderer类是OpenRA游戏引擎的核心渲染组件,负责管理和协调游戏的渲染流程。它主要处理两种渲染类型:世界渲染和UI渲染。
主要功能:
-
初始化与配置:
- 创建和管理窗口及图形上下文
- 设置各种渲染器和缓冲区
- 初始化字体和深度缓冲区
-
渲染流程管理:
- 提供BeginWorld()和BeginUI()方法,分别开始世界和UI的渲染
- 通过EndFrame()完成一帧的渲染并提交结果
- 使用帧缓冲区进行离屏渲染,然后将结果合成到屏幕上
-
绘制操作:
- 提供批量绘制精灵和几何图形的方法
- 管理裁剪区域和深度缓冲区
- 支持抗锯齿和像素艺术缩放
-
资源管理:
- 创建和管理纹理、着色器和缓冲区
- 处理窗口缩放和分辨率变化
- 实现IDisposable接口以正确释放资源
-
辅助功能:
- 截图功能
- 剪贴板操作
- 鼠标焦点控制
渲染流程:
- 通过BeginWorld()开始世界渲染,将世界内容渲染到worldBuffer中
- 通过BeginUI()开始UI渲染,可能会将worldBuffer的内容合成到screenBuffer中
- 通过EndFrame()完成渲染,将screenBuffer的内容绘制到实际屏幕上
整个设计支持高效的批处理渲染,并且能够处理不同的分辨率、缩放和HiDPI显示。
流程图
特殊语法与技巧
-
属性表达式体:
public Size WorldFrameBufferSize => worldSheet.Size;这是C# 6.0引入的表达式体属性,简化了只读属性的定义。
-
空条件运算符:
worldBuffer?.Dispose();如果worldBuffer为null,不会调用Dispose方法;否则调用它的Dispose方法。
-
字典创建与LINQ:
Fonts = modData.Manifest.Get<Fonts>().FontList.ToDictionary(x => x.Key, x => new SpriteFont(...));使用LINQ的ToDictionary方法从集合创建字典。
-
Lambda表达式:
Window.OnWindowScaleChanged += (oldNative, oldEffective, newNative, newEffective) => {...};用于事件处理和回调的简洁语法。
-
泛型约束:
public void DrawBatch<T>(IVertexBuffer<T> vertices, ...) where T : struct限制泛型类型T必须是值类型(struct)。
-
线程池使用:
ThreadPool.QueueUserWorkItem(_ => {...});用于在后台线程执行耗时操作,如保存截图。
-
字符串内插:
throw new InvalidOperationException($"BeginWorld called with renderType = {renderType}, expected RenderType.None.");使用$前缀和{}将变量嵌入字符串中。
-
using语句与性能计时:
using (new PerfTimer("SpriteFonts")) {...}利用using语句和IDisposable模式自动调用Dispose方法,用于性能计时。
-
泛型方法:
public IVertexBuffer<T> CreateVertexBuffer<T>(int length) where T : struct允许方法根据调用时指定的类型参数工作。
-
空合并运算符:
public IRenderer[] WorldRenderers = Array.Empty<IRenderer>();使用Array.Empty()返回一个空数组,比new T[0]更高效。
-
接口定义在类内部:
public interface IBatchRenderer { void Flush(); }在类内部定义接口,将相关功能组织在一起。
-
属性的自动实现:
public SpriteRenderer WorldSpriteRenderer { get; }简化了只读属性的定义,编译器自动生成后备字段。
-
三元运算符:
var size = (graphicsSettings.Mode == WindowMode.Windowed) ? graphicsSettings.WindowedSize : graphicsSettings.FullscreenSize;简洁地根据条件选择不同的值。
-
默认参数值:
public int WorldDownscaleFactor { get; private set; } = 1;为属性或字段提供默认值。
-
Stack数据结构:
readonly Stack<Rectangle> scissorState = new();用于管理裁剪状态的堆栈,支持嵌套裁剪区域。
-
密封类:
public sealed class Renderer : IDisposable防止类被继承,可能出于性能或设计考虑。
-
枚举定义:
enum RenderType { None, World, UI }在类内部定义枚举,限制其作用域。
这个Renderer类展示了现代C#编程的许多最佳实践和模式,特别是在图形渲染和资源管理方面。它采用了面向对象设计,并利用了C#的众多语言特性来编写高效、可维护的代码。
python的paramiko 库如何使用
ptyhon 如何为自闭症儿童的定制图像查看游戏
python如何自动生成markdown格式文件
microPython的源码解析之 objgetitemiter.c
chatGPT真的会给出windows序列号
C#进行串口应用开发如何判断串口通信故障的具体原因
python生成伪随机数序列库randomstate
量化交易系统中+如何设计和实现高频交易算法?
如何用c#语言进行开发一个edge浏览器插件
c++ qt如何进行对象序列化与反序列化
C#进行串口应用开发如何实现串口数据的校验
python 只用20行代码完成一个web应用开发
c#如何使用 USB(Universal Serial Bus)进行通信
车载系统软件工程师如何处理车载系统的多语言支持
python开发的开源数学软件系统SageMath
microPython的源码解析之 objboundmeth.c
python的fractions库如何使用
python 如何控制鼠标键盘
python加PyQT如何开发一个端口扫描工具
python的scipy提供什么功能
车载系统软件工程师如何处理车载系统的启动时间和响应速度优化
NI-Motion通过National Instruments的FlexMotion软件控制运动控制卡上的轴进行运动C语言代码示例
python进行函数式编程的工具toolz
python编写一个简单神经网络计算代码带有反向传播,不用任何框架
python如何开发一个端口转发工具
python 如何不用循环利用对数欧拉方法实现全向量化
车载系统软件工程师如何处理车载系统的实时数据流处理
python的xmlrpc库如何使用
c#视觉应用开发中如何在C#中进行图像去重叠?
车载系统软件工程师如何处理车载系统的电磁兼容性(EMC)
如何将列的数字编号,转化为EXcel的字母表示的列编号
python如何绘制股票的K线图
Python在空域交通管理中的应用
智能农业设备软件工程师如何实现农业设备的电磁兼容性(EMC)
c#视觉应用开发中如何在C#中进行图像去色散?
c#视觉应用开发中如何在C#中处理图像噪声?
车载系统软件工程师如何实现车载系统的个性化设置和配置
python的Godot Engine库如何安装使用以及功能和用途
智能农业设备软件工程师如何实现农业设备的故障预测和预防
C#进行串口应用开发如何改变串口的默认端口号
C#进行串口应用开发如何通过串口实现模拟串口的虚拟化
OpenCV的图像分割分水岭算法(watershed algorithm)示例
Python如何实现粒子效果如烟雾、火焰、雨滴等.
python有哪些定时触发的框架
python如何快速创建命令行接口(CLI)
C#进行串口应用开发如何管理同时打开多个串口的通信
量化交易系统如何获取历史市场数据?
量化交易系统中+如何处理回测中的数据偏差问题?
python web应用开发神器 入门十六
C#进行串口应用开发如何区分串口接收到的数据帧
NI-Motion 实现一个轴的基于速度的直线运动期间还包括速度覆盖(即在运动过程中改变速度)的操作 C语言示例代码
microPython的源码解析之 compile.c
python如何创建SOCKS 代理连接
车载系统软件工程师如何处理车载系统的兼容性测试和验证
windows程序如何转linux开发
c#视觉应用开发中如何在C#中进行图像锐化?
pyrhon 如何将实时的tick行情,处理成1分钟k线数据
streamlit如何布局
opencv库的功能
python的pure-eval库
c#视觉应用开发中如何在C#中实现光流(Optical Flow)算法?
量化交易系统中+如何设计高效的数据库架构?
openai 的API 从自然语言生成sql语句
车载系统软件工程师如何确保车载系统的法规和标准符合性
车载系统软件工程师如何处理车载传感器数据(如雷达、激光雷达)
Python如何使用pickle库来复制、保存和加载一个空间
microPython的源码解析之 bc.c
microPython的源码解析之 modio.c
智能农业设备软件工程师如何集成和管理农业设备的系统维护
openai联合创始人用1000行纯C语言手搓的GPT-2训练代码
为什么Python对VR社区很重要
车载系统软件工程师如何实现车载导航和GPS系统
C#进行串口应用开发如何定位串口通信故障的原因与解决方案
microPython的源码解析之 reader.c
C#进行串口应用开发如何实现串口的监听服务
python生成和解决迷宫的库maze
Python的faker库,测试工作者的福音
C#进行串口应用开发如何修改Windows下的串口权限
microPython的源码解析之 runtime.c
microPython的源码解析之 objtype.c
c#视觉应用开发中如何在C#中处理3D图像数据?
python web应用开发神器 入门二十五
车载系统软件工程师如何实现车载语音识别和控制
智能农业设备软件工程师如何实现农业设备的用户权限管理
linux 的gdb软件如何使用
python的装饰器模式
量化交易系统中+如何扩展系统以支持更多用户和更大交易量?
microPython的源码解析之 frozenmod.c
车载系统软件工程师如何实现车载系统的紧急制动和碰撞预警
智能农业设备软件工程师如何处理设备的实时数据流处理
Python的使用opencv库人脸识别
C#进行串口应用开发如何优化串口通信的实时性与吞吐量
如何应聘普通测试工程师
C#进行串口应用开发如何实现基于串口的远程监控与报警系统
如何知道对方主机用了虚拟ip
python的sympy库介绍
使用 Python 和 Gretel.ai 生成合成位置数据
C#进行串口应用开发如何实现串口通信的自动重连与重传功能
量化交易系统中+如何处理实时流数据?
Python如何进行时间同步
2183

被折叠的 条评论
为什么被折叠?



