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

OSG编程指南<十三>:OSG渲染状态

1、前言

  在 OSG 中存在两棵树,即场景树和渲染树。渲染树是一棵以 StateSet 和 RenderLeaf
为节点的树,它可以做到 StateSet 相同的 RenderLeaf 同时渲染而不用切换 OpenGL状态,并且做到尽量少但在多个不同 State 间切换。渲染树在 CullVisitor 的 cull 过程中逐渐创建。后面所讲到的纹理、光照及材质,都是关于渲染状态的控制。

2、渲染状态

  OSG 支持绝大部分的 OpenGL 固定功能管道(fixed function pipeline)渲染,如 Alpha 检验、Blending融合、剪切平面、颜色蒙板、面拣选(face culling)、深度和模板检验、雾效、点和线的光栅化(rasterization)等。OSG 的渲染状态也允许应用程序指定顶点着色(vertex shader)和片段着色(fragment shader)。

2.1 osg::StateSet 类

  osg::StateSet 类派生自 osg::Referenced,以实现更好的数据共享。也就是说,共享同一个 osg::StateSet的 osg::Node 或 osg::Drawable 类不需要额外的代码来清理其内存空间。

  用户的应用程序需要在 osg::StateSet 中设置渲染状态。可以将 StateSet 关联到场景图形中的任意一个节点(Node)或关联到 Drawable 类。正如大部分开发者所知,OpenGL 程序的开发需要尽量使状态量的变化实现最小化,并避免冗余的状态设置,StateSet 对象能够自动实现这些优化过程。在 OSG 遍历整个场景图形时,StateSet 类会对 OpenGL 的状态属性堆栈进行管理。因此,用户程序可以对不同的场景图形子树作不同的状态设置。在每个子树的遍历过程中,OSG 将会高效地执行保存和恢复渲染状态的操作。用户需要尽量使关联到场景图形的 StateSet 最少化。StateSet 越少,内存的占用也越少,OSG在一次场景图形遍历中所耗费的工作量也越少。

  OSG 将渲染状态分成两个部分,分别为渲染属性(Attribute)和渲染模式(Mode)。渲染属性也就是控制渲染特性的状态变量,如雾的颜色或 Blend 融合函数都是 OSG 的状态属性。OSG 中的渲染模式和 OpenGL 的状态特性几乎是一一对应的,这些特性在 OpenGL 中通过函数 glEnable()和 glDisable()进行控制。用户程序可以设置模式量以允许或禁止某个功能,如纹理映射、灯光等。简单来说,渲染模式是指渲染的某个功能,而渲染属性是这个功能的控制变量和参数。如果要设置渲染状态的值,用户程序需要执行以下两步操作:

(1)为将要设置状态的 Node 或 Drawable 对象提供一个 StateSet 实例。
(2)在 StateSet 实例中设置状态的渲染模式和渲染属性。

直接从某个 Node 或 Drawable 对象中获得一个 StateSet 实例可以使用下面的方法:

osg::StateSet* state = obj->getOrCreateStateSet();

  在上面的程序段中,obj 是一个 Node 或 Drawable 实例;getOrCreateStateSet()是这些类定义的方法,这个方法返回一个指向 StateSet 实例的指针,该实例属于 obj。如果 obj 还没有设置过与之关联的StateSet,那么这个方法返回一个新指针并将其关联到 obj 上。

  StateSet 继承自 Referenced 类。与 StateSet 关联的 Node 或 Drawable 类内部使用 ref_ptr<>来引用StateSet 实例,因此,不是长时间引用 StateSet 的情况下,也可以使用标准 C++指针来定义 state。如果state 变量是某个函数内的局部变量,且应用程序不会长时间引用这个StateSet,那么上面代码的使用是完全正确的。上面代码中的 state 变量指向该 obj 对象的 StateSet 指针,当应用程序获得了一个 StateSet的指针时,就可以设置属性和模式了。

2.2 渲染属性和渲染模式

  OSG 为每个状态属性定义了不同的类,以便应用程序采用。所有的属性类均继osg::StateAttribute,osg::StateAttribute 类是一个无法直接实例化的虚基类。

  OSG 将所有的属性和模式分为两大部分,即纹理(texture)和非纹理(non-texture)。本节将主要探讨非纹理渲染状态的设置。纹理渲染状态的设置将在后面的章节讨论。OSG 之所以为纹理属性的设置提供不同的接口,主要是因为纹理属性需要特别为多重纹理设置纹理单元(texture unit)。

