OsgEarth 加载CESIUM或ReadyMap 后,再加载模型,结果黑乎乎
最近做了一个有关地球的OsgEarth的项目发现OsgEarth 加载CESIUM或ReadyMap 后再加载模型结果黑乎乎。。。折腾数日发现了解决方法。。。原理就不说了直接贴代码/** * file ModelShaderLoader.h * brief 模型加载自建着色器工具类解决osgEarth ReadyMap / CesiumNative模型发黑问题 * details * 两套渲染环境二选一自动选择对应着色器 * 1. ReadyMap不启用GL3RealizeOperation使用osg_*别名版本shader屏幕微分法线漫反射 * 2. CesiumNative启用GL3RealizeOperation(Core Profile)不能使用osg_*别名 * 使用显式location顶点属性Node回调实时推送MVP矩阵uniform同样屏幕微分法线漫反射。 * 功能加载模型、挂载对应shader、全局缩放。 * author xxx * date 2026-09-23 * note * - 两套环境模型加载都正常着色器分支自动切换消除模型发黑 * - CesiumNative环境禁止调用setUseModelViewAndProjectionUniforms/setUseVertexAttributeAliasing会崩溃。 * warning * - CesiumNative使用NodeCallback实时更新矩阵uniform有极小额外开销 * - 着色器状态使用OVERRIDE子节点材质无法覆盖。 */ #pragma once #include QObject #include osg/ref_ptr #include osg/NodeCallback #include string class ModelShaderLoader : public QObject { Q_OBJECT public: enum class RenderEnv { ReadyMap, CesiumNative }; explicit ModelShaderLoader(QObject* parent nullptr); void setRenderEnv(RenderEnv env); /** * brief 加载模型并自动挂载对应环境的自建shader修复发黑 * param[in] filePath 模型路径 * param[in] scale 模型缩放系数 * return osg::ref_ptrosg::Node 组装完成节点失败返回nullptr */ osg::ref_ptrosg::Node loadModel(const std::string filePath, double scale); private: RenderEnv _renderEnv RenderEnv::ReadyMap; // ReadyMap 版本shader源码osg_*别名 static const char* vertSrcReadyMap; static const char* fragSrcReadyMap; // CesiumNative CoreProfile版本shader源码无osg_*别名 static const char* vertSrcCesium; static const char* fragSrcCesium; // Cesium环境回调遍历节点更新MVP uniform struct MatrixUpdateCallback; }; /** * file ModelShaderLoader.cpp * brief ModelShaderLoader实现自建shader解决osgEarth Cesium/ReadyMap模型发黑 * author xxx * date 2026-09-23 */ #include ModelShaderLoader.h #include osgViewer/Viewer #include osg/State #include osg/Texture #include osg/NodeVisitor #include osg/MatrixTransform #include osg/Uniform #include osg/Shader #include osg/Program #include osgDB/ReadFile #include iostream // ReadyMap Shaderosg_*别名 const char* ModelShaderLoader::vertSrcReadyMap R( #version 330 core in vec4 osg_Vertex; in vec4 osg_MultiTexCoord0; uniform mat4 osg_ModelViewProjectionMatrix; uniform mat4 osg_ModelViewMatrix; out vec2 vTexCoord; out vec3 vViewPos; void main() { gl_Position osg_ModelViewProjectionMatrix * osg_Vertex; vViewPos (osg_ModelViewMatrix * osg_Vertex).xyz; vTexCoord osg_MultiTexCoord0.xy; } ); const char* ModelShaderLoader::fragSrcReadyMap R( #version 330 core uniform sampler2D tex0; in vec2 vTexCoord; in vec3 vViewPos; out vec4 fragColor; void main() { vec3 dx dFdx(vViewPos); vec3 dy dFdy(vViewPos); vec3 normal normalize(cross(dx, dy)); vec3 lightDir normalize(vec3(0.6, 0.7, 0.4)); float diff max(dot(normal, lightDir),0.0); vec4 texColor texture(tex0, vTexCoord); vec3 rgb texColor.rgb * (0.3 0.7 * diff); fragColor vec4(rgb, texColor.a); } ); // CesiumNative ShaderCoreProfile无osg_* const char* ModelShaderLoader::vertSrcCesium R( #version 330 core layout(location 0) in vec3 aPos; layout(location 1) in vec2 aTexCoord; uniform mat4 uMVP; uniform mat4 uModelView; out vec2 vTexCoord; out vec3 vViewPos; void main() { gl_Position uMVP * vec4(aPos,1.0); vViewPos (uModelView * vec4(aPos,1.0)).xyz; vTexCoord aTexCoord; } ); const char* ModelShaderLoader::fragSrcCesium R( #version 330 core uniform sampler2D tex0; in vec2 vTexCoord; in vec3 vViewPos; out vec4 fragColor; void main() { vec3 dx dFdx(vViewPos); vec3 dy dFdy(vViewPos); vec3 normal normalize(cross(dx, dy)); vec3 lightDir normalize(vec3(0.6, 0.7, 0.4)); float diff max(dot(normal, lightDir),0.0); vec4 texColor texture(tex0, vTexCoord); vec3 rgb texColor.rgb * (0.3 0.7 * diff); fragColor vec4(rgb, texColor.a); } ); // Cesium 矩阵更新回调每帧遍历获取模型矩阵上传MVP struct ModelShaderLoader::MatrixUpdateCallback : public osg::NodeCallback { osg::ref_ptrosg::Uniform _uMVP; osg::ref_ptrosg::Uniform _uModelView; MatrixUpdateCallback() { _uMVP new osg::Uniform(uMVP, osg::Matrixf()); _uModelView new osg::Uniform(uModelView, osg::Matrixf()); } void operator()(osg::Node* node, osg::NodeVisitor* nv) override { osg::Matrixd MVP node-getMatrix() * nv-getViewMatrix() * nv-getProjectionMatrix(); osg::Matrixd MV node-getMatrix() * nv-getViewMatrix(); _uMVP-set(MVP); _uModelView-set(MV); traverse(node, nv); } }; ModelShaderLoader::ModelShaderLoader(QObject* parent) : QObject(parent) { } void ModelShaderLoader::setRenderEnv(RenderEnv env) { _renderEnv env; } osg::ref_ptrosg::Node ModelShaderLoader::loadModel(const std::string filePath, double scale) { osg::ref_ptrosg::Node modelNode osgDB::readNodeFile(filePath); if (!modelNode) { std::cerr [ModelShaderLoader] load model failed: filePath std::endl; return nullptr; } osg::ref_ptrosg::Program program new osg::Program; osg::ref_ptrMatrixUpdateCallback matrixCb; if (_renderEnv RenderEnv::ReadyMap) { std::cout [ModelShaderLoader] ReadyMap env, use osg_alias shader std::endl; program-addShader(new osg::Shader(osg::Shader::VERTEX, vertSrcReadyMap)); program-addShader(new osg::Shader(osg::Shader::FRAGMENT, fragSrcReadyMap)); program-addBindAttribLocation(osg_Vertex, 0); program-addBindAttribLocation(osg_MultiTexCoord0, 1); } else { std::cout [ModelShaderLoader] CesiumNative env, use core profile shader std::endl; program-addShader(new osg::Shader(osg::Shader::VERTEX, vertSrcCesium)); program-addShader(new osg::Shader(osg::Shader::FRAGMENT, fragSrcCesium)); matrixCb new MatrixUpdateCallback(); } // 遍历Geode挂载shader struct ApplyShaderVisitor : public osg::NodeVisitor { osg::Program* _prog; MatrixUpdateCallback* _mtxCb; ApplyShaderVisitor(osg::Program* p, MatrixUpdateCallback* cb) : osg::NodeVisitor(TRAVERSE_ALL_CHILDREN), _prog(p), _mtxCb(cb) {} void apply(osg::Geode geode) override { for (unsigned int i 0; i geode.getNumDrawables(); i) { osg::Drawable* draw geode.getDrawable(i); osg::StateSet* ss draw-getOrCreateStateSet(); ss-setAttributeAndModes(_prog, osg::StateAttribute::ON | osg::StateAttribute::OVERRIDE); ss-setMode(GL_LIGHTING, osg::StateAttribute::OFF | osg::StateAttribute::OVERRIDE); ss-addUniform(new osg::Uniform(tex0, 0)); if (_mtxCb) { ss-addUniform(_mtxCb-_uMVP.get()); ss-addUniform(_mtxCb-_uModelView.get()); } } } }; ApplyShaderVisitor vis(program.get(), matrixCb.get()); modelNode-accept(vis); // 外层缩放节点 绑定矩阵回调Cesium环境 osg::ref_ptrosg::MatrixTransform scaleXform new osg::MatrixTransform; scaleXform-setMatrix(osg::Matrix::scale(osg::Vec3(scale, scale, scale))); scaleXform-addChild(modelNode.get()); if (matrixCb) { scaleXform-addUpdateCallback(matrixCb.get()); } return scaleXform; } // 1. CesiumNative开启GL3RealizeOperation auto loader new ModelShaderLoader(this); loader-setRenderEnv(ModelShaderLoader::RenderEnv::CesiumNative); auto modelNode loader-loadModel(xxx.obj, 1.0); // 2. ReadyMap不开启GL3RealizeOperation打开alias开关 auto loader new ModelShaderLoader(this); loader-setRenderEnv(ModelShaderLoader::RenderEnv::ReadyMap); auto modelNode loader-loadModel(xxx.obj, 1.0);

