
1. 为什么OCC的WebGL案例值得花时间折腾如果你正在看OCC的WebGL案例大概率已经踩过或者即将踩到FreeType这个坑。OCCOpen CASCADE Technology本身是一套庞大的几何建模内核它的WebGL案例并不是一个开箱即用的demo而是一个需要你自己把依赖链补齐、把静态库编出来的半成品工程。很多人第一次编译的时候看到一堆undefined reference或者cannot open file freetype.lib就卡住了然后去网上搜搜到的答案要么是Linux下的要么是让你直接下预编译包但版本对不上。我这次做的事情就是把OCC WebGL案例从FreeType配置开始一路走到静态库生成完整跑通一遍。这篇文章适合两类人一类是需要在Windows环境下用Visual Studio编译OCC WebGL案例的开发者另一类是想搞清楚FreeType在OCC体系里到底扮演什么角色、为什么非要静态库不可的人。整个过程涉及源码下载、编译工具链选择、工程配置、静态库生成、以及最终和OCC案例的对接每一步我都会说清楚为什么这么做而不是只给命令。先说结论OCC WebGL案例依赖FreeType来渲染文字而FreeType在Windows下默认编出来是动态库但OCC的WebGL案例工程配置里链接的是静态库版本。如果你直接拿动态库去链接要么符号找不到要么运行时缺DLL。所以核心任务就是把FreeType编成静态库并且确保编译选项和OCC的运行时库匹配。2. FreeType在OCC WebGL案例里的真实角色2.1 文字渲染为什么绕不开FreeTypeOCC的WebGL案例本质上是一个把OCC的几何数据通过Emscripten或者原生WebGL接口渲染到浏览器里的示例。几何体本身可以用顶点和索引描述但一旦涉及到标注、尺寸文字、坐标轴标签就需要字体渲染。FreeType是一个开源的字体引擎它能读取TrueType、OpenType等字体文件把字形轮廓解析成位图或者矢量路径。OCC内部封装了一层字体管理底层调用的就是FreeType。你可以把FreeType理解成一个“字体翻译官”字体文件是二进制格式里面存的是贝塞尔曲线和控制点FreeType把这些曲线栅格化成像素点阵渲染引擎才能把文字画到屏幕上。没有它OCC的WebGL案例里所有带文字的部分都会变成空白或者方块。2.2 为什么必须是静态库而不是动态库这里有一个很实际的工程约束。OCC的WebGL案例在Windows下通常是用Visual Studio打开的工程文件里配置的附加依赖项写的是freetype.lib而且这个lib必须是静态库版本。原因有两个第一静态库在链接阶段直接把代码嵌入最终的可执行文件或模块里不依赖运行时的DLL搜索路径第二OCC的WebGL案例有时候会被编译成插件或者被其他宿主程序加载如果FreeType是动态库宿主程序的工作目录一变DLL就找不到了。我试过用动态库版本的FreeType去链接编译能过但运行的时候直接报“找不到freetype.dll”。后来把DLL复制到输出目录虽然能跑了但一旦把生成的模块放到别的目录又挂了。所以静态库是更稳妥的选择尤其是你要把编译结果分发给别人的时候。2.3 版本选择不要盲目追新FreeType的版本更新比较频繁但OCC的WebGL案例对FreeType的API调用是基于某个特定版本的。我建议用FreeType 2.10.x或者2.11.x这两个大版本在OCC的代码里兼容性最好。太新的版本比如2.13可能改了某些内部结构体或者函数签名导致OCC的封装层编译报错。我一开始用了2.13.2结果在FT_Load_Glyph的调用处报了一个参数类型不匹配换回2.11.1就正常了。提示下载FreeType源码的时候去它的官方发布页面找freetype-2.11.1.tar.gz或者对应的zip包不要用Git仓库直接clone最新代码因为开发分支的API可能不稳定。3. 从源码到静态库FreeType编译的完整链路3.1 编译环境准备VS版本和工具集的选择FreeType在Windows下的编译方式有好几种可以用Visual Studio的解决方案文件也可以用CMake生成工程还可以用MSYS2或者MinGW。我这次用的是Visual Studio 2019配合CMake因为OCC的WebGL案例本身也是VS工程工具集保持一致能避免很多运行时库冲突。具体来说你需要安装Visual Studio 2019或者2022但要注意OCC案例的工程文件可能是VS2017格式用高版本打开需要重定向CMake 3.20以上一个解压工具比如7-Zip安装VS的时候记得勾选“使用C的桌面开发”工作负载里面包含了MSVC编译器和Windows SDK。如果你只装了VS Code那是不够的因为我们需要cl.exe和link.exe。3.2 CMake配置关键开关一个都不能错把FreeType源码解压到一个没有中文和空格的路径下比如D:\dev\freetype-2.11.1。然后打开CMake GUI设置源码路径和构建路径建议在源码同级建一个build目录。点击Configure选择“Visual Studio 2019”作为生成器平台选x64。Configure完成后你会看到一堆选项。下面这几个是必须注意的选项名推荐值原因BUILD_SHARED_LIBSOFF关掉才能生成静态库CMAKE_MSVC_RUNTIME_LIBRARYMultiThreaded (/MT)和OCC案例的运行时库保持一致FT_DISABLE_ZLIBON避免额外依赖zlibOCC案例不需要压缩字体FT_DISABLE_BZIP2ON同上减少依赖链FT_DISABLE_PNGON除非你需要FreeType直接输出PNG否则关掉FT_DISABLE_HARFBUZZONHarfBuzz是复杂文本排版用的OCC案例用不到这里重点说CMAKE_MSVC_RUNTIME_LIBRARY。OCC的WebGL案例工程默认用的是/MT多线程静态运行时如果你的FreeType用的是/MD多线程DLL运行时链接的时候会报LNK2038错误提示RuntimeLibrary不匹配。我一开始没注意这个编出来的lib怎么都链接不上后来把FreeType的运行时库改成/MT才解决。注意如果你用的是CMake 3.15以下的版本可能没有CMAKE_MSVC_RUNTIME_LIBRARY这个变量那就需要手动在生成的VS工程里改每个项目的运行时库设置比较麻烦。所以建议用新一点的CMake。3.3 编译与安装生成lib文件的正确姿势Configure通过之后点击Generate然后在build目录下会生成freetype.sln。用VS打开这个解决方案选择Release配置x64平台然后右键“ALL_BUILD”项目点击生成。编译过程大概一两分钟取决于机器性能。编译成功后你会在build\Release目录下看到freetype.lib。但这个lib还不是最终可用的因为FreeType的头文件路径和lib路径需要被OCC案例引用。你可以直接把这个lib复制到一个统一的第三方库目录比如D:\dev\libs\freetype\lib然后把源码里的include目录复制到D:\dev\libs\freetype\include。这里有一个细节FreeType编译出来的lib名字可能叫freetype.lib也可能叫freetype_static.lib取决于CMake的配置。如果叫freetype_static.lib你需要把它重命名为freetype.lib因为OCC案例的工程文件里写死了这个名字。或者你也可以在OCC的工程里改附加依赖项但改工程文件容易在团队协作时出问题重命名更省事。3.4 验证静态库是否真的可用编完lib之后不要急着去编OCC案例先写一个最小的测试程序验证一下。新建一个空的C控制台项目把FreeType的include路径加到附加包含目录把lib路径加到附加库目录附加依赖项写freetype.lib。然后写一段代码#include ft2build.h #include FT_FREETYPE_H int main() { FT_Library library; FT_Error error FT_Init_FreeType(library); if (error) { return -1; } FT_Done_FreeType(library); return 0; }编译链接如果能生成exe并且运行不报错说明静态库是好的。如果报LNK2019未解析的外部符号那多半是运行时库不匹配或者lib没链接上。这一步虽然简单但能帮你提前排除掉80%的配置问题。4. 把FreeType静态库接入OCC WebGL案例4.1 OCC案例的工程结构速览OCC的WebGL案例通常位于OCC源码的samples/webgl或者类似目录下。它可能是一个Visual Studio解决方案里面包含多个项目一个核心渲染库、一个示例应用、以及一些依赖项。你需要用VS打开这个解决方案然后找到链接FreeType的那个项目。在项目属性里你需要配置三个地方C/C - 常规 - 附加包含目录加上FreeType的include路径比如D:\dev\libs\freetype\include链接器 - 常规 - 附加库目录加上FreeType的lib路径比如D:\dev\libs\freetype\lib链接器 - 输入 - 附加依赖项加上freetype.lib如果你之前编FreeType的时候用的是/MT那OCC案例的项目也要确认运行时库是/MT。在“C/C - 代码生成 - 运行时库”里查看如果是/MD改成/MT。但要注意OCC的其他依赖项可能要求/MD所以改之前先确认一下整个解决方案的运行时库设置是否统一。4.2 编译OCC案例时最常见的三个报错第一个报错是cannot open file freetype.lib。这说明链接器找不到lib文件。检查附加库目录的路径是否正确以及lib文件名是否真的是freetype.lib。有时候CMake生成的lib在build\Release下但你复制的时候漏了。第二个报错是LNK2038: 检测到RuntimeLibrary的不匹配。这就是前面说的运行时库问题。解决办法是把FreeType和OCC案例的运行时库改成一致的。如果OCC案例必须用/MD那你就得重新编一个/MD版本的FreeType静态库。虽然叫“静态库”但它仍然可以链接/MD的运行时只是运行时要带对应的DLL。第三个报错是LNK2019: 无法解析的外部符号 FT_Init_FreeType。这说明lib链接上了但符号没找到。可能的原因是lib的架构不对比如你编的是x64的lib但OCC案例是Win32的。检查平台是否一致。另一个可能是lib是动态库的导入库.lib而不是静态库。动态库的导入库里只有符号跳转没有实际代码链接静态库的时候会找不到实现。4.3 静态库生成后的目录组织建议为了让后续的编译和分发更方便我建议把FreeType的产物按下面的结构组织D:\dev\libs\freetype\ ├── include\ │ ├── ft2build.h │ └── freetype\ │ ├── freetype.h │ └── ... ├── lib\ │ └── freetype.lib └── bin\ └── (如果有动态库版本放这里但静态库方案不需要)然后在OCC案例的工程里用相对路径或者环境变量来引用这个目录。比如设置一个环境变量FREETYPE_ROOT在工程里写$(FREETYPE_ROOT)\include。这样换机器的时候只需要改环境变量不用改工程文件。5. 踩坑实录那些让我熬夜的编译问题5.1 字符集问题导致的链接错误FreeType的源码里有一些字符串处理默认用的是多字节字符集。但OCC的WebGL案例可能用的是Unicode字符集。如果两边不一致链接的时候会报一些奇怪的符号错误比如_FT_Stream_Open找不到。解决办法是在CMake配置FreeType的时候把CMAKE_MBCS或者字符集相关的选项设成和OCC一致。或者在VS里手动把FreeType项目的字符集改成“使用Unicode字符集”。我遇到过一次FreeType编出来的lib里符号是FT_Init_FreeType但OCC案例期望的是_FT_Init_FreeType带下划线前缀这就是调用约定或者字符集不匹配导致的。后来统一用Unicode字符集问题消失。5.2 预处理器定义不一致引发的诡异错误FreeType在编译的时候会根据预处理器定义来决定某些结构体的大小。比如FT_CONFIG_OPTION_USE_ZLIB这个宏如果FreeType编译时定义了但OCC案例编译时没定义那FT_FaceRec结构体的大小可能就不一样导致运行时内存错乱。这种问题不会在编译期报错而是在运行的时候崩溃非常难查。我的做法是把FreeType编译时用到的所有预处理器定义记录下来然后在OCC案例的工程里也加上同样的定义。具体来说在CMake生成的ftoption.h文件里能看到哪些宏被打开了。你可以把这个文件里的关键定义复制到OCC案例的预处理器定义里。5.3 静态库重复链接导致的符号冲突如果你的OCC案例还依赖其他库而那些库也链接了FreeType就可能出现符号重复定义。比如OCC本身可能已经带了一个FreeType的封装你又链接了一个外部的FreeType静态库链接器就会报LNK2005: FT_Init_FreeType 已经在 xxx.lib 中定义。解决办法是确认OCC案例是否真的需要外部FreeType。如果OCC自带了FreeType的源码或者预编译库那你就不需要再单独编一个。但实际情况是OCC的WebGL案例通常把FreeType作为外部依赖所以你需要把OCC自带的那个排除掉。在工程里找到链接OCC自带FreeType的地方把那个lib从附加依赖项里删掉。6. 编译成功之后验证与运行6.1 生成物的检查清单编译成功后你会在输出目录下看到OCC WebGL案例的可执行文件或者模块文件。检查以下几点文件大小是否合理如果只有几十KB可能是链接没成功只生成了一个空壳用dumpbin /dependents命令查看依赖的DLL列表确认没有freetype.dll如果生成了.lib文件用dumpbin /symbols查看是否包含FreeType的符号6.2 运行时文字渲染的验证运行OCC WebGL案例打开一个带有文字标注的模型。如果文字能正常显示说明FreeType静态库工作正常。如果文字显示为方块或者不显示可能是字体文件路径不对。OCC案例通常需要一个字体文件比如arial.ttf放在工作目录下。检查案例的配置文件或者源码里指定的字体路径。6.3 把编译结果分发给别人时的注意事项如果你要把编译好的OCC WebGL案例发给同事或者客户记得把字体文件一起打包。另外虽然FreeType是静态链接的但OCC本身可能还依赖其他DLL比如TKernel.dll、TKMath.dll等。用dumpbin /dependents查一下把需要的DLL都带上。如果不想带DLL那就得把OCC也编成静态库那是另一个大工程了。7. 关于编译选项的一些个人经验编译FreeType的时候我建议把优化选项开到/O2这样生成的静态库性能更好。但如果你在调试OCC案例可以先用/Od编一个调试版本的FreeType方便定位问题。等调试通过了再换成/O2重新编。另外FreeType的源码里有一些可选模块比如FT_CONFIG_OPTION_USE_PNG如果你不需要FreeType直接输出PNG图片就把它关掉。每多一个依赖就多一个编译和链接的坑。我一开始把能开的都开了结果光解决zlib和libpng的依赖就花了一下午。后来全部关掉只保留核心功能编译一次通过。还有一个细节FreeType的静态库在链接的时候可能需要额外的系统库比如gdi32.lib或者user32.lib。如果链接报错说找不到GetDC或者TextOut就在附加依赖项里加上这两个lib。OCC案例本身可能已经链接了这些系统库但如果你单独测试FreeType就需要自己加。最后说一个关于CMake生成器的选择。如果你用Visual Studio 2019生成器选“Visual Studio 16 2019”。如果你用2022选“Visual Studio 17 2022”。不要选“Ninja”或者“MinGW Makefiles”除非你明确知道自己在做什么。因为OCC案例是VS工程用VS生成器能保证工程文件格式一致减少不必要的麻烦。整个流程走下来最耗时的部分其实是排查运行时库不匹配和字符集不一致这两个问题。一旦这两个搞定剩下的就是按部就班的配置。我现在的习惯是每换一台机器先把FreeType的编译脚本写成一个批处理文件把CMake配置和编译命令都固化下来这样下次直接运行脚本就行不用再手动点CMake GUI。这个脚本我放在版本控制里团队里谁需要编译直接拉下来跑一遍省去了重复沟通的成本。