【Unity3D】选中物体描边特效
1 前言
描边的难点在于如何检测和识别边缘,当前实现描边特效的方法主要有以下几种:
1)基于顶点膨胀的描边方法
在 SubShader 中开 2 个 Pass 渲染通道,第一个 Pass 通道渲染膨胀的顶点,即将顶点坐标沿着法线方向向外扩展,并使用纯色给扩展后的顶点着色,第二个 Pass 通道渲染原顶点,并覆盖第一个 Pass 通道渲染的内部。
该方案实现简单,算法效率高,但是对于拐角较大的两个面交界处(法线突变较大处),会出现描边断裂,并且描边的宽度会受到透视投影影响。
基于模板测试和顶点膨胀的描边方法解决了描边断裂和描边宽度受透视影响问题。
2)基于法线和视线的描边方法
对于物体的任意一顶点,判断视线向量(该点指向相机的向量)与该点法线向量的夹角(记为 θ)是否近似 90 度,如果近似 90 度,就将该点识别为边缘,并进行边缘着色。实际应用中,通常采用模糊描边着色法,而不是阈值法,即将 sin(θ) 作为该点渲染描边色的强度。
该方案属于内描边,实现简单,算法效率高,但是物体中间 θ 值近似 90 度的地方(凹陷处)也会被描边。
3)基于屏幕纹理的描边方法
对渲染后的屏幕纹理进行二次渲染,根据像素点周围颜色的差异判断是否是物体边缘像素,如果是边缘,需要重新进行边缘着色。判断边缘的具体做法是:对该像素点周围的像素点的亮度进行卷积运算,得到该点的梯度(反映该点附近亮度突变的强度),根据梯度阈值判断该点是否是边缘。
该方案属于内描边,实现有一定难度,算法效率一般,算法依赖梯度阈值,并且受颜色、光照、阴影等影响较大如:地面的影子可能会被描边。
案例见→边缘检测特效。
4)基于深度和法线纹理的描边方法
对渲染后的屏幕纹理进行二次渲染,根据像素点周围深度和法线的差异判断是否是物体边缘像素,如果是边缘,需要重新进行边缘着色。判断边缘的具体做法是:对该像素点周围的像素点的深度和法线进行卷积运算,得到该点的梯度(反映该点附近深度和法线突变的强度),根据梯度阈值判断该点是否是边缘。
该方案属于内描边,效果较好,实现较难,算法依赖梯度阈值。
案例见→基于深度和法线纹理的边缘检测方法。
5)基于模板纹理模糊膨胀的描边方法
首先使用纯色对选中的物体进行渲染,得到模板纹理,接着对模板纹理进行模糊处理,使模板颜色往外扩,得到模糊纹理,再根据模板纹理和模糊纹理对所有物体重新渲染,渲染规则:如果该像素点在模板纹理内部,就渲染原色,如果在模板纹理外部,就根据模糊纹理的透明度判断渲染原色还是模糊纹理色。
该方案属于外描边,效果较好,实现较难,但算法不依赖阈值。
本文代码资源见→Unity3D选中物体描边特效。
2 基本原理
本文采用基于模板纹理模糊膨胀的描边方法,本节将通过图文详细介绍该算法的原理。
1)原图
2)模板纹理
说明:清屏颜色为 (0, 0, 0, 0),后面会用到 。通过 Graphics.ExecuteCommandBuffer(commandBuffer) 对选中的物体进行渲染,得到模板纹理。
3)模糊纹理
说明:通过对模板纹理进行模糊处理, 使模板颜色向外扩展,得到模糊纹理,外扩的部分就是需要描边的部分。
4)合成纹理
说明:根据模板纹理和模糊纹理对所有物体重新渲染,渲染规则:如果该像素点在模板纹理内部,就渲染原色,如果在模板纹理外部,就根据模糊纹理的透明度判断渲染原色还是模糊纹理色,如下:
// 由于模糊纹理的外部清屏颜色是(0, 0, 0, 0), blur.a=0, 因此模糊纹理的外部也会被渲染为原色
color.rgb = lerp(source.rgb, blur.rgb, blur.a); // lerp(a,b,x)=(1-x)*a+x*b
color.a = source.a;
5)描边颜色和宽度渐变
描边颜色由模板颜色决定,通过设置模板颜色随时间变化,实现描边颜色渐变,通过设置模板透明度随时间变化,实现描边在出现和消失,视觉上感觉描边在扩大和缩小。
fixed4 frag(v2f i) : SV_Target // 片段着色器
{
float t1 = sin(_Time.z); // _Time = float4(t/20, t, t*2, t*3)
float t2 = cos(_Time.z);
// 描边颜色随时间变化, 描边透明度随时间变化, 视觉上感觉描边在膨胀和收缩
return float4(t1 + 1, t2 + 1, 1 - t1, 1 - t2);
}
渐变模板图如下:
渐变描边效果如下:
6)缺陷
如果描边的物体存在重叠,由于所有物体共一个模板纹理,将存在描边消融现象。
模板纹理如下:
描边消融如下:
可以看到,正方体、球体、胶囊体、圆柱体下方及人体都没有描边特效,因为它们在模板纹理的内部,被消融掉了。
3 代码实现
OutlineEffect.cs
using System;
using UnityEngine;
using UnityEngine.Rendering;
public class OutlineEffect : MonoBehaviour {
public static Action<CommandBuffer> renderEvent; // 渲染事件
public float offsetScale = 2; // 模糊处理像素偏移
public int iterate = 3; // 模糊处理迭代次数
public float outlineStrength = 3; // 描边强度
private Material blurMaterial; // 模糊材质
private Material compositeMaterial; // 合成材质
private CommandBuffer commandBuffer; // 用于渲染模板纹理
private RenderTexture stencilTex; // 模板纹理
private RenderTexture blurTex; // 模糊纹理
private void Awake() {
blurMaterial = new Material(Shader.Find("Custom/Outline/Blur"));
compositeMaterial = new Material(Shader.Find("Custom/Outline/Composite"));
commandBuffer = new CommandBuffer();
}
private void OnRenderImage(RenderTexture source, RenderTexture destination) {
if (renderEvent != null) {
RenderStencil(); // 渲染模板纹理
RenderBlur(source.width, source.height); // 渲染模糊纹理
RenderComposite(source, destination); // 渲染合成纹理
} else {
Graphics.Blit(source, destination); // 保持原图
}
}
private void RenderStencil() { // 渲染模板纹理
stencilTex = RenderTexture.GetTemporary(Screen.width, Screen.height, 0);
commandBuffer.SetRenderTarget(stencilTex);
commandBuffer.ClearRenderTarget(true, true, Color.clear); // 设置模板清屏颜色为(0,0,0,0)
renderEvent.Invoke(commandBuffer);
Graphics.ExecuteCommandBuffer(commandBuffer);
}
private void RenderBlur(int width, int height) { // 对模板纹理进行模糊化
blurTex = RenderTexture.GetTemporary(width, height, 0);
RenderTexture temp = RenderTexture.GetTemporary(width, height, 0);
blurMaterial.SetFloat("_OffsetScale", offsetScale);
Graphics.Blit(stencilTex, blurTex, blurMaterial);
for (int i = 0; i < iterate; i ++) {
Graphics.Blit(blurTex, temp, blurMaterial);
Graphics.Blit(temp, blurTex, blurMaterial);
}
RenderTexture.ReleaseTemporary(temp);
}
private void RenderComposite(RenderTexture source, RenderTexture destination) { // 渲染合成纹理
compositeMaterial.SetTexture("_MainTex", source);
compositeMaterial.SetTexture("_StencilTex", stencilTex);
compositeMaterial.SetTexture("_BlurTex", blurTex);
compositeMaterial.SetFloat("_OutlineStrength", outlineStrength);
Graphics.Blit(source, destination, compositeMaterial);
RenderTexture.ReleaseTemporary(stencilTex);
RenderTexture.ReleaseTemporary(blurTex);
stencilTex = null;
blurTex = null;
}
}
说明: OnRenderImage 方法是MonoBehaviour的生命周期方法,在所有的渲染完成后由 MonoBehavior 自动调用,该方法依赖相机组件,由于 OnRenderImage 在渲染后调用,因此被称为后处理操作,它是 Unity3D 特效的重要理论分支;Graphics.Blit(source, dest, material) 用于将 source 纹理按照 material 材质重新渲染到 dest;CommandBuffer 携带一系列的渲染命令,依赖相机,用来拓展渲染管线的渲染效果;OutlineEffect 脚本组件必须挂在相机上。
OutlineObject.cs
using UnityEngine;
using UnityEngine.Rendering;
public class OutlineObject : MonoBehaviour {
private Material stencilMaterial; // 模板材质
private void Awake() {
stencilMaterial = new Material(Shader.Find("Custom/Outline/Stencil"));
}
private void OnEnable() {
OutlineEffect.renderEvent += OnRenderEvent;
// _StartTime用于控制每个选中的对象颜色渐变不同步
stencilMaterial.SetFloat("_StartTime", Time.timeSinceLevelLoad * 2);
}
private void OnDisable() {
OutlineEffect.renderEvent -= OnRenderEvent;
}
private void OnRenderEvent(CommandBuffer commandBuffer) {
Renderer[] renderers = gameObject.GetComponentsInChildren<Renderer>();
foreach (Renderer r in renderers) {
commandBuffer.DrawRenderer(r, stencilMaterial); // 将renderer和material提交到主camera的commandbuffer列表进行渲染
}
}
}
说明:被选中的物体将会添加 OutlineObject 脚本组件,用于渲染选中对象的模板纹理,每个选中对象独立持有 stencilMaterial,互不干扰,描边的渐变相位(由_StartTime控制)可以由选中对象独立控制,这样每个模板的颜色就可以独立控制,从而实现每个选中对象描边各异的效果。
SelectController.cs
using System.Collections.Generic;
using UnityEngine;
public class SelectController : MonoBehaviour {
private List<GameObject> targets; // 选中的游戏对象
private List<GameObject> loseFocus; // 失焦的游戏对象
private RaycastHit hit; // 碰撞信息
private void Start() {
Camera.main.gameObject.AddComponent<OutlineEffect>();
targets = new List<GameObject>();
loseFocus = new List<GameObject>();
hit = new RaycastHit();
}
private void Update() {
if (Input.GetMouseButtonUp(0)) {
GameObject hitObj = GetHitObj();
if (hitObj == null) { // 未选中任何物体, 已描边的全部取消描边
targets.ForEach(obj => loseFocus.Add(obj));
targets.Clear();
}
else if (Input.GetKey(KeyCode.LeftControl) || Input.GetKey(KeyCode.RightControl)) {
if (targets.Contains(hitObj)) { // Ctrl重复选中, 取消描边
loseFocus.Add(hitObj);
targets.Remove(hitObj);
} else { // Ctrl追加描边
targets.Add(hitObj);
}
} else { // 单选描边
targets.ForEach(obj => loseFocus.Add(obj));
targets.Clear();
targets.Add(hitObj);
loseFocus.Remove(hitObj);
}
DrawOutline();
}
}
private void DrawOutline() { // 描边
targets.ForEach(obj => {
if (obj.GetComponent<OutlineObject>() == null) {
obj.AddComponent<OutlineObject>();
} else {
obj.GetComponent<OutlineObject>().enabled = true;
}
});
loseFocus.ForEach(obj => {
if (obj.GetComponent<OutlineObject>() != null) {
obj.GetComponent<OutlineObject>().enabled = false;
}
});
loseFocus.Clear();
}
private GameObject GetHitObj() { // 获取屏幕射线碰撞的物体
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out hit)) {
return hit.collider.gameObject;
}
return null;
}
}
说明:通过单击物体,给选中的物体添加 OutlineObject 脚本组件,再由 OutlineEffect 控制描边。按住 Ctrl 键再单击物体会追加选中,如果重复选中,会取消描边。
StencilShader.shader
Shader "Custom/Outline/Stencil"
{
Properties
{
_StartTime ("startTime", Float) = 0 // _StartTime用于控制每个选中的对象颜色渐变不同步
}
SubShader
{
Pass
{
CGPROGRAM // CG语言的开始
// 编译指令 着色器名称 函数名称
#pragma vertex vert // 顶点着色器, 每个顶点执行一次
#pragma fragment frag // 片段着色器, 每个像素执行一次
#pragma fragmentoption ARB_precision_hint_fastest // fragment使用最低精度, fp16, 提高性能和速度
// 导入头文件
#include "UnityCG.cginc"
float _StartTime;
struct a2v // 顶点函数输入结构体
{
float4 vertex: POSITION; // 顶点坐标
};
struct v2f // 顶点函数输出结构体
{
float4 pos : SV_POSITION;
};
v2f vert(a2v v) // 顶点着色器
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
return o;
}
fixed4 frag(v2f i) : SV_Target // 片段着色器
{
float t1 = sin(_Time.z - _StartTime); // _Time = float4(t/20, t, t*2, t*3)
float t2 = cos(_Time.z - _StartTime);
// 描边颜色随时间变化, 描边透明度随时间变化, 视觉上感觉描边在膨胀和收缩
return float4(t1 + 1, t2 + 1, 1 - t1, 1 - t2);
}
ENDCG // CG语言的结束
}
}
FallBack off
}
说明: StencilShader 用于渲染模板纹理,并且其颜色和透明度随时间变化,实现描边颜色渐变、宽度在膨胀和收缩效果。_StartTime 用于控制时间偏移,由物体被选中的时间决定,每个物体被选中的时间不一样,因此选中物体模板颜色各异,描边颜色也各异。
BlurShader.shader
Shader "Custom/Outline/Blur"
{
Properties
{
_MainTex ("stencil", 2D) = "" {}
_OffsetScale ("offsetScale", Range (0.1, 3)) = 2 // 模糊采样偏移
}
SubShader
{
Pass
{
ZTest Always
Cull Off
ZWrite Off
Lighting Off
Fog { Mode Off }
CGPROGRAM // CG语言的开始
#pragma vertex vert // 顶点着色器, 每个顶点执行一次
#pragma fragment frag // 片段着色器, 每个像素执行一次
#pragma fragmentoption ARB_precision_hint_fastest // fragment使用最低精度, fp16, 提高性能和速度
#include "UnityCG.cginc"
sampler2D _MainTex;
half _OffsetScale;
half4 _MainTex_TexelSize; //_MainTex的像素尺寸大小, float4(1/width, 1/height, width, height)
struct a2v // 顶点函数输入结构体
{
float4 vertex: POSITION;
half2 texcoord: TEXCOORD0;
};
struct v2f // 顶点函数输出结构体
{
float4 pos : POSITION;
half2 uv[4] : TEXCOORD0;
};
v2f vert(a2v v) // 顶点着色器
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
half2 offs = _MainTex_TexelSize.xy * _OffsetScale;
// uv坐标向四周扩散
o.uv[0].x = v.texcoord.x - offs.x;
o.uv[0].y = v.texcoord.y - offs.y;
o.uv[1].x = v.texcoord.x + offs.x;
o.uv[1].y = v.texcoord.y - offs.y;
o.uv[2].x = v.texcoord.x + offs.x;
o.uv[2].y = v.texcoord.y + offs.y;
o.uv[3].x = v.texcoord.x - offs.x;
o.uv[3].y = v.texcoord.y + offs.y;
return o;
}
fixed4 frag(v2f i) : COLOR // 片段着色器
{
fixed4 color1 = tex2D(_MainTex, i.uv[0]);
fixed4 color2 = tex2D(_MainTex, i.uv[1]);
fixed4 color3 = tex2D(_MainTex, i.uv[2]);
fixed4 color4 = tex2D(_MainTex, i.uv[3]);
fixed4 color;
// max: 2个向量中每个分量都取较大者, 这里通过max函数将模板的边缘向外扩, rgb=stencil.rgb
color.rgb = max(color1.rgb, color2.rgb);
color.rgb = max(color.rgb, color3.rgb);
color.rgb = max(color.rgb, color4.rgb);
color.a = (color1.a + color2.a + color3.a + color4.a) / 4; // 透明度向外逐渐减小
return color;
}
ENDCG // CG语言的结束
}
}
Fallback off
}
说明: BlurShader 用于渲染模糊纹理,通过对模板纹理模糊化处理,实现模板颜色外扩,外扩的部分就是需要描边的部分。
CompositeShader.shader
Shader "Custom/Outline/Composite"
{
Properties
{
_MainTex ("source", 2D) = "" {}
_StencilTex ("stencil", 2D) = "" {}
_BlurTex ("blur", 2D) = "" {}
_OutlineStrength ("OutlineStrength", Range(1, 5)) = 3
}
SubShader
{
Pass
{
ZTest Always
Cull Off
ZWrite Off
Lighting Off
Fog { Mode off }
CGPROGRAM // CG语言的开始
#pragma vertex vert // 顶点着色器, 每个顶点执行一次
#pragma fragment frag // 片段着色器, 每个像素执行一次
#pragma fragmentoption ARB_precision_hint_fastest // fragment使用最低精度, fp16, 提高性能和速度
#include "UnityCG.cginc"
sampler2D _MainTex;
sampler2D _StencilTex;
sampler2D _BlurTex;
float _OutlineStrength;
float4 _MainTex_TexelSize; //_MainTex的像素尺寸大小, float4(1/width, 1/height, width, height)
struct a2v // 顶点函数输入结构体
{
float4 vertex: POSITION;
half2 texcoord: TEXCOORD0;
};
struct v2f // 顶点函数输出结构体
{
float4 pos : POSITION;
half2 uv : TEXCOORD0;
};
v2f vert(a2v v) // 顶点着色器
{
v2f o;
o.pos = UnityObjectToClipPos(v.vertex);
o.uv = v.texcoord;
if (_MainTex_TexelSize.y < 0)
o.uv.y = 1 - o.uv.y; // 在Direct3D平台下, 如果我们开启了抗锯齿, 则_MainTex_TexelSize.y 会变成负值
return o;
}
fixed4 frag(v2f i) : COLOR // 片段着色器
{
fixed4 source = tex2D(_MainTex, i.uv);
fixed4 stencil = tex2D(_StencilTex, i.uv);
if (any(stencil.rgb))
{ // 绘制选中物体
return source;
}
else
{ // 绘制选中物体以外的图像
fixed4 blur = tex2D(_BlurTex, i.uv);
fixed4 color;
color.rgb = lerp(source.rgb, blur.rgb * _OutlineStrength, saturate(blur.a - stencil.a));
color.a = source.a;
return color;
}
}
ENDCG // CG语言的结束
}
}
Fallback Off
}
说明: CompositeShader 用于渲染合成纹理,根据模板纹理和模糊纹理对所有物体重新渲染,渲染规则:如果该像素点在模板纹理内部,就渲染原色,如果在模板纹理外部,就根据模糊纹理的透明度判断渲染原色还是模糊纹理色。
4 运行效果
单击物体选中描边,按住 Ctrl 键单击物体追加选中描边,单击地面或空白地方所有已描边物体取消描边(删除了地面的碰撞体组件)。
5 拓展
HighlightingSystem 插件也实现了基于模板纹理模糊膨胀的描边方法,插件资源在Unity3D选中物体描边特效的【Assets\Plugins\HighlightingSystem】目录下。
该插件与本文的区别在于模板纹理的渲染方式不同,本文通过 CommandBuffer 渲染模板纹理,HighlightingSystem 通过第二个相机渲染模板纹理(camera.Render() 方法),具体操作如下:在 HighlightingEffect 脚本组件的 OnPreRender 方法中,通过 Copy 主相机新生成一个相机,并将其 cullingMask 设置为 highlightingLayer(值为7),用于渲染图层为 7 的物体的模板纹理,被添加 HighlightableObject 脚本组件的物体在渲染模板纹理时其图层将会被临时更改为 7,渲染结束后又恢复原图层。
该插件存在一个缺陷,7 号图层需要预留出来,否则 7 号图层的物体周围将存在一个模糊的灰色边缘。
HighlightingSystem 插件的使用方法如下:将 HighlightingEffect 脚本组件挂在相机下,给需要描边的物体添加 SpectrumController 脚本组件。运行时,已添加 SpectrumController 脚本组件的物体将会被自动添加 HighlightableObject 脚本组件。
6 推荐阅读
声明:本文转自【Unity3D】选中物体描边特效
【Unity3D】选中物体描边特效的更多相关文章
- unity3d点击屏幕选中物体
原文 http://blog.csdn.net/mycwq/article/details/19906335 前些天接触unity3d,想实现点击屏幕选中物体的功能.后来研究了下,实现原理就是检测从 ...
- OSG描边特效osgFX::Outline的修改
对一个三维场景中的物体实现描边特效,可以参考osg范例osgoutline 这个描边特效使用了模板缓存Stencil来实现,参见源代码osgFX/Outline.cpp 使用了两个Pass 第一个Pa ...
- unity3d 游戏插件 溶解特效插件 - Dissolve Shader
unity3d 游戏插件 溶解特效插件 - Dissolve Shader 链接: https://pan.baidu.com/s/1hr7w39U 密码: 3ed2
- ThreeJs 选中物体事件
选中物体变红色demo: https://threejs.org/examples/#webgl_raycast_sprite <!DOCTYPE html> <html lang= ...
- Unity合并选中物体的Mesh
using System.Collections; using System.Collections.Generic; using UnityEngine; using UnityEditor; pu ...
- three.js使用卷积法实现物体描边效果
法线延展法 网上使用法线延展法实现物体描边效果的文章比较多,这里不再描述. 但是这种方法有个缺点:当两个面的法线夹角差别较大时,两个面的描边无法完美连接.如下图所示: 卷积法 这里使用另一种方法卷积法 ...
- unity3D 游戏物体同时绑定单击、双击事件
前言 在unity中我们常用的获取鼠标点击的方法有 在3D场景中,一般用在Update方法中,每一帧调用 void Update(){ )){ Debug.log("鼠标左键点击" ...
- Cesium源码剖析---Post Processing之物体描边(Silhouette)
Cesium在1.46版本中新增了对整个场景的后期处理(Post Processing)功能,包括模型描边.黑白图.明亮度调整.夜视效果.环境光遮蔽等.对于这么炫酷的功能,我们绝不犹豫,先去翻一翻它的 ...
- Unity3D 角色(物体) 移动方法 合集
1. 简介 在Unity3D中,有多种方式可以改变物体的坐标,实现移动的目的,其本质是每帧修改物体的position. 2. 通过Transform组件移动物体 Transform 组件用于描述物体在 ...
- Unity3d游戏角色描边
本文发布于游戏程序员刘宇的个人博客,欢迎转载,请注明来源https://www.cnblogs.com/xiaohutu/p/10834491.html 游戏里经常需要在角色上做描边,这里总结一下平时 ...
随机推荐
- java - classpath 的配置
classpath C:\Program Files\Java\jdk\jre\lib\rt.jar
- 百度网盘(百度云)SVIP超级会员共享账号每日更新(2024.01.23)
一.百度网盘SVIP超级会员共享账号 可能很多人不懂这个共享账号是什么意思,小编在这里给大家做一下解答. 我们多知道百度网盘很大的用处就是类似U盘,不同的人把文件上传到百度网盘,别人可以直接下载,避免 ...
- .NET周刊【1月第2期 2024-01-21】
国内文章 NCC Mocha v0.1.0 发布,.NET 开发的基于 OpenTelemetry 的 APM 系统 https://mp.weixin.qq.com/s/gUx-dqlYqcwgQN ...
- [转帖]AES算法(五)GCM工作模式
https://zhuanlan.zhihu.com/p/376692295 在以前介绍的基本工作模式中,ECB.CFB.OFB 三种模式可以解决 ECB 模式中相同明文生成相同密文的缺陷,CTR 又 ...
- [转帖]Web技术(六):QUIC 是如何解决TCP 性能瓶颈的?
文章目录 一.QUIC 如何解决TCP的队头阻塞问题? 1.1 TCP 为何会有队头阻塞问题 1.2 QUIC 如何解决队头阻塞问题 1.3 QUIC 没有队头阻塞的多路复用 二.QUIC 如何优化T ...
- Jmeter学习之六_进行https证书处理的工作
Jmeter 进行https证书处理的工作 背景 继续学习中,想着能够抓取一下https相关的信息 所以计划些一下处理过程 但是感觉自己这一块比较薄弱. 场景设计这一块应该是专业人去搞, 我这边先只是 ...
- [转帖]Linux查看raid1和raid10分别由哪些盘组成,在哪个槽位
查找有问题的盘 MegaCli64 -PDList -aALL |grep "Firmware state" 6个盘,2个坏了 查看raid级别和硬盘的状态 MegaCli64 - ...
- 【转贴】西数全新推出企业级金盘SSD:2.5寸U.2接口、最大7.68TB、96层TLC
西数全新推出企业级金盘SSD:2.5寸U.2接口.最大7.68TB.96层TLC https://www.cnbeta.com/articles/tech/951353.htm 硬件发展日新月异 &q ...
- Raid卡在Write back 与Write through 时的性能差异
还是读姜老师的 mysql技术内核innodb存储引擎这本书里面的内容. 之前知道raid卡的设置会影响性能, 预计也是十几倍的性能差距, 但是从来没有用数据库进行过验证 书中有针对不通raid卡的设 ...
- 查看 Oracle 数据库内 没有Primary key 类型主键的表信息
查看 Oracle 数据库内 没有Primary key 类型主键的表信息 SELECT * FROM user_tables A WHERE NOT EXISTS ( SELECT * FROM u ...