1.设置渲染属性(Attribute)

  如果要设置一项属性,首先需要将修改的属性类实例化,设置该类的数值,然后用 osg::StateSet::setAttribute()将其关联到 StateSet。下面的代码段用于实现面剔除(face culling)的属性:

//获取变量 geom 的 StateSet 指针
osg::StateSet*state = geom->getOrCreateStateSet();
//创建并添加 CullFace 属性类
osg::CullFace*cf = new osg::CullFace( osg::CullFace::BACK );
state->setAttribute(cf);

  在上面的代码段中,geom 是一个 Geometry 几何类对象(当然也可以是任何其他派生自 Drawable和 Node 的对象),获取 geom 的 StateSet 指针后,代码创建了一个新的 osg::CullFace 对象,并将其关联到状态量。

2.设置渲染模式(Mode)
  用户可以使用 osg::StateSet::setMode()设置允许或禁止某种模式。例如,下面的代码将打开雾效模式的许可:

//获取一个 StateSet 实例
osg::StateSet*state = geom->getOrCreateStateSet();
//允许这个 StateSet 的雾效模式
state->setMode( GL_FOG, osg::StateAttribute::ON );

setMode()的第一个输入参数可以是任何一个在 glEnable()或 glDisable()中合法的 OpenGL 枚举量Glenum;第二个输入参数可以是 osg::StateAttribute::ON 或 osg::StateAttribute::OFF。事实上,这里用到了位屏蔽技术,这个技术将在后面讲解继承状态时说明。

3.设置渲染属性和模式

  OSG 提供了一个简单的、可以同时设置属性和模式的单一函数接口。在很多情况下,属性和模式之间都存在显著的关系。例如,CullFace 属性的对应模式为 GL_CULL_FACE。如果要将某个属性关联到一个 StateSet,同时要求打开其对应模式的许可,可以使用osg::StateSet::setAttributeAndModes()方法。

下面的代码段将关联 Blend 融合检验的属性,同时许可颜色融合模式。

//创建一个 BlendFunc 属性
osg::BlendFunc*bf = new osg::BlendFunc();
//关联 BlendFunc 并许可颜色融合模式
state->setAttributeAndModes( bf );

  setAttributeAndModes()的第二个输入参数用于允许或禁止第一个参数中渲染属性对应的渲染模
式,其默认值为 ON。这样,用户的应用程序只需用一个函数就可以方便地指定某个渲染属性,并许可其对应的渲染模式。

2.3 状态继承

  当读者设置节点的渲染状态时,这个状态将被赋予当前的节点及其子节点。如果子节点对同一个渲染状态设置了不同的属性参数,那么新的子节点状态参数将会覆盖原有的。也就是说,默认情况下子节点可以改变自身的某个状态参数或者继承父节点的同一个状态。

  状态继承特性在许多情况下都非常实用,但有时渲染可能需要更多特性。假设场景图形中有一个包含了实体多边形几何体的节点,如果要以线框模式来渲染场景图形,读者的程序就需要覆盖这种多边形渲染模式状态,而不论它出现在什么位置。OSG 允许用户根据场景图形中任意位置的渲染属性和模式需求单独改变原有的状态继承特性。可以选择以下几种枚举形式。

osg::StateAttribute::OVERRIDE:如果将一个渲染属性和模式设置为 OVERRIDE,那么所有的子节点都将继承这一属性或模式,子节点对它们的更改将会无效。
osg::StateAttribute::PROTECTED:这种形式可以视为 OVERRIDE 的一个例外。凡是设置为PROTECTED 的渲染属性或模式,均不会受到父节点的影响。
osg::StateAttribute::INHERIT:这种模式强制子节点继承父节点的渲染状态,其效果是子节点
的渲染状态被解除,而使用父节点的状态替代。

  读者可以对这些参数进行位或叠加操作,然后再作为 setAttribute()和 setMode()建立一个场景图形和 setAttributeAndModes()的第二个参数输入。下面的代码段将强制使用线框模式渲染场景图形:

//获取根节点的渲染状态 StateSet
osg::StateSet*state = root->getOrCreateStateSet();
//创建一个 PolygonMode 渲染属性
osg::PolygonMode*pm = new osg::PolygonMode)
osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE );
//强制使用线框渲染
state->setAttributeAndModes( pm,osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE );

  使用 PROTECTED 参量可以保证父节点的渲染状态不会覆盖子节点的渲染状态。例如,读者可能创建了一个发光的场景,其中包含有使用亮度照明的光源几何体,如果其父节点禁用了光照,那么光源几何体的渲染将会出错。这时,对光源几何体的 GL_LIGHTING 渲染状态使用 PROTECTED 就可以保证它依然可用。

3、渲染状态示例

3.1 效果

在这里插入图片描述

3.2 源码

// TestOSGProject.cpp : 此文件包含 "main" 函数。程序执行将在此处开始并结束。#include <windows.h>
#include <osgViewer/Viewer>
#include <iostream>
#include <osg/Node>
#include <osg/Geode>
#include <osg/Geometry>
#include <osg/Group>
#include <osg/Billboard>
#include <osg/Texture2D>
#include <osg/Image>
#include <osg/Vec3>
#include <osg/Vec2>
#include <osg/PositionAttitudeTransform>
#include <osg/MatrixTransform>
#include <osgDB/ReadFile>
#include <osgUtil/Optimizer>
#include <osg/ClipNode>
#include <osgViewer/ViewerEventHandlers> //事件监听
#include <osgGA/StateSetManipulator> //事件响应类,对渲染状态进行控制
#include <osgUtil/Simplifier> //简化几何体
#include <osgUtil/DelaunayTriangulator> 
#include <osgUtil/TriStripVisitor>
#include <osgUtil/SmoothingVisitor>#pragma comment(lib, "OpenThreadsd.lib")
#pragma comment(lib, "osgd.lib")
#pragma comment(lib, "osgDBd.lib")
#pragma comment(lib, "osgUtild.lib")
#pragma comment(lib, "osgGAd.lib")
#pragma comment(lib, "osgViewerd.lib")
#pragma comment(lib, "osgTextd.lib")osg::ref_ptr<osg::Node> createClipNode(osg::ref_ptr<osg::Node> subgraph)
{osg::ref_ptr<osg::Group> root = new osg::Group();osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet();//多边形线形绘制模式,正面和反面都绘制osg::ref_ptr<osg::PolygonMode> polymode = new osg::PolygonMode();polymode->setMode(osg::PolygonMode::FRONT_AND_BACK, osg::PolygonMode::LINE);//启用多边形线形绘制模式,并指定状态继承属性为 OVERRIDEstateset->setAttributeAndModes(polymode, osg::StateAttribute::OVERRIDE | osg::StateAttribute::ON);//多边形线形绘制节点osg::ref_ptr<osg::Group> wireframe_subgraph = new osg::Group;//设置渲染状态wireframe_subgraph->setStateSet(stateset.get());wireframe_subgraph->addChild(subgraph.get());root->addChild(wireframe_subgraph.get());osg::ref_ptr<osg::MatrixTransform> transform = new osg::MatrixTransform;//更新回调,实现动态裁剪osg::ref_ptr<osg::NodeCallback> nc = new osg::AnimationPathCallback(subgraph->getBound().center(),osg::Vec3(0.0f, 0.0f, 1.0f), osg::inDegrees(45.0f));transform->setUpdateCallback(nc.get());//创建裁剪节点osg::ref_ptr<osg::ClipNode> clipnode = new osg::ClipNode();osg::BoundingSphere bs = subgraph->getBound();bs.radius() *= 0.4f;//设置裁剪节点的包围盒osg::BoundingBox bb;bb.expandBy(bs);//根据前面指定的包围盒创建 6 个裁剪平面clipnode->createClipBox(bb);//禁用拣选clipnode->setCullingActive(false);transform->addChild(clipnode.get());root->addChild(transform.get());//创建未被裁剪的节点osg::ref_ptr<osg::Group> clippedNode = new osg::Group;clippedNode->setStateSet(clipnode->getStateSet());clippedNode->addChild(subgraph.get());root->addChild(clippedNode.get());return root.get();
}int main()
{//添加到根节点osg::ref_ptr<osg::Group> root = new osg::Group();osg::ref_ptr<osg::Node> childNode = osgDB::readNodeFile("cessna.osg");osg::ref_ptr<osg::Node> node = createClipNode(childNode.get());root->addChild(node.get());//优化场景数据osgUtil::Optimizer optimizer;optimizer.optimize(root.get());osg::ref_ptr<osgViewer::Viewer> viewer = new osgViewer::Viewer();//方便查看在多边形之间切换,以查看三角网viewer->addEventHandler(new osgGA::StateSetManipulator(viewer->getCamera()->getOrCreateStateSet()));viewer->addEventHandler(new osgViewer::StatsHandler());viewer->addEventHandler(new osgViewer::WindowSizeHandler());viewer->setSceneData(root.get());viewer->setUpViewInWindow(600, 600, 1000, 800);return viewer->run();
}