相关新闻

RV1106电源树设计:高精度供电与信号完整性协同设计指南

RV1106电源树设计:高精度供电与信号完整性协同设计指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/24 11:41:49 阅读更多 →
ROHS分析仪选型指南:从需求到参数的全流程解析

ROHS分析仪选型指南:从需求到参数的全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/24 11:41:49 阅读更多 →
企业知识库不该只是“网盘 + 搜索”:这套系统把文档变成可问、可追溯、可复用的知识

企业知识库不该只是“网盘 + 搜索”:这套系统把文档变成可问、可追溯、可复用的知识

文档越来越多,真正的问题却不是“存在哪里”,而是:谁能看、能不能快速找到、AI 回答有没有依据、内部知识能否继续转化为内容。本项目是一套可私有化部署的多部门权限企业知识库。它用 Spring Boot React 管理文档与权限,用关键词…

2026/9/24 11:41:49 阅读更多 →

最新新闻

Foobar2000播放SACD完全指南:从插件配置到闪退排查

Foobar2000播放SACD完全指南:从插件配置到闪退排查

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/24 12:27:19 阅读更多 →
S32K148 SAI深度解析:多协议音频接口与eDMA协同设计

S32K148 SAI深度解析:多协议音频接口与eDMA协同设计

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/24 12:27:18 阅读更多 →
非接触式生命体征监测技术路线与落地场景全解析

