当前位置: 首页 > news >正文

02 | Raylib渲染架构

本文目录

  • 🎨 2 | Raylib 渲染架构 —— 从 Camera2D 踩坑到 RenderTexture2D
    • 📌 1. 问题:窗口该多大?
    • ❌ 2. Phase 1:固定窗口 + SetWindowSize
    • ❌ 3. Phase 2:Camera2D —— 理想很丰满
      • 🕳️ 坑 1:黑边
      • 🕳️ 坑 2:Camera2D + Texture 管线冲突
      • 🕳️ 坑 3:缩放锯齿
    • ✅ 4. Phase 3:RenderTexture2D —— 最终方案
      • 4.1 核心架构
      • 4.2 鼠标坐标转换
      • 4.3 背景图的处理
    • 🚀 5. Phase 4:全屏高清( canvasMult )
      • 5.1 问题
      • 5.2 解决方案
    • 📊 6. 架构演进总结
    • 🔜 下篇预告
    • 📚 系列目录

🎯系列第 2 篇· 前面我们讲了扫雷的核心算法,现在来看这些内容是怎么渲染到屏幕上的。
📖上一篇:项目概览与扫雷核心算法


🎨 2 | Raylib 渲染架构 —— 从 Camera2D 踩坑到 RenderTexture2D


📌 1. 问题:窗口该多大?

扫雷的棋盘尺寸是动态的:

难度棋盘像素(40px 格子)需要窗口
Beginner 9×9360 × 360很小就够了
Expert 16×301200 × 640需要大窗口

如果每次切难度都SetWindowSize()—— Windows DPI 缩放会干预,窗口忽大忽小,全屏时布局全乱。

解决方案迭代了 4 个阶段。前两个是坑,后两个是答案。


❌ 2. Phase 1:固定窗口 + SetWindowSize

intwinW=game.cols*CELL_SIZE+2*PADDING;intwinH=game.rows*CELL_SIZE+UI_PANEL_HEIGHT+PADDING;SetWindowSize(winW,winH);

📋Phase 1 的问题SetWindowSize()每次切难度窗口会跳变。下面是当时记录的典型问题:

  • ❌ Beginner → Expert:窗口从 400×500 跳到 1300×680,肉眼可见闪烁
  • ❌ Windows DPI 缩放抢夺控制权,窗口有时仅 200×200
  • ❌ 全屏/窗口切换时布局完全错位

问题清单

问题严重性
Windows DPI 缩放使窗口只有 200×200❌ 致命
切难度窗口跳变⚠️ 体验差
全屏/窗口切换布局重算⚠️ 不可靠
用户不能自由拖拽缩放❌ 不友好

💡教训:永远不要让窗口尺寸跟着游戏内容变。用固定画布 + 自动缩放代替。


❌ 3. Phase 2:Camera2D —— 理想很丰满

改用固定内部画布(960×640),用 Raylib 的Camera2D把画布坐标自动映射到窗口:

Camera2D camera={0};camera.zoom=fminf((float)screenW/CANVAS_WIDTH,(float)screenH/CANVAS_HEIGHT);BeginMode2D(camera);// 所有绘制用 960×640 坐标EndMode2D();

鼠标坐标用GetScreenToWorld2D()转换回画布空间。

🕳️ 坑 1:黑边

Camera2D 只渲染画布区域,宽高比不一致时左右/上下全是黑边。背景图填不满。

🕳️ 坑 2:Camera2D + Texture 管线冲突

BeginTextureMode(canvasTarget);BeginMode2D(camera);// 嵌套使用!渲染管线冲突

Raylib 的这两个模式嵌套时缩放行为不可预测。高 DPI 窗口下文字画错位置、点击区域完全偏移。

🕳️ 坑 3:缩放锯齿

Camera2D 用 GL_NEAREST,对棋盘像素风格是好事,但文字全是锯齿。