相关文章:

OSG编程指南<十三>:OSG渲染状态

1、前言 在 OSG 中存在两棵树&#xff0c;即场景树和渲染树。渲染树是一棵以 StateSet 和 RenderLeaf 为节点的树&#xff0c;它可以做到 StateSet 相同的 RenderLeaf 同时渲染而不用切换 OpenGL状态&#xff0c;并且做到尽量少但在多个不同 State 间切换。渲染树在 CullVisito…...

不同路径 II(力扣LeetCode)动态规划

不同路径 II 题目描述 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish”&#xff09;。 现在考虑网格中有障碍物。…...

探索深度学习:从理论到实践的全面指南

探索深度学习&#xff1a;从理论到实践的全面指南 摘要&#xff1a; 本文旨在提供一个关于深度学习的全面指南&#xff0c;带领读者从理论基础到实践应用全方位了解这一技术。我们将介绍深度学习的历史、基本原理、常用算法和应用场景&#xff0c;并通过Python代码示例和Tens…...

统计二叉树中的伪回文路径 : 用位运用来加速??

题目描述 这是 LeetCode 上的 「1457. 二叉树中的伪回文路径」 &#xff0c;难度为 「中等」。 Tag : 「DFS」、「位运算」 给你一棵二叉树&#xff0c;每个节点的值为 1 到 9 。 我们称二叉树中的一条路径是 「伪回文」的&#xff0c;当它满足&#xff1a;路径经过的所有节点值…...

【数据结构】树与二叉树(廿四):树搜索指定数据域的结点(算法FindTarget)

文章目录 5.3.1 树的存储结构5. 左儿子右兄弟链接结构 5.3.2 获取结点的算法1. 获取大儿子、大兄弟结点2. 搜索给定结点的父亲3. 搜索指定数据域的结点a. 算法FindTargetb. 算法解析c. 代码实现a. 使用指向指针的指针b. 直接返回找到的节点 4. 代码整合 5.3.1 树的存储结构 5.…...

vue3怎么提升效率的?为什么vue3比vue2快?效率提升主要在哪些方面?

官方文档中说vue3在 客户端渲染效率比vue2提升了1.3~2倍&#xff0c; SSR渲染效率比vue2提升了2~3倍&#xff0c;那么究竟是怎么提升的呢&#xff1f; 一、静态提升 在 vue3项目中的package.json文件中&#xff0c;可以看到这个 vue/compiler-sfc&#xff0c;它是用来解析(.v…...

C语言文件操作 | 文件分类、文件打开与关闭、文件的读写、文件状态、文件删除与重命名、文件缓冲区

欢迎关注博主 Mindtechnist 或加入【Linux C/C/Python社区】一起学习和分享Linux、C、C、Python、Matlab&#xff0c;机器人运动控制、多机器人协作&#xff0c;智能优化算法&#xff0c;滤波估计、多传感器信息融合&#xff0c;机器学习&#xff0c;人工智能等相关领域的知识和…...

从零开始的c语言日记day37——数组指针练习

