openFrameworks 3DPrimitivesExample 深度解析从基本几何体绘制到纹理映射与网格变形【免费下载链接】openFrameworksopenFrameworks is a community-developed cross platform toolkit for creative coding in C.项目地址: https://gitcode.com/gh_mirrors/op/openFrameworks本指南以 openFrameworks 官方示例 3DPrimitivesExample 为线索系统讲解如何在 C 创意编程中绘制六种基本 3D 形体平面、立方体、球体、二十面体球、圆柱、圆锥并掌握线框/实体渲染、法线与坐标轴可视化、分辨率控制、纹理贴图静态图片与实时摄像头画面以及基于网格mesh的顶点级变形。读完本文你将能够直接复现该示例并把它作为构建任意 3D 场景的起点。示例定位与学习目标官方 README 明确指出这个示例用于演示如何绘制基本 3D 形状draw basic 3D shapes。学习它之后你将理解如何以多种状态绘制基本 3D 形体如何更换纹理texture如何调整纹理在几何体上的映射方式mapping。示例对应的源码位于 examples/3d/3DPrimitivesExample/src共三个文件入口 main.cpp、应用类声明 ofApp.h 与核心实现 ofApp.cpp。这些几何体全部来自 openFrameworks 核心库的 3d 模块其类型定义与实现集中在 libs/openFrameworks/3d/of3dPrimitives.h 和 of3dPrimitives.cpp。运行示例与预期行为按 openFrameworks 常规流程即可运行本示例在对应平台使用 Project Generator 生成工程后编译或直接使用make等构建工具打开 Project Generator导入examples/3d/3DPrimitivesExample选择目标平台桌面版 Linux/macOS/Windows 均可并生成工程编译运行。启动后窗口由 main.cpp 创建为1024×768 的窗口模式OF_WINDOW可切换为OF_FULLSCREEN。预期看到6 个基本几何体排列成网格两行三列见ofApp.cpp中plane/box/sphere/icoSphere/cylinder/cone的setPosition调用ofApp.cpp屏幕上的文字覆盖层overlay显示操作说明提示按哪些键切换绘制状态。六种基本几何体及其特性示例在setup()中按窗口尺寸比例实例化了六种几何体ofApp.cpp几何体类型构造方式关键特征平面ofPlanePrimitiveset(width*1.5, height*1.5)可指定列数与行数支持网格变形立方体ofBoxPrimitiveset(width*1.25)六个独立侧面可单独取出操作球体ofSpherePrimitivesetRadius(width)UV 球基于经/纬线切片二十面体球ofIcoSpherePrimitivesetRadius(width)ICO 球基于面细分迭代圆柱ofCylinderPrimitiveset(width*.7, height*2.2)顶盖、底盖、柱身三段网格圆锥ofConePrimitiveset(width*.75, height*2.2)底面与锥身两部分关于 UV 球与 ICO 球的差异of3dPrimitives.h 中的注释解释得很清楚ofSpherePrimitive是基于 UV 切片的球体而ofIcoSpherePrimitive是基于面而非 UV 切片的球体。本示例同时展示两者正是为了让学习者直观对比这两种拓扑结构——在后续分解/变形模式mode 3中二者的网格操作方式也因此不同。从构造函数默认值of3dPrimitives.cpp可以看出ofPlanePrimitive默认 200×100 尺寸、6 列 3 行ofSpherePrimitive默认半径 20、分辨率 16ofIcoSpherePrimitive默认半径 20、细分迭代 2 次。渲染状态实体、线框、法线与坐标轴README 点名的核心 API 之一是通过不同的绘制方法控制同一几何体的显示形态primitive.drawWireframe();—— 仅绘制线框primitive.draw();—— 绘制实体填充表面primitive.drawNormals();—— 绘制法线primitive.drawAxes();—— 绘制坐标轴。在底层这些方法都定义在基类of3dPrimitive中of3dPrimitives.cppdrawWireframe()等价于以OF_MESH_WIREFRAME模式调用draw()而draw()默认以OF_MESH_FILL填充模式渲染最终统一交给当前渲染器执行。示例中实体与线框同时叠加渲染先以material.begin()配合ofFill()绘制填充表面再用ofNoFill()与ofSetColor(0,0,0)绘制黑色线框并通过setScale(1.01f)将线框略微放大以覆盖在实体表面之上见 ofApp.cpp 等处。这一技巧在技术美术中很常见可以让物体轮廓更清晰。法线与坐标轴的绘制发生在相机视角内ofApp.cppif (bDrawNormals) { ofSetColor(225, 0, 255); plane.drawNormals(20, bSplitFaces); // ... 其余几何体 } if (bDrawAxes) { plane.drawAxes(plane.getWidth()*.5 30); // ... 其余几何体 }drawNormals(length, bFaceNormals)的第二个参数决定绘制顶点法线还是面法线当bFaceNormals为真时代码会取三角形三个顶点坐标的平均值作为法线起点of3dPrimitives.cpp。若网格未启用法线库会输出警告drawNormals(): mesh normals are disabled。示例中按z键可切换bSplitFaces标志观察顶点法线与面法线的区别。分辨率控制setResolution 的精确语义README 强调的第三个 API 是primitive.setResolution()。示例用数字键1/2/3/4切换四档分辨率ofApp.cpp各几何体参数如下键sphereicoSphere细分迭代plane列×行cylinder半径/高度/顶盖段数cone半径/高度/顶盖段数box1403×24,2,04,1,012816×48,4,17,2,1231628×512,9,210,5,26448412×920,13,420,9,310从源码可确认每个类型的实现语义ofPlanePrimitive::setResolution(int columns, int rows)会保存分辨率并调用set()重建网格of3dPrimitives.cppofSpherePrimitive::setResolution(int res)重建 UV 球网格of3dPrimitives.cppofIcoSpherePrimitive的setResolution(iterations)传入的是细分的迭代次数0 表示不细分所以数值范围与其他类型完全不同圆柱、圆锥通过setResolution(radiusSegments, heightSegments, capSegments)分别控制三个方向的密度of3dPrimitives.cpp立方体setResolution(res)对六个面统一设置也可用setResolutionWidth/Height/Depth单独控制of3dPrimitives.h。分辨率越高网格越细腻曲面越平滑但顶点数与绘制开销也随之增加而低分辨率网格恰恰是后续顶点变形效果最可见的前提。纹理贴图静态图片与实时摄像头示例演示了两种纹理来源由左右方向键切换的mode控制mode 1加载自文件的of.png图片纹理mode 2ofVideoGrabber实时摄像头画面纹理。setup()中加载纹理并设置重复包裹模式ofApp.cpp// GL_REPEAT for texture wrap only works with NON-ARB textures // ofDisableArbTex(); texture.load(of.png); texture.getTexture().setTextureWrap( GL_REPEAT, GL_REPEAT ); vidGrabber.setup(640, 480);这里有个关键细节GL_REPEAT 纹理重复包裹只对非 ARB即标准规范化坐标纹理生效因此必须先调用ofDisableArbTex()。同时摄像头每帧调用update()刷新仅 mode 2 或程序启动前 10 秒内见 ofApp.cpp。绘制阶段通过texture.getTexture().bind()/vidGrabber.getTexture().bind()绑定纹理ofApp.cpp并在绘制结束后unbind()。纹理的映射坐标通过两类方法设定README 点名的核心 APIprimitive.mapTexCoordsFromTexture( texture.getTexture() ); // 按纹理尺寸自动映射 primitive.mapTexCoords(0, 0, 5, 5); // 手动指定 UV 范围mapTexCoordsFromTexture在底层会根据纹理是否为矩形 ARB 纹理决定使用归一化坐标tex_t/tex_u还是像素尺寸来调用mapTexCoordsof3dPrimitives.cpp。而mapTexCoords(u1,v1,u2,v2)则把网格原有的 UV 坐标整体重映射到新范围——例如mapTexCoords(0, 0, 5, 5)会让纹理在几何体表面重复 5×5 次配合GL_REPEAT包裹模式即可形成平铺效果。其实现遍历所有纹理坐标并逐点做线性映射of3dPrimitives.cpp。对于平面示例还使用了plane.resizeToTexture(texture.getTexture())/resizeToTexture(vidGrabber.getTexture(), .5)让平面尺寸与纹理长宽比匹配第二个参数是缩放系数。ofBoxPrimitive同样提供resizeToTextureof3dPrimitives.h。四种显示模式与网格级变形左右方向键在 4 个mode间循环ofApp.cppmode 0无纹理纯光照渲染mode 1绑定文件纹理of.png并对所有几何体调用mapTexCoordsFromTexturemode 2绑定摄像头纹理同理自动映射mode 3变形/分解模式——不贴图mode 1 或 3 才绑定文件纹理的判断在 ofApp.cpp但每帧从网格mesh层面操作顶点并统一执行mapTexCoords(0,0,5,5)实现 5×5 平铺。mode 3 是本示例最能体现基本几何体 可编程网格思想的部分每种几何体被以不同方式拆解见draw()中 mode 3 分支ofApp.cpp平面把网格复制到ofVboMesh deformPlane遍历每个索引将顶点 z 坐标叠加cos(planeAngleX) * 50的波动形成波浪面vert.z cos(planeAngleX) * 50;立方体通过box.getSideMesh(i)取出六个侧面对应ofBoxPrimitive::BoxSides枚举见 of3dPrimitives.h沿各自法线方向平移形成六面分离效果球体与 ICO 球用getMesh().getUniqueFaces()取出独立三角形沿面法线方向推动顶点球体用正弦函数、ICO 球用ofSignedNoise噪声形成膨胀/呼吸效果最后setFromTriangles(triangles)写回网格圆柱getTopCapMesh()/getBottomCapMesh()/getCylinderMesh()分离顶盖、底盖与柱身分别做平移与缩放动画圆锥getCapMesh()与getConeMesh()分离底面与锥身锥身持续旋转。进入 mode 3 时代码会把各几何体的绘制模式切回OF_PRIMITIVE_TRIANGLE_STRIPbox 仅支持OF_PRIMITIVE_TRIANGLES因为变形操作需要重建正确的索引结构ofApp.cpp。这也解释了z键的bSplitFaces功能将网格拆分为独立三角形以观察面法线但拆分后顶点步长信息会丢失所以按z关闭拆分时会通过setResolution重建网格。光照与材质让形体立体起来为了让几何体呈现体积感示例搭建了完整的光照环境ofApp.cpp开启平滑光照ofSetSmoothLighting(true)创建3 个点光源ofLight pointLight/pointLight2/pointLight3各配不同漫反射diffuse与高光specular颜色材质ofMaterial material设置光泽度 120注释说明 shininess 取值范围 0–128128 最亮与白色高光ofSetSphereResolution(24)提高全局球体绘制精度。update()中用三角函数驱动三个点光源沿不同轨迹运动ofApp.cpp让光照随时间变化这比静态光源更能展示曲面法线的正确性。绘制时开启深度测试ofEnableDepthTest()与光照ofEnableLighting()并按l键可切换是否绘制光源小球体本身pointLight.draw()以光源颜色着色见 ofApp.cpp。相机则通过cam.setGlobalPosition({0, 0, cam.getImagePlaneDistance(ofGetCurrentViewport())})自动对准场景ofApp.cpp并随时间让几何体绕 X/Y 轴缓慢自转rotateDeg(spinX/spinY, ...)。按住鼠标可暂停自转bMousePressed方便近距离观察某一形体。交互键位速查表README 提示参照应用内的 overlay 了解按键。综合 overlay 文本ofApp.cpp与keyPressed实现完整键位如下按键功能f切换全屏s切换实体填充绘制w切换线框绘制1/2/3/4切换四档分辨率见上文表格←/→循环切换 mode 0–3普通/文件纹理/摄像头纹理/变形n切换法线可视化z切换面拆分观察面法线mode 3 下自动关闭a切换坐标轴可视化l切换光源可视化h切换帮助文字鼠标按下暂停几何体自转注意1/2/3/4按键也会把bSplitFaces置为 false避免拆分状态干扰分辨率切换。如何在此基础上扩展该示例把 openFrameworks 的 3D 工作流完整串联起来ofNode的变换系统setPosition、rotateDeg、setScale、ofMesh的顶点/法线/纹理坐标操作、ofLight/ofMaterial的光照材质管线以及ofTexture与ofVideoGrabber的纹理来源。你可以在此基础上把 mode 3 中的变形算法换成自定义的ofSignedNoise三维噪声场做出水面或地形用ofIcoSpherePrimitive配合getUniqueFaces()做低多边形风格化模型将ofVideoGrabber换成ofVideoPlayer实现视频纹理在 3D 物体上的投影。进一步研读 libs/openFrameworks/3d/of3dPrimitives.h 中各类的完整 API如ofBoxPrimitive::setSideColor、getSideIndices、of3dPrimitive::setUseVbo等以及 examples/3d 目录下其他示例如 3DPrimitivesExample 同目录的 ofBoxExample、ofNodeExample即可把基本几何体组合成完整的 3D 创意应用。【免费下载链接】openFrameworksopenFrameworks is a community-developed cross platform toolkit for creative coding in C.项目地址: https://gitcode.com/gh_mirrors/op/openFrameworks创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考