🐛代价6 个 Fix(#77-#82)实现 Camera2D 方案,又用 6 个 Fix(#108-#112)全部推翻。


✅ 4. Phase 3:RenderTexture2D —— 最终方案

4.1 核心架构

// 1️⃣ 创建固定画布纹理canvasTarget=LoadRenderTexture(CANVAS_WIDTH,CANVAS_HEIGHT);// 2️⃣ 每帧先画到纹理BeginTextureMode(canvasTarget);ClearBackground(BLANK);// ... 所有游戏内容(用 1280×720 坐标)DrawToast();EndTextureMode();// 3️⃣ 等比缩放 + 居中,blit 到窗口floatcanvasScale=fminf((float)screenW/CANVAS_WIDTH,(float)screenH/CANVAS_HEIGHT);floatoffsetX=(screenW-CANVAS_WIDTH*canvasScale)/2.0f;floatoffsetY=(screenH-CANVAS_HEIGHT*canvasScale)/2.0f;// 🖼️ blit 时用 bilinear 过滤,避免马赛克SetTextureFilter(canvasTarget.texture,TEXTURE_FILTER_BILINEAR);Rectangle src={0,0,CANVAS_WIDTH,-CANVAS_HEIGHT};Rectangle dst={offsetX,offsetY,CANVAS_WIDTH*canvasScale,CANVAS_HEIGHT*canvasScale};DrawTexturePro(canvasTarget.texture,src,dst,(Vector2){0,0},0.0f,WHITE);

🖼️RenderTexture2D 渲染管线

每帧渲染循环

🎮 游戏逻辑
UpdateGame()

🖌️ 画到画布
BeginTextureMode()
1280×720

📐 缩放计算
canvasScale = min(sW/1280, sH/720)

🖼️ Blit 到窗口
DrawTexturePro()

🖥️ 屏幕显示

🎯 鼠标输入
ScreenToCanvas()

4.2 鼠标坐标转换

staticVector2ScreenToCanvas(Vector2 screenPos){return(Vector2){(screenPos.x-canvasOffsetX)/canvasScale,(screenPos.y-canvasOffsetY)/canvasScale};}

⚠️对照 Fix #88:如果不转换,按钮画在画布 (640, 200) 但碰撞检测以为在窗口 (640, 200)。窗口一缩放,点击完全错位

4.3 背景图的处理

背景图(泼墨风格background.png)直接画到全屏窗口,不在画布内:

BeginDrawing();ClearBackground(BLACK);// 画背景到全屏(窗口坐标系)DrawTexturePro(bgTexture,src,(Rectangle){0,0,screenW,screenH},...);// 画遮罩到全屏DrawRectangle(0,0,screenW,screenH,Fade(BLACK,0.7f));// 画游戏到画布(画布坐标系)BeginTextureMode(canvasTarget);// ...

这样窗口宽高比任意变化,背景都填满,画布永远居中。


🚀 5. Phase 4:全屏高清( canvasMult )

5.1 问题

1280×720 画布在全屏(2560×1600)被放大2 倍以上。即使 bilinear 过滤,文字边缘仍有"柔化感"。中文笔画密集,放大后笔画之间模糊粘连(Fix #221)。

📋canvasMult 要解决的问题:全屏时 1280×720 画布被放大到 2~3 倍。

  • 修复前:中文笔画密集处在放大后互相粘连模糊(英文几乎无影响)
  • 修复后:canvasMult 将渲染目标重建为 2560×1440+,Camera2D 保持 1280×720 布局,下采样后中文清晰锐利

5.2 解决方案

staticvoidUpdateCanvasScale(void){// ... 计算 canvasScale ...if(canvasScale>1.0f){intmult=(int)ceilf(canvasScale);if(mult<1)mult=1;if(mult>4)mult=4;targetW=CANVAS_WIDTH*mult;// 1280 → 2560targetH=CANVAS_HEIGHT*mult;// 720 → 1440canvasMult=mult;}// 重建高分辨率画布if(canvasTarget.texture.width!=targetW){UnloadRenderTexture(canvasTarget);canvasTarget=LoadRenderTexture(targetW,targetH);SetTextureFilter(...,TEXTURE_FILTER_BILINEAR);}}

绘制时用Camera2D.zoom = canvasMult映射:

BeginTextureMode(canvasTarget);Camera2D cam={0};cam.zoom=(float)canvasMult;BeginMode2D(cam);// ... 所有 1280×720 布局代码零改动 ...EndMode2D();EndTextureMode();
显示器canvasScalemult实际 RT 分辨率
1280×7201.011280×720
1920×10801.522560×1440
2560×16002.022560×1440
3840×2160 (4K)3.033840×2160

🔑关键:所有现有布局代码零改动。Camera2D 自动把 1280×720 的绘制指令映射到高分辨率画布上,下采样回窗口时清晰度远超纯 1280 放大。


📊 6. 架构演进总结

🖼️架构演进路线

❌ Phase 1
固定窗口
SetWindowSize

❌ Phase 2
Camera2D
黑边+锯齿

✅ Phase 3
RT + BILINEAR
1280×720

🚀 Phase 4
canvasMult
高清全屏

阶段画布自适应文字清晰度代码复杂度
❌ Phase 1 固定窗口随棋盘变一般简单
❌ Phase 2 Camera2D960×640❌ 锯齿多复杂(坑多)
✅ Phase 3 RT+BILINEAR1280×720✅ 英文清晰适中
🚀Phase 4 canvasMult自适应高分辨率✅ 中文也清晰稍复杂

💡最终选型RenderTexture2D+ 固定 1280×720 逻辑画布 +TEXTURE_FILTER_BILINEAR+ 全屏canvasMult高分辨率重建 +Camera2Dzoom 自动映射。


🔜 下篇预告

第 3 篇:222 个 Bug 修复教会我的事
栈溢出、浮点 UB、字体缺失、异步保存、初始化顺序错误…… 精选 15 个让你看完直呼"我也踩过"的经典修复故事。(可能是全系列最有趣的一篇)


📚 系列目录

#标题状态
1项目概览与扫雷核心算法✅ 已发布
2Raylib 渲染架构← 本文✅ 已发布
3222 个 Bug 修复教会我的事✅ 已发布
4中英文双语的工程实现📝 待发布
5持久化、撤销、提示:非核心功能📝 待发布
6200 个单元测试:C 项目也能 TDD📝 待发布
7960×640 → 1280×720:全局缩放重构实录📝 待发布

👍如果你觉得有帮助,点赞 + 收藏 + 关注,三连支持作者继续写下去 🚀

http://www.jsqmd.com/news/1084906/

相关文章:

  • Switch手柄PC适配神器BetterJoy:5分钟上手完整指南
  • 深度解析unveilr:2025年高效小程序反编译解决方案
  • OpenCV实战:从基础阈值到智能分割,详解五大图像分割算法与应用
  • 如何用 dupeguru 终极指南:快速释放硬盘空间的重复文件查找工具
  • 深度解析:如何为老旧安卓设备构建高性能电视直播应用架构
  • 揭秘Il2CppDumper:Unity手游逆向工程的核心引擎深度解析
  • 如何让老旧安卓电视流畅看直播?MyTV-Android轻量级解决方案揭秘
  • PhotoGIMP终极指南:3步让GIMP界面和Photoshop一模一样
  • 如何用SMU Debug Tool终极优化AMD Ryzen处理器性能:完整调试指南
  • 070、NumPy 实战:用 NumPy 从零实现一个简单的神经网络前向传播
  • 爽翻!只需输入需求,这几款AI论文写作工具自动生成毕业论文初稿!
  • 华为EC6109系列盒子免拆焕新:海思HI3798mv200芯片通刷当贝桌面精简固件指南
  • AMD Ryzen SDT调试工具终极指南:5分钟解锁CPU隐藏性能的免费秘籍
  • GanttProject项目管理三大挑战破解指南:从混乱到高效
  • Windows右键菜单整理5步法:用ContextMenuManager打造高效工作流
  • 如何用OpenRGB统一管理所有RGB设备:告别多软件混乱的终极指南
  • MOE混合专家模型
  • ArcGIS渔网创建实战:从投影转换到精准裁剪的完整避坑指南
  • 四旋翼轨迹跟踪PID-ADRC位置+姿态轨迹跟踪、四旋翼动力学模(自抗扰ADRC与传统PID对比)
  • Obsidian Pandoc插件:打造无缝文档转换的终极解决方案
  • I3C总线协议实战:CCC命令、寄存器配置与数据传输详解
  • 扁桃体反复发炎?3个常被忽略的日常习惯可能是元凶
  • 免费开源:5分钟掌握AMD处理器深度调试的完整指南
  • 解密AMD Ryzen调试神器:5大突破性功能实战指南
  • Scannet数据集高效获取指南:从官方脚本到网盘备份
  • 告别网盘下载烦恼:LinkSwift 直链助手让你的下载体验飞起来
  • 告别网盘限速:LinkSwift浏览器脚本终极下载解决方案
  • SMU Debug Tool深度解析:AMD Ryzen处理器底层调试与性能优化实战指南
  • [Common 17-55] ‘set_property‘ expects at least one object
  • CADENCE 17.4进阶应用:高效构建BUS总线网络与差分信号设计