一、 取地址数组储存在了*p里&#xff0c;里面储存的是整个数组的地址但本质也是第一个元素的地址解引用后1为4个字节所以就可以打印数组了。但一般不用这种方法 这样更方便一些 打印多维数组 如果不用这样传参&#xff0c;用指针传参怎么做呢&#xff1f; Main里函数的arr表示…...

codeforces 1851F

题目链接 题目大意&#xff1a;给你一个长度为n的数组a, 和一个整数k(2<n<2e5, k<30, a[i]<pow(2,k))。 任选一个x&#xff0c;求(a[i] ^ x) & (a[j] ^ x) 的最大值(1<i,j<n, i!j, x<pow(2,k))。 由于中间有个&&#xff0c;所以我们要求两个数最高…...

js把格式为YYYY-MM-DD HH:mm:ss的时间转换为UTC时间ISO 8601格式

// 要转换的日期字符串 const inputDate 2023-11-25 14:54:01; // 将日期字符串转换为Date对象 const dateObj new Date(inputDate); // 获取时间戳&#xff08;毫秒&#xff09; const timestamp dateObj.getTime(); // 转换格式 const outputDate new Date(tim…...

使用 Java 来读取 Excel 文件,检查每一行中的 URL,并将不符合条件的行标记为红色

-- 日、时、分、秒&#xff0c;这是计时的单位&#xff0c;惜时就应该惜日、惜时、惜分、惜秒。 用 Java 来读取 Excel 文件&#xff0c;检查每一行中的 URL&#xff0c;并将不符合条件的行标记为红色。以下是一个简单的示例&#xff0c;使用 Apache POI 进行 Excel 操作&#…...

雷达公式实现(matlab)

雷达公式实现 代码来源&#xff1a;《雷达系统分析与设计(MATLAB版)(第三版)》 function [snr] radar_eq(pt,freq,g,sigma,b,nf,loss,range) % This program implements Eq.(1.63) %% Inputs:% pt——峰值功率&#xff0c;W% freq——雷达中心频率&#xff0c;Hz% g——天线…...

CMake构建一个转换为3d tile的开源代码成功

之前CMake构建一个转换为3d tile的开源代码&#xff0c;生成解决方案之后&#xff0c;从VS2019打开&#xff1b; 总是报一个错误&#xff0c;跟 mocs_compilation_Debug.cpp 这个QT相关文件有关&#xff0c;它生成的obj&#xff0c;总是报模块计算机x64和目标计算机x86冲突&am…...

Java线程通信