非接触式生命体征监测技术路线与落地场景全解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/24 12:27:18 阅读更多 →
Fusion 360电路设计实战:从原理图到PCB全流程经验与技巧

Fusion 360电路设计实战:从原理图到PCB全流程经验与技巧

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/24 12:27:18 阅读更多 →
自制皮安表:跨阻放大器与自动量程的微弱电流测量方案

自制皮安表:跨阻放大器与自动量程的微弱电流测量方案

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/24 12:27:18 阅读更多 →
重装系统后恢复C盘.docx文件的实战指南

重装系统后恢复C盘.docx文件的实战指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/24 12:26:17 阅读更多 →

日新闻

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

基于YOLOv8的渔船作业监控系统:从环境搭建到边缘部署全流程

简介:这是一套面向计算机、人工智能、自动化等专业学生与教师的毕业设计级项目资源,围绕YOLOv8实现渔船作业监控系统,可用于毕设、课程设计、大作业或项目立项演示。压缩包共97个文件,约24.21MB,以70个Python源码文件为…

2026/9/24 0:00:19 阅读更多 →
单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

单细胞注释实战:基于Scanpy的标记基因与参考映射流程解析

简介:一份基于单细胞RNA测序数据的细胞类型注释算法研究Python毕业设计源码,针对计算机相关专业正在做毕设或需要项目实战的学习者,可用于课程设计与期末大作业。项目代码完整、经导师指导评审通过,可直接运行,覆盖数据…

2026/9/24 0:00:19 阅读更多 →
C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

C#源生成器实战:用增量生成器替代反射,告别AOT崩溃

第一次在项目里被反射卡住,是在一个老旧的WinForms模块里:几十个类依赖PropertyChanged通知,运行时反射读属性、发通知,每次启动慢半拍不说,一上.NET Native/AOT裁剪模式几乎全面崩盘。后来我把这段逻辑全部改成C#源生…

2026/9/24 0:00:19 阅读更多 →

周新闻

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

Flutter for OpenHarmony游戏卡片渐变背景实战:从原理到性能优化

直接铺开项目本身吧。这几个月我一直在折腾一件事:用Flutter给OpenHarmony做一款游戏集合类的App,说白了就是把若干小游戏塞进一个壳里,用统一入口分发。这个方向本身不算新鲜,真正让我花了不少心思的,是首页那堆游戏卡…

2026/9/23 4:55:02 阅读更多 →
Word表格编号全攻略:从列表编号到题注交叉引用

Word表格编号全攻略:从列表编号到题注交叉引用

写Word文档,最让人头疼的往往是那些“看起来不起眼”的小问题。比如表格编号这事:今天在表后面多加了两个空白行,明天给客户交稿前发现整个章节的编号全部错位,光是挨个改序号就能耗掉大半个下午。我前阵子帮人整理一份上百页的技…

2026/9/24 9:10:42 阅读更多 →
从第一个站到第二个站:独立开发者的静态网站选型与落地实践

从第一个站到第二个站:独立开发者的静态网站选型与落地实践

1. 项目概述1.1 核心需求解析做独立开发者这几年,说实话,第一个网站上线的那天晚上我兴奋得没睡着。但等它跑了半年,流量惨淡、功能臃肿、代码自己都懒得看第二遍之后,我才慢慢琢磨明白一个道理:第一个网站是练手&…

2026/9/23 9:53:41 阅读更多 →

月新闻

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能

持续集成 流水线自动化与 声明式交付 实践:原型怎样变成可用功能分类:[AI/大模型]细分主题:AI 增强型 CI/CD 流水线自动化与 GitOps 实践:Agent 工作流、工具调用与任务拆解:从原型到生产的验收清单很多团队在尝试用大…

2026/9/23 9:53:40 阅读更多 →
容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场

容器编排 生产环境运维与排障实战:复盘记录怎样真正派上用场分类:[工程技术]细分主题:Kubernetes 生产环境运维与排障实战:可复制的项目复盘模板与决策记录大部分团队的事故复盘报告,最后都变成了躺在 Confluence 或钉…

2026/9/23 9:53:40 阅读更多 →
容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步

容器 容器化技术与镜像安全管理:核心链路应该先拆哪一步分类:[工程技术]细分主题:Docker 容器化技术与镜像安全管理:核心链路的逐步实现与关键代码取舍面对一个积累了五六年历史包袱的单体架构应用(包含 Web 接口、后台…

2026/9/23 9:53:40 阅读更多 →