线程通信 案例 package com.itheima.d4;public class ThreadTest {public static void main(String[] args) {Desk desk new Desk();//创建3个生产者线程new Thread(() -> {while (true) {desk.put();}}, "厨师1").start();new Thread(() -> {while (true) {…...

计算4人队形的最可能分布

2 2 2 1 2 2 2 2 2 1 2 2 2 2 2 1 2 2 3 3 3 x 3 3 2 2 2 1 2 2 2 2 2 1 2 2 在6*6的平面上2个点随机分布&#xff0c;有3种分布方式&#xff0c;2a1&#xff0c;2a2&#xff0c;2a3&#xff0c;占比为1&#xff1a;5&#xff1a;1. 3 3 …...

如何解决 Java 中的 IllegalArgumentException 异常?

非法参数异常&#xff08;IllegalArgumentException&#xff09;的抛出是为了表明一个方法被传递了一个非法参数。该异常扩展了 RuntimeException 类&#xff0c;因此属于在 Java 虚拟机&#xff08;JVM&#xff09;运行期间可能抛出的异常。它是一种未检查异常&#xff0c;因此…...

Vue 双向数据绑定

之前通过v-bind来完成的数据绑定&#xff0c;属性值和表达式进行绑定&#xff0c;表达式的值发生变化了属性值也跟着发生变化。 单向数据绑定&#xff1a; <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>首页</titl…...

电脑开机过程中,程序的启动的顺序是怎么样的?

电脑的启动过程涉及多个步骤,程序按照特定的顺序启动。这个过程通常如下: 电源开启: 当你按下电源按钮时,电源供应器(PSU)开始向电脑的各个组件供电。 自检加电(POST): 这是电脑启动过程的第一步。在这个阶段,基本输入输出系统(BIOS)或统一可扩展固件接口(UEFI)执行…...

JSON详细教程

&#x1f60a;JSON详细教程 &#x1f6a9;JSON简介☃️JSON语法规则&#x1f50a;JSON和JavaScript对象的区别 ☃️JSON数据类型字符串&#x1f50a;数字&#x1f50a;布尔值&#x1f50a;数组&#x1f50a;对象&#x1f50a;Null ☃️JSON对象&#x1f50a;访问JSON对象的值&a…...

DSP介绍及CCS

文章目录 CCS版本编译器CCS使用注意严禁中文 CCS的基本操作新建工程导入现有工程调整字体的大小工程界面恢复标签的使用 仿真盒小虫子进入在线Debug 仿真器芯片TMS320F28355基本介绍特性 DSP中特殊指令dsp指令中的EALLOW EDIS CCS TI官网 版本 CCS版本&#xff1a; CCS8.3.1…...

Vim 调用外部命令学习笔记

Vim 外部命令集成完全指南 文章目录 Vim 外部命令集成完全指南核心概念理解命令语法解析语法对比 常用外部命令详解文本排序与去重文本筛选与搜索高级 grep 搜索技巧文本替换与编辑字符处理高级文本处理编程语言处理其他实用命令 范围操作示例指定行范围处理复合命令示例 实用技…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路

进入2025年以来&#xff0c;尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断&#xff0c;但全球市场热度依然高涨&#xff0c;入局者持续增加。 以国内市场为例&#xff0c;天眼查专业版数据显示&#xff0c;截至5月底&#xff0c;我国现存在业、存续状态的机器人相关企…...

【大模型RAG】Docker 一键部署 Milvus 完整攻略

本文概要 Milvus 2.5 Stand-alone 版可通过 Docker 在几分钟内完成安装&#xff1b;只需暴露 19530&#xff08;gRPC&#xff09;与 9091&#xff08;HTTP/WebUI&#xff09;两个端口&#xff0c;即可让本地电脑通过 PyMilvus 或浏览器访问远程 Linux 服务器上的 Milvus。下面…...

大语言模型如何处理长文本?常用文本分割技术详解

为什么需要文本分割? 引言:为什么需要文本分割?一、基础文本分割方法1. 按段落分割(Paragraph Splitting)2. 按句子分割(Sentence Splitting)二、高级文本分割策略3. 重叠分割(Sliding Window)4. 递归分割(Recursive Splitting)三、生产级工具推荐5. 使用LangChain的…...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

Nginx server_name 配置说明

Nginx 是一个高性能的反向代理和负载均衡服务器&#xff0c;其核心配置之一是 server 块中的 server_name 指令。server_name 决定了 Nginx 如何根据客户端请求的 Host 头匹配对应的虚拟主机&#xff08;Virtual Host&#xff09;。 1. 简介 Nginx 使用 server_name 指令来确定…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序

一、开发环境准备 ​​工具安装​​&#xff1a; 下载安装DevEco Studio 4.0&#xff08;支持HarmonyOS 5&#xff09;配置HarmonyOS SDK 5.0确保Node.js版本≥14 ​​项目初始化​​&#xff1a; ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

Java线上CPU飙高问题排查全指南

一、引言 在Java应用的线上运行环境中&#xff0c;CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时&#xff0c;通常会导致应用响应缓慢&#xff0c;甚至服务不可用&#xff0c;严重影响用户体验和业务运行。因此&#xff0c;掌握一套科学有效的CPU飙高问题排查方法&…...

七、数据库的完整性

七、数据库的完整性 主要内容 7.1 数据库的完整性概述 7.2 实体完整性 7.3 参照完整性 7.4 用户定义的完整性 7.5 触发器 7.6 SQL Server中数据库完整性的实现 7.7 小结 7.1 数据库的完整性概述 数据库完整性的含义 正确性 指数据的合法性 有效性 指数据是否属于所定…...

【LeetCode】3309. 连接二进制表示可形成的最大数值(递归|回溯|位运算)

LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 题目描述解题思路Java代码 题目描述 题目链接&#xff1a;LeetCode 3309. 连接二进制表示可形成的最大数值&#xff08;中等&#xff09; 给你一个长度为 3 的整数数组 nums。 现以某种顺序 连接…...